STM32中的位带(bit-band)操作

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在 cm3 中,有两个区中实现了位带。其中一个是 sram 区的最低 1mb 范围,第二个则是片内外设区的最低 1mb范围。这两个区中的地址除了可以像普通的 ram 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。
位带操作的概念其实 30 年前就有了,那还是8051 单片机开创的先河,如今,cm3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版。
cm3 使用如下术语来表示位带存储的相关地址:
位带区:支持位带操作的地址区
位带别名:对别名地址的访问最终作用到位带区的访问上(这中途有一个地址映射过程)
在位带区中,每个比特都映射到别名地址区的一个字——这是只有 lsb 有效的字。当一个别名地址被访问时,会先把该地址变换成位带地址。
对于读操作,读取位带地址中的一个字,再把需要的位右移到 lsb,并把 lsb 返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程。
支持位带操作的两个内存区的范围是:
0x2000_0000‐0x200f_ffff(sram 区中的最低 1mb)
0x4000_0000‐0x400f_ffff(片上外设区中的最低 1mb)
对 sram 位带区的某个比特,记它所在字节地址为 a,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
aliasaddr=0x22000000+((a-0x20000000)*8+n)*4=0x22000000+(a-0x20000000)*32+n*4
对于片上外设位带区的某个比特,记它所在字节的地址为 a,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
aliasaddr=0x42000000+((a-0x40000000)*8+n)*4=0x42000000+(a-0x40000000)*32+n*4
上式中,“*4”表示一个字为 4 个字节,“*8”表示一个字节中有 8 个比特。
这里再不嫌啰嗦地举一个例子:
1. 在地址 0x20000000 处写入 0x3355aacc
2. 读取地址0x22000008。本次读访问将读取 0x20000000,并提取比特 2,值为 1。
3. 往地址 0x22000008 处写 0。本次操作将被映射成对地址 0x20000000 的“读-改-写”操作(原子的),把比特2 清 0。
4. 现在再读取 0x20000000,将返回 0x3355aac8(bit[2]已清零)。
位带别名区的字只有 lsb 有意义。另外,在访问位带别名区时,不管使用哪一种长度的数据传送指令(字/半字/字节),都把地址对齐到字的边界上,否则会产生不可预料的结果。
[cpp] view plain copy///////////////////////////////////////////////////////////////
//位带操作,实现51类似的gpio控制功能
//具体实现思想,参考第五章(87页~92页).
//io口操作宏定义
#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+12) //0x4001080c
#define gpiob_odr_addr (gpiob_base+12) //0x40010c0c
#define gpioc_odr_addr (gpioc_base+12) //0x4001100c
#define gpiod_odr_addr (gpiod_base+12) //0x4001140c
#define gpioe_odr_addr (gpioe_base+12) //0x4001180c
#define gpiof_odr_addr (gpiof_base+12) //0x40011a0c
#define gpiog_odr_addr (gpiog_base+12) //0x40011e0c
#define gpioa_idr_addr (gpioa_base+8) //0x40010808
#define gpiob_idr_addr (gpiob_base+8) //0x40010c08
#define gpioc_idr_addr (gpioc_base+8) //0x40011008
#define gpiod_idr_addr (gpiod_base+8) //0x40011408
#define gpioe_idr_addr (gpioe_base+8) //0x40011808
#define gpiof_idr_addr (gpiof_base+8) //0x40011a08
#define gpiog_idr_addr (gpiog_base+8) //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) //输入

如果说除了小米以外,还有哪些手机能够被称为价格屠夫?
通用领域大规模条件性知识图谱数据集
云计算已证明将会彻底改变会计行业,但是物联网呢?
OPPOR1s拆解评测:超越前代不止于屏
亚马逊前高管解答如何打造一台创新机器?
STM32中的位带(bit-band)操作
Nature解析中国AI现状,2030能引领全球吗?
瑞萨电子携手高通加速无线充电在主流智能手机中的应用
如何给LED小间距屏加触摸屏
沉铜(PTH)工艺的核心关键点
08年美国国际包装博览会
夏季高温天气将对光伏电站组件带来多个不利影响
同样是全面屏和骁龙710,坚果Pro 2S与360手机N7 Pro有何区别?你会选哪款?
吸引着手机厂商跨界做电视的主要原因是什么
整流电源的过压保护-压敏电阻及其应用
如何识别交换机性能的好坏,有哪些方法
魅蓝5S今日下午发布 国民质检员张全蛋捧场!
科大智能次发行募集资金总额不超过6.26亿元
半导体业者面临“看不见”难题,需看得到更小微粒的“新眼睛”
如何使用Arduino和Darlington ULN2803控制大功率电路