C语言volatile关键字学习

目录

C语言volatile关键字学习


C语言volatile关键字学习

volatile原意是“易变的”,在嵌入式环境中用volatile关键字声明的变量,在每次对其值进行引用的时候 都会从原始地址取值。由于该值“易变”的特性所以,针对其的任何赋值或者获取值操作都会被执行(而不 会被优化)。由于这个特性,所以该关键字在嵌入式编译环境中经常用来消除编译器的优化,可以分为以下 三种情景:

  1. 修饰硬件寄存器;

  2. 修饰中断服务函数中的非自动变量;

  3. 在有操作系统的工程中修饰会被多个应用修改的变量;

  • 修饰硬件寄存器

以STM32F103的HAL库函数中GPIO的定义举例,如下为HAL库中GPIO寄存器定义:

/**
* @brief General Purpose I/O
*/
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;

其中__IO的定义是:

#define __IO volatile /*!< Defines 'read / write' permissions */

然后定义GPIO是:

#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *)GPIOG_BASE)

而GPIOx_BASE的定义是这样的:

#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x00000C00UL)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x00001000UL)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x00001400UL)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x00001800UL)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x00001C00UL)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x00002000UL)

其中APB2外设基地址的定义:

#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)

最后再来看外设基地址的定义:

#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */

综合起来,将宏定义一一展开,仅用GPIOA来看,其它的以此类推:

#define GPIOA ((GPIO_TypeDef *)(0x40000000UL + 0x00010000UL + 0x00000800UL))

如此定义之后,那么GPIOA的CRL的地址就是:

(volatile uint32_t *)(0x40000000UL + 0x00010000UL + 0x00000800UL)

CRH的地址就是:

(volatile uint32_t *)(0x40000000UL + 0x00010000UL + 0x00000800UL + 2)

后面的寄存器以此类推,因而在程序中使用:

GPIOA->CRH |= 0x01;

那么实现的功能就是对GPIOA的CRH的寄存器的最低位拉高。如果在定义GPIO的寄存器结构体里面 没有使用__IO uint16_t,而是仅使用uint16_t,那么在程序中再用语句:

GPIOA->CRH |= 0x01;

就有可能会被编译器优化,不执行这一语句,从而导致拉高CRH的最低位这个功能无法实现;但是库函 数中使用了volatile来修饰,那么编译器就不会对此语句优化,在每次执行这一语句的时候都会从CRH对应 的内存地址里面去取值或者存值,保证了每次执行都是有效的。

  • 在有操作系统的工程中修饰会被多个任务修改的变量

在嵌入式开发中,不仅仅有单片机裸机开发,也有带有操作系统的开发,通常两者使用C语言开发的较 多。在有操作系统(比如RTOS、UCOS-II、Linux等)的设计中,如果有多个任务在对同一个变量进行赋值 或取值,那么这一类变量也应使用volatile来修饰保证其可见性。所谓可见即:当前任务修改了这一变量的值, 同一时刻,其它任务此变量的值也发生了变化。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值