缓冲区是否需要标记为易失性?

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

我见过很多解释 volatile 关键字用法的例子,但所有这些都引用单个变量,例如在 ISR 中递增的刻度计数器,然后在主循环中读取。但是主循环和 ISR 之间共享的缓冲区怎么样,其中主循环是生产者和 ISR 消费者(或其他方式)。

假设我有一个简单的环形缓冲区结构:

struct RingBuffer
{
 uint8_t *data;
 uint32_t write_index;
 uint32_t read_index;
};

struct RingBuffer buffer;

data必须是易失性的吗?或者整个buffer(这也意味着函数参数也必须是易失性的)?我查看了这种数据结构的一些实现,但我看不到任何易失性,这让我很烦恼。 我在 stm32 上编写了一个简单的程序,用于驱动带缓冲的液晶显示器。代码如下所示:

struct OutputLine
{
    GPIO_TypeDef *gpio;
    uint16_t pin;
};

typedef struct OutputLine OutputLine_t;

struct HD44780
{
    OutputLine_t enable;
    OutputLine_t rs;
    OutputLine_t rw;
    OutputLine_t data[4]; // data[0..3] == D4..7
    Fifo_t fifo;
};

在主循环中,我检查液晶屏是否忙,如果不忙,则队列中是否有东西。到 lcd 的命令是在定时器 irq 中发送的

while (1)
  {
    /* USER CODE END WHILE */
      HD44780_Update(&lcd);
    /* USER CODE BEGIN 3 */
  }

void HD44780_Update(HD44780_t *lcd)
{
    if (Fifo_Empty(&lcd->fifo)) {
        return;
    }
    if (HD44780_Busy(lcd)) {
        return;
    }
    HD44780_FifoItem_t item;
    Fifo_Read(&lcd->fifo, &item);
    HD44780_WriteByte(lcd, item.byte, item.rs);
}

void TIM6_IRQHandler(void)
{
    if (LL_TIM_IsActiveFlag_UPDATE(TIM6)) {
        LL_TIM_ClearFlag_UPDATE(TIM6);
        i = (i + 1) & 1;
        HD44780_ClearDisplay(&lcd);
        HD44780_Puts(&lcd, strings[i], strlen(strings[i]));
    }
}

无论 Fifo_t 是否不稳定,结果都是正确的。

c embedded
1个回答
0
投票

什么是

volatile
?它可能是 C 语言中最容易被误解的关键字(也许除了
restrict
)。

  • volatile
    通知编译器该对象(变量)容易产生副作用。这意味着它可以被正常程序执行路径中的编译器不可见的内容更改。例如,通过硬件(DMA、硬件寄存器)或信号(异常)处理程序,这些处理程序不被程序调用。

示例:

uint32_t counter;

void TIM6_IRQHandler(void)
{
    /* .... */
    counter++;
}

void foo(void)
{
    while(counter < 1000);
    printf("x");
}

以及生成的代码:https://godbolt.org/z/h3qz7Y5W6

foo:
        ldr     r3, .L8
        ldr     r3, [r3]
.L6:
        cmp     r3, #1000
        bcc     .L6

如您所见,如果没有

volatile
counter
只会加载到寄存器一次,并且即使
foo
达到
counter
,函数
1000
也会以死循环结束。对于
volatile
counter
,每次使用之前都会从内存中读取,因为编译器知道它容易产生副作用:https://godbolt.org/z/KThnG3sq4

生成的代码:

foo:
        ldr     r2, .L8
.L6:
        ldr     r3, [r2]
        cmp     r3, #1000
        bcc     .L6

现在,每次使用时都会将

counter
加载到寄存器中。

volatile
不保证原子性缓存一致性。

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