#timer #overflow #microcontroller
#таймер #переполнение #Микроконтроллер
Вопрос:
Я использую микроконтроллер с 16-разрядным таймером, отсчитывающим время. Текущее значение может быть считано из регистра. Однако мне нужен 32-разрядный счетчик. Каждый раз, когда таймер переполняется, он генерирует прерывание. Мое текущее решение выглядит как приведенный ниже код. Каждый раз, когда таймер переполняется, переменная counter_high
увеличивается. Текущее значение счетчика считывается как комбинация counter_high
и регистра таймера.
volatile uint16_t counter_high = 0;
uint32_t get_counter(void)
{
return (counter_high << 16) | timer->counter;
}
void timer_overflow(void)
{
counter_high ;
}
Кажется, это работает. Однако я начал задаваться вопросом, что произойдет, если таймер переполнится во время get_counter()
выполнения? Я мог бы получить старое значение counter_high
в сочетании с новым значением timer->counter
или наоборот.
Существует ли наилучшая практика для предотвращения этой проблемы?
Комментарии:
1. по сути, вы хотите прочитать значение low, а затем проверить наличие переполнения. если переполнение, то отрегулируйте высокий уровень и повторите выборку низкого уровня, и вы должны быть в безопасности. или вы можете установить низкое значение нуля в зависимости от того, какой конец вызова функции вы хотите сделать наиболее близким к реальному времени.
Ответ №1:
Прочитайте counter_high
до и после чтения timer->counter
. Если значение, прочитанное для counter_high
, не меняется, то вы знаете, что timer->counter
не выполнялось переключение между чтениями, и, следовательно, вы можете доверять значению, из которого вы прочитали timer->counter
.
Однако, если counter_high
изменено между двумя чтениями, то вы знаете, что timer->counter
когда-то между двумя чтениями произошло переключение. Это означает, что вы не можете доверять значению, из которого вы прочитали timer->counter
, потому что вы не знаете, прочитали ли вы его до или после переноса. Но теперь вы знаете, timer->counter
что недавно был выполнен переход, поэтому вы можете прочитать его еще раз и знать, что он не собирается повторять переход во второй раз.
uint32_t get_counter(void)
{
uint32_t first_counter_high = counter_high;
uint32_t counter_low = timer->counter;
uint32_t second_counter_high = counter_high;
if (first_counter_high != second_counter_high)
{
counter_low = timer->counter; // Read timer->counter again, after rollover.
}
return (second_counter_high << 16) | counter_low;
}