STM32位带操作技术解析 摘要 STM32的位带操作技术允许通过普通加载/存储指令直接访问单个比特位。该技术通过位带别名区实现将每个比特映射为32位字支持对SRAM和外设区域的特定位进行原子操作。位带区包含SRAM(0x20000000-0x200FFFFF)和外设(0x40000000-0x400FFFFF)两个区域通过特定地址转换公式实现位访问。这种技术源于8051的位寻址在CM3内核中得到增强提供了更高效的位操作方式。实际应用中可通过宏定义简化GPIO端口操作实现对单个引脚的快速读写STM32中的位带(bit-band)操作支持了位带操作后可以使用普通的加载/存储指令来对单一的比特进行读写。在 CM3 中有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围第二个则是片内外设区的最低 1MB范围。这两个区中的地址除了可以像普通的 RAM 一样使用外它们还都有自己的“位带别名区”位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时就可以达到访问原始比特的目的。位带操作的概念其实 30 年前就有了那还是8051 单片机开创的先河如今CM3 将此能力进化这里的位带操作是 8051 位寻址区的威力大幅加强版。CM3 使用如下术语来表示位带存储的相关地址位带区支持位带操作的地址区位带别名对别名地址的访问最终作用到位带区的访问上这中途有一个地址映射过程在位带区中每个比特都映射到别名地址区的一个字——这是只有 LSB 有效的字。当一个别名地址被访问时会先把该地址变换成位带地址。对于读操作读取位带地址中的一个字再把需要的位右移到 LSB并把 LSB 返回。对于写操作把需要写的位左移至对应的位序号处然后执行一个原子的“读改写”过程。支持位带操作的两个内存区的范围是0x2000_0000‐0x200F_FFFFSRAM 区中的最低 1MB0x4000_0000‐0x400F_FFFF片上外设区中的最低 1MB对 SRAM 位带区的某个比特记它所在字节地址为 A,位序号为 n(0n7)则该比特在别名区的地址为AliasAddr0x22000000((A-0x20000000)*8n)*40x22000000(A-0x20000000)*32n*4对于片上外设位带区的某个比特记它所在字节的地址为 A,位序号为 n(0n7)则该比特在别名区的地址为AliasAddr0x42000000((A-0x40000000)*8n)*40x42000000(A-0x40000000)*32n*4上式中“*4”表示一个字为 4 个字节“*8”表示一个字节中有 8 个比特。这里再不嫌啰嗦地举一个例子1. 在地址 0x20000000 处写入 0x3355AACC2. 读取地址0x22000008。本次读访问将读取 0x20000000并提取比特 2值为 1。3. 往地址 0x22000008 处写 0。本次操作将被映射成对地址 0x20000000 的“读改写”操作原子的把比特2 清 0。4. 现在再读取 0x20000000将返回 0x3355AAC8bit[2]已清零。位带别名区的字只有 LSB 有意义。另外在访问位带别名区时不管使用哪一种长度的数据传送指令字/半字/字节都把地址对齐到字的边界上否则会产生不可预料的结果。[cpp]view plain copy/////////////////////////////////////////////////////////////////位带操作,实现51类似的GPIO控制功能//具体实现思想,参考CM3权威指南第五章(87页~92页).//IO口操作宏定义#define BITBAND(addr, bitnum) ((addr 0xF0000000)0x2000000((addr 0xFFFFF)5)(bitnum2))#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))//IO口地址映射#define GPIOA_ODR_Addr (GPIOA_BASE12) //0x4001080C#define GPIOB_ODR_Addr (GPIOB_BASE12) //0x40010C0C#define GPIOC_ODR_Addr (GPIOC_BASE12) //0x4001100C#define GPIOD_ODR_Addr (GPIOD_BASE12) //0x4001140C#define GPIOE_ODR_Addr (GPIOE_BASE12) //0x4001180C#define GPIOF_ODR_Addr (GPIOF_BASE12) //0x40011A0C#define GPIOG_ODR_Addr (GPIOG_BASE12) //0x40011E0C#define GPIOA_IDR_Addr (GPIOA_BASE8) //0x40010808#define GPIOB_IDR_Addr (GPIOB_BASE8) //0x40010C08#define GPIOC_IDR_Addr (GPIOC_BASE8) //0x40011008#define GPIOD_IDR_Addr (GPIOD_BASE8) //0x40011408#define GPIOE_IDR_Addr (GPIOE_BASE8) //0x40011808#define GPIOF_IDR_Addr (GPIOF_BASE8) //0x40011A08#define GPIOG_IDR_Addr (GPIOG_BASE8) //0x40011E08//IO口操作,只对单一的IO口!//确保n的值小于16!#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入