【星云 Orbit-F4 开发板】03b. 按键玩法一:独立按键中断扫描法
概述
本教程基于STM32F407 HAL库,实现模块化的定时中断按键扫描功能,采用去抖动算法与自锁机制确保稳定检测。代码分为按键模块、蜂鸣器模块、定时器模块及主程序,结构清晰,便于移植扩展。
目录结构
Project/
├── Inc/
│ ├── key.h // 按键模块头文件
│ ├── beep.h // 蜂鸣器模块头文件
│ ├── timer.h // 定时器模块头文件
├── Src/
│ ├── key.c // 按键模块实现
│ ├── beep.c // 蜂鸣器模块实现
│ ├── timer.c // 定时器模块实现
│ ├── main.c // 主程序
模块化代码实现
1. 按键模块 (key.h/key.c)
功能:封装按键初始化与扫描逻辑,支持多按键独立检测。
// key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f4xx_hal.h"
#define KEY_NUM 2 // 按键数量
#define DEBOUNCE_TIME 20 // 去抖动时间阈值(单位:ms)
// 按键结构体
typedef struct {
GPIO_TypeDef *port; // GPIO端口
uint16_t pin; // GPIO引脚
uint8_t is_pressed; // 当前按键状态(1:按下, 0:释放)
uint8_t lock; // 自锁标志
uint16_t debounce_cnt; // 去抖动计数器
} Key_TypeDef;
// 全局按键数组声明(在key.c中定义)
extern Key_TypeDef keys[KEY_NUM];
void KEY_Init(void); // 按键初始化
void KEY_Scan(void); // 按键扫描(需在定时中断中调用)
#endif
// key.c
#include "key.h"
// 定义两个按键的硬件配置
Key_TypeDef keys[KEY_NUM] = {
{GPIOA, GPIO_PIN_0, 0, 0, 0}, // Key1: PA0(下拉输入)
{GPIOA, GPIO_PIN_1, 0, 0, 0} // Key2: PA1(下拉输入)
};
// 按键初始化
void KEY_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置按键引脚为下拉输入
GPIO_InitStruct.Pin = keys[0].pin | keys[1].pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// 按键扫描函数(需在1ms定时中断中调用)
void KEY_Scan(void) {
for (uint8_t i = 0; i < KEY_NUM; i++) {
uint8_t current_state = HAL_GPIO_ReadPin(keys[i].port, keys[i].pin);
if (current_state == GPIO_PIN_SET) { // 检测到高电平(按键按下)
if (keys[i].lock == 0) { // 首次检测到按下
keys[i].debounce_cnt++;
if (keys[i].debounce_cnt >= DEBOUNCE_TIME) {
keys[i].is_pressed = 1; // 标记按键按下
keys[i].lock = 1; // 自锁,防止重复触发
keys[i].debounce_cnt = 0;
}
}
} else { // 按键释放
keys[i].lock = 0;
keys[i].debounce_cnt = 0;
keys[i].is_pressed = 0; // 清除按下标记
}
}
}
2. 蜂鸣器模块 (beep.h/beep.c)
功能:控制蜂鸣器鸣叫时间,支持短鸣触发。
// beep.h
#ifndef __BEEP_H
#define __BEEP_H
#include "stm32f4xx_hal.h"
#define BEEP_PORT GPIOB
#define BEEP_PIN GPIO_PIN_0
#define BEEP_SHORT_MS 40 // 短鸣持续时间(ms)
void BEEP_Init(void); // 蜂鸣器初始化
void BEEP_Trigger(uint16_t duration_ms); // 触发蜂鸣器
#endif
// beep.c
#include "beep.h"
static volatile uint16_t beep_counter = 0; // 蜂鸣器计时器
// 蜂鸣器初始化
void BEEP_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置蜂鸣器引脚为推挽输出
GPIO_InitStruct.Pin = BEEP_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BEEP_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_RESET); // 初始关闭
}
// 触发蜂鸣器(duration_ms: 持续时间)
void BEEP_Trigger(uint16_t duration_ms) {
beep_counter = duration_ms;
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_SET); // 启动蜂鸣器
}
// 更新蜂鸣器状态(需在1ms定时中断中调用)
void BEEP_Update(void) {
if (beep_counter > 0) {
beep_counter--;
if (beep_counter == 0) {
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_RESET); // 关闭
}
}
}
3. 定时器模块 (timer.h/timer.c)
功能:配置TIM3定时器,提供1ms中断用于按键扫描与蜂鸣器控制。
// timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f4xx_hal.h"
void TIMER_Init(void); // 定时器初始化
#endif
// timer.c
#include "timer.h"
#include "key.h"
#include "beep.h"
TIM_HandleTypeDef htim3;
// 定时器初始化
void TIMER_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim3.Instance = TIM3;
htim3.Init.Prescaler = 84 - 1; // 84MHz / 84 = 1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000 - 1; // 1ms中断周期 (1MHz / 1000)
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim3);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
HAL_TIM_Base_Start_IT(&htim3); // 启动定时器中断
}
// 定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM3) {
KEY_Scan(); // 按键扫描
BEEP_Update(); // 更新蜂鸣器状态
}
}
4. 主程序 (main.c)
功能:模块初始化与按键服务处理。
#include "main.h"
#include "key.h"
#include "beep.h"
#include "timer.h"
int main(void) {
HAL_Init();
KEY_Init(); // 初始化按键
BEEP_Init(); // 初始化蜂鸣器
TIMER_Init(); // 初始化定时器
while (1) {
// 检测按键触发
for (uint8_t i = 0; i < KEY_NUM; i++) {
if (keys[i].is_pressed) {
BEEP_Trigger(BEEP_SHORT_MS); // 触发蜂鸣器
keys[i].is_pressed = 0; // 清除按键状态
}
}
}
}
逻辑详解
模块化设计
- 按键模块:封装GPIO配置与扫描逻辑,支持多按键独立检测。
- 蜂鸣器模块:提供触发接口与状态更新,分离控制逻辑。
- 定时器模块:统一管理中断,确保1ms精准调度。
去抖动机制
- 在
KEY_Scan()
中,通过debounce_cnt
累计中断次数,超过阈值后标记按键按下,避免抖动误触发。
- 在
自锁标志
- 按键按下后设置
lock
标志,防止长按重复触发;释放后自动复位。
- 按键按下后设置
蜂鸣器控制
- 触发时设置
beep_counter
,定时中断中递减至0后关闭,确保鸣叫时间精确。
- 触发时设置
总结
本代码通过模块化设计,将按键扫描、蜂鸣器控制与定时器管理分离,提升了代码可读性与可维护性。采用HAL库标准接口,便于移植到其他STM32系列。实际应用中,可根据需求调整按键数量、去抖动时间及硬件配置。