主题 | 内容 | 教学目的/扩展视频 |
---|---|---|
触摸开关 | 电路原理,跳线设置,驱动程序与调用。 | 能读出键值即可。 |
师从洋桃电子,杜洋老师
📑文章目录
- 一、硬件设计基础
- 1.1 按键电路原理
- 1.2 时钟配置要点
- 二、深度代码分析与实现原理图解
- 2.1 代码结构框图
- 2.2 核心变量说明
- 三、核心代码段解析
- 四、状态转换原理(详解)
- 4.1 关键节点技术解析
- 五、关键技术指标
- 六、代码优化建议
- 七、典型问题分析
- 八、硬件关联分析
- 九、扩展功能实现
- 十、相关资源
▲ 回顾上期🔍STM32触摸按键原理与驱动开发实战 | 零基础入门STM32第四十二步
一、硬件设计基础
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 代码结构框图
2.2 核心变量说明
变量名 | 类型 | 作用域 | 功能描述 |
---|---|---|---|
a | u8 | main | 短按次数计数器(记录单击/双击/三击) |
c | u8 | main | 长按持续时间计数器(单位:10ms) |
b | u8 | 循环体 | 双击检测循环计数器 |
三、核心代码段解析
// 长按检测核心逻辑
while((!GPIO_ReadInputDataBit(...)) && c<KEYA_SPEED1) {
c++;
delay_ms(10); // 阻塞式延时
}
执行流程:
- 持续检测按键保持按下状态
- 每10ms递增计数器c
- 退出条件:
- 按键释放(
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
四、状态转换原理(详解)
对应代码逻辑映射:
图形节点 | 代码段位置 | 关键变量 | 时间参数 |
---|---|---|---|
② | delay_ms(20) | - | 20ms消抖 |
③ | while(...c<100) | c | 10ms/cycle |
⑥ | for(b=0;b<20...) | b | 20ms/cycle |
4.1 关键节点技术解析
- S3节点(长按判断)
// 对应代码段
while((按键保持按下) && c<KEYA_SPEED1){
c++; delay_ms(10);
}
- 时间阈值:
S3 = KEYA_SPEED1 * 10ms
- 退出条件:物理释放或超时
- D2节点(双击窗口)
// 对应代码段
for(b=0; b<KEYA_SPEED2; b++){
delay_ms(20);
if(二次按下检测)...
}
- 时间窗口:
D2 = KEYA_SPEED2 * 20ms
- 事件触发条件:窗口期内检测到二次按压
- 状态转换逻辑
if(c >= KEYA_SPEED1) { // 长按分支
// 长按处理
} else { // 单击分支
if(a == 1) { // 首次单击
// 启动D2窗口检测
}
}
- 时间线标记
|--消抖20ms--|--长按检测(0-1000ms)--|--D2窗口400ms--|
五、关键技术指标
参数项 | 计算公式 | 典型值 |
---|---|---|
消抖时间 | DEBOUNCE_TIME ×1 | 20ms |
长按判定时间 | KEYA_SPEED1 ×10ms | 1000ms |
双击间隔阈值 | KEYA_SPEED2 ×20ms | 400ms |
三击总时间 | 2×KEYA_SPEED2 ×20ms | 800ms |
系统响应延迟 | DEBOUNCE_TIME + 检测周期 | 40-60ms |
六、代码优化建议
- 非阻塞式重构:
// 使用系统tick替代delay
uint32_t start = HAL_GetTick();
while(HAL_GetTick()-start < 20) {
// 空循环实现非阻塞等待
}
- 状态机改进:
typedef enum {
STATE_IDLE,
STATE_DEBOUNCE,
STATE_PRESSED,
STATE_WAIT_RELEASE
} KeyState;
KeyState key_state = STATE_IDLE;
- 定时器中断优化:
// 配置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时钟
- 上拉输入模式确保稳定检测
- 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)