针对嵌入式领域竞赛需求,特设计一款超轻量化按钮模块方案。该方案具备高可复制性与易理解性,旨在为参赛者提供便捷高效的硬件接入解决方案,助力快速搭建嵌入式系统交互模块。
key.c
可以根据直接的需求来调整DEBOUNCE LONG_PRESS DOUBLE_CLICK
注意,DOUBLE_CLICK不要太长,因为这里采用了堵塞等待双击,如果不需要双击功能可以把DOUBLE_CLICK置0;
#include "key.h"
// 初始化按钮
void Button_Init(Button *btn, GPIO_TypeDef *port, uint16_t pin) {
btn->port = port;
btn->pin = pin;
btn->state = 0;
btn->click_cnt = 0;
// 设置GPIO为输入模式,这里可以采用mubemx进行配置
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(port, &GPIO_InitStruct);
// 设置GPIO为上拉模式
}
BtnEvent Button_Update(Button *btn) {
static const uint16_t DEBOUNCE = 20, LONG_PRESS = 1000, DOUBLE_CLICK = 200;
uint8_t pressed = (HAL_GPIO_ReadPin(btn->port, btn->pin) == GPIO_PIN_RESET);
uint32_t now = HAL_GetTick();
switch (btn->state) {
case 0: // 空闲状态
if (pressed) {
btn->state = 1; // 进入按下消抖
btn->last_time = now;
}
break;
case 1: // 按下消抖
if (now - btn->last_time >= DEBOUNCE) {
if (pressed) {
btn->state = 2; // 确认按下
btn->last_time = now;
} else {
btn->state = 0; // 抖动,回到空闲
}
}
break;
case 2: // 按下状态
if (!pressed&&now- btn->last_time < LONG_PRESS) {
btn->state = 3; // 进入松开消抖
btn->last_time = now;
} else if (!pressed&&now - btn->last_time >= LONG_PRESS) {
btn->state = 0;
return BTN_LONG_PRESS; // 长按触发
}
break;
case 3: // 松开消抖
if (now - btn->last_time >= DEBOUNCE) {
if (!pressed) { // 确认松开
btn->click_cnt++;
btn->state = 4; // 等待双击
btn->last_time = now;
} else {
btn->state = 2; // 抖动,回到按下状态
}
}
break;
case 4: // 等待双击
if (now - btn->last_time > DOUBLE_CLICK) {
BtnEvent event = (btn->click_cnt >= 2) ? BTN_DOUBLE_CLICK : BTN_CLICK;
btn->click_cnt = 0;
btn->state = 0;
return event;
} else if (pressed) {
btn->state = 1; // 再次按下,重新消抖
btn->last_time = now;
}
break;
}
return BTN_NONE;
}
key.h
#ifndef KEY_H
#define KEY_H
#include "main.h"
#include "gpio.h"
// 按钮事件类型
typedef enum {
BTN_NONE,
BTN_CLICK,
BTN_DOUBLE_CLICK,
BTN_LONG_PRESS
} BtnEvent;
// 按钮状态结构体
typedef struct {
GPIO_TypeDef *port; // 按钮GPIO端口
uint16_t pin; // 按钮引脚
uint32_t last_time; // 上次时间戳
uint8_t state; // 当前状态
uint8_t click_cnt; // 点击计数
} Button;
// 初始化按钮
void Button_Init(Button *btn, GPIO_TypeDef *port, uint16_t pin);
// 更新按钮状态
// 返回按钮事件
BtnEvent Button_Update(Button *btn) ;
#endif // !KEY_H