STM32开发系列之寄存器(二)GPIO口位带操作实现

位带操作顾名思义就是可以单独对CPU寄存器某个位进行读写操作,采取这种方式,将会大大减少我们写代码的难度,不用像上篇博客那样进行与或运算和移位,降低了出错的可能性。如果之前有使用过8051单片机的话,应该体会过这种操作的好处。
例如,在51单片机中,P1.0上挂了一个灯,我们想要他点亮,可以直接P1.0=0或者P1.0=1这样写,直接对P1端的某个IO口进行操作。但在STM32中并不允许这样操作,而为了在STM32中实现类似51那样的操作,就引入了位带操作这种机制。
在学习位带操作时,我们可以参考下Cortex-M3权威指南这份文档,从83页开始对位带操作的原理有很详细的讲解,在这里,我就不想将文档里的东西再照搬一遍,就说下我自己对这个位带操作机制的理解。
我们还是以上篇的按键控制LED灯为例,上一篇中写到GPIOB->CRL |= 0X00300000;,这一句我们还可以地址这样来写(*volatile unsigned long)0X4001 0C00 = (3<<20)|(0<<22);//这里暂时不管其他位,为什么这样写呢?我们先打开STM32的参考手册,在里面看到这样的一个表格
在这里插入图片描述
在这里插入图片描述
从以上图片我们看到GPIOB的起始地址为0X40010C00,且CRL寄存器的偏移地址为0X00,,这样GPIOB的CRL寄存器的地址便是0X40010C00,再接着我们看到CRL控制GPIOB5的bit为20、21、22、23,给这四个bit进行赋值。
在这里插入图片描述
配置好CRL后,我们就要控制GPIOB5的输出高低电平,原理跟CRL一样,先找出ODR寄存器的地址,再赋值。

(*volatile unsigned long)0X4001 0C0C = 1<<5;

然后我们要开启GPIOB的时钟,就要先找到APB2ENR寄存器的地址,在文档中我们找到RCC在位带区的地址为0x4002 1000,且RCC->APB2ENR的偏移地址为0x18,所以(*volatile unsigned long)0x4002 1018 = 1<<3;,就这样我们完成了点亮LED灯。但在实际操作上,我们基本不会看到这样写代码,更多的是看到如下代码,原理也跟上面所说的差不多。

#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)) 

BITBAND()是根据ST官方提供的别名区地址计算公式

AliasAddr= 0x42000000+((A‐0x40000000)*8+n)*4 =0x42000000+ (A‐0x40000000)*32 + n*4

来得到某个比特的别名区地址,“*4”表示一个字为4 个字节,“*8”表示一个字节中有8 个比特,addr为GPIO寄存器的别名区地址,bitnum为GPIO上第几个口。 MEM_ADDR()则是将地址转为指针,这样,我们就可以来对IO口进行以下的定义

#define GPIOA_ODR_ADDR    (0x4001 0800+12) //0x4001080C 
#define GPIOB_ODR_ADDR    (0X4001 0C00+12) //0x40010C0C 
#define GPIOC_ODR_ADDR    (0x4001 1000+12) //0x4001100C
#define PAOUT(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAIN(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
#define PBOUT(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBIN(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
#define PCOUT(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCIN(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入

实际上,在stm32f10x.h里已经有对GPIOA~G的别名区地址的宏定义,可以直接调用,不用自己去查。
现在我们采用位带操作来写下按键控制LED灯:

//LED初始化
RCC->APB2ENR|=1<<3;       
 GPIOB->CRL&=0XFF0FFFFF; 
 GPIOB->CRL|=0X00300000;     
 PBOUT(5) = 1;                   //  GPIOB->ODR |= 1<<5; 
 //按键初始化
 RCC->APB2ENR|=1<<6;       
 GPIOE->CRL&=0XFFF0FFFF;    
 GPIOE->CRL|=0X00080000;        
 PEOUT(4) = 1;                    //   GPIOE->ODR   |= 1<<4;
//按键扫描
if(PEIN(4) == 0)                  //(GPIOE->IDR&0x10)>>4
 {
 	PBOUT(5) = 0;             //GPIOB->ODR |= ~(1<<5)
 }
 else{
  PEOUT(5)=1;                     //  GPIOB->ODR |= 1<<5; 
 }

最后说句,学位带操作时,自己根据公式算下别名区地址,会比较容易搞懂,单纯看,有时会越看越晕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值