STM32 HAL库学习指南:GPIO入门详解
单片机的GPIO(General Purpose Input/Output,通用输入输出)是单片机最基础且常用的外设功能之一,主要用于与外部设备进行简单的数据交互。
一、GPIO的基本功能
读取外部信号(如按钮状态、传感器电平)。
控制外部设备(如LED亮灭、电机启停)。
二、GPIO的八种基本模式
在学习GPIO的八种模式前应当了解到一些基础的原件及其工作原理
1.MOS管
MOS管对于单片机IO口输出部分学习至关重要,下面是对其原理的简单叙述(NMOS管以IRF540为例,PMOS管以IRF9540为例,且由于本文主要介绍IO口的功能,故对于MOS管的寄生电容等内部工艺问题不做解释)。
1.1NMOS
NMOS可以理解为一个人为控制的大坝,当栅极(G)的电压大于源极(S)电压的一定数值时,漏极(D)和源极之间可以近似的看作导通的一条支路。即时DS近似导通。(
来自于下图数据手册节选)。
1.2PMOS
与NMOS相近,PMOS也是需要栅极和源极满足一定关系时开始导通。但与之相反的是PMOS需要源极电压大于栅极电压一定数值时,漏极和源极之间才会产生近似导通的现象。例如当时DS近似导通(
来自于下图数据手册节选)。
2.施密特触发器
2.1运放搭建的简易过零比较器
过零比较器原理是设定一个电压值0V,当输入电压大于0V时输出为高电平逻辑,当输入电压低于0V时,输出低电平逻辑。
2.2施密特触发器
施密特触发器相较于过零比较器而言,原理相近都是比较后输出。施密特触发器具有高低两个阈值,当输入电压高于高阈值时输出高电平,低于低于低阈值电压时,逻辑转换为低电平,当其输入处于两个阈值之间时保持上一个逻辑电平
在学习GPIO的八种模式时,应当结合其内部电路图学习,将其从IO口分为上下两部分进行学习
输入模式
1. 输入浮空模式(Input Floating)
输入浮空是指外部的IO口不进行上下拉电阻的配置,当其外部没有输入时其电压的高低不定
电流流向如下图所示。
2. 输入上拉模式(Input Pull-Up)
输入模式的上拉电阻打开,使其在外部没有输入时默认为高电平
3. 输入下拉模式(Input Pull-Down)
输入模式的上拉电阻打开,使其在外部没有输入时默认为低电平
4. 模拟输入模式(Analog Input)
模拟输入模式会在后续单独讲解,这里不过多讲述。
输出模式
5. 推挽输出模式(Push-Pull Output)
推挽模式下其内部N沟道MOS管和P沟道MOS管都处于活跃状态,当输出高电平时,P 沟道 MOS 管导通,N 沟道 MOS 管截止,引脚输出与电源电压相同的高电平;当输出低电平时,N 沟道 MOS 管导通,P 沟道 MOS 管截止,引脚输出接近地电平的低电平。
6. 开漏输出模式(Open-Drain Output)
开漏输出只有一个 N 沟道 MOS 管。当输出低电平时,MOS 管导通,引脚输出接近地电平的低电平;当输出高电平时,MOS 管截止,此时引脚处于高阻态(高阻态相当于支路中有一个无穷大的电阻,因此会导致支路电流趋近于0)。
7. 复用推挽输出(Alternate Function Push-Pull)
复用推挽输出模式会在后续串行通讯部分单独讲解,这里不过多讲述。
8. 复用开漏(Alternate Function Open-Drain)
复用开漏模式会在后续串行通讯部分单独讲解,这里不过多讲述。
三、使用CUBEMX配置GPIO功能的使用
1选择时钟基准源
2选择下载调试接口
3配置系统时钟
4选择输入输出模式
4.1输出模式配置
如下图配置功能从上到下依次序:IO口初始点平高低、IO口输出模式(开漏和推挽输出)、上下拉配置(对于F1系列单片机可以不配置该项)、IO口翻转速度、IO口名称
4.2输入模式配置
输入模式里配置上下拉模式取决于外部电路的设计
5设置项目名称以及连携调试软件环境
从上到下依次为项目名称,项目位置,开发工具及其版本
6选择生成代码的格式
7生成代码并打开
四、代码编写
1.代码配置
下方代码为CUBEMX生成的GPIO引脚配置代码
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO时钟配置*/
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*配置GPIO输出控制 */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = LED_Pin;//配置输出的引脚编号,例如PA1的编号为1
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//配置输出模式(开漏OD和推挽PP)
GPIO_InitStruct.Pull = GPIO_NOPULL;//配置上下拉模式
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;//配置引脚反转速度有慢、中、快三种速度
HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);//配置输出引脚的端口,如PA1中的A
/*配置GPIO引脚号输入模式*/
GPIO_InitStruct.Pin = KEY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);
}
2.常用函数
2.1输入功能读取外部电平函数
下面代码为STM32从外部读取高低电平逻辑的函数。使用时一般为判断其为1或0
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIO_PinState bitstatus;
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
if ((GPIOx->IDR & GPIO_Pin) != 0x00U)
{
bitstatus = GPIO_PIN_SET;
}
else
{
bitstatus = GPIO_PIN_RESET;
}
return bitstatus;
}
/*语句的运用*/
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_PIN)==1)
{
/*这里写想要执行的功能*/
}
2.2.1输出功能输出高低电平
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = (uint32_t)GPIO_Pin;
}
else
{
GPIOx->BRR = (uint32_t)GPIO_Pin;//G4系列单片机
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;//F1系列单片机
}
}
/*代码运用调用函数形式*/
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, GPIO_PIN_SET);//引脚输出高电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, GPIO_PIN_RESET);//引脚输出低电平
/*代码运用调用寄存器形式*/
GPIOA->BSRR = GPIO_PIN_13;//各个系列单片机通用书写高电平
GPIOA->BRR = GPIO_PIN_13;//对于G4系列单片机输出低电平语句
GPIOA->BRR = GPIO_PIN_13 << 16u;//对于F1系列单片机输出低电平语句
2.2.2输出功能切换高低电平
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
uint32_t odr;
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
/* get current Output Data Register value */
odr = GPIOx->ODR;
/* Set selected pins that were at low level, and reset ones that were high */
GPIOx->BSRR = ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin);
}
/*代码运用*/
void HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_13);//该函数语句将IO口从上一个逻辑电平取反,如果是逻辑1则使用后为逻辑0
五、外部电路设计
1按键输入电路
下图为按键输入的基本电路,当按键按下时输入到IO口的逻辑为0,但是由于按键在生产工艺中的局限性,使得按键在按下时具有一定的抖动,从而导致其在按下过程中会输出多个逻辑0和多个逻辑1。我们一般采用软件消抖的方式解决这个问题(由于电路图中设计一端接地故需要让输入语句判断是否为逻辑电平0)
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_PIN)==0)
{
HAL_Delay(20);
while(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_PIN)==0);
/*这里写想要执行的功能*/
}
或者我们可以使用硬件在按键旁边并联一个电容的方式消除抖动,电路图如下
2.LED驱动电路
图1
图2
对于图1来说,单片机IO口设置推挽或者开漏两种模式都可以使LED灯发生变化,但是对于图2的LED来说,IO口只有配置成为推挽模式才能有驱动其亮灭的能力,因此对于工程上而言,一般使用第一种电路图来进行设计。
还有一种及其不推荐的电路图,该电路图也能实现LED的亮灭,但是由于LED的工艺因素,这样的连接可能会造成LED对地短接,即IO口与GND直连,LED导通,此时如果IO口输出高电平,则会使得IO口与GND之间电流无限大,从而烧坏单片机IO口。
LED灯,长正短负
六、按键控制点亮一个LED
根据上述的原理,我们可以设置一个简单的按键控制LED亮灭的工程,具体代码以及效果图如下
代码:
在main函数的while循环直接添加下列代码即可。
while (1)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)==0)
{
HAL_Delay(20);
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)==0);
HAL_Delay(20);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
代码链接如下:
通过网盘分享的文件:LED.zip
链接: https://pan.baidu.com/s/198hYKZUC6iQNjKs2zEXrJg?pwd=cnsd 提取码: cnsd
–来自百度网盘超级会员v3的分享
效果视频:
效果演示
文中讲述IO口内部电路图节选自部分数据手册链接如下
SN74LVC1G14 Single Schmitt-Trigger Inverter datasheet (Rev. Y)
IRF540NPBF -PDF数据手册-参考资料-立创商城
IRF9540NPBF -PDF数据手册-参考资料-立创商城
STM32F103C8 – 主流高性能系列Arm Cortex-M3 MCU,配有64 KB Flash存储器,72 MHz CPU,电机控制,USB和CAN – 意法半导体STMicroelectronics
作者:拙璋