映射操作
Bit-banding 简称位带、位段支持位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写
对于硬件 I/O 密集型的底层程序最有用处
优点
[*]使代码更简洁
[*]在多任务中,用于实现共享资源在任务间的“互锁”访问
多任务的共享资源必须满足一次只有一个任务访问它,即“原子操作”,位带操作可以满足这个条件
以前的”读-改-写“需要 3 条指令,导致这中间留有两个能被中断的空当,可能会出现紊乱危象
紊乱危象主程序是一个任务,ISR 是另一个任务,这两个任务并发执行
ISR 所作的改动已丢失:指 ISR 对输出端口所做的修改 0x3,被后面的主程序的修改 0x0 覆盖了
https://i-blog.csdnimg.cn/direct/6331fce5584a48e8b8327ee419803ced.png
位带操作把这个“读-改-写”做成一个硬件级别支持的原子操作(具体如何实现我还不知道),避免了紊乱危象
位带操作并不只限于以字为单位的传送,也可以按半字和字节为单位传送。例如,可以使用 LDRB/STRB 来以字节为长度单位去访问位带别名区,同理可用于 LDRH/STRH。但是不管用哪一个,都必须保证目标地址对齐到字的边界上
产生由来在 STM32 中不能直接操作寄存器的某一个 Bit 位,比如 PA 端口 port input data register 中的 ODR,只能以 32bit 去操作
https://i-blog.csdnimg.cn/direct/0b3cca85b7194ff2be81ac5f6a4cef3a.png
而 port output data register 这种 ODR 可以单独操作某个 bit 位
https://i-blog.csdnimg.cn/direct/7062e913a4544a1bafaab0966dc2ba48.png
为了能直接操作 ODR 的某个 Bit 位,在内核中开辟了一块地址区域(位带别名):可将 ODR这类 Bit 位(位带区)映射到位带别名区域对应的地址,只需操作映射后的地址,就可实现操作 ODR1 位
原理
[*]位带区: 支持位带操作的地址区
[*]位带别名: 对别名地址的访问最终作 CM3 将用到位带区的访问上(中途有一个地址映射过程)
位带区大小 1MB,由于每个 bit 都要映射到位带别名区,每个 bit 需要膨胀为一个 字(只有 LSB 有效的字)。对于一个 32 位的 MCU 来说,1MB 需要 1MB * 32bit = 32MB(这里计算式不严谨)的位带别名区空间
当一个别名地址被访问时,会先把该地址变换成位带地址:
对于读操作,读取位带地址中的一个字,再把需要的位右移到 LSB,并把 LSB 返回
对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程
示例
[*]在地址 0x20000000 处写入一个字的数据 0x3355AACC
[*]读取地址 0x22000008,本次读访问将读取 0x20000000,并提取 bit2,值为 1
[*]往地址 0x22000008 处写 0,本次操作将被映射成对地址 0x20000000 的“读-改-写”操作(原子的),把 bit2 清 0
[*]现在再读取 0x20000000,将返回 0x3355AAC8(bit2 已清零)
位带别名区的字只有 LSB 有意义。在访问位带别名区时,不管使用哪一种长度的数据传送指令(字/半字/字节),都把地址对齐到字的边界上,否则会产生不可预料的结果
https://i-blog.csdnimg.cn/direct/0e2a8823ab194833a6c21c2a99beda25.png
CM3在 Cortex-M3 中有两个区实现了位带操作
SRAM 区的最低 1MB 范围
片内外设区的最低 1MB 范围
https://i-blog.csdnimg.cn/direct/b58d02ea15c247019d22d66fafaff09f.png
https://i-blog.csdnimg.cn/direct/b8a6596b16294e85a0013771e470925c.png
https://i-blog.csdnimg.cn/direct/609dbe2e7df34442b5504bd4c6ab4df5.png
// SRAM 位带区: 0X2000 0000~0X2010 0000
// SRAM 位带别名区:0X2200 0000~0X23FF FFFF
// 外设 位带区: 0X4000 0000~0X4010 0000
// 外设 位带别名区:0X4200 0000~0X43FF FFFF
// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x02000000 + ((addr & 0x00FFFFFF) << 5) + (bitnum << 2))
/*
* addr & 0xF0000000,取地址的高 4 位,提取 SRAM 或 外设地址 的高地址
* 如 2,+ 0x02000000 则 =0X2200 0000,即是 SRAM
* 如 4,+ 0x02000000 则 =0X4200 0000,即是 外设
*
* addr & 0x00FFFFFF,屏蔽高 8 位,偏移位带区多少个字节
* <<5 等于 *8*4,位带区一个地址表示一个字节,一个 bit 膨胀成一个字(32位MCU),即 4byte
* <<2 等于 *4,一个 bit 膨胀成一个字(32位MCU),即 4byte
*
* SRAM位带别名地址
* AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4
* 外设位带别名地址
* AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4
*/
// 把一个地址转换成一个指针
#define MEM_ADDR(addr)*((volatile unsigned long*)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define DEVICE_REG0 ((volatile unsigned long *) (0x40000000))
#define DEVICE_REG0_BIT0 ((volatile unsigned long *) (0x42000000))
#define DEVICE_REG0_BIT1 ((volatile unsigned long *) (0x42000004))
*DEVICE_REG0 = 0xAB;// 使用正常地址访问寄存器
*DEVICE_REG0 = *DEVICE_REG0 | 0x2; // 使用传统方法设置 bit1
*DEVICE_REG0_BIT1 = 0x1; // 通过位带别名地址设置 bit1
CM4
#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))
// IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE + 20) // 0x40020014
#define GPIOB_ODR_Addr (GPIOB_BASE + 20) // 0x40020414
#define GPIOC_ODR_Addr (GPIOC_BASE + 20) // 0x40020814
#define GPIOD_ODR_Addr (GPIOD_BASE + 20) // 0x40020C14
#define GPIOE_ODR_Addr (GPIOE_BASE + 20) // 0x40021014
#define GPIOF_ODR_Addr (GPIOF_BASE + 20) // 0x40021414
#define GPIOG_ODR_Addr (GPIOG_BASE + 20) // 0x40021814
#define GPIOH_ODR_Addr (GPIOH_BASE + 20) // 0x40021C14
#define GPIOI_ODR_Addr (GPIOI_BASE + 20) // 0x40022014
页:
[1]