STM32F407位带操作说明以及volatile的用法。
1.位带操作的意义
GPIO_SetBits、GPIO_ResetBits、GPIO_WriteBit操作IO口的性能没有达到极致,因为这些函数都需要进行现场保护和现场恢复的动作,比较耗时间,没有进行一步到位,使用位带操作则没有上述的烦恼,简单快速。
2.公式
关于IO引脚对应的访问地址,可以参考以下公式
寄存器的位带别名 = 0x42000000 + (寄存器的地址-0x40000000)*32 + 引脚编号*4
例子如下:
uint32_t *PF9_BitBand = (uint32_t *)(0x42000000 + (GPIOF_BASE + 0x14 - 0x40000000)*32 + 9*4);
用法:*PF9_BitBand=1;
uint32_t *PF9_BitBand是定义一个PF9_BitBand的指针,等号右边的(uint32_t *)是强制转换。
GPIOF_BASE是F口的基地址,那为啥要加0X14呢,这是因为配置是输出口地址偏移为0x14,如果是输入口为0X10.具体可以看如下的结构体。
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
3.代码调整
将端口的访问封装为Pxout、Pxin,例如端口F引脚电平设置PFout,端口A引脚电平读取PAin。
#define PFout(n) *(volatile uint32_t *)(0x42000000 + (GPIOF_BASE + 0x14 - 0x40000000)*32 + n*4) #define PAin(n) *(volatile uint32_t *)(0x42000000 + (GPIOA_BASE + 0x10 - 0x40000000)*32 + n*4)
使用:PFout(9)=PAin(0); //简单的按键控制灯
可以看出寄存器地址用了volatile修饰,为啥要用他修饰呢看下一点。
4.volatile的用法。
如果不加volatile你会发现 PFout(9)=PAin(0);这个语句根本点不亮灯
按键例子1,任何时刻按下按键,灯无法响应:
#define PAin(n) *((uint32_t *)(0x42000000 + (((uint32_t)&GPIOA->IDR) - 0x40000000)*32 + (n)*4)) PFout(9) = PAin(0); 经过编译阶段,会得到恒定的结果。 PFout(9)=1;
这就是volatile一种用法之一:
ARM定义寄存器的时候,寄存器是指向一个地址,要加上volatile进行修饰,让编译器不要优化而省略该变量的访问。
volatile关键字分析,往往应用在三种场合
1)多线程编程共享全局变量的时候,该全局变量要加上volatile进行修饰,让编译器不要省略该变量的访问。
2)裸机编程的时候,某函数与中断服务函数共享全局变量的时候,该全局变量要加上volatile进行修饰,让编译器不要省略该变量的访问。
3)ARM定义寄存器的时候,寄存器是指向一个地址,要加上volatile进行修饰,让编译器不要优化而省略该变量的访问。
编译器不要优化该变量指的是防止编译器出现优化过度,导致代码运行失效。
