上次我们讲解了操作GPIO的方法和核心代码,分别是寄存器版和固件库版,今天我们讲最后一种操作GPIO的方法,—位带操作。
之前我们在学习51单片机的过程中,
我们是用“sbit”关键字---位定义的方法去操作一个引脚,
CPU在(读/写)时也就是1位的数据量,
那为什么51单片机就可以使用这样位定义“sbit”的操作?
:因为,我们可以点开<reg51.h>这个头文件,
中看到 SFR 关键字,后门跟的是寄存器的名字,或一串16进制数,
这就是51中的存储器映射和寄存器映射,
通过SFR关键字把引脚也封装了起来,这样我们也就可以位操作。
#include"reg51.h" //C51头文件
sbit LED0 = P2^0; //位定义
void delay(unsigned int n);
void main()
{
while(1) //led灯的闪烁
{
LED0 = 0;
delay(500);
LED0 = 1;
delay(500);
}
}
void delay(unsigned int n)//非标准延时,大约是1μs
{
unsigned int i=0,j=0;
for(i=0;i<n;i++)
{
for(j=0;j<120;j++);
}
}
问题来了,难道咱们的STM32就没有这么便捷的操作嘛?
:肯定是有啊!但是STM32 没有这样的关键字,而是通过访问位带别名区来实现。
在 STM32 中,有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,令一个是外设区最低 1MB 空间。这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这 1MB 的空间的每一个位膨胀成一个 32 位的字,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。
简单说,就是通过一个公式,和一些宏定义的封装,
我们就可以实现,类似“sbit”的位操作,
咱们直接上代码,去理解~
#include "stm32f10x.h"
// 这里只定义了 GPIO ODR和IDR这两个寄存器的位带别名区地址,其他寄存器的没有定义
// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))
// 把一个地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//拿GPIOB端口的引脚为例
// GPIO ODR 和 IDR 寄存器地址映射
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
// 单独操作 GPIO的某一个IO口,n表示具体是哪一个IO口
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
void Delay(uint32_t Count); //声明简单延时函数
void LED_GPIO_Config(void); //声明LED端口初始化函数
/*==================主程序入口======================*/
int main(void)
{
LED_GPIO_Config();
while(1)
{
// PB5 = 0,点亮LED
PBout(5)= 0;
Delay(0x0FFFFF);
// PB5 = 1,熄灭LED
PBout(5)= 1;
Delay(0x0FFFFF);
}
}
/*=================声明函数的具体实现================*/
void LED_GPIO_Config(void)
{
// 定义一个GPIO_InitTypeDef类型的结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOB的时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
// 选择要控制的IO口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
// 设置引脚为推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 设置引脚速率为50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
/*调用库函数,初始化GPIOB0*/
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 关闭LED
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
// 简单延时函数
void Delay(uint32_t Count)
{
for(; Count != 0; Count--);
}