Глобальная переменная меняет свое значение при повторном запуске цикла

У меня проблема с глобальной переменной CalibrationValue. Первоначально он имеет правильное значение и также правильно отображается на выходе. Как только функция цикла перезапускается, значение переменной оказывается неверным, хотя оно и не перезаписывается. Может кто-то мне помочь, пожалуйста? На изображении печатаются следующие значения:

  1. новое значение ИИ
  2. неверное значение калибровки (!)
  3. sumOfValues ​​(необходим для расчета калибровочного значения) правильный
  4. numValues ​​(необходимо для расчета калибровочного значения) правильно
  5. Текст и значение калибровки (правильно!)
    int op_mode_Pin = 7;                      // Цифровой входной контакт, который передает op_mode_Ardui
    int op_mode_Ardui;                        // Режим оператора Arduino
    int returnValue1 = 12;                    // Первое цифровое возвращаемое значение для передачи веса коробки
    int returnValue2 = 13;                    // Второе цифровое возвращаемое значение для передачи веса коробки
    
    int sensorPin = A0;                       // Выбираем входной контакт для потенциометра
    long weightValue = 0;                     // Переменная для хранения значения с датчика
    long calibrationValue = 0;                // Значение, которое заполняется в процессе калибровки
    int numValues = 50;                       // Количество взятых значений для расчета среднего
    int thresBoxWeight = 300;                 // Вес для разделения тяжелых и легких ящиков
    
    unsigned long sumOfValues;                // Сумма взятых значений
    unsigned long aveOfValues;                // Среднее значение
    
    char stringBuffer[30];                    // Буфер для объединения строки и переменных
    
    void setup() {
      // Инициируем последовательную связь с экраном
      Serial.begin(9600);
    
      // Инициируем некоторые переменные
      //значение веса = 0;
      //значение калибровки = 0;
      // число значений = 50;
      //thresBoxWeight = 300;
      
      // Установить входные контакты для op_mode_Ardui
      pinMode(op_mode_Pin, INPUT);            // устанавливает цифровой вывод 7 в качестве входа
      
      // Установить выходные контакты для объявления Box
      pinMode(returnValue1, OUTPUT);          // устанавливает цифровой вывод 12 в качестве выхода
      pinMode(returnValue2, OUTPUT);          // устанавливает цифровой вывод 13 в качестве выхода
     
    }
    
    void loop() {
      Serial.println(analogRead(sensorPin));
      Serial.println(calibrationValue);
      // Сброс переменных Sum и Average для следующего цикла
      sumOfValues = 0;
      aveOfValues = 0;
      op_mode_Ardui = digitalRead(op_mode_Pin);
      
      if (op_mode_Ardui == HIGH){
        for (int i=0; i<numValues; i++){
          weightValue = analogRead(sensorPin);                                                  // считываем значение с датчика
          sumOfValues = sumOfValues + weightValue;                                              // Строим сумму всех значений
        }
        Serial.println(sumOfValues);
        Serial.println(numValues);
        
        calibrationValue = sumOfValues / numValues;
        sprintf(stringBuffer, "Calibration accomplished with value: %d", calibrationValue);     // Объединяем текст и переменную для вывода
        Serial.println(stringBuffer);                                                           // вывод текста и переменной на экран
        delay(3000);
      }
    }

, 👍1


1 ответ


Лучший ответ:

1

Это:

sprintf(stringBuffer, "Calibration accomplished with value: %d", calibrationValue);

является переполнением буфера: функция sprintf() записывает 40-байтовый строку в 30-байтовый буфер. Что бы ни случилось, оно останется в памяти после того, как буфер будет перезаписан sprintf(). Видимо это оказалось codingValue. Вы должны использовать snprintf(), а не sprintf(), так как первый предназначен для предотвратить такое переполнение.

Или, что еще лучше, придерживайтесь Serial.print[ln]():

Serial.print("Calibration accomplished with value: ");
Serial.println(calibrationValue);

ОБНОВЛЕНО: вы можете заметить, что 13108 совпадает с 0x3334, который на архитектура с прямым порядком байтов хранится в памяти как 0x34 0x33. Это коды ASCII для "43", которые являются частью того, что sprintf() записал в прошлом конец буфера.

,

интересно, что переполнение буфера в stringBuffer перезаписывает переменную, определенную выше в файле. В большинстве компиляторов ваши глобальные переменные размещаются в памяти примерно в том порядке, в котором вы их объявляете. (Оптимизация компиляторов на 32- и 64-битных платформах может привести к некоторому изменению порядка, чтобы структуры переменного размера помещались в меньший объем памяти при соблюдении выравнивания по словам/длинным словам/двойному выравниванию, но большинство Arduino представляют собой 8-битные устройства, в которых нет такой вещи, как выравнивание по словам.), @Duncan C

Это сработало. Я никогда не думал, что переполнение может повлиять на глобальные переменные. Спасибо., @Gary77

На машине реального режима, такой как Arduino, переполнение буфера уничтожает все, что находится в ОЗУ. Глобальные переменные, данные в куче, что угодно. И ничего не мешает., @Duncan C

@DuncanC: я не знаю, как компоновщик определяет порядок распределения. Я попытался скомпилировать код OP с помощью avr-g++ 7.3.0, поставляемого с Arduino IDE 1.8.13, и раздел .bss содержит timer0_fract, timer0_millis, timer0_overflow_count, stringBuffer, sumOfValues, caulibationValue и Serial, в этом порядке. Если я удаляю -flto из командной строки компилятора, я получаю другое распределение, начиная с stringBuffer, aveOfValues (который был оптимизирован с помощью -flto), sumOfValues, CalibrationValue, @Edgar Bonet