Безопасное расширение TCNT1/ICR1 до 24 бит с помощью TOV1
Я надеюсь рассчитать внешние события на Arduino 328p, которые будут происходить достаточно медленно, чтобы они переполняли 16-битный счетчик TCNT1. В другом сценарии я бы заранее настроил счетчик, но в данном случае мне нужно полное разрешение таймера для других целей.
Поэтому я включаю прерывание TOV1 и просто увеличиваю 8-битное значение, позволяя ему переворачиваться по мере необходимости.
Затем при прерывании ICP1 я сдвигаю это значение << 16 в 32-битную переменную и добавляю значение, захваченное в ICR1, получая 24-битное значение, которое я могу использовать для измерения медленного интервала.
Есть ли в этом плане скрытые подводные камни? Возможные условия гонки, когда TOV1 еще не был обновлен, если ICR1 фиксируется только тогда, когда TCNT1 только что перевернулся, или генерирует TOV1 после захвата ICR1, но до того, как я переключу счетчик переполнения?
@Jim Mack, 👍0
1 ответ
Лучший ответ:
Действительно, существует возможное условие расы. Рассмотрим этот наивный код, который предоставляет 32-битные метки времени:
/* Подсчет переполнений. */
volatile uint16_t timer1_overflow_count;
ISR(TIMER1_OVF_vect)
{
timer1_overflow_count++;
}
/* События ввода меток времени. */
volatile uint32_t captured_timestamp;
volatile bool did_capture;
ISR(TIMER1_CAPT_vect)
{
captured_timestamp = timer1_overflow_count << 16 | ICR1;
did_capture = true;
}
Поскольку TIMER1_CAPT имеет более высокий приоритет, чем TIMER1_OVF, то:
Если
ICF1
поднимается доTOV1
,TIMER1_CAPT_vect
запускается первым, и это нормально.Если
ICF1
иTOV1
поднимаются одновременно,TIMER1_CAPT_vect
запускается первым, что все еще нормально, так как захваченное значение равно0xffff
.Если
TOV1
поднимается передICF1
, то любой ISR может работать первым:если процессор может сразу обработать IRQ TIMER1_OVF, то
TIMER1_OVF_vect
запускается первым, и результат веренесли обработка прерывания задерживается до тех пор, пока
ICF1
не повысится, тоTIMER1_CAPT_vect
запускается первым и захватывает неправильное значение дляtimer1_overflow_count
.
Решение
Существует необработанное переполнение, когда bit_is_set(TIFR1, TOV1)
.
Переполнение может произойти одновременно или после захвата, и в
этом случае оно не влияет на метку времени. Мы знаем, что это произошло до
захвата, когда захваченное значение мало, где “маленький” может быть
определен как имеющий самый значимый бит. Если это так,
то мы должны добавить 0x10000
к полученной нами метке времени.
ISR, защищенный от гонки, может быть записан следующим образом:
ISR(TIMER1_CAPT_vect)
{
uint16_t captured_low = ICR1;
uint16_t captured_high = timer1_overflow_count;
if (bit_is_set(TIFR1, TOV1) && !(captured_low & 0x8000)) {
captured_high++;
}
captured_timestamp = (uint32_t) captured_high << 16 | captured_low;
did_capture = true;
}
- ATmega328P - проблема с использованием таймера 2 для генерации тона
- Точность синхронизации Arduino nano
- Интервальный таймер на Arduino: Сомнения по поводу библиотеки TimerOne
- Можно ли отсоединить прерывание на определенное время
- Заставить TCNT оставаться ниже OCRxA на ATmega328P
- Проблема с таймером 0
- Эмуляция Arduino Uno с помощью QEMU: прерывания не работают
- Прерывание занимает больше времени, чем ожидалось
Спасибо за ваш анализ. Я понимаю все о вашем решении, кроме фразы *мы должны добавить
0x0100
к метке времени*. В коде вы (эффективно) добавляете "0x10000", что я и ожидал бы от пропущенного переполнения. Опечатка?, @Jim Mack@JimMack: Упс! Вы правы, я допустил опечатку. Исправлено, спасибо!, @Edgar Bonet