嵌入式分享#62:volatile 到底在防谁?

描述

 搞嵌入式 C 的朋友,应该都见过 volatile。它常出现在寄存器、中断标志位、状态变量这些地方,但也特别容易被误解。

很多新手会觉得:volatile 不就是修饰变量的吗?甚至怕出问题,所有变量都加一个。其实这玩意不是护身符,用错了照样坑。

先说结论:

volatile 是用来告诉编译器:这个变量或地址可能被程序之外的因素改变,每次访问都必须真实发生,不要缓存,不要乱优化。

 

1. 一个典型坑

比如读取 GPIO 输入寄存器,等待引脚变成高电平:

  •  
  •  
  •  
  •  
unsigned int gpio_val = *(unsigned int *)0x12345678;while (gpio_val == 0) {    // 期望一直读取 GPIO 状态}
这段代码看起来没毛病,但实际可能直接死循环。

原因很简单:gpio_val 只在进入循环前读了一次。后面 while 判断的一直是这个普通变量,而不是重新读取硬件寄存器。

正确写法应该是:

  •  
  •  
  •  
  •  
#define GPIO_IN_REG (*(volatile unsigned int *)0x12345678)while (GPIO_IN_REG == 0) {    // 每次循环都会重新读取寄存器}
这就是 volatile 的价值:防止编译器把硬件寄存器当普通变量优化。

 

2. 为什么嵌入式必须懂 volatile?

在嵌入式里,很多变量或地址的值,不一定由当前代码修改。

比如:

  • GPIO 输入寄存器会被外部电平改变;

  • ADC 数据寄存器会被硬件更新;

  • 中断服务函数会修改标志位;

  • DMA 可能在后台修改内存。

如果不加 volatile,编译器可能认为这个值“不变”,于是缓存到 CPU 寄存器里,后续不再真实访问内存或硬件地址。

这在普通 C 程序里可能没问题,但在嵌入式里就容易出现“调试看着正常,运行直接抽风”的问题。

 

3. 硬件寄存器必须加

典型写法如下:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
#define GPIO_CTRL_REG (*(volatile unsigned int *)0x12345678)#define GPIO_IN_REG   (*(volatile unsigned int *)0x1234567C)unsigned int read_gpio(void){    return GPIO_IN_REG;}void set_gpio_output(void){    GPIO_CTRL_REG |= (1 << 0);}
读取寄存器时,必须每次读到硬件最新值;写控制寄存器时,也必须真实写入硬件,不能被编译器省略或延迟。

	

4. 几个常见误区

误区一:volatile 能保证原子性。

错。volatile 只保证每次真实访问,不保证操作不可打断。

volatile int cnt;
cnt++;

cnt++ 本质是“读-改-写”三步,多线程同时执行仍然会有竞态。该用锁、原子操作时,还是要老老实实用。

误区二:所有变量都加 volatile 更安全。

错。volatile 会限制编译器优化,滥用会降低效率。普通局部变量、临时计算变量,不需要加。

误区三:volatile 不能和 const 一起用。

也错。比如只读硬件寄存器:

#define ADC_DATA_REG (*(volatile const unsigned int *)0x12345680)

const 表示程序不能写,volatile 表示硬件可能改。两者并不冲突。

 

5. 总结

一句话记住:

volatile 防的是编译器优化,不是防多线程竞态。

它常用于硬件寄存器、中断共享变量、DMA 状态标志、轮询等待外部状态等场景。

但它不能替代锁、原子操作、内存屏障和 cache 一致性维护。

所以,volatile 的正确姿势不是“能加就加”,而是:该加的地方必须加,不该加的地方别乱加。

(完)


本人专注 Linux 嵌入式全栈开发,可提供从硬件方案评估与设计、Linux/Android BSP 适配、驱动开发、外设调试、系统移植到产品交付的全流程技术支持。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分