【星云 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 = 1MHzhtim3.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系列。实际应用中,可根据需求调整按键数量、去抖动时间及硬件配置。