我对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条回答
xiatianyun
1楼-- · 2019-07-21 19:52
谢谢分享,很受启示。
warship
2楼-- · 2019-07-21 20:18
 精彩回答 2  元偷偷看……
warship
3楼-- · 2019-07-22 02:16
首先是,如何才能事先得到变量的地址呢?
这就需要用到C语言的一项功能,声明变量时可以指定地址:
方法是使用attribute,格式是__attribute__((at(addr)))
注意要求是双下划线,再加双括号,
addr就是我们可以指定的地址。
warship
4楼-- · 2019-07-22 05:23
 精彩回答 2  元偷偷看……
warship
5楼-- · 2019-07-22 06:04
本帖最后由 warship 于 2018-7-8 09:58 编辑

这里我建议大家使用下面的方法:
这个方法沿袭了前面寄存器位带操作的成果
便于加深理解,并且感觉比上面的结构体少占内存。
代码是:
#define MyFlag_Addr    0x2000A100   //选择SRAM区的一个固定地址
//定义一个全局变量作为集中的标志位寄存器, 分配于上述固定地址
volatile u32 MyFlag __attribute__((at(MyFlag_Addr)));

#define Myflg(n) BIT_ADDR(MyFlag_Addr,n)  //可以通过改变n访问不同的标志位

//也可以通过下列宏定义访问单一的标志位, 每一个标志位可以自定义一个有明确意义的名称
#define Myflg0   BIT_ADDR(MyFlag_Addr,0)  //标志位0
#define Myflg1   BIT_ADDR(MyFlag_Addr,1)  //标志位1

//访问时用例:  Myflg(n)=1; 或者: Myflg0=1;
完整的测试代码,详见21楼
warship
6楼-- · 2019-07-22 10:21
本帖最后由 warship 于 2018-7-4 20:13 编辑

另外,还有一种方法也一样,那就是: 直接定义一个常数指针变量指向该标志位寄存器的别名区基址
volatile u32* const pMyFlag=(void *)(0x22000000+((MyFlag_Addr &0xFFFFF)<<5));
//用const通知编译器:该指针不能再被修改而指向其它地址

//访问时用例:  *(pMyFlag+n)=1; 或者数组形式: pMyFlag[n]=1;

以上两种方法和所贴出来的代码,我今天已经完全验证过了,可以放心使用。

一周热门 更多>