ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
>[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/nb-iot) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> ## **按键原理简介** &emsp;&emsp;在LED相关课程中,把GPIO配置为输出模式,然后通过控制该GPIO输出高/低电平来控制LED的亮/灭。然而,与之相反,按键是一种被动器件,即由外界把按键相关的信号输入到主控芯片中,因此需要把GPIO配置为输入模式。 ### **电路原理简介** 开发板中带有一个名为“User”的按键,如图所示。 ![](https://img.kancloud.cn/d6/5b/d65b0eade5a60e99322ca3538044ccf8_1786x988.png =500x) ### 其原理图如下所示。 ![](https://img.kancloud.cn/72/72/72724ce3891769c3d158d516f93744a9_545x425.png =300x) >[warning] 如您未能看懂此原理图,需要补充一下相关知识。 <br/> 从原理图中可以看出,此按键与STM32F030的PA0相连接。在把PA0配置为输入模式后,当按键被按下时,PA0接电源(3v3),即输入高电平。与此同时,可以通过内部下拉的方式实现没有按下时即输入低电平。此时,程序通过检测PA0输入电平即可检测按键是否被按下: * 按键没有被按下时,PA0输入低电平 * 按键被按下后,PA0输入高电平 >[warning] 如果读者不了解何谓内部上拉,可暂时略过。 <br/> ## **按键HAL API 设计** 通过前面的原理分析,可以知道按键是一种被动器件,需要检测PA0的输入电平高低变化。在设计此类型API时,可以使用注册回调的思想,即上层应用在HAL中注册已给回调函数,一旦HAL检测到按键按下,就会回调给上层应用,如图所示。 ![](https://img.kancloud.cn/6f/9f/6f9fcc37250b3a2c294174f952c0cf32_464x481.png =300x) ### 接下来通过代码来讲解这个设计。 <br/> **编写代码** 笔者在本节课配套的源代码中新建了 hal\_button.h 和 hal\_button.c文件,如图所示。 ![](https://img.kancloud.cn/65/e0/65e0e2a0acc1732b26ea88f0ed1f552a_600x1056.png =300x) ### 打开本节课配套的工程,笔者把hal\_button.c以及必要的标准库文件添加进工程了,如图所示。 ![](https://img.kancloud.cn/80/6f/806fc990ce3d3a179714fe2d5c52f688_375x535.png =300x) <br/> hal_button.h文件的代码如下: ``` #ifndef __HAL_BUTTON_H__ #define __HAL_BUTTON_H__ /*定义按键连接的GPIO口PA0*/ #define HAL_BUTTON_PORT GPIOA #define HAL_BUTTON_PIN GPIO_Pin_0 /*定义GPIOA对应的时钟 */ #define HAL_BUTTON_CLOCK RCC_AHBPeriph_GPIOA #define HAL_BUTTON_TRIGGER_LEVEL 1 /* * 按键初始化 * @param onClick - 按键回调函数,当按键被按下时执行此函数 */ void halButtonInit(void (*onClick)(void)); /* * 检测到按键按下时,执行回调回调函数 */ void halButtonPoll(void); #endif /* #ifndef __HAL_BUTTON_H__ */ ``` <br/> hal_button.c文件的代码如下: ### ``` #include "hal_button.h" #include "hal_system.h" #include "stm32f0xx_gpio.h" static void (*halButtonOnClick)(void) = 0; /* * 按键初始化 * @param onClick - 按键回调函数,当按键被按下时执行此函数 */ void halButtonInit(void (*onClick)(void)) { halButtonOnClick = onClick;//保存回调函数 GPIO_InitTypeDef buttonGPIO;//定义GPIO配置 buttonGPIO.GPIO_Mode = GPIO_Mode_IN;//配置为输入模式 buttonGPIO.GPIO_PuPd = GPIO_PuPd_DOWN;//配置为下拉 buttonGPIO.GPIO_Speed = GPIO_Speed_2MHz;//速率为2MHz buttonGPIO.GPIO_Pin = HAL_BUTTON_PIN;//引脚 RCC_AHBPeriphClockCmd(HAL_BUTTON_CLOCK, ENABLE);//使用按键时钟 GPIO_Init(HAL_BUTTON_PORT, &buttonGPIO);//初始化PA0 } /* * 检测到按键按下时,执行回调函数 */ void halButtonPoll(void) { /* 如果按键没有被按下,如果没有则返回*/ if (GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) != HAL_BUTTON_TRIGGER_LEVEL) return; /* 延迟10ms,去除机械按键抖动 */ halSystemDelayUs(1000 * 10); /* 再次检测按键是否被按下,如果没有则返回*/ if (GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) != HAL_BUTTON_TRIGGER_LEVEL) return; /* 等待按键松开 */ while(GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) == HAL_BUTTON_TRIGGER_LEVEL) halSystemDelayUs(1000); /* 如果定义回调函数,则调用回调函数 */ if (halButtonOnClick != 0) halButtonOnClick(); } ``` **处理机械按键抖动** 上述代码包含了按键抖动的处理,对其简单讲解一下。由于按键内部采用了弹簧,所以当按键被按下或松开的时候,会产生一定的震动,这种震动可以称为机械抖动。这种机械抖动会导致电平的抖动,如下图所示。 ![](https://img.kancloud.cn/a4/15/a415fb7df144334f27cf3b409d8bdb75_582x324.png =300x) ### 上图展示了按钮从按下到松开这个过程的电平变化。按键被按下时产生的抖动称为前沿抖动,松开时产生的是后沿抖动。这个抖动时间一般持续5~10ms。因此在代码上当检测到按钮被按下后,需要延后10ms后再检测一次按钮是否真的被按下。 <br/> ## **使用按键 HAL API** 编写好按键 HAL API后,按键的使用非常简单。在配套工程的main.c文件中添加如下代码: ``` /* * 按键回调函数,当按键被按下后即执行此函数 */ static void buttonOnClick() { halLedToggle();//反转LED灯的亮灭状态 } int main(void) { halSystemInit();//系统初始化 halLedInit();//LED初始化 /*按键初始化,并且传入了回调函数*/ halButtonInit(buttonOnClick); /*使用while循环来持续检测按键是否被按下*/ while (1) { halButtonPoll(); } } ``` 上述代码实现了当按键别按下并松开后,反转LED灯的状态,代码非常简洁! <br/> <br/> ## **商务合作** 如有以下需求,可扫码添加管理员好友,注明“**商务合作**” * 项目定制开发,技术范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等; * 入驻平台,成为讲师; * 接项目赚外快; * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](images/screenshot_1637114207608.png =150x) (非商务合作**勿扰**,此处**非**技术支持)