在51单片机中,我们可以通过sbit命令来操作存储器中的位,在STM32中,我们同样可以操作存储器中特定的位。
1、为何使用位带操作?
总结来说,一个是因为访问速度快,另一个是因为安全。
如果在裸机开发中,位带操作相比于直接的读-改-写操作除了访问速度快一点以外好像也没有什么可以说的了,但是如果在带操作系统的开发中,多任务并发运行的时候就有可能在任务切换的过程中发生不可预料的问题,而位带操作由于是属于硬件完成的不可被异常打断的操作(原子操作),所以相对于读-写-改的操作模式的话会更安全些。
2、位带操作的优点
《Cortex M3权威指南》中推荐的是在IO密集型的底层代码中使用。在时序要求较严格的情况下,可以使用位带操作,如使用IO口模拟某种通信协议。另外,由于位带操作异常不可打断(原子操作),在带操作系统的开发中出于安全性考虑可以使用位带操作。
3、位带操作原理
STM32中有两个位带:SRAM位带区(地址0x2000,0000~0x200F,FFFF共1MB)以及片上外设位带区(地址0x2000,0000~0x200F,FFFF共1MB),分别将其一一对应到各自的32M大小的位带别名区。
即:位带区的一个字节空间对应位带别名区的32个字节空间,而一个字节包含8bit,因此位带区的每1bit对应位带别名区的32个bit,共4个字节,每个字节对应一个地址,即4个地址,取这4个地址中最低位的地址对应选中的bit位。
SRAM位带别名区计算公式:Address=0x2200,0000+(m-0x2000,0000)*8*4+n*4
片上外设位带别名区计算公式:Address=0x4200,0000+(m-0x4000,0000)*8*4+n*4
式中:m为位带区的地址(0x2000,0000~0x200F,FFFF),n为位带区对应字节的第0~7位,
为了计算方便,统一为如下公式:
Address=(m & 0xF000,0000)+0x0200,0000+(m & 0x000F,FFFF)<<5+n<<2
其中:(m & 0xF000,0000)目的是为了取出最高位的4和2,分别对应SRAM和片上外设;0x0200,0000为固定偏移;(m & 0x000F,FFFF)<<5是为了计算m偏移0x2000,0000的量,<<5相当于乘以32;n<<2计算每一位对应的字节地址偏移;
4、用位带控制GPIO口点灯
以GPIOA口的pin0口点灯操作为例。我们知道,GPIOA口的ODR寄存器的低16位控制GPIOA PA0~PA15的输出,如下图:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) //按公式计算位带别名区的地址
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) //强制unsigned long类型转换后解引用
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) //获得的位带别名区地址所存储的值
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启APB2时钟
GPIO_InitTypeDef GPIO_InitStructure={GPIO_Pin_0,GPIO_Speed_50MHz,GPIO_Mode_Out_PP};//声明GPIO类型结构体并初始化
GPIO_Init(GPIOA, &GPIO_InitStructure);//配置好GPIOA口
while (1)
{
BIT_ADDR((uint32_t)&GPIOA->ODR ,0)=0x001;//为GPIOA口ODR寄存器的0位赋值1,对应pin0口
Delay_ms(500);//延时500ms
BIT_ADDR((uint32_t)&GPIOA->ODR ,0)=0x000;//为GPIOA口ODR寄存器的0位赋值0,对应pin0口
Delay_ms(500);
BIT_ADDR((uint32_t)&GPIOA->ODR ,0)=0x111;//仅末位值有效,无论位带别名区的32位空间中存储什么值,都只取末尾的值赋给对应的位带区的1位
Delay_ms(500);
BIT_ADDR((uint32_t)&GPIOA->ODR ,0)=0x110;//仅末位值有效,无论位带别名区的32位空间中存储什么值,都只取末尾的值赋给对应的位带区的1位
Delay_ms(500);
}
}
5、程序现象