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)和源极之间可以近似的看作导通的一条支路。即Vg-Vs>4V时DS近似导通。(4V来自于下图数据手册节选)。

1.2PMOS

        与NMOS相近,PMOS也是需要栅极和源极满足一定关系时开始导通。但与之相反的是PMOS需要源极电压大于栅极电压一定数值时,漏极和源极之间才会产生近似导通的现象。例如当Vg-Vs<4V时DS近似导通(4V来自于下图数据手册节选)。

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

作者:拙璋

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 HAL库学习指南:GPIO入门详解

发表回复