参考资料:《STM32F4xx 中文参考手册》存储器和总线构架章节、 GPIO 章节,《Cortex®-M4 内核编程手册》 2.2.5 Bit-banding。
位带简介
位操作就是可以单独的对一个比特位读和写。F429 中没有这样的关键字,而是通过访问位带别名区来实现。
有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,另一个是外设区最低 1MB 空间
这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这 1MB 的空间的每一个位膨胀成一个 32 位的字,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。
外设位带区
外设位带区的地址为: 0X40000000~0X400F0000,大小为 1MB,这 1MB 的大小包含了 APB1/2 和 AHB1 上所以外设的寄存器, AHB2/3 总线上的寄存器没有包括。外设位带区经过膨胀后的位带别名区地址为:0X42000000~0X43FFFFFF。
SRAM位带区
SRAM 的位带区的地址为: 0X2000 0000~X200F 0000,大小为 1MB,经过膨胀后的位带别名区地址为: 0X2200 0000~0X23FF FFFF,大小为 32MB。
位带区和位带别名区地址转换
可以通过指针的形式访问位带别名区地址从而达到操作位带区比特位的效果。
外设位带别名区地址
对于片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则
该比特在别名区的地址为:
AliasAddr = 0x42000000 + (A-0x40000000)*8*4 +n*4
0X42000000 是外设位带别名区的起始地址, 0x40000000 是外设位带区的起始地址,(A-0x40000000)表示该比特前面有多少个字节,一个字节有 8 位,所以8,一个位膨胀后是 4 个字节,所以4, n 表示该比特在 A 地址的序号,因为一个位经过膨胀后是四个字
节,所以也*4。
SRAM位带别名区地址
对于 SRAM 位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则
该比特在别名区的地址为:
AliasAddr = 0x22000000+ (A-0x20000000)*8*4 +n*4
统一公式
两个公式合并成一个公式,把“位带地址+位序号”转换成别名区地址统一成一个宏。
// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr &0x000FFFFF)<<5)+(bitnum<<2))
addr & 0xF0000000 是为了区别 SRAM 还是外设,实际效果就是取出 4 或者 2,如果是外设,则取出的是 4, +0X02000000 之后就等于 0X42000000, 0X42000000 是外设别名区的起始地址。如果是 SRAM,则取出的是 2, +0X02000000 之后就等于 0X22000000,0X22000000 是 SRAM 别名区的起始地址。
addr & 0x000FFFFF 屏蔽了高三位,相当于减去 0X20000000 或者 0X40000000,但是为什么是屏蔽高三位?因为外设的最高地址是: 0X4010 0000,跟起始地址 0X40000000 相减的时候,总是低 5 位才有效,所以干脆就把高三位屏蔽掉来达到减去起始地址的效果,具体屏蔽掉多少位跟最高地址有关。 SRAM 同理分析即可。 <<5 相当于84, <<2 相当于*4,这两个我们在上面分析过。
PS:位带区域的最小单位表示的是外设或者SRAM某个寄存器的地址,我们一般的配置或者读写操作是对这个地址指向的内容。而位带别名区,将这个地址放大扩展,使得我们可以直接操作相应的位就达到操作最小某个寄存器位的目的,比如GPIOA的数据输出寄存器,0x40020016,我们读端口0,需要对这个地址转换为相应类型的指针再读0x01,而位带别名区扩展了该位,可以直接读写位,不用间接引用。
参考引用:
- 野火---《零死角玩转STM32-F429挑战者》
- 《STM32F4xx中文参考手册》
- 《Cortex-M4内核编程手册》