一、GPIO简介
GPIO(General Purpose Input Output)通用输入输出口,GPIO是控制或者采集外部器件信息的外设,即负责输入和输出。GPIO按组分配(GPIOA~GPIOG),每组16个引脚(PA0~PA15),其中部分IO口只能使用3.3V电平,但绝大部分IO口兼容5V电平(带有FT标志)。若引脚设置为模拟输入模式,则不能接入5V电平。在STM32中,所有的GPIO都挂载在APB2总线上。
GPIO有以下八种工作模式:
模式 | 性质 | 特征 |
浮空输入 | 数字输入 | 可读取引脚电平,若引脚悬空,则电平不确定 |
上拉输入 | 数字输入 | 可读取引脚电平,内部连接上拉电阻,悬空时默认高电平 |
下拉输入 | 数字输入 | 可读取引脚电平,内部连接下拉电阻,悬空时默认低电平 |
模拟输入 | 模拟输入 | GPIO无效,引脚直接接入内部ADC |
开漏输出 | 数字输出 | 可输出引脚电平,高电平为高阻态,低电平接VSS |
推挽输出 | 数字输出 | 可输出引脚电平,高电平接VDD,低电平接VSS |
复用开漏输出 | 数字输出 | 由片上外设控制,高电平为高阻态,低电平接VSS |
复用推挽输出 | 数字输出 | 由片上外设控制,高电平接VDD,低电平接VSS |
二、GPIO基本结构
保护二极管:IO引脚上下两边两个二极管用于防止引脚外部过高、过低的电压输入,当引脚电压高于VDD时,上方的二极管导通,当引脚电压低于VSS时,下方的二极管导通,防止不正常电压引入对内部造成伤害。
上下拉电阻:上下拉电阻用于控制引脚默认电平,内部上拉电阻工作时,引脚默认为高电平;下拉电阻工作时,引脚默认为低电;如果都不工作,引脚的电平是不确定的,即为浮空模式。
施密特触发器:可以消除输入信号的干扰,当输入电压高于正向阈值电压,输出为高电平;当输入电压低于负向阈值电压,输出为低电平;在输入电压未达到另一阈值之前,电平输出保持不变。如下图所示,即使波动多次穿过阈值电压,输出信号也不会发生变化,只有在达到另一阈值时,输出信号才会进行翻转。
P-MOS管和N-MOS管:P-MOS管高电平导通,低电平关闭,下方的N-MOS低电平导通,高电平关闭。开漏输出时,P-MOS管无效,N-MOS管截止时输出高阻态,导通时输出低电平;推挽输出时,P-MOS管和N-MOS管都有效,P-MOS管导通输出高电平,N-MOS管导通输出低电平。
三、工作模式
1.浮空输入模式:浮空输入模式下,上拉/下拉电阻断开,输出被禁止,输入经过施密特触发器直接接入输入数据寄存器,此时IO口输入电平是不确定的,完全取决于外部电路。
能够清晰的知道外部电路什么时候输入高电平,什么时候输出低电平,可以用于按键检测等场景。
2.上拉输入模式:内部上拉电阻导通,输出被禁止,此时如果外部没有信号输入,引脚默认为高电平,内部上拉电阻阻值较大,所以只是“弱上拉”,不适合做电流型驱动。
常用于低电平触发的外部设备,如二极管、按键等。
3. 下拉输入模式:内部下拉电阻导通,输出被禁止,此时如果外部没有信号输入,引脚默认为低电平,内部下拉电阻阻值较大,不适合做电流型驱动。
常用于常用于高电平触发的外部设备,如二极管、按键等。
4.模拟输入模式:内部上下拉电阻均断开,MOS管关闭,输入信号不经过施密特触发器,保留原始的电压信号,通过模拟通道进行输入。
常用于ADC、DAC等模拟信号处理。
5.开漏输出模式:此时P-MOS管被控制在截止状态,IO口状态取决于N-MOS管,当输出数据寄存器①值为0时,经过取反输出逻辑1到N-MOS管使其导通,输出为低电平;当输出数据寄存器值为1时,N-MOS管截止,输出为高阻态。
注意此时施密特触发器是工作的,且上下拉电阻均断开,可以看成浮空输入模式,可以读取IO引脚状态。
6.推挽输出模式:此时两个MOS管在同一时间只能由一个导通,若输出数据寄存器①的值为0,经过取反操作后,输出逻辑1到P-MOS管和N-MOS管的栅极,此时P-MOS管截止,N-MOS管导通,引脚输出低电平。若寄存器的值为1,则输出逻辑0到两个MOS管,此时P-MOS管导通,N-MOS管截止,输出为高电平。
该状态下施密特触发器也是打开的,可以读取IO口的电平状态。
7.复用功能输出:一个IO口可以是多个外设的功能引脚,当选择复用功能室,引脚的状态由对应的外设控制,而不是输出数据寄存器。开漏复用模式和推挽复用模式与前面同理。
四、寄存器介绍
STM32F1通用GPIO口包含7个32位寄存器控制,包括:
2个32位端口配置寄存器(CRL和CRH)
2 个 32 位端口数据寄存器(IDR 和 ODR)
1 个 32 位端口置位/复位寄存器 (BSRR)
1 个 16 位端口复位寄存器(BRR)
1 个 32 位端口锁定寄存器 (LCKR)
我们常用的寄存器只有4个:CRL、CRH、IDR、ODR
1.端口配置寄存器(GPIOx_CRL和GPIOx_CRH)
这两个寄存器的作用是控制GPIO口的工作模式和工作速度,CRL控制每组IO端口(A~G)的低8位的模式,即PIN0~PIN7引脚,CRH控制每组IO端口的高8位的模式,即PIN8~PIN15引脚。
IO口工作模式配置表:
GPIOx_CRL寄存器
CRL寄存器控制PIN0~PIN7引脚的状态,每个IO口占用CRL的4个位,高两位为CNF,控制IO口工作模式,低两位为MODE,控制IO口的工作速度。
下面代码的意思是将PA0引脚设置为输入模式
GPIOx->CRL,这句话表示要操作GPIOx的低8位,就是Px0 ~ Px7;
CRL寄存器与0xFFFFFFF0按位与运算,即高28位不变,低4位清零,表示要控制PA0引脚;0xFFFFFF0F表示操作PA1引脚,余下同理。
GPIOA->CRL|=8<<0,意思是将1000左移0位(不移位),然后与CRL寄存器进行按位或运算,将第3位置1,第0~2位置0,即CNF0设置为10,MODE0设置为00,表示将PA0设置为输出模式。
GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;
//GPIOA->CRL=GPIOA->CRL & 0b1111 1111 1111 1111 1111 1111 1111 0000;
//GPIOA->CRL=GPIOA->CRL|(0b1000)<<0;
同理将PA1设置为推挽输出50MHz:
GPIOA->CRL&=0XFFFFFF0F;GPIOA->CRL|=3<<4;
GPIOx_CRH寄存器
CRH寄存器控制PIN8~PIN15引脚的状态,控制方式与CRL寄存器同理。
2.端口输入数据寄存器(IDR)
IDR寄存器用于读取IO口的电平状态,该寄存器为只读寄存器,低16位有效,对应16个IO口,对应位为1,表示IO口为高电平,对应位为0,表示IO口为低电平。
3.端口输出数据寄存器(ODR)
ODR寄存器用于控制GPIOx输出高电平或低电平,该寄存器低16位有效,分别对应每组GPIO口的16个引脚,如果对应某位写0,则表示该IO口输出低电平,如果写1,则表示该IO口输出高电平。
4.端口置位/复位寄存器(BSRR)
BSRR寄存器只能写入,32位有效,对于低16位,对应位写1,相应IO口会输出高电平,对应位写0,则对IO口没有影响;高16位刚好相反,对应位写1会输出低电平,对应位写0没有影响。对于ODR寄存器,在设置电平时,需要将ODR寄存器的值读出,然后进行重新赋值,而使用BSRR寄存器直接对某个IO口进行设置即可。
四、相关函数介绍
1.GPIO初始化
首先开启时钟,然后定义结构体成员,最后调用HAL_GPIO_Init()函数进行初始化,代码如下:
void GPIO_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOA_CLK_ENABLE; /* GPIOA时钟使能 */
gpio_init_struct.Pin = GPIO_PIN_0; /* 引脚设置 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 模式设置 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上下拉设置 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 速度设置 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化GPIO */
}
2. HAL_GPIO_WritePin 函数
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
该函数用于设置引脚输出高电平或低电平,通过BSRR寄存器进行操作
形参1是端口号,选择范围:GPIOA~GPIOG
形参2是引脚号,选择范围:GPIO_PIN_0~GPIO_PIN_15
形参3是要设置的输出状态,GPIO_PIN_SET表示高电平,GPIO_PIN_RESET表示低电平
3. HAL_GPIO_TogglePin 函数
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
该函数用于设置引脚的电平翻转,也是通过BSRR寄存器进行操作
形参1是端口号,选择范围:GPIOA~GPIOG
形参2是引脚号,选择范围:GPIO_PIN_0~GPIO_PIN_15
文章为本人学习笔记,源于正点原子官方资料。