我对STM32所用位带操作宏应用推广探索研究

在原子例程的sys.h中,使用宏定义建立了位带操作的基础,
使得操作IO端口可以像51一样实现位操作。
其实深入了解了位带操作的原理,几乎就可以实现对STM32所有外设寄存器的访问,
极端情况下,什么库函数版本,什么寄存器版本都可以不用,直接精准地操控所有寄存器的每一位的读写!!!

知道了STM32将所有外设寄存器的每一位都建立了位带别名区,
你只要再花一点点时间,彻底搞明白下面的三句宏定义,位带操作就都不在话下了:
#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))

这三句是一环套一环的,
首先第一句:
#define BITBAND(addr, bitnum) ((addr & 0xF000 0000)+0x200 0000+((addr &0xF FFFF)<<5)+(bitnum<<2))
这一句定义了位带存储地址的计算方法,
知道了寄存器的地址,以及我们关心的寄存器的某一比特位,就可以根据此计算方法算出其对应的别名区地址
这个计算公式不仅对外设寄存器对应的别名区计算有用,对用户SRAM对应的别名区一样适用。
addr & 0xF000 0000 只取绝对地址的最高4位,实际上是用来区分段的,是寄存器段还是SRAM段。
+0x200 0000(值为32M)是别名区相对位段区的地址偏移量,别名区在相应位段上方的32M处;
(addr &0xF FFFF)<<5) 位段地址膨胀32倍,左移5位即可;
(bitnum<<2)由于每1比特膨胀为32位,32位占用4个字节的存储位置,所以计算地址时要乘以4,左移2位即是;

然后是第二句
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
上一句计算出来的地址只是一个数值,要将它强制转化成一个地址(并且声明这个地址存储的是一个32位的long型变量)
用(unsigned long )(addr) 即可,这样就成了一个真正的有血有肉的地址了。
前面再加一个
号,就可以访问这个地址得到其中的变量值了。
在C语言中,unsigned char *p; 定义p为一个指向unsigned char的地址指针;而 *p=1;就是向这个指针指向的地址所存储的变量赋值为1了。
至于中间加一个volatile关键字,则指示编译器不要自作主张对此进行优化,必须每次老老实实地去直接访问这个地址!!!

第三句呢?毫无难度,就是以前两句宏为基础的结合
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
给定寄存器的绝对地址addr,以及我们关心的比特位号bitnum,
先用BITBAND宏算出别名区对应的地址值
再用MEM_ADDR宏去访问这个地址

简单吧,这就是所有的位操作的奥秘了!!
有了这三句,你就可以完成所有的位操作,让我们举一个实例,比方说要置位GPIO A口的第9位,即让PA9输出高电平。
我们只须知道控制GPIO A的寄存器ODR的地址就行了,这个去查一下用户手册就行了,
一般手册上会给出两项,一是外设寄存器的基址,GPIOA的基址是0x4001 0800, 再找ODR,手册上一般给出其偏移量0C,
也就是说,GPIOA的ODR寄存器是0x4001 0800+0C=0x4001 080C
什么?你不知道寄存器的地址怎么查? 哈哈,早有人替你查好了,并且为你查好,定义了下列宏:
#define GPIOA_ODR_Addr (0x4001 0800+0C) //0x4001080C
并且一切为你着想,好事做到底,还定义了宏:
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n)

简单到你想置位GPIO A口的第9位,只须使用语句:PAout(9)=1;就行了。

怎么是这样的呢?因为有前面这些宏定义为基础,
反正闲着没事儿,我就当一回编译器,把这句PAout(9)=1一步步地编译出来,宏的展开就是一个替换的过程:
PAout(9)=1;因为定义了PAout(n) 要替换成 BIT_ADDR(GPIOA_ODR_Addr,n),所以展开成:
BIT_ADDR(GPIOA_ODR_Addr,9)=1;因为定义了BIT_ADDR(addr, bitnum) 要替换成 MEM_ADDR(BITBAND(addr, bitnum)),所以展开成:
MEM_ADDR(BITBAND(GPIOA_ODR_Addr, 9))=1;因为定义了BIT_ADDR(addr, bitnum) 要替换成 MEM_ADDR(BITBAND(addr, bitnum)),所以展开成:
MEM_ADDR((GPIOA_ODR_Addr & 0xF0000000)+0x2000000+((GPIOA_ODR_Addr &0xFFFFF)<<5)+(9<<2))=1;
最后一步,因为定义了MEM_ADDR(addr)要替换成 *((volatile unsigned long )(addr))
所以展开成为如下的语句,不要晕倒哦,
((volatile unsigned long *)((GPIOA_ODR_Addr & 0xF0000000)+0x2000000+((GPIOA_ODR_Addr &0xFFFFF)<<5)+(9<<2)))=1;

神奇吧?
一句 PAout(9)=1;
与 *((volatile unsigned long *)((GPIOA_ODR_Addr & 0xF0000000)+0x2000000+((GPIOA_ODR_Addr &0xFFFFF)<<5)+(9<<2)))=1;
是完全等效的。
而这,就是宏定义的效能和魅力!

晚上有直播课程这些底层开发的技术我会详细讲晚上7点就开始迟了就错过喽!
https://www.99qibang.cn/information/451650c454d94280a207068182d66ca2.html
https://www.99qibang.cn/information/ab4b35fecaf3426198229c632ea85e37.html
https://www.99qibang.cn/information/d70c8d90ec5045d7b170625c7e096e33.html
https://www.99qibang.cn/information/2f0c451607574650b4bf620aea4ae30f.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值