GPIO与LED模块

GPIO与LED模块

1、GPIO基础知识

GPIO(通用输入/输出口)是微控制器上可以由用户自定义控制的引脚。对于嵌入式开发初学者来说,理解GPIO的几个关键概念至关重要:

1.GPIO模式配置

模式配置功能适用场景
输入模式将引脚配置为输入模式,用于读取外部信号读取按钮状态、传感器信号等
输出模式将引脚配置为输出模式,用于驱动外部设备或信号LED控制、电机驱动等
模拟模式将引脚配置为模拟模式,用于模拟信号输入或输出ADC读取、DAC输出等
备用功能/复用功能将引脚配置为某个外设的备用功能,如UART、SPI或I2C等通信接口、定时器输出等

关于复用功能:

在HAl库中,复用功能可以直接用cubeMAX查看

而在标准库中,则需要在用户手册或者宏定义中查看。在代码我们配置哪一个宏定义,就使用哪一个功能,这是标准库开发中问我们需要做的.

那么在cubeMAX中我们应该怎么做呢?

2.输出类型

推挽输出模式 (Push-Pull)开漏输出模式 (Open-Drain)
功能可以驱动高电平(逻辑1)和低电平(逻辑0)只能拉低电平(逻辑0),高电平由外部上拉电阻提供
工作原理有两个晶体管配置,一个用于拉高电平,一个用于拉低电平只有一个下拉晶体管,高电平依赖外部上拉电阻
优点驱动能力强,适用于大多数数字输出应用支持多路信号线"线与"逻辑,适合共享总线
缺点高频切换时可能导致较大功耗和信号干扰无法直接驱动高电平,需要外部电阻
应用示例LED控制、开关控制、信号输出I2C总线、外部信号线共享

推挽输出和开漏输出的本质区别就在于电路图上的P-MOS(开漏输出的P-MOS永远截止)

推挽输出电路原理图

在这里插入图片描述

开漏输出原理图

在这里插入图片描述

另外开漏输出具有”线与“特性,但凡有一个引脚输出低电平,所有的引脚电压都会被拉低

在这里插入图片描述

既然stm32能够配置输出模式,那么传统的 51 单片机可以这样配置吗?为什么?

不完全可以,因为结构不同:传统的 8051 单片机的IO口结构与 STM32 不同,通常被称为准双向口(Quasi-bidirectional Port)。

51 的IO口结构(以 P1,P2,P3 为例,P0 有些特殊):

  • 内部有一个上拉电阻: 每个引脚内部连接了一个弱的上拉电阻到 VDD。
  • 一个下拉晶体管: 有一个连接到 GND 的 N-MOS 晶体管,用于将引脚拉低
  • 工作方式:
    • 输出低电平(0):下拉 N-MOS 导通,将引脚拉低到 GND。此时,内部的上拉电阻也被这个 N-MOS 短路了。
    • 输出高电平(1): 下拉 N-MOS 关闭。引脚通过 内部的上拉电阻 被拉高到 VDD。

所以我们只能简单地控制输入输出。

3.输出速度

分别有:低速、中速、高速、超高速

速度设置的影响

更高的速度会导致:

  • 更多的功耗
  • 更大的电磁干扰(EMI)
  • 更快的信号上升和下降时间

对于LED控制这类应用,通常选择低速即可,这能降低功耗并减少EMI。

4. STM32 HAL库GPIO配置

STM32 HAL(Hardware Abstraction Layer,硬件抽象层)库提供了一套完整的API来配置和控制GPIO引脚,通过我们的cubeMAX来控制。相比直接操作寄存器,HAL库更易用且可移植性更好。

首先我们先打开一个工程中的cubeMAX,我们会发现其中的芯片引脚是可以配置输出模式的。(以PCB8为例)

在这里插入图片描述

对于初学者来说不知道这些引脚在哪里怎么办呢,事实上这些引脚就在原理图中,我们并不需要知道原理图中的电路是怎么构成的,我们只需要知道这些引脚在哪里,我们需要用那个IO口来控制它就够了。

那么我们以今天要学习的LED模块为例,打开我们的西门子原理图可以看到LED是共阴极的,而在主控芯片上,我们会发现找不到LED的引脚,这是因为官方的板子对于LED模块是需要我们用杜邦线来进行飞线的,而不是固定在某个端口上。

在这里插入图片描述

板子实物图如下,我们需要将17的LED部分连到8的MCU I/O口引出部分,而具体连到哪个引脚就看我们自己连接自己去配置

在这里插入图片描述

那么接下来以连接到PG2-PG7为例

在这里插入图片描述

在这里插入图片描述

上面6个选项分别为:初始电平、GPIO模式、上下拉、输出速度、用户标签

对于我们配置LED模块来说,初始电平默认为低电平,GPIO模式为输出模式,上下拉不启用,用户标签我们可以分别定义为LED1-6
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

配置完了之后创建工程,打开我们的工程

在这里插入图片描述

打开我们GPIO初始化函数,可以看到定义了一个结构体和初始化。

在这里插入图片描述

再打开结构体的定义,就可以看到我们刚才在cubeMAX里面配置的那5项,由cubeMAX帮我们生成了相关代码

在这里插入图片描述

接着我们介绍一下GPIO初始化函数中的内容。
在这里插入图片描述

除了我们刚才讲过的结构体定义和初始化,接下来的几句代码分别是:

  • /* GPIO Ports Clock Enable */使能所使用的GPIO引脚的时钟

  • /*Configure GPlo pin output Level */配置 GPIO 引脚输出电平 其中写入GPIO_PIN_RESET(为0)默认LED初始熄灭

  • /*Configure GPIO pins : PGPin PGPin PGPin PGPinPGPin PGPin */配置 GPIO 引脚

使用结构体访问其中成员,进行配置。配置完后写入GPIO的结构体。

这就是我们GPIO初始化的内容,这也是我们需要关注的,而不是掌握具体函数是如何实现的,是如何连接到硬件上的。

STM32 HAL GPIO配置实际应用

为LED控制配置GPIO的基本流程:

1首先定义GPIO配置结构体并初始化为默认值

2使能对应GPIO端口的时钟

3配置引脚模式为推挽输出(对LED最适合)

4设置上拉/下拉配置(LED控制通常不需要)

5设置输出速度(低速即可)

6应用配置到指定的GPIO端口和引脚

2、LED控制原理

在介绍LED控制原理之前,我们先讲一个小插曲,在GPIO配置选项中,有一个上下拉电阻的选项,而在西门子原理图我们也可以看见LED模块中有着上拉电阻。

在这里插入图片描述

那么什么是上下拉电阻呢?

上拉电阻和下拉电阻是GPIO配置中非常重要的概念。它们用于确保GPIO引脚在未连接外部设备时的状态,以及在连接外部设备时的状态。

无上下拉电阻上拉电阻下拉电阻
定义引脚不连接内部上下拉电阻,适用于浮空或外部电路自带上下拉电阻的情况。内部连接一个电阻到VCC,当引脚未被驱动时拉高到逻辑高电平。内部连接一个电阻到地(GND),当引脚未被驱动时拉低到逻辑低电平。
适应场景确保引脚默认为高电平 常用于按键输入(按键按下接地) 适用于需要默认高电平的场合确保引脚默认为低电平 常用于需要默认低状态的场合 适用于低功耗设计(低电平通常功耗更低)
注意:引脚可能处于浮空状态,容易受到外部干扰。

那么为什么需要在西门子原理图中有了上拉电阻,可以去掉它们吗?

答案是否定的。GD32中的LED是共阴极,高电平点亮。那么就表明我们需要输出一定的电压来保证其点亮,而上拉电阻就确保GPIO能提供足够的源电流(通常20mA左右)。假如去掉上拉电阻就可能导致我们的输出电压不足,另外这个上拉电阻还能起到保护电路的作用,直接连接LED到GPIO引脚时可能烧毁LED或损坏MCU。

STM32的上下拉电阻

在STM32微控制器中,每个GPIO引脚都有内置的上拉和下拉电阻,可以通过软件配置:

  • 上拉电阻值:典型值约为40KΩ (STM32F4系列)
  • 下拉电阻值:典型值约为40KΩ (STM32F4系列)
  • HAL配置选项:GPIO_NOPULL、GPIO_PULLUP、GPIO_PULLDOWN

注意:内部上下拉电阻值较大,驱动能力有限。对于需要更强驱动能力的场合,建议使用外部上下拉电阻。

1.LED控制的基本方式

对于普通的LED控制,我们通常使用GPIO的输出模式,并且选择推挽输出类型。这种配置可以提供足够的驱动能力来点亮LED。

GPIO模式:输出模式

输出类型:推挽输出

输出速度:低速即可

上下拉通常不需要

2.LED连接方式

高电平点亮方式和低电平点亮方式,具体看原理图来判断是哪种方式。

3.STM32 HAL库中的LED控制实现

使用HAL库控制LED的常用函数:

1HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
用于设置GPIO引脚输出状态,可以点亮或熄灭LED

2HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
用于切换GPIO引脚状态,实现LED闪烁效果

3HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
读取GPIO引脚当前状态,可用于获取LED当前状态

4.LED控制代码示例

#include "led_app.h"
#include "gpio.h" // 确保包含了HAL库的GPIO头文件

uint8_t ucLed[6] = {1,0,1,0,1,1};  // LED 状态数组 (6个LED)

/**

 * @brief 根据ucLed数组状态更新6个LED的显示

 * @param ucLed Led数据储存数组 (大小为6)
   */
   void led_disp(uint8_t *ucLed)
   {
   uint8_t temp = 0x00;                // 用于记录当前 LED 状态的临时变量 (最低6位有效)
   static uint8_t temp_old = 0xff;     // 记录之前 LED 状态的变量, 用于判断是否需要更新显示

   for (int i = 0; i < 6; i++)         // 遍历6个LED的状态
   {
       // 将LED状态整合到temp变量中,方便后续比较
       if (ucLed[i]) temp |= (1 << i); // 如果ucLed[i]为1, 则将temp的第i位置1
   }

   // 仅当当前状态与之前状态不同的时候,才更新显示
   if (temp != temp_old)
   {
       // 使用HAL库函数根据temp的值设置对应引脚状态 (假设高电平点亮)
       HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, (temp & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LED 0 (PB12)
       HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, (temp & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LED 1 (PB13)
       HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, (temp & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LED 2 (PB14)
       HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, (temp & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LED 3 (PB15)
       HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8,  (temp & 0x10) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LED 4 (PD8)
       HAL_GPIO_WritePin(GPIOD, GPIO_PIN_9,  (temp & 0x20) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LED 5 (PD9)

   temp_old = temp;                // 更新记录的旧状态

   }
   }

/**

 * @brief LED 显示处理函数 (主循环调用)
   */
   void led_proc(void)
   {
   led_disp(ucLed);                    // 调用led_disp函数更新LED状态
   }      

常见问题

为什么我的LED不亮?

  • GPIO配置问题:检查GPIO配置是否正确(输出模式、推挽输出)
  • 时钟使能:确认对应GPIO端口的时钟已使能
  • 电平逻辑:检查LED极性是否匹配代码逻辑(高电平点亮还是低电平点亮)
  • 硬件连接:检查限流电阻值是否合适,LED是否连接正确
    oid led_proc(void)
    {
    led_disp(ucLed); // 调用led_disp函数更新LED状态
    }

### 常见问题

为什么我的LED不亮?

- **GPIO配置问题**:检查GPIO配置是否正确(输出模式、推挽输出)
- **时钟使能**:确认对应GPIO端口的时钟已使能
- **电平逻辑**:检查LED极性是否匹配代码逻辑(高电平点亮还是低电平点亮)
- **硬件连接**:检查限流电阻值是否合适,LED是否连接正确
- **电源问题**:确认微控制器供电电压是否正常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值