Переменная, обновленная в 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++;
}
@towe, 👍0
Обсуждение1 ответ
Лучший ответ:
Вам нужно объявить его изменчивым в обоих местах, и отключать прерывания при его чтении. Магистраль должна считывать его побайтно, иначе между любыми двумя операциями чтения байтов может возникнуть прерывание, в результате чего результат чтения четырех байтов будет считаться мусором.
'volatile' информирует компилятор о том, что переменная может не содержать того, что было в прошлый раз (чего компилятор в противном случае мог бы ожидать, если компилируемый код не изменяет ее). читать.
Еще лучший способ защитить операции чтения, чем отключение прерываний, состоит в том, что ISR обновляет счетчик, а затем устанавливает однобайтовый флаг, указывающий на наличие нового счетчика. Основная линия будет отслеживать этот флаг и считывать счетчик только тогда, когда флаг установлен, а затем очищать флаг. Это может работать до тех пор, пока основная ветка проверяет флаг достаточно часто, чтобы он никогда не замечал флаг до тех пор, пока не произойдет следующее обновление ISR. Но если вы можете придерживаться этого ограничения, это лучше, чем отключение прерываний.
Если для отключения прерываний используется слишком много подзадач, это может отрицательно сказаться на времени отклика или даже привести к пропущенным прерываниям. Это вряд ли будет проблемой для относительно простых программ, которые большинство из нас пишет и запускает большую часть времени. Но когда вы начинаете отслеживать и контролировать более сложные процессы и более активно используете систему прерываний, вредные привычки имеют свойство возвращаться, чтобы укусить вас.
Да, это было в значительной степени проблемой.
Я реализовал ваше предложение об однобайтовом флаге, который устанавливает ISR и запросы цикла while. Если он был установлен, tempstepcounter
обновляется, будучи защищенным cli()
и sei()
., @towe
- Проблема прерывания библиотеки MPU6050 Arduino Jeff Rowberg
- Точность синхронизации Arduino nano
- Мега: присоединение Interrupt на выводе 18/19/20/21 не работает
- Arduino Mega — включение режима CTC отключает таймер
- Считать данные датчика повторно через указанное время?
- Обнаружение, когда прерывание таймера не выполняется
- Проблема с использованием Arduino Mega Timer2 с прерыванием PinChange
- DS3231 с Arduino Nano для точной синхронизации
Подсказка: Пожалуйста, добавьте объявление переменной. Это изменчиво? И защищена ли она критической секцией (т.е. без прерываний) при чтении и записи? Операции с 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