
本章所要实现的功能是:通过开发板上的 4 个按键控制 LED。 按键介绍 按键是一种电子开关, 使用时轻轻按开关按钮就可使开关接通, 当松开手时,开关断开。我使用的按键及内部简易图通常如下图所示。 ![]() 按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态,如果按键按下,初始导通状态变为断开,初始断开状态变为导通。通常的按键所用开关为机械弹性开关,当机械触点断开 、闭合时,电压信号如下图所示。 ![]() 由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的,一般为 5ms 到 10ms。按键稳定闭合时间的长短则由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起按键被误读多次。为了确保 CPU 对按键的一次闭合仅作一次处理,必须进行消抖。 按键消抖有两种方式,一种是硬件消抖,另一种是软件消抖。为了使电路更加简单,通常采用软件消抖。我们开发板也是采用软件消抖,一般来说一个简单的按键消抖就是先读取按键的状态,如果得到按键按下之后,延时 10ms,再次读取按键的状态,如果按键还是按下状态,那么说明按键已经按下。其中延时10ms 就是软件消抖处理,至于硬件消抖,大家可以百度了解下,网上都有非常详细的介绍。 硬件设计 我们STM32F1开发板上有4 个控制按键,其硬件电路如图所示。 ![]() ![]() ![]() 从原理图我们可以知道,按键 K_UP 连接在 STM32F1 芯片的 PA0 引脚上,按键 K_LEFT、K_DOWN、K_RIGHT 分别连接在 STM32F1 芯片的 PE2、PE3、PE4 引脚上。 需要注意的是:K_UP 按键另一端是接在 3.3V 上,按下时输入到芯片管脚即为高电平;K_LEFT、K_DOWN、K_RIGHT 按键另一端是全部接在 GND 上,这个和我们学习 51 单片机是一样的,采用独立式按键接法,按下时输入到芯片管脚即为低电平。为什么在 K_UP 上接一个 3.3V,主要是为后面章节实验使用,到时候我们再具体介绍。 软件设计 整个程序实现的流程步骤如下: (1)初始化按键使用的端口及时钟 (2)按键检测处理 (3)按键控制处理 按键初始化函数 我们打开工程中 key.c 文件,按键初始化代码如下:
KEY_Init()函数用来初始化按键的端口及时钟。要知道按键是否按下,就需 要读取按键所对应的 IO 口电平状态,因此我们需要把 GPIO 配置为输入模式(不清楚输入模式对应的选项值,可以参考“使用库函数点亮 LED”内容,里面介绍了怎么快速查找代码),因为 K_UP 按键一端是接 3.3V 的,当按下后 PA0管脚即为高电平,所以需要将 PA0 管脚配置为下拉输入模式 GPIO_Mode_IPD,这样 PA0 管脚的默认电平就为低电平,如果读取到 PA0 管脚的电平为高电平时,就说明 K_UP 按键按下。其他几个按键是共地,所以需要配置为上拉输入模式GPIO_Mode_IPU,分析方法和 K_UP 类似。 在函数内我们看到有几个参数不是库函数内的,比如KEY_LEFT_Pin、KEY_Port,这种情况一般是我们自己定义的宏,通常放在对应的头文件内,我们打开 key.h,可以看到如下代码:
key.h 文件内定义了按键的端口、管脚、读取管脚的电平状态、函数声明等信息。里面使用了两种方式定义读取管脚电平宏,一种是通过位操作,另一种是通过读取管脚电平的库函数。由于位操作比较方便,因此我们就使用位操作的方式,库函数定义的宏全部注释掉。 按键检测函数 要知道哪个按键被按下,就需要编写按键检测函数,具体代码如下:
KEY_Scan 函数带一个形参 mode,该参数用来设定是否连续扫描按键,如果mode 为 0,只能操作一次按键,只有当按键松开后才能触发下次的扫描,这样做的好处是可以防止按下一次出现多次触发的情况。如果 mode 为 1,函数是支持连续扫描的,即使按键未松开,在函数内部有 if(mode==1)这条判断语句,因此key 始终是等于 1 的,所以可以连续扫描按键,当按下某个按键,会一直返回这个按键的键值,这样做的好处是可以很方便实现连按操作。函数内的 delay_ms(10)即为软件消抖处理,通常延时 10ms 即可。 KEY_Scan 函数还带有一个返回值,如果未有按键按下,返回值即为 0,否则返回值即为对应按键的键值,如 KEY_UP、KEY_DOWN、KEY_LEFT、KEY_RIGHT,这都是头文件内定义好的宏,方便大家记忆和使用。函数内定义了一个 static 变量,所以该函数不是一个可重入函数。还有一点要注意的就是该函数按键的扫描是有优先级的,因为函数内用了 if...else if...else 格式,所以最先扫描处理的按键是 K_UP,其次是 K_DOWN,然后是 K_LEFT,最后是 K_RIGHT。如果需要将其优先级设置一样,那么可以全部用 if 语句。 主函数 最后我们看下主函数,代码如下:
主函数实现的功能比较简单,首先将使用到的硬件初始化(这里说的初始化表示端口和时钟全部初始化,后面就不再强调),比如 LED、蜂鸣器和按键。然后在 while 循环内调用按键扫描函数,扫描函数传入的参数值为 0,即 mode=0,所以这里只对它单次按键操作,将扫描函数返回后的值保存在变量 key 内,通过switch 语句进行比较,从而控制 LED。 程序后面的 if(i%20==0)判断语句, 表示当 i 能够整除 20 的时候就进入 LED状态翻转,后面有一个 delay_ms(10)延时;也就是间隔 200ms,led1 就会翻转一次状态。将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁。当按下 K_UP 键,D2 指示灯点亮;当按下 K_DOWN 键,D2 指示灯熄灭;当按下 K_LEFT 键,D3 指示灯熄灭,当按下 K_RIGHT 键,D3 指示灯点亮。 |
STM32 GUI LTDC 最大像素时钟评估方法
【2025·STM32峰会】GUI解决方案实训分享1-对LVGL咖啡机例程的牛刀小试以及问题排查
OpenBLT移植到STM32F405开发板
为什么要先开启STM32外设时钟?
【STM32MP157】从ST官方例程中分析RPMsg-TTY/SDB核间通信的使用方法
【经验分享】STM32实例-RTC实时时钟实验④-获取RTC时间函数与中断服务函数
STM32 以太网 MAC Loopback 的实现
STM32功能安全设计包,助力产品功能安全认证
基于STM32启动过程startup_xxxx.s文件经验分享
HRTIM 指南