单片机
返回首页

【STM32 HAL库】GPIO:按键和LED

2025-10-24 来源:bilibili

文章内容偏向HAL库的移植和使用,以个人观点及了解为主,若与事实不符,则以www.st.com、www.stmcu.org.cn等平台为准。


使用的软件:   

VSCode(1.36版,带C/C++ IntelliSense插件)                                              

Keil MDK(5.26版)


芯片:STM32H743ZIT6(Nucleo-H743平台,B-01版)


所使用的库:STM32Cube_FW_H7_V1.3.0,下载地址:https://www.st.com/content/st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32cube-mcu-mpu-packages/stm32cubeh7.html

参考资料:STM32H743xI参考手册、数据手册、部分HAL库文件、NUCLEO-H743原理图(B-01)

程序下载:https://pan.baidu.com/s/124IrMzjdF15hEtk_hhi4iA

cut-off

        计算机的组成部分通常分为5类:运算器、控制器、储存器、输入设备、输出设备。对于STM32H743ZIT6来说,最基本的输入/输出设备就是IO口(GPIO,General-Purpose Input/Output,通用输入/输出口)。所以就以GPIO的操作作为第一个实验。

        关于GPIO的内容,在参考手册的第10章。

        STM32H743ZIT6共有11组GPIO,即GPIOA-GPIOK,除GPIOK只有PK0-PK7这8个引脚外,其他各组GPIO都有16个引脚Pn0-Pn15(n=A,B......J)。每一个IO口都可以独立地配置、操作。

cut-off

        GPIO的工作模式

GPIO的工作模式

        如上图所示,GPIO有3种输入通用模式、1种模拟模式、2种通用输出模式和2种复用输出模式,从上到下依次为:

            1、浮动输入模式

            2、上拉输入模式

            3、下拉输入模式

            4、模拟输入模式

            5、带上拉/下拉功能的开漏输出模式

            6、带上拉/下拉功能的推挽输出模式

            7、带上拉/下拉功能的开漏复用模式

            8、带上拉/下拉功能的推挽复用模式

        在这8种功能中,功能1-3类似,差别就是启用或禁用上拉/下拉电阻;功能4使用模拟信号,而且既可以作为输出口,又可以作输入口;功能5-8都是输出功能,但功能5、6可以直接用软件修改IO口的电平状态,而功能7、8的电平状态由硬件改变。

        其实7、8的复用输出功能也可以归为输出功能,但是与通用输出功能不同的是,在复用模式下,有专门的驱动电路来驱动IO口,IO的电平状态不再受到GPIO相关寄存器的控制,其控制权将交给驱动IO口的外设,如UART、I2C等。不过GPIO的工作模式却没有复用输入模式,这是因为输入信号不需要专用的电路,只需要把信号直接送给对应的外设就可以了。但如果某一个IO口启用了复用功能(不论输入还是输出),虽然HAL库提供的GPIO读函数依然有效,但禁止使用HAL库提供的写函数修改IO口电平状态,同时也不建议读取IO口电平状态。

cut-off

        GPIO的结构:

GPIO的结构

        GPIO的结构大同小异。就输入电压而言,可以分为两类,一类是支持5V容忍度的IO,一类是不支持5V容忍度的IO。所谓的5V容忍度,指的是某个IO口既可以使用3.3V高电平,又可以使用5V高电平且不会损坏,但要串接保护电阻。不过还是参考后者,即不支持5V容忍度的IO。

        不管何种模式,图片右边的两个保护二极管都不会被禁用,也不会影响输入的电平,所以不做讨论。

        对于3种输入模式,右边的上拉/下拉电阻可以根据选择使用,蓝色框为输入驱动器,实际上这部分主要是一个施密特触发器。在输入模式下,从IO输入的信号会先向模拟部分(橙色)提供一路信号(不管用不用,都会提供,由相应的模拟外设选择输入的信号,下同),之后经过施密特触发器,向复用输入功能部分(红色)提供一路信号,同时也向GPIO的输入数据寄存器(紫色)提供一路信号,作为从外部读取的电平。

        在模拟功能下,除了橙色部分的模拟信号输入/输出外,其他部分都无效或者禁止使用。

        如果启用了输出功能,那么就有两路可用的输出驱动信号,一路是来自GPIO输出数据寄存器的驱动信号(来自紫色的框中),另一路是复用功能驱动信号(来自左下黄色框中),具体选用哪一路信号,由输出驱动电路(绿色框)种的选择器确定,并将这一路信号送给输出控制部分,用来产生MOS管的驱动信号,通过MOS管的关断和导通,实现电平的变换。

        如果和功能5-8关联起来的话,功能5、6使用的驱动信号由GPIO输出数据寄存器提供,功能7、8使用的驱动信号由复用功能驱动信号提供。在开漏模式下,P-MOS始终关闭,通过启用上拉电阻或者外接上拉电阻的形式来产生高电平,如果没有有效的上拉电阻,那么IO口的电平状态是不确定的。如果是推挽模式,那么P-MOS和N-MOS会交替导通和关断,保证高电平足够高、低电平足够低。

cut-off

        GPIO的复用:

        STM32H743的外设非常多,其中某一些外设需要使用GPIO来输入、输出信号,此时就要把某些GPIO的控制权交给外设,由外设管理IO的电平状态。此时GPIO不能再作为通用IO使用,而是工作于由用户选定的功能,这种工作状态就叫做GPIO的复用。GPIO有16种复用模式,从AF0-AF15,详细的复用功能表再STM32H743xI的数据手册中,位于第五章靠后的部分。

GPIOA的复用功能

        虽然一个引脚支持数种复用功能,但同一时刻,只能选择一种复用功能。

        另外,所有GPIO都可以挂接在EXTI上,实现外部的上升沿、下降沿和跳变沿中断。关于EXTI的内容,目前不会使用太多,所以就先不细讲。

cut-off

        现在,来编写一个程序,使用1个按键和3个LED,当按键按下时,LED的状态随之改变。也就是这个视频实现的效果。

00:10

【STM32H743】第一个实验当然是玩灯了

2951观看 0弹幕

cut-off

        首先,来分析需求。

        NUCLEO-H743 B-01(B-01是版本号)开发板带有3个LED和1个用户按键。为了使用更多的硬件,也就是使用EXTI,所以把与按键连接的GPIO配置为中断模式,进入中断后按照一定的顺序修改LED状态,如果多次按下按键则循环给定的状态。LED的状态分4种:红、蓝、绿、全亮。如果LED已经是全亮的状态,那么下一个状态时红色,并开始循环。而按键是以按下触发中断的。

cut-off

        因为ST官网提供的NUCLEO-H743原理图只有C-01版,所以原理图就参考NUCLEO-F767开发板。

        红色LED阴极接地,阳极通过保护电阻连接PB14,蓝色LED也类似,连接在PB7,按键连接在PC13,按下按键产生高电平,释放按键产生低电平。

LED_G原理图

        而绿色LED配备了由运放组成的专用驱动电路,这种连接方式,会将同相端输入的直接反应到输出端。输入的信号可以又两路:PB0和PA5,这么做也许是考虑到了PA5带有DAC功能(DAC1_OUT2),但输出的电流不足以驱动LED,而设计的驱动电路。对于PB0来说,因为信号是同相的,所以也是高电平点亮LED。

        所以硬件信息就是:

            LED_R    PB14    高电平点亮

            LED_B    PB7      高电平点亮

            LED_G    PB0      高电平点亮

            KEY         PC13   上升沿中断

cut-off

        拷贝一份工程模板,将文件夹命名为"STM32H743_LED按键",在Driver/src下新建LED.c、KEY.c,在Driver/inc下新建LED.h、KEY.h。

        因为是已经写好的程序,所以就不重新编写了。        

        先同时打开KEY.h和LED.h。

动图

打开两个文件

        现在来看一看代码

KEY.h与LED.h

        每个文件的#ifndef-#define-#endif结构已经是惯例了,用于防止头文件重复编译。使用了HAL库的工程,用户头文件只需要添加stm32h7xx_hal.h即可,它通过包含stm32h7xx_hal_conf.h间接包含了所有已启用外设的头文件和stm32h7xx.h,不必再次包含每个外设的头文件。

        在KEY.h文件中先是用两个宏定义,定义了按键的GPIO引脚(6-7行),PC13,又声明了一个按键初始化函数(9行)。

        LED.h中也是类似,定义了每个LED的GPIO引脚,声明了三个函数,分别用于初始化LED(LED_Init())、点亮LED(LED_On())、熄灭LED(LED_Off())。另外6-12行定义了一个枚举类型LEDSelection_TypeDef,这个类型用于选择一个LED。

KEY.c中的程序

        现在来看看KEY.c文件。

        这个文件中的KEY_Init()函数大致分为3部分:开启GPIO时钟(7)、初始化GPIO引脚(9-12)、配置相应的中断(15-16).

        在HAL库中,与RCC外设有关的内容全部都在stm32h7xx_hal_rcc.c/.h、stm32h7xx_hal_rcc_ex.c/.h中,__HAL_RCC_GPIOC_CLK_ENABLE()是一条位于stm32h7xx_hal_rcc.h中第938行的宏。stm32h7xx_hal_rcc.h中还有其他类似的宏,用于外设的时钟管理,可以在启用正则表达式搜索功能后用__HAL_RCC_.*_CLK_ENABLE()搜索到,对应的DISABLE宏可以用__HAL_RCC_.*_CLK_DISABLE()搜索,也可以使用__HAL_RCC.*ABLE()匹配所有项.

动图

HAL库提供的外设时钟使能宏

        GPIO_InitStructure的数据类型是GPIO_InitTypeDef,它的定义在stm32h7xx_hal_gpio.h中

GPIO_InitTypeDef的定义

        这个结构中有5个成员。

            Pin:指定要配置的引脚,可单选或多选

            Mode:指定要配置的IO模式

            Pull:指定上拉/下拉电阻配置

            Speed:指定输出速度(仅输出模式、复用模式有效)

            Alternate:指定引脚的复用功能

        其中,前4个成员的参数定义在stm32h7xx_hal_gpio.h中,复用功能参数定义在stm32h7xx_hal_gpio_ex.h中,因为还没有用到复用模式,所以就暂不讨论。上一张图使用了正则表达式搜索,搜索结果的大致位置在右侧预览窗口中有显示。如果不知道该怎么选,可以直接查看对应的代码,比如IO模式:

GPIO_InitTypeDef::Mode的各个参数

        与上升沿中断对应的参数是143行的GPIO_MODE_IT_RISING。

        KEY.c第9行的参数来自KEY.h中第7行的定义,这个定义是stm32h7xx_hal_gpio.h中参数的二次定义,主要目的是为了让程序更加灵活,KEY.c第12行的GPIOPort_KEY的用法和目的也是类似的。KEY.c第11行,启用下拉电阻,保证按键IO空闲时为低电平,按键按下有产生上升沿。

        KEY.c的15、16行用于配置NVIC(嵌套向量中断控制器),15行用于配置NVIC的对应优先级,16行用于开启中断。NVIC是Cortex-M7内核的部分,所以暂不深入了解。经过HAL库的配置后,NVIC拥有4位抢占优先级和0位响应优先级(优先级分组4),但因为目前要编写的程序只有一个中断,所以这一部分可以随意配置。

        在这个程序中,按键中断时通过EXTI来实现的,PC13占用了EXTI13,所以在配置中断优先级时,中断号参数为EXTI15_10_IRQn。关于中断号(IRQn)的全部定义,可以在stm32h743xx.h中找到。

stm32h743xx.h中定义的中断号

        在LED.c中的LED_Init()函数的作用和代码与KEY_Init()类似,只不过没有中断优先级配置而已。

LED.c中的程序

        两个LED操作的函数结构相同,都是利用switch-case结构选择对应的LED灯,唯一的区别就是点亮灯需要输出高电平,IO状态为SET;熄灭灯需要低电平,IO状态为RESET。

            LED_On()和LED_Off()的参数s的类型就是在LED.h中定义的LEDSelection_TypeDef,用来选择LED灯,除单独选择红、绿、蓝之外,还有一个全部选择。

cut-off

        Driver文件夹下的4个文件编写好之后,要在stm32h7xx_it.c、main.c/.h中编写一些代码。首先是stm32h7xx_it.c,要在其中编写EXTI的中断响应程序,这个程序比较简单,只有4行(177-180)

        其中只是调用了HAL_GPIO_EXTI_IRQHandler()函数,传递的参数为产生外部中断的引脚号,也就是GPIO_InitTypeDef::Pin的值。这个函数是由stm32h7xx_hal_gpio.c/.h提供的,在右侧窗口中可以看到函数的源码,先检测并复位中断标志,再调用HAL_GPIO_EXTI_Callback()函数,即产生回调。关于回调的内容,将在下一篇文件讲到,现在只需要知道,只要产生有效的中断,HAL_GPIO_EXTI_Callback会被HAL库调用即可。

        之后,要在main.h中包含KEY.h和LED.h(同时要把对应的.c文件在MDK中添加到Driver组中,不然会报错)。

main.h中的代码

        在main.c中,要通过调用LED.c和KEY.c中的函数达到预期效果,所以编写的程序比较多。

main.c中的部分代码

        在main.c中,14、15、17行是添加的部分,注意要添加在10-12行的三个函数之后。前两行用于初始化按键及LED,最后一行是让程序进入一个初始状态,也就是上电后,红灯会立刻亮。之后编写这样一段程序:

还是main.c中的代码

        这一部分负责响应回调(或者说响应中断),函数头部分就是stm32h7xx_hal_gpio.c中521行的函数头,不过要去掉__weak修饰符(下一篇文章讲)。这样相当于重写stm32h7xx_hal_gpio.c中的HAL_GPIO_EXTI_Callback()函数,当HAL_GPIO_EXTI_IRQHandler()调用HAL_GPIO_EXTI_Callback()时,会调用main.c中的函数,而不是stm32h7xx_hal_gpio.c中的。


        第26行定义了一个静态变量LEDSelection,初值选择蓝色LED,之后用一个switch-case来选择按键按下这种情况(其实不用也可以,但如果允许多个引脚产生中断的话,就需要了)。第32-36行是实现LED状态改变的部分,先关闭全部LED(32行),在开启选中的LED(33行),最后选择下一个LED状态(35-36行)。其中35-36行选择不同LED状态的原理就是利用LEDSelection是枚举型变量,对其加一就会变成下一个成员的值,在用一个if作溢出判断即可。


        最后,在MDK中编译下载就可以了。


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章