Условие гонки с переполнением таймера

#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;
}