GPIO
一.相关寄存器
片上外设挂载在三条总线上,它们是APB1、APB2(Advanced Peripheral Bus)和AHB(Advanced High-Speed Bus)。APB1挂载低速外设,APB2和AHB挂载高速外设。相应总线的最低地址我们成为总线的基地址,也就是挂载在该总线上的首个外设的基地址。下表列出了各总线地址。

GPIO属于高速外设,挂载到APB2上。GPIO有8组分为A、B、C、D、E、F、G、H,每组有16位。各组的基地址见下表:

为了控制GPIO的行为,每组GPIO有7个相关寄存器,它们都是32位的,在该外设基地址上顺序排列,寄存器的位置可以用相对于该GPIO端口的偏移来描述。比如,对GPIOB来说:

- CRL: 配置寄存器,32位,用于配置低八位引脚的工作模式和速度
- CRH: 配置寄存器,32位,用于配置高八位引脚的工作模式和速度
- IDR: 输入数据寄存器,32位低16位有效,只读,从这里可以读引脚电平
- ODR: 输出数据寄存器,32位低16位有效,可读可写,写这里可以直接控制输出状态
- BSRR: 位设置和清除寄存器,32位,只写,高16位写1清除相应的ODR位,低16位写1设置相应的ODR位。
- BRR: 位清除寄存器,32位低16位有效,只写,写1清除相应的ODR位。
- LCKR: 端口配置锁定寄存器,32位低17位有效,可读可写,位[15:0]用于锁定相应的GPIO端口配置;位[16],锁键,可随时读出,但只可以通过锁键写入序列修改。写入序列:写1->写0->写1->读0->读1。对bit16执行写入序列的操作后,LCKR被锁定,同时由其指定的引脚所对应的CRL,CRH中的设置也被锁定,直至系统复位才能修改。
GPIO端口中的每个位都可以分别设置为多种模式:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟输入
- 开漏输出
- 推挽输出
- 推挽式复用功能
- 开漏复用功能
typedef enum{
GPIO_Mode_AIN = 0x0, //analog in
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28, //in pull down
GPIO_Mode_IPU = 0x48, //in pull up
GPIO_Mode_Out_OD = 0x14, //open drain
GPIO_Mode_Out_PP = 0x10, //push pull
GPIO_Mode_AF_OD = 0x1c, //alternate fuction open drain
GPIO_Mode_AF_PP = 0x18, /alternate function push pull
} GPIOMode_TypeDef;
输出模式可以有不同的速率(10MHz, 2MHz, 50MHz)。输入模式下读取外部引脚电平实际是按照APB2总线的速率采样到输入数据寄存器中的,一般为72MHz。
typedef enum{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

二.使用GPIO
1. 打开GPIO端口时钟
即设置RCC_APB2ENR中的GPIO的相应位。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);
或者:
RCC_APB2ENR |= RCC_APB2Periph_GPIOx;
这里,库函数的原型是
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
其中第一个参数是该外设时钟使能位在RCC_APB2ENR中的掩码或组合,这些掩码已经定义成宏,名字为RCC_APB2Periph_xxx其中xxx是外设的名字,比如GPIOA、AFIO、ADC1等。
第二个参数必须是FunctionState类型,这是一个枚举类型,定义为:
typedef enum {DISABLE = 0, ENABLE = !DISALBE} FunctionalState;
这个函数负责置位RCC_APB2ENR中的外设使能位。
2. 设置GPIO工作方式
设置GPIOx的CRL或CRH,从而选定该IO端口相应位的工作模式(即前述的八种之一)以及速率。表达这些设定需要4个bit(有四种输入模式分别对应3种速度,外加4种输入模式,共16种情况),而一个端口有16个独立的IO位,所以需要两个4字节的空间表述一个端口的IO设置。CRL对应低8位IO口,CRH对应高八位。如果使用库函数的话,具体bit设置细节不需操心了(没意思)。只需要知道有GPIOMode_TypeDef中描述的四种输入输出方式,而且每种输出可以设置三种速度(10MHz, 2MHz, 50MHz)。
GPIO_TypeDef结构体:
该结构体用来描述某个IO端口的各个寄存器内容,不用来定义变量,而是用来进行指针转换,进而引用某个寄存器。这个类型定义的指针一般是某个GPIO端口的基地址,通过它可以访问该GPIO的所有寄存器。
typedef struct{ //成员的顺序跟实际寄存器偏移顺序相同
__IO uint32_t CRL; //#define __IO volatile
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
···
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
···
#define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)
GPIO_InitTypeDef结构体:
该结构体用来描述一个GPIO端口某个引脚的工作方式,包括Mode和Speed。
typedef struct{
uint16_t GPIO_Pin; //这个值可以是1 << i,其中i为0~15,表示Pin0~Pin15。也可以用已定义的常量宏GPIO_Pin_0 ~ GPIO_Pin_15来给他赋值。
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
要实现对GPIO端口寄存器的设置还要执行GPIO_Init函数。
void GPIO_Init( GPIO_TypeDef* GPIOx,
GPIO_InitTypeDef* GPIO_InitStruct);
该函数将变量GPIO_InitStuct中的配置信息写入到GPIOx所指定的GPIO端口寄存器中。
3. 进行IO操作
IO操作库函数:
基本操作
void GPIO_SetBits ( GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin
)
void GPIO_ResetBits ( GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin
)
//读出GPIOx所指定的端口中相应引脚的值
uint8_t GPIO_ReadInputDataBit( GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读出GPIOx端口所有位的值,一个16位的无符号整数
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
//返回GPIOx端口某个引脚目前正输出的值
uint8_t GPIO_ReadOutputDataBit( GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//返回GPIOx端口正输出的值,16位
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
typedef enum{
Bit_RESET = 0,
Bit_SET
}BitAction;
//向某个端口的某个引脚写入某个比特位(0或1)
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
//让某个端口输出特定的值,16位
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal));
了解
//位设置锁定
void GPIO_PinLockConfig ( GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin
)
//todo
void GPIO_PinRemapConfig ( uint32_t GPIO_Remap,
FunctionalState NewState
)
void GPIO_StructInit ( GPIO_InitTypeDef * GPIO_InitStruct )
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface) ;
三. IO复用和重映射
MCU中的各种外设都需要进行IO交换,然而引脚数量有限,故GPIO引脚可以复用为片内外设的IO端口。一个外设默认与某个具体的GPIO端口对应,也可以通过设置复用重映射改变对应关系。
比如,USART1_TX引脚与PA9复用、USART1_RX与PA10复用。这是重映射寄存器位默认为0时的对应关系。若此位为1,USART1_TX与PB6、USART_RX与PB7对应。
使用USART1时,要先配置好相应的复用GPIO引脚的工作状态,然后配置USART1外设本省的状态就OK了。
四. AFIO寄存器描述
注意: 对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作之前,要打开AFIO的时钟(在RCC_APB2ENR中)。
4.1 事件控制寄存器(AFIO_EVCR)
用于控制Cortex的EVENTOUT是否使能,并连接到某个端口的具体引脚上。
4.2 复用重映射和调试IO配置寄存器(AFIO_MAPR)
主要用来设置各种外设IO的重映射方式。
4.3 外设中断配置寄存器组
用来控制外部中断对应的GPIO位置。EXTIi(i = 0 ~ 15)只能对应GPIOx的Pin_i,但是可以有多个GPIO端口可选。
本文详细介绍了GPIO的相关寄存器,包括配置寄存器、输入数据寄存器、输出数据寄存器等,并阐述了如何打开GPIO端口时钟、设置工作模式以及进行IO操作。还探讨了IO复用和重映射的概念,以及AFIO寄存器在事件控制、复用重映射和中断配置中的作用。
237

被折叠的 条评论
为什么被折叠?



