嵌入式关键字volatile有什么含意 并给出三个不同的例子

发布时间 2023-06-19 22:05:37作者: FBshark

参考文章1:嵌入式面经

参考文章2:关于STM32库中 __IO 修饰符(volatile修饰符)

volatile 初印象

最初接触到 volatile,是看野火的自己编写库函数的章节,其中在寄存器结构体中的寄存器成员前加了 "__IO" 修饰,这个 __IO 就是 volatile。

1 //volatile 表示易变的变量,防止编译器优化
2 #define __IO volatile
3 typedef unsigned int uint32_t;
4 typedef unsigned short uint16_t;
5
6 /* GPIO 寄存器列表 */
7 typedef struct {
8 __IO uint32_t MODER; /*GPIO 模式寄存器 地址偏移: 0x00 */
9 __IO uint32_t OTYPER; /*GPIO 输出类型寄存器 地址偏移: 0x04 */
10 __IO uint32_t OSPEEDR; /*GPIO 输出速度寄存器 地址偏移: 0x08 */
11 __IO uint32_t PUPDR; /*GPIO 上拉/下拉寄存器 地址偏移: 0x0C */
12 __IO uint32_t IDR; /*GPIO 输入数据寄存器 地址偏移: 0x10 */
13 __IO uint32_t ODR; /*GPIO 输出数据寄存器 地址偏移: 0x14 */
14 __IO uint16_t BSRRL; /*GPIO 置位/复位寄存器低 16 位部分 地址偏移: 0x18 */
15 __IO uint16_t BSRRH; /*GPIO 置位/复位寄存器 高 16 位部分地址偏移: 0x1A /
16 __IO uint32_t LCKR; /GPIO 配置锁定寄存器 地址偏移: 0x1C /
17 __IO uint32_t AFR[2]; /GPIO 复用功能配置寄存器 地址偏移: 0x20-0x24 /
18 } GPIO_TypeDef;

当时火哥大致讲解了原因:

寄存器很多时候是由外设或 STM32 芯片状态修改的,也就是说即使 CPU 不执行代码修改这些变量,变量的值也有可能被外设修改、更新,所以每次使用这些变量的时候,我们都要求 CPU 去该变量的地

址重新访问。

若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从 CPU 的某个缓存获取该变量值,这时可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。

总结一下:

  1. 当变量可变允许除了程序之外的比如硬件来修改他的内容时,应该用 volatile 修饰。
  2. 加volative的原理是:访问该数据任何时候都会直接访问该地址处内容,即通过cache提高访问速度的优化被取消。

 

 

应用场合:

场合1:多线程变量

对于共享的内存地址(多线程变量),多个程序都对它操作的时候。你的程序并不知道,这个内存何时被改变了。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。

场合2:外设通过映象的方式的内存

现在硬件设备往往也有自己的私有内存地址,比如显存,他们一般是通过映象的方式,反映到一段特定的内存地址当中、因此当其自己改变了其内存的数据时,程序的缓存的数据可能还没有改。

场合3:硬件寄存器(尤其指:状态寄存器