中断驱动代码(非 RTOS)中的同步

问题描述 投票:0回答:1

我遇到了以下问题。
假设我们只有一个没有任何 RTOS 的主循环固件。
我们有一个任务是在一些周期性的中断处理程序中填充缓冲区(让它成为执行 ADC 读取并将结果放入某个缓冲区的定时器中断)并在主循环中从该缓冲区读取。

#define BUF_LEN 10

uint16_t buffer[BUF_LEN];
int head = 0, tail = 0;

int next_ring_pos(int pos, int len) {
    return (pos + 1) % len;
}

void process_sample() {
    uint16_t sample_value;

    if (tail != head) {
        sample_value = buffer[tail];
        tail = next_ring_pos(tail, BUF_LEN);
        //do something with sample_value
    }
}

void put_sample(uint16_t sample_value) {
    if (tail != head) {
        buffer[head] = sample_value;
        head = next_ring_pos(head, BUF_LEN);
    } else {
        //do something if buffer overflow detected
    }
}

put_sample
从定时器的中断服务例程 (ISR) 中定期调用,process_sample 在
main(1)
循环中定期调用。
从我的角度来看,我们在这里不会有任何同步问题,因为 ISR 只改变头指针而主循环只改变尾指针。
但我关心这部分:
if (tail != head)
.
这里有可能出现竞争条件吗?
如果是,那么我还有一个问题:有什么方法可以同步中断代码和低优先级代码(主循环)?我们不能使用任何等待原语(如任何条件变量和使用原子操作轮询),因为我们不能等待 ISR。

如果竞争条件是可能的,那么我还有另一个问题:有什么方法可以同步中断代码和低优先级代码(主循环)?我们不能使用任何等待原语(如任何条件变量和使用原子操作轮询),因为我们不能等待 ISR。

microcontroller race-condition
1个回答
0
投票

您担心 ISR 中可以被主循环使用的内容被修改,这绝对是正确的。而且你也是正确的,因为 ISR 修改了

head
并且主循环修改了
tail
,所以这更安全。

但是,使用

head
tail
的比较来知道缓冲区中是否有数据(或空间)是非常容易出错的。例如,如果
head == tail
,缓冲区是满的还是空的?

更好的方法是在一个单独的变量中明确跟踪队列中有多少项目。那么这是唯一需要在主循环和 ISR 之间共享的变量。它还使决定缓冲区中是否有数据(或空间)变得微不足道。该变量在修改时需要在主循环中进行保护,以确保在读写过程中不会调用 ISR。最简单的方法是在修改期间禁用中断。

如果我正在写这篇文章,我会做这样的事情:

#define BUF_LEN 10u

uint16_t buffer[BUF_LEN];
uint8_t head = 0u;                 // Where to insert
uint8_t tail = 0u;                 // Where to remove
volatile uint8_t num_entries = 0u; // How many entries

uint8_t next_ring_pos(uint8_t pos, uint8_t len)
{
    return (pos + 1u) % len;
}

void process_sample(void)
{
    uint16_t sample_value;

    if (mum_entries > 0u)
    {
        sample_value = buffer[tail];
        tail = next_ring_pos(tail, BUF_LEN);

        _disable_irq();
        -- num_entries;
        _enable_irq();
    
        //do something with sample_value
    }
    else
    {
        // Queue was empty
    }
}

void put_sample(uint16_t sample_value)
{
    if (num_entries < BUF_LEN)
    {
        buffer[head] = sample_value;
        head = next_ring_pos(head, BUF_LEN);

        ++ num_entries;
    }
    else
    {
        //do something if buffer overflow detected
    }
}

注意

volatile
上的
num_entries
限定词。这是必需的,因为 ISR 可能会在执行
process_sample()
期间修改其值,因此我们需要确保在禁用/启用中断时使用的是最新值。

© www.soinside.com 2019 - 2024. All rights reserved.