Переменная, обновленная в ISR, неправильно учитывается

Я использую таймер для запуска прерывания в настраиваемые периоды времени. В этом ISR контакт переключается, а переменная uint32_t увеличивается.

В основной программе цикл while ожидает, пока эта переменная не достигнет определенного порога, чтобы затем отключить таймер с помощью выбора источника синхронизации.

while(stepcounter <= threshold) {
}
disablecounter();

Иногда (в зависимости от уровня оптимизации, наличия небольшой задержки в цикле while и т. д.) эта проверка кажется неудачной — переход к disablecounter(), хотя stepcounter не достиг порога.

Если я составлю цикл while следующим образом:

while(stepcounter <= threshold) {
}
while(stepcounter <= threshold) {
}
disablecounter();

работает нормально. Это также работает:

while(stepcounter <= threshold) {
  while(stepcounter <= threshold) {
  }
}
disablecounter();

Я предполагаю, что при сравнении быстро меняющихся 32-битных чисел с пороговым значением происходит какое-то странное поведение. Эта проблема также возникает при использовании uint16_t для счетчика шагов, но после большего количества циклов.

Для анализа я просто запускаю этот цикл снова и снова; установка stepcounter обратно на 0 и перезапуск таймера. С uint32_t обычно происходит сбой после 5-го или около того цикла. Для uint16_t происходит сбой примерно через 25 циклов.

Такое использование tempstepcounter также работает, но только при одновременном сравнении с обоими счетчиками:

while(tempstepcounter <= threshold || stepcounter <= threshold) {
  tempstepcounter = stepcounter;
}
disablecounter();

Есть идеи по этому поводу? Это потому, что 32-битные числа считываются из регистров дольше, чем время от ISR до ISR? При этом частота ISR должна быть около 32 кГц.

Код ISR:

ISR(TIMER1_COMPA_vect) {
  PORTF ^= (1 << PORTF0));
  stepcounter++;
}  

, 👍0

Обсуждение

Подсказка: Пожалуйста, добавьте объявление переменной. Это изменчиво? И защищена ли она критической секцией (т.е. без прерываний) при чтении и записи? Операции с 32-битными целыми числами не являются атомарными для микропроцессора AVR. Даже 16-битные целые числа., @Mikael Patel

stepcounter объявлен как volatile. Он записывается только (когда работает таймер) внутри ISR, но не во время цикла while() - честно говоря, не уверен, как я буду его там защищать., @towe

Как выглядит код ISR?, @Elliot Alderson

@ElliotAlderson Обновил вопрос, @towe

Возможный дубликат [Могут ли прерывания arduino возникать в середине оператора if?](https://arduinoprosto.ru/q/66026/can-arduino-interrupts-occur-in-a-middle-of-if-statement ), @Gerben

Вы также должны защитить чтение., @Mikael Patel


1 ответ


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

2

Вам нужно объявить его изменчивым в обоих местах, и отключать прерывания при его чтении. Магистраль должна считывать его побайтно, иначе между любыми двумя операциями чтения байтов может возникнуть прерывание, в результате чего результат чтения четырех байтов будет считаться мусором.

'volatile' информирует компилятор о том, что переменная может не содержать того, что было в прошлый раз (чего компилятор в противном случае мог бы ожидать, если компилируемый код не изменяет ее). читать.

Еще лучший способ защитить операции чтения, чем отключение прерываний, состоит в том, что ISR обновляет счетчик, а затем устанавливает однобайтовый флаг, указывающий на наличие нового счетчика. Основная линия будет отслеживать этот флаг и считывать счетчик только тогда, когда флаг установлен, а затем очищать флаг. Это может работать до тех пор, пока основная ветка проверяет флаг достаточно часто, чтобы он никогда не замечал флаг до тех пор, пока не произойдет следующее обновление ISR. Но если вы можете придерживаться этого ограничения, это лучше, чем отключение прерываний.

Если для отключения прерываний используется слишком много подзадач, это может отрицательно сказаться на времени отклика или даже привести к пропущенным прерываниям. Это вряд ли будет проблемой для относительно простых программ, которые большинство из нас пишет и запускает большую часть времени. Но когда вы начинаете отслеживать и контролировать более сложные процессы и более активно используете систему прерываний, вредные привычки имеют свойство возвращаться, чтобы укусить вас.

,

Да, это было в значительной степени проблемой. Я реализовал ваше предложение об однобайтовом флаге, который устанавливает ISR и запросы цикла while. Если он был установлен, tempstepcounter обновляется, будучи защищенным cli() и sei()., @towe