一、位带操作的原理
1.1 位带操作的基本概念
位操作就是可以单独的对一个比特位读和写,在51 单片机中通过关键字 sbit
来实现位定义,STM32 没有这样的关键字,而是通过访问位带别名区来实现位定义。
1.2 为什么需要位带操作
在引脚初始化后,控制引脚输入/输出是通过操作寄存器ODR/IDR
的值来进行,例如让PA0输出高电平程序入下:
GPIOA->ODR |= 1 << 0;
这个操作包含了三个隐含的操作:
- 读取整个ODR寄存器值到内存中的寄存器
- 改写内存中寄存器第0位值
- 改写后值写入ODR寄存器
相比之下位带操作通过专门的硬件支持,直接对特定的位进行操作,省去了读取寄存器,修改和再写回的过程。这样做主要有以下两点优势:
- 当我们需要频繁操作IO口的时候,位带操作效率更高
- 在多任务中实现共享资源在任务间互斥操作(将读-改-写做成一个硬件级别支持的原子操作)
1.3 具体实现
位带区的一个比特位经过膨胀之后,变大到 4 个字节,由于STM32 的系统总线是 32 位的,按照 4 个字节访问的时候是最快的,如下图:
地址转换:AliasAddr = 0x42000000 + (A - 0x40000000) * 32 + n * 4
A是片上外设所在字节地址,序号为n
代码实现:这里使用volatile修饰避免被编辑器优化,确保每次读取的都是内存地址的值
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
二、参考资料
- 1 <<CM3权威指南>>第五章(87页~92页).
- 2 野火零死角玩转 STM32