-
/** * @brief Universal Synchronous Asynchronous Receiver Transmitter */ typedef struct { __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ } USART_TypeDef;
- 上面这段程序是来自STM32F103库函数中的结构体定义,涉及到了符号__IO。
- 这里__IO 表示 volatile,这是标准 C 语言中的一个修饰字,core_m3.h 文件定义了这个宏:
-
/* IO definitions (access restrictions to peripheral registers) */ /** \defgroup CMSIS_glob_defs CMSIS Global Defines <strong>IO Type Qualifiers</strong> are used \li to specify the access to peripheral variables. \li for automatic generation of peripheral register debug information. */ #ifdef __cplusplus #define __I volatile /*!< Defines 'read only' permissions */ #else #define __I volatile const /*!< Defines 'read only' permissions */ #endif #define __O volatile /*!< Defines 'write only' permissions */ #define __IO volatile /*!< Defines 'read / write' permissions */ /* following defines should be used for structure members */ #define __IM volatile const /*! Defines 'read only' structure member permissions */ #define __OM volatile /*! Defines 'write only' structure member permissions */ #define __IOM volatile /*! Defines 'read / write' structure member permissions */
-
__IO
的定义本质- 在 STM32 库函数中,
__IO
是一个宏定义,用于修饰变量,表示这个变量是一个 “输入 / 输出”(Input/Output)变量。 - 它主要用于对硬件寄存器的访问,确保对这些变量的操作能够正确地反映到硬件上,并且能够及时获取硬件状态的变化。
__IO
的定义与编译器和硬件的内存映射(Memory - Mapping)机制有关。在 ARM Cortex - M 系列(STM32 基于此架构)处理器中,硬件寄存器是通过内存映射的方式来访问的。这意味着硬件寄存器被映射到了特定的内存地址空间,对这些内存地址的读写操作就相当于对硬件寄存器的操作。
- 在 STM32 库函数中,
-
__IO
的实现方式和作用原理- 编译器层面的作用
- 当编译器看到
__IO
修饰的变量时,它会根据这个宏定义来生成特殊的指令序列,以确保对变量的读写操作能够正确地与硬件寄存器交互。 volatile
关键字告诉编译器这个变量的值可能会在程序的控制流之外被改变(例如,被硬件中断或者其他外部设备改变),所以编译器在优化代码时不会对这个变量进行过度的优化。volatile
关键字可以确保每次对变量的读取都是从硬件寄存器中获取最新的值,而每次对变量的写入都能够及时地更新到硬件寄存器中。
- 当编译器看到
- 硬件交互层面的作用
- 以 STM32 的 GPIO(通用输入输出)寄存器为例,当设置一个 GPIO 引脚为输出模式并控制其输出电平高低时,需要对相应的 GPIO 控制寄存器和数据寄存器进行操作。这些寄存器都是被定义为
__IO
类型的变量。 -
/** * @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;
GPIOC->ODR
是一个__IO
类型的变量,它代表了 GPIOC 端口的数据输出寄存器。- 当执行
GPIOC->ODR = 0x0001;
这样的代码时,由于__IO
的存在,编译器会确保这个赋值操作能够直接将值0x0001
写入到对应的硬件寄存器中,从而使得 GPIOC 端口的相应引脚输出高电平。 - 当读取
GPIOC - > IDR
这个__IO
变量时,编译器会从硬件寄存器中获取当前引脚的输入状态值。
- 以 STM32 的 GPIO(通用输入输出)寄存器为例,当设置一个 GPIO 引脚为输出模式并控制其输出电平高低时,需要对相应的 GPIO 控制寄存器和数据寄存器进行操作。这些寄存器都是被定义为
- 编译器层面的作用
-
与普通变量的对比示例
- 普通变量
- 对于普通的变量,编译器在优化代码时会根据自己的规则进行一些优化。例如,如果一个变量在一个循环中没有被其他外部因素改变,编译器可能会将这个变量的值缓存起来,而不是每次都从内存中读取。这样可以提高程序的执行效率,但在与硬件寄存器交互的场景下就会出现问题。
- 比如,定义一个普通变量
int normal_variable;
,在一个循环中对其进行操作: -
for(int i = 0; i < 10; i++) { normal_variable = i; // 假设这里没有其他代码改变normal_variable的值 }
- 编译器可能会认为
normal_variable
的值在每次循环中除了赋值操作外没有其他改变,所以可能会将normal_variable
的值存储在 CPU 的寄存器中,而不是每次都从内存中读取和写入。
__IO
变量- 对于
__IO
变量,由于其代表的是硬件寄存器,其值随时可能被硬件改变(如外部设备的输入改变了 GPIO 引脚的状态),或者需要及时更新到硬件(如更新 GPIO 引脚的输出电平)。 __IO uint32_t gpio_register;
这个变量代表一个 GPIO 寄存器。- 当读取
gpio_register
时,每次读取都必须从硬件寄存器获取最新的值,因为硬件状态可能随时改变。 - 当写入
gpio_register
时,写入操作必须立即更新到硬件寄存器,以确保硬件能够按照程序的要求进行操作。所以编译器不会对__IO
变量进行像普通变量那样的过度优化,以保证与硬件的正确交互。
- 对于
- 普通变量
嵌入式STM32-C语言中__IO的含义
于 2024-12-18 15:57:14 首次发布