2.4.8.使用 Volatile

2.4.8.1.共享数据

  • 由 main() 以及一个或多个 ISR 读取/写入的任何全局变量都必须标注为 Volatile
  • Volatile 会向编译器指明,该变量可能由已知程序流程以外的程序(例如 ISR)修改过
  • 这可确保编译器完全按照 C/C++ 语言代码中的要求保留对全局变量进行 Volatile 读写的次数。编译器不会:
    • 消除冗余读取或写入
    • 重新排序访问

表 2.13 说明了启用优化时需要使用 Volatile。如果不使用 volatile 限定符(在 flag 上),编译器会删除 if 块(在 main() 中),这是因为其分析指明 flag 始终为 0 且 if 条件始终为 false。volatile向编译器指明 main() 以外的程序(本例中为 ISR)可以更新 flag

表 2.13 使用 volatile
主应用程序 中断服务例程
volatileintflag;intx;intmain(){flag=0;...if(flag==1)x++;...}
externintflag;interruptvoidISR(void){...flag=1;...}

2.4.8.2.外设访问

  • 访问表示存储器映射外设的存储器位置时,必须使用 Volatile 关键字。
  • 此类存储器位置可能会以编译器无法预测的方式更改值。
  • 这可确保编译器完全按照 C 代码中的要求保留对存储器的读写次数。
  • 缺少 Volatile 限定符可能会导致编译器错误地优化掉或重新排序读取/写入。
列表 2.2 使用 Volatile 进行外设寄存器访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
staticinlinevoidGPIO_writePin(uint32_tpin,uint32_toutVal){volatileuint32_t*gpioDataReg;uint32_tpinMask;//// Check the arguments.//ASSERT(GPIO_isPinValid(pin));gpioDataReg=(uint32_t*)GPIODATA_BASE+((pin/32U)*GPIO_DATA_REGS_STEP);pinMask=(uint32_t)1U<<(pin%32U);if(outVal==0U){gpioDataReg[GPIO_GPxCLEAR_INDEX]=pinMask;}else{gpioDataReg[GPIO_GPxSET_INDEX]=pinMask;}}