最近一个项目中,需要实现单键多功能控制:
单击执行某个动作; 双击切换模式; 长按执行开关机。

下面是具体实现方式分享
一、按键
#define KEY_ONOFF PORTA0 //此IO内部开启上拉
二、按键结构体定义
按键设计了一个结构体 `KEY_HandleTypeDef`, 用来存储每个按键的各种状态信息:
typedef struct
{
u8 state; // 当前按键状态(0=松开,1=按下)
u8 pressed_ct; // 短按计数
u16 long_press_ct; // 长按计数
u8 release_time; // 松开延时检测
u8 click; // 按键事件标志:1=单击,2=双击,3=长按
u8 longpress_flag; // 长按触发标志(防止重复触发)
u16 longpress_cnt; // 长按连续动作计数器
} KEY_HandleTypeDef;
KEY_HandleTypeDef key0 = {0};
注:可支持后续拓展更多,比如3击,4击,或者长按连发、多键组合等。
三、核心扫描函数 k0_scan()
该函数建议每 10ms 调用一次,负责检测按键按下/松开状态,并进行计数和事件识别。
void k0_scan(void)
{
// ① 按下检测
if (KEY_ONOFF == 0)
{
if (key0.state == 0)
{
key0.state = 1;
if (key0.pressed_ct < 100) key0.pressed_ct++;
key0.release_time = 30; // 释放延时(30*10ms=300ms)
}
// 长按计数
if (key0.long_press_ct < 301) key0.long_press_ct++;
// 超过3秒判定为长按
if (key0.long_press_ct == 300)
{
key0.long_press_ct = 300;
key0.click = 3; // 标记长按事件
key0.pressed_ct = 0;
key0.longpress_cnt = 0;
if (!key0.longpress_flag)
{
key0.longpress_flag = 1;
//长按
}
}
}
// ② 松开检测
else
{
key0.state = 0;
key0.long_press_ct = 0;
if (key0.release_time) key0.release_time--;
}
// ③ 松开后判断单击/双击
if (key0.release_time == 0)
{
if (!key0.longpress_flag)
{
if (key0.pressed_ct == 1)
{
key0.click = 1; // 单击
key0.pressed_ct = 0;
}
else if (key0.pressed_ct == 2)
{
key0.click = 2; // 双击
key0.pressed_ct = 0;
}
}
else
{
// 长按松开后清除标志
key0.longpress_flag = 0;
key0.pressed_ct = 0;
key0.click = 0;
key0.longpress_cnt = 0;
}
}
}
四、逻辑说明
功能 | 条件 | 判定逻辑 | 执行动作 |
---|---|---|---|
单击 | 按下并快速松开一次 | 在 300ms 内没有再次按下 | key0.click = 1 |
双击 | 在第一次松开后, 300ms 内再次按下 | 第二次松开时检测到 `pressed_ct == 2 | key0.cl``ick = 2 |
长按 | 按住时间 ≥ 3 秒 | long_press_ct` 计数达到 300(10ms × 300 = 3s) | key0.click = 3 ,触发开关机逻辑 |
五、事件处理函数
在主循环中调用 KEY_handle()
进行按键事件分发处理:
void KEY_handle(void)
{
// 单击事件
if (key0.click == 1)
{
key0.click = 0;
// 执行单击动作
// 比如:切换模式 / 调节亮度 / 唤醒设备 等
}
if (key0.click == 2) // 双击
{
}
if (key0.click == 3) // 长按
{
}
}