SRM32-GPIO_位带操作(3)

本文基于STM32F407ZGT6,适用于绝大部分M3/M4内核的STM32芯片。
——————————————————————————————
STM32的IO口初始化完毕了,然后我们想进行一个点亮流水灯\打开蜂鸣器这样的操作。
很简单,只要把IO口置高电平或低电平即可,我们可以调用STM32的库函数去实现这样的操作。
可以实现该功能的库函数有五个:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//设置某一个GPIO口为高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//设置某一个GPIO口为低电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); //用于配置BSRRH/BSRRL寄存器,设置一个IO口电平
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);//用于配置ODR,设置一组IO口电平
void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//用于GPIO口输出电平的反转

一般我们使用的是前两个函数GPIO_SetBits和GPIO_ResetBits。

其实使用库函数去设置电平已经很方便了,但是我们想要更方便更直观一些,就像操作51单片机一样直接操作IO口。
那么这就涉及了STM32的位带(原子)操作了。

以下内容摘自《Cortex M3权威指南》
其实M3的内核与M4内核的区别不大,总线结构和储存结构存在差别还有M4引入了浮点运算。

在CM3中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。
在这里插入图片描述
在这里插入图片描述
位带区:在这里插入图片描述
位带别名区:在这里插入图片描述
位带区的每个bite对应位带别名区地址(32bite)
即位带区0X2000 0000地址的第0位(1个bite)对应了位带别名区的地址0X2200 0000—0X2200 0004,一共4个字节(32个bite)。
在这里插入图片描述
具体的例子:

-1. 在地址 0x20000000 处写入 0x3355AACC

  • 2.读取位带别名区地址 0x22000008。本次读访问将读取到0x20000000,并提取了比特 2,值为 1。
  • 3.往位带别名区地址 0x22000008 处写 0。本次操作将被映射成对地址 0x20000000 的“读-改-写”操作(原子的),把比特 2 清 0。
  • 4.现在再从位带区地址读取 0x20000000,将返回值 0x3355AAC8(bit[2]已清零)。
    位带操作C语言实现方法:
    在 C 编译器中并没有直接支持位带操作。比如, C 编译器并不知道同一块内
    存能够使用不同的地址来访问,也不知道对位带别名区的访问只对 LSB 有效。欲在 C 中使用
    位带操作,最简单的做法就是#define 一个位带别名区的地址。
    例如:在这里插入图片描述
    下面引用以下原子哥写好的例程:
	 
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#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)) //这条语句很巧妙,把前面两个地址合并到一个语句中来
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#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)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

这样子STM32的GPIO口原子操作就完成了。想要输出高低电平将由
库函数模式:

GPIO_ResetBits(GPIO_Pin_9);//GPIO9设置低
GPIO_SetBits(GPIO_Pin_10);//GPIO10设置高

变成了位带操作:

//LED端口定义
#define LED0 PFout(9)	// 宏定义
#define LED1 PFout(10)	// 宏定义
LED0=0;			  //GPIOF9置低,LED0亮
LED1=1;				//GPIOF9置高,LED1灭	

可以看出用位带操作程序可读性提高了,而且位带操作使程序写起来更加简便。免除了库函数的跳转执行,程序的指令减少了,效率也提高了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值