06、W601之位带操作
一、什么是位带操作
简单来说,就是用四个位表示一个位,把目标地址抽离出来映射成另一块地址,直接来进行操作。
那么为什么要这么干呢?因为CM3内核是不能像51单片机一样对某个独立IO口(P1.1 = 0)进行设置的,为了实现这个功能,CM3内核提出了位带操作这个概念。
二、位带区和位带别名区地址
如图所示,Peripheral区和SRAM区分别有一个1MB位带区(Bit Band region),两个区分别对应了一个32MB的位带别名区(Bit Band Alias)。
因为32位单片机一个地址对应的是一个字节(0x20000001-0x20000000=1),一个字节是8个Bit,所以从1M Bit膨胀到32M Bit相当于一个Bit变成了4Bit。
总结:位带区一个比特对应位带别名区的一个地址,位带别名区的地址每隔四位对应一个位带区的位
三、计算公式为
bit_word_addr = bit_band_alias_base + (byte_offset×32) + (bit_number×4)
bit_word_addr:是最终映射在位带别名区的目标地址,也就是可以进行(P1.1 = 1)这样操作的地址。
bit_band_alias_base:位带别名区的基地址
byte_offset:需要映射位的地址相对于位带区的基地址(0x40000000)的偏移量
bit_number是目标位所在位置(0-31)(一般PA5,bit_number就是5,不过还是要具体看手册决定)
四、举个栗子
//PA13是外设地址,位带区基地址是0x40000000,位带别名区基地址为0x42000000
#define HR_APB_BASE_ADDR 0x40010000
#define HR_GPIOA_BASE_ADDR (HR_APB_BASE_ADDR + 0x0C00)
#define HR_GPIOA_DATA (HR_GPIOA_BASE_ADDR + 0x0)
//我们用位带操作来控制LED亮灭————以PA13为例
//控制led的亮灭就是控制IO口的电平,控制IO口的电平就是控制GPIOA寄存器的数据寄存器,而数据寄存器相当于GPIOA的基地址偏移为0
#define LED_R *((volatile unsigned long *)(0x42000000+((HR_GPIOA_DATA - 0x40000000)*32)+(13*4)))
经过测试,这是对的。
下面就可以封装一下了,就直接用正点原子写的吧,上代码
//稍微解释一下
//addr & 0xF0000000的结果就是0x40000000,再加上0x2000000就是0x42000000
//addr& 0xFFFFF就是偏移量,在左移5其实就是乘以32(2的5次方),同理bitnum<<2就是bitnum*4
//其实跟上面直接写的是一样的
#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 PAout(n) BIT_ADDR(HR_GPIOA_DATA,n) //输出
#define PAin(n) BIT_ADDR(HR_GPIOA_DATA,n) //输入
#define PBout(n) BIT_ADDR(HR_GPIOB_DATA,n) //输出
#define PBin(n) BIT_ADDR(HR_GPIOB_DATA,n) //输入
至此,应该对位带操作了然于胸了。