计时器溢出竞赛条件

问题描述

我正在使用带有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结合起来,反之亦然。

是否有预防此问题的最佳做法?

解决方法

在阅读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;
}