1 简介
GPIO指的是通用输入输出接口(General Purpose Input/Output Port, GPIO). 根据数据手册中列出的每个I/O端口的特定硬件特征, GPIO端口的每个位可以由软件分别配置成多种模式:
- 浮空输入:肖特基触发器前的两个开关均悬空,这种情况下默认输入电平不确定,端口状态极易受外部干扰而改变。
- 上拉输入:肖特基触发器前的上开关闭合,这种情况下默认输入电平为高电平。
- 下拉输入:肖特基触发器前的下开关闭合,这种情况下默认输入电平为高电平。
- 模拟输入:肖特基触发器“禁用”
- 开漏输出:输出端P-MOS管“禁用”,只有N-MOS管起作用. 当输出寄存器为
1
时N-MOS关闭,输出端处于高阻模式,无驱动能力;当输出寄存器为0
时N-MOS开启,I/O口接到 $V_{SS}$ ,输出低电平。一般作为通信协议的驱动方式,可避免多机通信的情况下设备之间的相互影响。此外,开漏模式可以在I/O外接5V电源,当输出寄存器为0
时由外部电源将电压拉高至5V以提供5V的电平信号。 推挽输出:P-MOS和N-MOS均起作用,当输出寄存器为
1
时N-MOS关闭、P-MOS开启,输出高电平;当输出寄存器为0
时N-MOS开启、P-MOS关闭,I/O口接到 $V_{SS}$ ,输出低电平。PMOS管:P沟道MOS管,开关保持断开,当提供栅极电压后才会闭合。
NMOS管:N沟道MOS管,开关保持闭合,当提供山脊电压后才会断开。复用开漏输出:类似开漏输出,但由片上外设所控制。
- 复用推挽输出:类似推挽输出,但由片上外设所控制。
2 相关寄存器
- 端口配置寄存器(GPIOx_CRL、GPIOx_CRH): 每四位为1组,四位中前两位用于配置I/O口的输人/输出模式、后两位用于配置输出速度(状态翻转频率)。
- 端口输入数据寄存器(GPIOx_IDR): 高16位保留,低16位为对应I/O口的状态.
- 端口输出数据寄存器(GPIOx_ODR): 高16位保留,低16位为用户设定的I/O状态.
- 端口设置/清除寄存器(GPIOx_BSRR): 高16位为端口清除位,为1则清除对应的I/O状态为0,为1则不影响;低16位为设置位,为0不影响,为1则将对应I/O置1.
- 端口清除寄存器(GPIOx_BRR):功能同GPIOx_BSRR的高16位.
3 初始化
- 首先需要使能时钟,而GPIO属于搭在APB2总线上的外设,因而使能APB2外设时钟即可(RCC_APB2ENR寄存器).
- 设置GPIO口模式,包括设置IO口的输出/输入模式、输出速度以及选择使用的端口PIN, 即配置GPIO_CRL/CRH寄存器。
- 配置I/O口状态,即配置GIIO_BSRR/GPIO_BRR(置1只能通过配置BSRR实现,清0则均可)
4 库函数实现GPIO输出
- 使能时钟
- 函数原型:
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
- 输入:
RCC_APB2Periph
门控APB2外设时钟,也就是指定对应的外设来选定其对应的时钟,GPIOx
口对应为RCC_APB2Periph_GPIOA
;NewState
: 指定使能(ENABLE)还是禁用(DISABLE)该外设的时钟。
- 设置IO口模式
- 函数原型:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
输入:
GPIOx
用于指定使用的GPIO口(注意不是PIN);GPIO_InitStruct
为指向GPIO_InitTypeDef
类型结构体的指针,其成员为GPIO口的状态(输入/输出模式、输出速度、PIN脚). 结构体定义为:typedef struct { uint16_t GPIO_Pin; // IO口PIN脚 GPIOSpeed_TypeDef GPIO_Speed; // 输出速度 GPIOMode_TypeDef GPIO_Mode; // 输入/输出模式 }GPIO_InitTypeDef;
- 配置IO口状态
- 函数1
- 函数原型:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
- 输入:
GPIOx
选择IO口,GPIO_Pin
选择PIN脚. 可以用或操作同时选中多个PIN - 功能:将IO口的PIN脚状态置为高电平
- 函数原型:
- 函数2
- 函数原型:
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
- 输入:
GPIOx
选择IO口,GPIO_Pin
选择PIN脚. - 功能:清空IO口的PIN脚状态为0,即低电平
- 函数原型:
函数3
- 函数原型:
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
输入:
GPIOx
选择IO口,GPIO_Pin
选择PIN脚,BitVal
指定待写入的值(为枚举BitAction
类型,定义如下.)typedef enum { Bit_RESET = 0, Bit_SET }BitAction;
- 功能:将
BitVal
写入IO口的PIN脚
- 函数原型:
函数4
- 函数原型:
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
- 输入:
GPIOx
选择IO口,PortVal
为写入的数值,前边的均为一位一位操作,而这个函数是16位同时进行. - 功能:将PortVal写入IO口
- 函数原型:
- 代码实现
选择GPIOA口的2、4PIN,输出模式设为推挽输出模式、输出速度为50MHz,将其状态置1后翻转。
int main(void){
// 使能时钟
RCC_APB2PeriphClockCmd(GPIOA, ENABLE);
// 定义初始化结构体
GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_4;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_Out_PP; // 配置为推挽输出
// 传入初始化结构体并进行IO口初始化
GPIO_Init(GPIOA, &GPIO_InitStructrue);
// 置位
GPIO_SetBits(GPIOA, GPIO_InitStructrue.GPIO_Pin);
GPIO_WriteBit(GPIOA, GPIO_InitStructrue.GPIO_Pin, Bit_SET);
GPIO_Write(GPIOA, 0X10);
//清空
GPIO_ResetBits(GPIOA, GPIO_InitStructrue.GPIO_Pin);
GPIO_WriteBit(GPIOA, GPIO_InitStructrue.GPIO_Pin, (Bit_Action)0);
GPIO_Write(GPIOA, 0X00);
while(1){
}
}
5 库函数实现GPIO输入
使能时钟
设置IO口模式
读取IO口状态
- 函数1
- 函数原型:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 输入:
GPIOx
选择IO口,GPIO_Pin
选择IO口PIN脚 - 功能:读取输入数据寄存器的某一位的值,即IO口对应PIN脚的状态
- 函数原型:
- 函数2
- 函数原型:
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
- 输入:
GPIOx
选择IO口 - 功能:读取输入数据寄存器的值,即读取IO口的状态
- 函数原型:
- 函数3
- 函数原型:
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 输入:
GPIOx
选择IO口,GPIO_Pin
选择IO口PIN脚 - 功能:读取输出数据寄存器的某一位的值,即IO口对应PIN脚的状态,查看用户输出的量
- 函数原型:
- 函数4
- 函数原型:
uint8_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
- 输入:
GPIOx
选择IO口 - 功能:读取输出数据寄存器的值,即读取IO口的状态,查看用户输出的量
- 函数原型:
- 代码实现
选择GPIOA口的0、11PIN接上按键,输入/输出模式设为上拉模式、输出速度为50MHz,通过按键按下实现控制GPIOB口的0PIN的LED点亮和熄灭。
void init(void){
// 使能时钟
RCC_APB2PeriphClockCmd(GPIOA | GPIOB, ENABLE);
// 定义初始化结构体
GPIO_InitTypeDef LED_InitStructrue, KEY_InitStructrue;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_PP; // 配置为推挽输出
// 传入初始化结构体并进行IO口初始化
GPIO_Init(GPIOB, &GPIO_InitStructrue);
KEY_InitStructrue.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_11;
KEY_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
KEY_InitStructrue.GPIO_Mode = GPIO_Mode_IPU; // 配置为上拉输入
GPIO_Init(GPIOA, &KEY_InitStructrue);
}
uint8_t get_KeyNum(void){
uint8_t KeyNum = 0;
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0);
Delay_ms(20);
KeyNum = 1;
}
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 0){
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
void LED1_ON(void){
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
void LED1_OFF(void){
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
void LED1_Turn(void){
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0) == 0){
GPIO_SetBits(GPIOA, GPIO_Pin_0);
return;
}
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
uint8_t KeyNum;
int main(void){
init();
while(1){
KeyNum = get_KeyNum();
if(KeyNum == 1){
LED1_ON();
}
if(KeyNum == 2){
LED1_OFF();
}
}
}