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进行修饰,让编译器不要优化而省略该变量的访问。

编译器不要优化该变量指的是防止编译器出现优化过度,导致代码运行失效。

经验分享 程序员 微信小程序 职场和发展