Bootstrap

单片机按键双击与长按识别技术全解析 | 零基础入门STM32第四十三步

主题内容教学目的/扩展视频
触摸开关电路原理,跳线设置,驱动程序与调用。能读出键值即可。

师从洋桃电子,杜洋老师


📑文章目录

    • 一、硬件设计基础
      • 1.1 按键电路原理
      • 1.2 时钟配置要点
    • 二、深度代码分析与实现原理图解
      • 2.1 代码结构框图
      • 2.2 核心变量说明
    • 三、核心代码段解析
    • 四、状态转换原理(详解)
      • 4.1 关键节点技术解析
    • 五、关键技术指标
    • 六、代码优化建议
    • 七、典型问题分析
    • 八、硬件关联分析
    • 九、扩展功能实现
    • 十、相关资源


回顾上期🔍STM32触摸按键原理与驱动开发实战 | 零基础入门STM32第四十二步


一、硬件设计基础

1.1 按键电路原理

(图1:触摸按键连接示意图)


▲ 示意图说明:触摸芯片通过感应电极检测电容变化,输出数字信号控制LED

典型按键电路包含:

  • 10KΩ上拉电阻
  • 104陶瓷电容滤波
  • ESD保护二极管

1.2 时钟配置要点

void NVIC_Configuration(void){ //嵌套中断向量控制器 的设置
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
}

void RCC_Configuration(void){ //RCC时钟的设置  
	ErrorStatus HSEStartUpStatus;   
	RCC_DeInit();              /* RCC system reset(for debug purpose) RCC寄存器恢复初始化值*/   
	RCC_HSEConfig(RCC_HSE_ON); /* Enable HSE 使能外部高速晶振*/   
	HSEStartUpStatus = RCC_WaitForHSEStartUp(); /* Wait till HSE is ready 等待外部高速晶振使能完成*/   
	if(HSEStartUpStatus == SUCCESS){   
		/*设置PLL时钟源及倍频系数*/   
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //RCC_PLLMul_x(枚举2~16)是倍频值。当HSE=8MHZ,RCC_PLLMul_9时PLLCLK=72MHZ   
		/*设置AHB时钟(HCLK)*/   
		RCC_HCLKConfig(RCC_SYSCLK_Div1); //RCC_SYSCLK_Div1——AHB时钟 = 系统时钟(SYSCLK) = 72MHZ(外部晶振8HMZ)   
		/*注意此处的设置,如果使用SYSTICK做延时程序,此时SYSTICK(Cortex System timer)=HCLK/8=9MHZ*/   
		RCC_PCLK1Config(RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1),RCC_HCLK_Div2——APB1时钟 = HCLK/2 = 36MHZ(外部晶振8HMZ)   
		RCC_PCLK2Config(RCC_HCLK_Div1); //设置高速AHB时钟(PCLK2),RCC_HCLK_Div1——APB2时钟 = HCLK = 72MHZ(外部晶振8HMZ)   
		/*注:AHB主要负责外部存储器时钟。APB2负责AD,I/O,高级TIM,串口1。APB1负责DA,USB,SPI,I2C,CAN,串口2,3,4,5,普通TIM */  
		FLASH_SetLatency(FLASH_Latency_2); //设置FLASH存储器延时时钟周期数   
		/*FLASH时序延迟几个周期,等待总线同步操作。   
		推荐按照单片机系统运行频率:
		0—24MHz时,取Latency_0;   
		24—48MHz时,取Latency_1;   
		48~72MHz时,取Latency_2*/   
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //选择FLASH预取指缓存的模式,预取指缓存使能   
		RCC_PLLCmd(ENABLE);	//使能PLL
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //等待PLL输出稳定   
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //选择SYSCLK时钟源为PLL
		while(RCC_GetSYSCLKSource() != 0x08); //等待PLL成为SYSCLK时钟源   
	}  
}

二、深度代码分析与实现原理图解

2.1 代码结构框图

计时c累加
a=1
检测到二次按下
a=2
检测到三次按下
主程序开始
检测按键按下?
延时20ms去抖
确认按下?
进入状态判断
长按检测循环
满足长按条件?
执行长按操作
统计单击次数a
等待按键释放
进入双击检测
首次单击?
启动400ms窗口
a=2,更新状态
已双击?
启动三击检测
a=3,更新状态
执行对应操作
重置状态参数

2.2 核心变量说明

变量名类型作用域功能描述
au8main短按次数计数器(记录单击/双击/三击)
cu8main长按持续时间计数器(单位:10ms)
bu8循环体双击检测循环计数器

三、核心代码段解析

// 长按检测核心逻辑
while((!GPIO_ReadInputDataBit(...)) && c<KEYA_SPEED1) {
    c++;
    delay_ms(10); // 阻塞式延时
}

执行流程

  1. 持续检测按键保持按下状态
  2. 每10ms递增计数器c
  3. 退出条件:
    • 按键释放(GPIO_ReadInputDataBit返回1)
    • 达到长按阈值(c >= 100 → 100×10ms=1s)

// 双击检测逻辑
for (b = 0; b < KEYA_SPEED2; b++) {
    delay_ms(20);
    if (检测到二次按下) {
        a++; 
        break;
    }
}

时序特性

  • 总检测窗口:20×20ms=400ms
  • 实际有效间隔:两次按下间隔<400ms
  • 每20ms采样一次按键状态

// 三击扩展逻辑
if (a == 2) {
    for (b = 0; b < KEYA_SPEED2; b++) {
        delay_ms(20);
        if (三次按下检测) {
            a++;
            break;
        }
    }
}

特殊说明

  • 复用双击检测参数(KEYA_SPEED2)
  • 总检测窗口叠加:400ms + 400ms = 800ms
  • 需三次有效按下且每次间隔<400ms

四、状态转换原理(详解)

按键电平变低
20ms后仍为低
持续>1秒
≤1秒释放
启动400ms计时
400ms内再触发
超时未触发
400ms内再触发
超时未触发
① 初始状态
② 消抖确认
③ 长按判断
④ 长按事件
⑤ 单击释放
⑥ 等待双击
⑦ 双击确认
8️⃣ 三击确认


对应代码逻辑映射:

图形节点代码段位置关键变量时间参数
delay_ms(20)-20ms消抖
while(...c<100)c10ms/cycle
for(b=0;b<20...)b20ms/cycle

4.1 关键节点技术解析

  1. S3节点(长按判断)
// 对应代码段
while((按键保持按下) && c<KEYA_SPEED1){
    c++; delay_ms(10);
}
  • 时间阈值:S3 = KEYA_SPEED1 * 10ms
  • 退出条件:物理释放或超时
  1. D2节点(双击窗口)
// 对应代码段
for(b=0; b<KEYA_SPEED2; b++){
    delay_ms(20);
    if(二次按下检测)...
}
  • 时间窗口:D2 = KEYA_SPEED2 * 20ms
  • 事件触发条件:窗口期内检测到二次按压
  1. 状态转换逻辑
if(c >= KEYA_SPEED1) {          // 长按分支
    // 长按处理
} else {                        // 单击分支
    if(a == 1) {                // 首次单击
        // 启动D2窗口检测
    }
}
  1. 时间线标记
|--消抖20ms--|--长按检测(0-1000ms)--|--D2窗口400ms--|

完整工程代码示例⏬触摸按键驱动程序

五、关键技术指标

参数项计算公式典型值
消抖时间DEBOUNCE_TIME ×120ms
长按判定时间KEYA_SPEED1 ×10ms1000ms
双击间隔阈值KEYA_SPEED2 ×20ms400ms
三击总时间2×KEYA_SPEED2 ×20ms800ms
系统响应延迟DEBOUNCE_TIME + 检测周期40-60ms

六、代码优化建议

  1. 非阻塞式重构
// 使用系统tick替代delay
uint32_t start = HAL_GetTick();
while(HAL_GetTick()-start < 20) {
    // 空循环实现非阻塞等待
}
  1. 状态机改进
typedef enum {
    STATE_IDLE,
    STATE_DEBOUNCE,
    STATE_PRESSED,
    STATE_WAIT_RELEASE
} KeyState;

KeyState key_state = STATE_IDLE;
  1. 定时器中断优化
// 配置TIM3每10ms产生中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if(htim->Instance == TIM3) {
        key_process(); // 定时扫描按键
    }
}

七、典型问题分析

问题现象:快速三击被识别为双击
根本原因

// 第三次按下检测复用相同时间窗口
if(a == 2) { // 应在独立窗口检测
    // 当前逻辑导致时间窗口重叠
}

解决方案

// 独立三击检测窗口
if(a == 2) {
    uint32_t triple_start = HAL_GetTick();
    while(HAL_GetTick()-triple_start < 400) {
        if(检测到按下) {
            a++;
            break;
        }
    }
}

八、硬件关联分析

时钟系统
中断/轮询
控制信号
APB2总线
时钟树
GPIO时钟
按键电路
GPIO输入
STM32
LED驱动电路

关键点

  • APB2总线控制GPIO时钟
  • 上拉输入模式确保稳定检测
  • LED控制采用直接GPIO驱动

九、扩展功能实现

组合按键检测

if((KEYA状态 == 长按) && (KEYB状态 == 双击)) {
    // 执行特殊功能
}

灵敏度动态调整

// 根据使用频率自动优化阈值
static uint16_t dynamic_thres = 100;
if(双击误触发) dynamic_thres += 5;
if(长按不响应) dynamic_thres -= 3;

十、相关资源

[1] 洋桃电子B站课程-STM32入门100步
[2] STM32F103xx官方数据手册
[3] STM32F103X8-B数据手册(中文)
[4] STM32F103固件函数库用户手册(中文)
[5] 触摸按键驱动程序
[6] TTP223-TD 数据手册


💬 技术讨论(请在评论区留言~)

📌 下期预告:下一期将探讨触摸按键滑动程序,欢迎持续关注!

点击查阅🔍往期【STM32专栏】文章

版权声明:本文采用[CC BY-NC-SA 4.0]协议,转载请注明来源
实测开发版:洋桃1号开发版(基于STM32F103C8T6)
更新日志

  • v1.0 初始版本(2025-03-03)
;