【STM32开发】使用外部中断进行按键采集
本文详细介绍了STM32使用外部中断实现按键采集的完整流程。主要内容包括:1)STM32外部中断的工作原理及其在按键采集中的优势;2)硬件连接方案及开发环境搭建步骤;3)代码实现的关键环节,包括GPIO初始化、外部中断配置、NVIC优先级设置和中断处理函数编写;4)针对按键抖动和多按键处理等常见问题的解决方案。文章通过生动的比喻和详细的代码示例,帮助读者掌握这一嵌入式开发基础技术,并鼓励读者将其应
目录
一、STM32 的奇妙世界

在当今这个科技飞速发展的时代,嵌入式系统已如繁星般点缀在我们生活的各个角落,从我们日常佩戴的智能手环,到家中默默守护安全的智能家居控制器,再到工业领域中精确控制的自动化设备,嵌入式系统无处不在,而 STM32 系列微控制器则在这庞大的嵌入式世界中占据着举足轻重的地位。
以智能手环为例,它就像我们的贴身健康小卫士,不仅能实时监测我们的心率、血氧、睡眠等健康数据,还能接收手机的通知提醒,让我们不错过任何重要信息。而当我们想要查看历史数据、设置提醒或者切换显示界面时,只需轻轻按下手环上的按键,它就能迅速响应我们的操作。在智能家居控制器中,按键则是我们与家居设备交互的重要桥梁。我们可以通过按键来控制灯光的开关、调节电器的运行模式,实现对家居环境的便捷掌控。这些生活中常见的智能设备,都离不开按键采集功能,而 STM32 使用外部中断进行按键采集代码实现,正是让这些设备能够快速、准确响应按键操作的关键技术。
二、STM32 外部中断:按键采集的 “魔法钥匙”
在 STM32 的世界里,外部中断就像是一把神奇的 “魔法钥匙”,为我们开启了高效响应外部事件的大门。那么,这把 “魔法钥匙” 究竟是如何发挥作用的呢?
想象一下,你正在家中专注地做着自己的事情,突然门铃响了。这时,你会暂时放下手中的事情,去开门看看是谁来了。等你处理完门口的事情后,再回来继续做之前的事情。在这个场景中,门铃就相当于 STM32 的外部中断信号,而你则是 STM32 的 CPU。当外部中断信号触发时,CPU 会暂停当前正在执行的程序,转而去执行相应的中断处理程序,处理完成后再返回原来被中断的程序继续执行。
具体来说,STM32 的外部中断可以由外部引脚的电平变化或边沿触发。每个 GPIO 引脚都可以配置为外部中断源,通过设置外部中断线来实现。STM32 有多个外部中断线(如 EXTI0~EXTI15),这些中断线可以映射到不同的 GPIO 引脚 。当某个 GPIO 引脚有输入信号进来时,经过输入驱动器后会到达输入线。边沿检测电路会根据上升沿触发选择寄存器 (EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发。当检测到有边沿跳变时,会输出有效信号,该信号经过一系列电路处理后,最终会触发中断,使 CPU 执行中断服务函数。
了解了外部中断的原理,接下来我们要思考一个问题:为什么我们要使用外部中断来进行按键采集呢?
其实,使用外部中断进行按键采集,就好比你在家中安装了门铃,当有人来访时,门铃会及时提醒你,让你能够快速做出响应。如果不使用外部中断,而是采用轮询的方式来检测按键状态,就好像你需要不断地停下手中的事情,去门口看看有没有人来,这样不仅效率低下,而且还可能会错过一些重要的事件。
使用外部中断进行按键采集,能够让系统在按键按下的瞬间就立即做出响应,大大提高了系统的响应速度。同时,由于 CPU 不需要一直去查询按键状态,节省了 CPU 的资源,使得 CPU 可以专注于其他更重要的任务,从而提高了整个系统的运行效率。在一些对实时性要求较高的应用场景中,如工业控制、智能家居等,这种优势尤为明显。
三、代码实现前的准备
(一)硬件连接
在进行代码实现之前,我们首先要搭建好硬件环境,确保 STM32 与按键能够正确连接。这就好比建造一座房子,坚实的地基是关键,而硬件连接就是我们代码运行的 “地基”。
我们将按键的一端连接到 STM32 的一个 GPIO 引脚,另一端接地(GND)或接电源(VCC)。以将按键连接到 STM32 的 PA0 引脚为例,为了确保在按键未按下时,PA0 引脚有一个确定的电平状态,我们需要使用上拉电阻或下拉电阻。如果按键的另一端接地,我们可以使用上拉电阻,将 PA0 引脚通过一个电阻(如 10KΩ)连接到电源(3.3V) 。这样,在按键未按下时,PA0 引脚由于上拉电阻的作用,处于高电平状态;当按键按下时,PA0 引脚接地,变为低电平状态。反之,如果按键的另一端接电源,我们则使用下拉电阻,将 PA0 引脚通过一个电阻连接到地,在按键未按下时,PA0 引脚处于低电平状态,按键按下时变为高电平状态。
为了更直观地理解,下面给大家展示一个简单的硬件连接示意图:
+3.3V
|
|
10KΩ
|
|
PA0 ------| |------ GND
| |
| |
| |
| |
按键
在这个示意图中,我们可以清晰地看到 STM32 的 PA0 引脚通过一个 10KΩ 的上拉电阻连接到 3.3V 电源,按键的一端连接在 PA0 引脚与上拉电阻之间,另一端接地。这样的连接方式能够稳定地检测按键的按下和松开状态,为后续的代码实现提供可靠的硬件基础。
(二)开发环境搭建
接下来,我们要搭建开发环境,这就像是为我们的 “代码之旅” 准备一辆高性能的汽车。开发 STM32 常用的集成开发环境(IDE)有 Keil MDK 和 STM32CubeIDE,下面我将分别为大家介绍它们的安装和配置步骤。
Keil MDK:
- 下载:首先,我们需要从 Keil 官方网站(https://www.keil.com/)下载 Keil MDK 软件。在下载页面,根据你的计算机操作系统(如 Windows 或 Linux)选择合适的版本进行下载。下载完成后,你会得到一个安装文件,通常是一个.exe 格式的文件。
- 安装:双击下载的安装文件,进入安装向导。按照向导的提示,一步一步进行安装。在安装过程中,你可以选择安装路径,建议选择一个磁盘空间充足且路径简洁的位置,避免路径中包含中文或特殊字符,以免出现兼容性问题。
- 配置 License:安装完成后,打开 Keil MDK 软件。在菜单栏中选择 “File” -> “License Management”,进入许可证管理界面。在这里,你需要输入有效的许可证信息,如果你使用的是评估版,可以直接获取 30 天的试用许可证;如果你购买了正式版,可以输入相应的许可证密钥进行激活。
- 安装芯片支持包:Keil MDK 默认支持一些常见的芯片型号,但为了确保能够支持我们使用的 STM32 型号,我们需要安装对应的芯片支持包。在菜单栏中选择 “Pack Installer”,在弹出的窗口中搜索并安装你所使用的 STM32 芯片的支持包,如 “STM32F4xx_DFP”。
STM32CubeIDE:
- 下载:从 STMicroelectronics 官方网站(https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-ides/stm32cubeide.html)下载 STM32CubeIDE 安装包。根据你的计算机操作系统选择对应的版本,下载完成后得到一个压缩包。
- 解压:将下载的压缩包解压到你希望安装的目录,同样要注意避免路径中包含中文或特殊字符。
- 安装 Java 环境:STM32CubeIDE 基于 Java 开发,所以需要先安装 Java 运行时环境(JRE)。你可以从 Oracle 官方网站(https://www.oracle.com/java/technologies/javase-jre8-downloads.html)下载并安装 JRE 8 或更高版本。安装完成后,确保 Java 已正确配置到系统的环境变量中。
- 运行 STM32CubeIDE:解压完成后,进入解压目录,找到并双击 “st-stm32cubeide.exe” 文件,启动 STM32CubeIDE。首次启动时,会提示你选择工作区路径,工作区是存放你所有项目文件的地方,选择一个合适的路径后,勾选 “Use this as the default and do not ask again”,然后点击 “Launch”。
- 安装开发包:为了支持特定系列的 STM32 微控制器,我们需要导入相应的开发包。在菜单栏中选择 “Help” -> “STM32CubeMX Updates and Pack Installer”,在弹出的窗口中选择你所使用的 STM32 芯片系列(如 STM32F1、STM32F4 等),点击安装按钮进行安装。安装完成后,重启 STM32CubeIDE 使更改生效。
搭建好开发环境后,我们就可以开始编写代码,让 STM32 与按键进行 “互动” 啦!
四、代码实现过程:步步为营
(一)初始化 GPIO
初始化 GPIO 就像是为一场精彩的演出布置舞台,只有舞台搭建好了,后续的表演才能顺利进行。在这个过程中,我们需要将与按键连接的 GPIO 引脚配置为输入模式,并且要根据硬件连接的方式,选择合适的输入方式,如浮空输入、上拉输入或下拉输入。
如果按键的一端接地,另一端连接到 GPIO 引脚,为了确保在按键未按下时,GPIO 引脚处于高电平状态,我们通常会选择上拉输入模式。因为在这种模式下,内部上拉电阻使能,默认情况下引脚为高电平,当按键按下时,引脚被拉低,从而产生一个有效的低电平信号。相反,如果按键的一端接电源,另一端连接到 GPIO 引脚,我们则会选择下拉输入模式,这样在按键未按下时,引脚处于低电平状态,按键按下时变为高电平。而浮空输入模式则适用于按键连接的外部电路能够提供稳定的电平信号,不需要内部上拉或下拉电阻的情况。在这种模式下,引脚处于高阻抗状态,电平完全由外部电路决定。
下面,我们以使用标准库在 STM32F103 上配置 PA0 引脚为上拉输入模式为例,给出初始化 GPIO 的代码示例:
#include "stm32f10x.h"
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟,这一步就像是给GPIOA这个“演员”送上舞台,让它准备好表演
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA0引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
// 设置为上拉输入模式,就像是给PA0引脚找了一个“靠山”,让它在未被按键按下时保持高电平
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
// 设置GPIO速度,这里选择50MHz,就像是给PA0引脚设定了一个“速度限制”,确保它能快速响应
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA,将我们的配置应用到GPIOA上,就像是导演喊了一声“开始”,让PA0引脚按照我们的设定开始工作
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
在这段代码中,RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE) 用于使能 GPIOA 的时钟,因为在 STM32 中,只有使能了外设的时钟,外设才能正常工作。GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 指定了我们要配置的引脚是 PA0。GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU 将 PA0 引脚的模式设置为上拉输入模式。GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz 设置了 GPIO 的输出速度为 50MHz,这个速度可以根据实际需求进行调整,一般来说,对于按键采集这种对速度要求不是特别高的应用,50MHz 已经足够了。最后,GPIO_Init(GPIOA, &GPIO_InitStructure) 函数根据我们设定的参数初始化 GPIOA,使 PA0 引脚按照我们的配置工作。
(二)配置外部中断
配置外部中断就像是为一个重要的活动安排安保人员,确保在有特殊情况发生时能够及时响应。在 STM32 中,配置外部中断主要包括选择中断线、设置触发方式(上升沿触发、下降沿触发或双边沿触发)以及使能中断等步骤。
我们首先要选择与按键连接的 GPIO 引脚对应的中断线。在 STM32F103 中,GPIOA.0 对应的中断线是 EXTI_Line0。接下来,我们需要设置触发方式。如果我们希望在按键按下(即电平从高到低变化)时触发中断,就可以选择下降沿触发;如果希望在按键松开(即电平从低到高变化)时触发中断,就选择上升沿触发;而双边沿触发则是在电平上升和下降时都会触发中断。通常情况下,对于按键采集,我们选择下降沿触发,因为按键按下时的动作更具有明确的意义,而且可以减少按键抖动对中断触发的影响。最后,我们要使能中断,让系统能够响应中断请求。
下面是配置外部中断的代码示例:
#include "stm32f10x.h"
void EXTI_Config(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟和AFIO时钟,AFIO时钟就像是一个“信号桥梁”,用于连接GPIO和外部中断线
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 配置PA0为浮空输入,因为外部中断会检测引脚的电平变化,浮空输入可以让引脚的电平完全由外部按键决定
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PA0为EXTI_Line0的中断源,就像是给EXTI_Line0这个“安保人员”指定了负责的“区域”
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// 配置EXTI_Line0
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
// 设置为中断模式,告诉系统这是一个中断请求,需要及时处理
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
// 设置为下降沿触发,就像是告诉“安保人员”,只有在电平下降时才发出警报
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
// 使能EXTI_Line0中断,让“安保人员”开始工作,随时准备响应中断请求
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
在这段代码中,RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE) 使能了 GPIOA 和 AFIO 的时钟。GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING 将 PA0 配置为浮空输入,这样 PA0 的电平就完全由外部按键控制。GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0) 将 PA0 配置为 EXTI_Line0 的中断源,建立了 GPIO 引脚与中断线之间的联系。EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt 设置为中断模式,EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling 设置为下降沿触发,EXTI_InitStructure.EXTI_LineCmd = ENABLE 使能中断,完成了外部中断的配置。
(三)NVIC 中断优先级配置
NVIC 中断优先级配置就像是给一群排队的人分配优先级,让重要的人能够优先得到服务。在 STM32 中,NVIC(Nested Vectored Interrupt Controller)负责管理中断优先级,确保重要的中断能够及时得到处理,避免中断混乱。
NVIC 支持多级优先级设置,包括抢占式优先级和响应优先级(也称为子优先级)。抢占式优先级决定了中断是否可以打断其他正在执行的中断,而响应优先级则在具有相同抢占优先级的中断之间确定执行顺序。数值越小代表优先级越高,也就是说,高优先级的抢占优先级可以打断正在进行的低抢占优先级中断,而抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断,但当两个中断同时发生时,响应优先级高的先执行。
在配置 NVIC 中断优先级时,我们首先要选择中断优先级分组,STM32 提供了多种分组方式,如 NVIC_PriorityGroup_0 表示 0 位抢占优先级和 4 位子优先级,NVIC_PriorityGroup_1 表示 1 位抢占优先级和 3 位子优先级,以此类推。一般来说,我们会根据实际需求选择合适的分组方式。然后,我们要指定中断源,设置抢占式优先级和响应优先级,并使能中断。
下面是 NVIC 中断优先级配置的代码示例:
#include "stm32f10x.h"
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 配置NVIC为优先级组2,就像是给中断优先级设定了一个“分组规则”
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 配置按键中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
// 设置抢占优先级为1,就像是给这个中断分配了一个“插队权限”,数值越小,插队的优先级越高
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 设置响应优先级为1,在抢占优先级相同的情况下,响应优先级决定了执行顺序
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
// 使能中断通道,让这个中断可以被系统响应
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
在这段代码中,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2) 将 NVIC 配置为优先级组 2,在这个分组下,有 2 位抢占优先级和 2 位子优先级。NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn 指定了中断源为 EXTI0 中断。NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 设置抢占优先级为 1,NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1 设置响应优先级为 1,最后NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE 使能中断通道,完成了 NVIC 中断优先级的配置。
(四)编写中断处理函数
编写中断处理函数就像是为特殊情况制定应对方案,确保在中断发生时能够正确地处理事件。在编写中断处理函数时,我们需要注意以下几点:首先,要读取按键状态,判断按键是否真的被按下;其次,要处理按键抖动问题,避免因为按键抖动而产生多次中断;最后,要在处理完按键事件后,清除中断标志位,以便下次能够正确地响应中断。
按键抖动是由于机械按键在按下和松开的瞬间,触点会出现短暂的不稳定,导致电平在短时间内多次跳变。为了避免按键抖动对系统的影响,我们可以采用软件延时去抖动的方法,即在检测到按键按下后,延时一段时间(如 10ms),再次检测按键状态,如果此时按键仍然处于按下状态,才认为按键真的被按下,这样可以有效地消除按键抖动带来的影响。
下面是中断处理函数的代码示例:
#include "stm32f10x.h"
void EXTI0_IRQHandler(void)
{
// 确保是否产生了EXTI Line 0中断,就像是检查警报是否真的来自这个“区域”
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 软件延时去抖动,就像是给系统一个“冷静期”,避免因为抖动而误判
for (volatile int i = 0; i < 10000; i++);
// 再次读取按键状态,确认按键是否真的被按下
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
// 这里编写按键按下后的处理代码,比如控制LED灯的亮灭
// 假设LED连接在PB5引脚,并且配置为推挽输出模式
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
// 清除EXTI Line 0中断标志位,就像是解除警报,让系统准备好下次响应
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
在这段代码中,if (EXTI_GetITStatus(EXTI_Line0) != RESET) 用于判断是否产生了 EXTI Line 0 中断。for (volatile int i = 0; i < 10000; i++); 是一个简单的软件延时,用于消除按键抖动。if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) 再次读取按键状态,确认按键是否真的被按下。如果按键被按下,就可以在{} 中编写相应的处理代码,这里假设控制 LED 灯亮灭,通过GPIO_SetBits(GPIOB, GPIO_Pin_5) 将连接 LED 的 PB5 引脚置高,点亮 LED。最后,EXTI_ClearITPendingBit(EXTI_Line0) 清除中断标志位,为下一次中断做好准备。
五、实战演练:解决常见问题
(一)按键抖动问题及解决方法
在实际应用中,按键抖动是一个常见的问题,就像你在使用老式机械开关时,按下或松开开关的瞬间,会感觉到开关有轻微的震动。按键抖动就是指机械按键在按下和松开的瞬间,由于机械触点的弹性作用,导致触点在短时间内(一般为 5ms - 10ms)会产生多次闭合和断开,使得按键的电平信号出现不稳定的跳变现象。
这种抖动对于人类的感知来说可能微不足道,但对于 STM32 这样的高速微控制器而言,却可能导致严重的误判。因为 STM32 的运行速度非常快,它可能会在按键抖动的这段时间内检测到多次按键状态的变化,从而误认为进行了多次按键操作,这会对系统的正常运行产生很大的影响。比如在一个简单的计数器程序中,原本只希望按一次按键使计数器加 1,但由于按键抖动,可能会导致计数器被错误地增加多次,使得计数结果不准确。
为了解决按键抖动问题,我们可以采用软件消抖和硬件消抖两种方法。硬件消抖主要是通过在按键电路中添加电容、电阻等元件,利用它们的电气特性来平滑按键的电平信号,从而消除抖动。比如常见的 RC 滤波电路,通过电容的充放电特性,在按键按下或松开的瞬间,对电平的跳变进行缓冲,使电平变化更加平缓,避免 STM32 误检测。这种方法的优点是消抖效果稳定可靠,不占用软件资源,但缺点是需要额外的硬件成本,并且电路设计会相对复杂一些。
而软件消抖则是通过编写代码来实现消抖功能,它不需要额外的硬件成本,只需要在检测到按键状态变化后,通过软件延时一段时间(通常为 10ms - 20ms),等待按键抖动结束后,再读取按键的状态。如果此时按键状态仍然保持不变,则认为按键被真正按下或释放。这种方法的优点是简单易行,不需要增加硬件成本,缺点是会占用一定的 CPU 时间,降低系统的实时性。
在我们之前编写的中断处理函数中,采用的就是软件消抖的方法,下面我们再详细回顾一下软件消抖的代码示例:
#include "stm32f10x.h"
void EXTI0_IRQHandler(void)
{
// 确保是否产生了EXTI Line 0中断
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 软件延时去抖动,这里采用简单的循环延时,具体延时时间可根据实际情况调整
for (volatile int i = 0; i < 10000; i++);
// 再次读取按键状态,确认按键是否真的被按下
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
// 这里编写按键按下后的处理代码,比如控制LED灯的亮灭
// 假设LED连接在PB5引脚,并且配置为推挽输出模式
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
// 清除EXTI Line 0中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
在这段代码中,for (volatile int i = 0; i < 10000; i++); 就是软件延时部分,通过这段延时,避开了按键抖动的不稳定期。然后再通过if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) 再次检测按键状态,只有当按键在延时后仍然处于按下状态时,才认为按键是真正被按下,从而执行相应的处理代码。这样就有效地避免了按键抖动对系统的影响,保证了按键检测的准确性。
(二)多按键处理
在很多实际应用中,我们往往需要处理多个按键的情况,比如在一个电子设备的控制面板上,可能有多个功能按键,每个按键都对应着不同的操作。那么如何在代码中处理多个按键呢?
首先,我们需要为每个按键分配一个独立的 GPIO 引脚,并且将这些引脚都配置为输入模式。然后,针对每个按键对应的 GPIO 引脚,分别配置其外部中断,包括选择中断线、设置触发方式和使能中断等步骤,就像我们前面配置单个按键的外部中断一样,只不过现在是对多个按键进行重复配置。
在中断处理函数中,我们需要区分不同按键的中断请求。STM32 为每个中断线都分配了独立的中断服务函数,比如 EXTI0_IRQHandler 对应 EXTI_Line0 中断,EXTI1_IRQHandler 对应 EXTI_Line1 中断。我们可以在每个中断服务函数中,通过判断按键对应的 GPIO 引脚状态,来确定是哪个按键触发了中断。
例如,假设我们有两个按键,分别连接到 PA0 和 PA1 引脚,对应的中断线为 EXTI_Line0 和 EXTI_Line1,那么我们可以编写如下代码:
#include "stm32f10x.h"
// 按键1中断处理函数,按键1连接到PA0
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 软件延时去抖动
for (volatile int i = 0; i < 10000; i++);
// 再次读取按键状态,确认按键是否真的被按下
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
// 这里编写按键1按下后的处理代码,比如控制LED1灯的亮灭
// 假设LED1连接在PB5引脚,并且配置为推挽输出模式
GPIO_SetBits(GPIOB, GPIO_Pin_5);
}
// 清除EXTI Line 0中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
// 按键2中断处理函数,按键2连接到PA1
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) != RESET)
{
// 软件延时去抖动
for (volatile int i = 0; i < 10000; i++);
// 再次读取按键状态,确认按键是否真的被按下
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)
{
// 这里编写按键2按下后的处理代码,比如控制LED2灯的亮灭
// 假设LED2连接在PB6引脚,并且配置为推挽输出模式
GPIO_SetBits(GPIOB, GPIO_Pin_6);
}
// 清除EXTI Line 1中断标志位
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
在这个例子中,EXTI0_IRQHandler 函数处理按键 1 的中断请求,EXTI1_IRQHandler 函数处理按键 2 的中断请求。通过这种方式,我们就可以为每个按键编写相应的处理逻辑,实现对多个按键的有效处理。无论是在简单的控制电路中,还是在复杂的人机交互系统中,这种多按键处理的方法都能够满足我们的需求,让 STM32 能够准确地响应不同按键的操作,为我们的应用程序提供丰富的功能。
六、总结与展望
在本次探索 STM32 使用外部中断进行按键采集代码实现的旅程中,我们从 STM32 的基本概念入手,逐步深入到外部中断的原理剖析,详细讲解了硬件连接、开发环境搭建以及代码实现的每一个关键步骤,还针对实际应用中可能遇到的按键抖动和多按键处理等问题,给出了有效的解决方案。
通过初始化 GPIO、配置外部中断、设置 NVIC 中断优先级以及编写中断处理函数,我们成功地让 STM32 能够快速、准确地响应按键操作,实现了按键采集的功能。这不仅为我们后续开发更复杂的嵌入式系统奠定了坚实的基础,也让我们对 STM32 的强大功能有了更深入的认识。
现在,你已经掌握了这一关键技术,不妨将其应用到实际项目中,比如开发一个简单的智能家居控制面板,通过按键来控制家电设备的开关和调节;或者制作一个多功能的电子时钟,利用按键实现时间设置、闹钟设置等功能。在实践中,你会发现更多有趣的应用场景,也能进一步提升自己的开发能力。
如果你想要进一步探索,还可以尝试结合其他外设,如显示屏(LCD、OLED)、传感器(温度传感器、光线传感器)、无线通信模块(蓝牙、Wi-Fi)等,实现更复杂的功能。例如,结合温度传感器和按键,制作一个智能温控系统,通过按键设置温度阈值,当温度超过或低于阈值时,系统自动进行调节,并在显示屏上显示当前温度和工作状态;或者利用无线通信模块和按键,实现远程控制功能,通过手机 APP 发送指令,STM32 接收到指令后,根据按键设置的功能进行相应的操作。
希望大家能够在 STM32 的世界里不断探索、实践,创造出更多有趣、实用的项目。如果你在学习和实践过程中有任何问题或心得,欢迎在评论区留言分享,让我们一起交流、共同进步!
更多推荐
所有评论(0)