位操作由51单片机而来,结合自己最近看到的一个关于Cortex-M3位操作的介绍,说一下自己对CM3的位带操作的理解以及自己的实际应用。
支持了位操作以后,可以使用普通的加载/存储指令来对单一的bit进行读写。在CM3中,有两个区中实现了位带。其中一个是SRAM区的最低1MB(0x20000000-0x200FFFFF)范围,第二个是片内外设区的最低1MB(0x40000000-0x400FFFFF)范围。这两个区中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个bit膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始bit的目的。
将位带操作的位带区的每一bit映射到别名地址区的一个字(这是只有LSB有效的字)。当一个别名地址被访问时,会先把该地址变换成位带地址。对于读操作,读取位带地址中的一个字,把需要的位右移到LSB,并把LSB返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程。SRAM区的别名区是从0x22000000开始的,外设的别名区是从0x42000000开始的。
简言之,位带操作就是通过位带别名区对位带区进行位操作。
例如要设置地址0x2000 0000中的bit2,则使用位带操作与不使用位带操作的汇编代码如下:
Without Bit-Band:
LDR R0,=0x20000000;
LDR R1, [R0];
ORR.W R1, #0x04;
STR R1, [R0];
With Bit-Band:
LDR R0, =0x22000008;
MOV R1, #1;
STR R1, [R0];
读取地址0x20000000中的bit2,有位带与无位带操作的比较:
无位带:
LDR R0, = 0x20000000;
LDR R1, [R0];
UBFX.W R1, R1, #2, #1;
有位带:
LDR R0, = 0x22000008;
LDR R1, [R0];
由上述可见,位带区操作使程序变的简洁,位带操作对于硬件I/O密集型的底层程序最有用处,另外还可以在多任务中,实现共享资源在多任务之间的“互锁”访问。
但C编译器中没有直接支持位带操作,要在C语言中使用位带操作,可以定义如下两个宏:
把“位带地址+位序号”转换成别名地址:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x20000000 + ((addr & 0xFFFFF)<<5) + (bitnum<<2))
把地址转换成一个指针:
#define MEM_ADDR(addr) *((volatile unsigned long *) (addr))
STM32的SRAM从0x20000000开始只有64K,可见整个内存区都支持位带操作。我在内存中定义一个变量uint32_t Flag,然后取其地址,addr= &Flag;那么我可以通过位带操作(用上面的两个宏)对Flag的每一位进行独立操作,如此,我便获得了32个标志位,每个标志位有0x0, 或者0x1两种状态。这种操作,在实际使用中暂未发现有什么问题,很稳定。所以,不用再为如何设置标志位而烦恼了。
另外,我们在阅读MCU的数据手册时,有时会发现其外设寄存器有些支持位操作,有些只能半字或者字访问,不知道跟位带区是否有关,这部分就没有细细去看了。有知道的朋友请赐教。
上述,只是对位带操作进行了简单介绍,对于它的优越之处,及其他详细信息可以参考《Cortex-M3权威指南》和《Cortex-M3技术参考手册》。本文也是依据这两份资料而来。如有不对之处,欢迎批评指正。
只是平时看资料的时候,觉得一些东西挺有意思的,便记录下来。既方便了大家,也留着自己以后查询。