我对STM32所用位带操作宏的超详细剖析、优势分析及应用推广探索研究(持续更新,欢迎讨论交流)

2019-07-20 23:21发布

本帖最后由 warship 于 2018-7-16 19:56 编辑

在原子例程的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))



************************************************************************************************
注:本文后文所探索的寄存器位段操作宏定义包含在另文所附范例(外部中断试验的工程包)中,并随时更新。
有需要研究探讨的网友,可移步下载http://www.openedv.com/forum.php ... d=274724&extra=


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
49条回答
warship
1楼-- · 2019-07-23 07:38
本帖最后由 warship 于 2018-7-9 17:08 编辑

没有人回帖讨论,有点孤独,但我还是将专题研究进行到底。
回到在SRAM区实现位操作的话题。

个人认为,12楼所介绍的方法是最理想的方法,
手工分配一个固定地址存放标志位综合寄存器就好了。
有些人可能要问,如果不用手工分配固定地址的方法行不行?
我今天也进行了一些探索,是完全可行的。

那就是在程序中查询地址。
闲话少说,直接上代码:
u32 MyFlag;   //定义一个我们用于存放各类标志位的全局变量
volatile u32*  pMyFlag;   //再定义一个指向此变量对应的别名区地址的指针

//   在系统初始化代码中,查询并计算出这个别名指针的具体值
        u32 *pwMyFlag;  //临时定义一个指向MyFlag变量本身的指针
        pwMyFlag=&MyFlag;    //查询该变量的地址赋值给pwMyFlag
        pMyFlag=(void *)(0x22000000+(((u32)pwMyFlag & 0xFFFFF)<<5));  //计算出对应的别名区地址

//这样,所有的准备工作就完成了,以后可在程序的任何地方使用位操作
//访问时用例-置位某标志:  *(pMyFlag+n)=1; 或者数组形式: pMyFlag[n]=1;
//条件语句用例: if(pMyFlag[3])    pMyFlag[3]=0;
warship
2楼-- · 2019-07-23 08:33
 精彩回答 2  元偷偷看……
39035605
3楼-- · 2019-07-23 11:17
厉害厉害
warship
4楼-- · 2019-07-23 16:41
39035605 发表于 2018-7-7 13:38
厉害厉害

多谢支持。
warship
5楼-- · 2019-07-23 22:40
本帖最后由 warship 于 2018-7-8 09:23 编辑

继续研究,对21楼的测试例程进行反汇编如下:
   143:         MyFlag=2;  //赋值为2,即最低四位(下同,共32位这里只关注低四位的变化)为0010
0x0800019A 2002      MOVS     r0,#0x02
0x0800019C 49FE      LDR      r1,[pc,#1016]  ; @0x08000598处所存的内容(MyFlag分配的地址值)赋值给R1
0x0800019E 6008      STR      r0,[r1,#0x00]        ;寻址这一MyFlag地址,将R0的值存入
   144:         if(Myflg1) Myflg1=0;  //如果BIT1为1,则清为0,此时MyFlag变为0000
   145:                  
0x080001A0 48FE      LDR      r0,[pc,#1016]  ; @0x0800059C,标志位Myflg0的地址(2203 0000),指向别名区
                                             
0x080001A2 6840      LDR      r0,[r0,#0x04]  ; 加#4后为标志位Myflg1的地址,指向别名区
0x080001A4 B110      CBZ      r0,0x080001AC
0x080001A6 2000      MOVS     r0,#0x00
0x080001A8 49FC      LDR      r1,[pc,#1008]  ; @0x0800059C,(此处存地址值2203 0000)
0x080001AA 6048      STR      r0,[r1,#0x04]
   146:         Myflg3=1;   //置位BIT3   
0x080001AC 2001      MOVS     r0,#0x01      
0x080001AE 49FB      LDR      r1,[pc,#1004]  ; @0x0800059C(此处存地址值2203 0000)
0x080001B0 60C8      STR      r0,[r1,#0x0C]  ; 加#12后为标志位Myflg3的地址,指向别名区
......
......

0x08000598 1800      DCW      0x1800       ; 这里存放了我们手工指定的地址值:2000 1800
0x0800059A 2000      DCW      0x2000
0x0800059C 0000      DCW      0x0000      ; 这里存放了计算出来的别名区基址:2203 0000
0x0800059E 2203      DCW      0x2203
warship
6楼-- · 2019-07-24 03:33
本帖最后由 warship 于 2018-7-8 09:47 编辑

由上楼可以清楚的看到:
编译出来的最终目标代码,为32位的变量MyFlag按我们手工指定,分配了固定地址:2000 1800
编译器根据我们的宏定义,算好了别名区基址:2203 0000
当我们访问Myflg0时,目标代码执行的是访问地址2203 0000
当我们访问Myflg1时,目标代码执行的是访问地址2203 0000+4
当我们访问Myflg2时,目标代码执行的是访问地址2203 0000+8
当我们访问Myflg3时,目标代码执行的是访问地址2203 0000+12
......
即访问Myflg[n]时,目标代码执行的是访问地址2203 0000+4*n;
KEILC编译器是把这些地址当成一般的正常地址来对待的,
而实际执行时,由STM32的内部机制,把对这些实际并不存在的地址的访问,
映射并执行成了对相应比特位的访问。

一周热门 更多>