2025年STM32 -- 实现按键的长按与短按检测(其他单片机可移植)

STM32 -- 实现按键的长按与短按检测(其他单片机可移植)目录 资源获取 一 前言 二 思路 三 实现代码 1 主要代码 四 完整代码 Key h Key c 该改进版本 1ms 太繁琐了 我改成了 25ms 检测一次 1 定时器部分 2 按键检测部分 五 参考 资源获取

大家好,我是讯享网,很高兴认识大家。

目录

资源获取

一 前言

二 思路

 三 实现代码

1.主要代码

四 完整代码

Key.h

Key.c

该改进版本(1ms太繁琐了,我改成了25ms检测一次) 

 1.定时器部分

2.按键检测部分

 五、参考


资源获取

欢迎关注微信公众号--星之援工作室 发送关键字(长短按检测)


讯享网

一 前言

今天在逛博客的时候,偶然看到了一篇关于按键检测的文章,兴趣使然自己尝试了一番,写了一些代码去验证自己的思路,通过验证完美实现了长按和短按检测,后续有时间的我也会更新一下连按检测等

二 思路

首先说一下我使用的思路,第一就是使用我们的单片机的定时器去做一个轮询判断,首先建立一个1ms反转一次的定时器,定时器的选择根据自己手上有的单片机去配置一下就行,我用到是STM32F103C8T6,这款芯片有三个通用定时器,我选择的是使用TIM4去进行一个按键的检测,定时器以每1ms检测一次按键事件的方式,判断是否按下或长按按键,连续按下定时器会进行一个计数,检测一直按下就将计时器每轮询一次就加一,从而计算出按键按下的时间


 三 实现代码

1.主要代码

注意:⚠️我在后面没有放Time定时器的配置函数,但是我之前发过配置文件,大家可以直接使用

文章连接 :STM32学习记录 -- 通用定时器的配置(TIM2-TIM5)

需要注意,如果有同学使用STM32F103C8T6配置,需要屏蔽TIM5,因为STM32F103C8T6没有TIM5噢

这段主要检测按键的哪一个被按下了

/* * @Function : 按键处理函数 * @Input : Gmode:0,不支持连续按;1,支持连续按; * @Output : None * @Return : 0,没有任何按键按下 * 1,KEY1按下 * 2,KEY2按下 * 3,KEY3按下 * @Others : 注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!! * @Date : 2022-07-23 / u8 KEY_Scan(u8 mode) { static u8 key_up = 1; // 按键按松开标志 if (mode) key_up = 1; // 支持连按 if (key_up && (KEY1 == KEY_ON || KEY2 == KEY_ON || KEY3 == KEY_ON || KEY4 == KEY_ON)) { key_up = 0; if (KEY1 == KEY_ON) return KEY1_PRES; else if (KEY2 == KEY_ON) return KEY2_PRES; else if (KEY3 == KEY_ON) return KEY3_PRES; else if (KEY4 == KEY_ON) return KEY4_PRES; } else if (KEY1 == KEY_OFF && KEY2 == KEY_OFF && KEY3 == KEY_OFF && KEY4 == KEY_OFF) key_up = 1; return 0; // 无按键按下 }

讯享网

这段代码主要是放在定时器里面做一个轮询判断,其中key_old,主要是保存上一次按下的按键值,方便我们进行判断,这里面的time_4 就是我们的计时器,我们通过判断time_4的值做一个长按和短按的检测,Key_Scan_Time是我自己定义的一个短按时长限制,我设置的是30

讯享网// 检测按键是否按下 static u16 time_4 static U8 key_old = 0; u8 Check_Key_ON_OFF() { u8 key; key = KEY_Scan(1); // 与上一次的键值比较 如果不相等,表明有键值的变化,开始计时 if (key != 0 && time_4 == 0) { key_old = key; time_4 = 1; } if (key != 0 && time_4 >= 1 && time_4 <= 100) // 100ms { time_4++; // 时间记录器 } if (key == 0 && time_4 > 0 && time_4 < Key_Scan_Time) // 短按 { switch (key_old) { case KEY1_PRES: printf("Key1_Short\n"); break; case KEY2_PRES: printf("Key2_Short\n"); break; case KEY3_PRES: break; case KEY4_PRES: break; default: break; } time_4 = 0; } else if (key == 0 && time_4 >= Key_Scan_Time) // 长按 { switch (key_old) { case KEY1_PRES: printf("Key1_Long\n"); break; case KEY2_PRES: printf("Key2_Long\n"); break; case KEY3_PRES: break; case KEY4_PRES: break; default: break; } time_4 = 0; } return 1; }

最后我只需要在定时器中断中放入 Check_Key_ON_OFF()函数,即可实现功能

/* * @Function : TIMER4定时器中断服务 * @Input : None * @Output : None * @Return : None * @Others : None * @Date : 2022-08-30 / #if GENERAL_TIM4 void TIM4_IRQHandler(void) // TIM4中断 { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) // 检查TIM3更新中断发生与否 { Check_Key_ON_OFF(); TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // 清除TIMx更新中断标志 } } #endif

四 完整代码

Key.h

讯享网#ifndef __KEY_H #define __KEY_H #include "stm32f10x.h" // 引脚定义 #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA #define KEY1_GPIO_PORT GPIOA #define KEY1_GPIO_PIN GPIO_Pin_0 #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC #define KEY2_GPIO_PORT GPIOC #define KEY2_GPIO_PIN GPIO_Pin_13 #define KEY3_GPIO_CLK RCC_APB2Periph_GPIOB #define KEY3_GPIO_PORT GPIOB #define KEY3_GPIO_PIN GPIO_Pin_14 #define KEY4_GPIO_CLK RCC_APB2Periph_GPIOB #define KEY4_GPIO_PORT GPIOB #define KEY4_GPIO_PIN GPIO_Pin_15 #define KEY1 GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) // 读取按键0 #define KEY2 GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN) // 读取按键1 #define KEY3 GPIO_ReadInputDataBit(KEY3_GPIO_PORT, KEY3_GPIO_PIN) // 读取按键2 #define KEY4 GPIO_ReadInputDataBit(KEY4_GPIO_PORT, KEY4_GPIO_PIN) // 读取按键2 #define KEY1_PRES 1 // KEY1按下 #define KEY2_PRES 2 // KEY2按下 #define KEY3_PRES 3 // KEY3按下 #define KEY4_PRES 4 // KEY3按下 #define Key_Scan_Time 30 // 短按时长时间判断 / 按键按下标置宏 * 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0 * 若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 即可 */ #define KEY_ON 1 #define KEY_OFF 0 /* * @Function : 初始化控制LED的IO * @Input : None * @Output : None * @Return : None * @Others : None * @Date : 2022-07-23 / void Key_GPIO_Config(void); /* * @Function : 检测是否有按键按下 * @Input : GPIOx:x 可以是 A,B,C,D或者 E * : GPIO_Pin:待读取的端口位 * @Output : None * @Return : KEY_OFF(没按下按键)、KEY_ON(按下按键) * @Others : None * @Date : 2022-07-23 / uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); /* * @Function : 按键处理函数 * @Input : Gmode:0,不支持连续按;1,支持连续按; * @Output : None * @Return : 0,没有任何按键按下 * 1,KEY1按下 * 2,KEY2按下 * 3,KEY3按下 * @Others : 注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!! * @Date : 2022-07-23 / u8 KEY_Scan(u8 mode); /* * @Function : STM32程序软件复位 * @Input : None * : None * @Output : None * @Return : None * @Others : None * @Date : 2022-08-23 / void Sys_Restart(void); #endif /* __KEY_H */ 

Key.c

#include "key.h" #include "delay.h" // 协议文件 /* * @Function : 初始化控制LED的IO * @Input : None * @Output : None * @Return : None * @Others : None * @Date : 2022-07-23 / void Key_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*开启按键端口的时钟*/ RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK | KEY2_GPIO_CLK | KEY3_GPIO_CLK, ENABLE); // 选择按键的引脚 GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN; // 设置按键的引脚为浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 使用结构体初始化按键 GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure); // 选择按键的引脚 GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN; // 设置按键的引脚为浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 使用结构体初始化按键 GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure); // 选择按键的引脚 GPIO_InitStructure.GPIO_Pin = KEY3_GPIO_PIN; // 设置按键的引脚为浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 使用结构体初始化按键 GPIO_Init(KEY3_GPIO_PORT, &GPIO_InitStructure); // 选择按键的引脚 GPIO_InitStructure.GPIO_Pin = KEY4_GPIO_PIN; // 设置按键的引脚为浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 使用结构体初始化按键 GPIO_Init(KEY4_GPIO_PORT, &GPIO_InitStructure); GPIO_ResetBits(KEY1_GPIO_PORT, KEY1_GPIO_PIN); GPIO_ResetBits(KEY2_GPIO_PORT, KEY2_GPIO_PIN); GPIO_ResetBits(KEY3_GPIO_PORT, KEY3_GPIO_PIN); GPIO_ResetBits(KEY4_GPIO_PORT, KEY4_GPIO_PIN); } /* * @Function : 检测是否有按键按下 * @Input : GPIOx:x 可以是 A,B,C,D或者 E * : GPIO_Pin:待读取的端口位 * @Output : None * @Return : KEY_OFF(没按下按键)、KEY_ON(按下按键) * @Others : None * @Date : 2022-07-23 / u8 Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) { /*检测是否有按键按下 */ if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON) { /*等待按键释放 */ while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON) ; return KEY_ON; } else return KEY_OFF; } /* * @Function : 按键处理函数 * @Input : Gmode:0,不支持连续按;1,支持连续按; * @Output : None * @Return : 0,没有任何按键按下 * 1,KEY1按下 * 2,KEY2按下 * 3,KEY3按下 * @Others : 注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!! * @Date : 2022-07-23 / u8 KEY_Scan(u8 mode) { static u8 key_up = 1; // 按键按松开标志 if (mode) key_up = 1; // 支持连按 if (key_up && (KEY1 == KEY_ON || KEY2 == KEY_ON || KEY3 == KEY_ON || KEY4 == KEY_ON)) { delay_ms(10); // 去抖动 key_up = 0; if (KEY1 == KEY_ON) return KEY1_PRES; else if (KEY2 == KEY_ON) return KEY2_PRES; else if (KEY3 == KEY_ON) return KEY3_PRES; else if (KEY4 == KEY_ON) return KEY4_PRES; } else if (KEY1 == KEY_OFF && KEY2 == KEY_OFF && KEY3 == KEY_OFF && KEY4 == KEY_OFF) key_up = 1; return 0; // 无按键按下 } /* * @Function : STM32程序软件复位 * @Input : None * : None * @Output : None * @Return : None * @Others : None * @Date : 2022-08-23 / void Sys_Restart(void) { __set_FAULTMASK(1); NVIC_SystemReset(); } // 检测按键是否按下 static u16 time_4 static u8 key_old = 0; u8 Check_Key_ON_OFF() { u8 key; key = KEY_Scan(1); // 与上一次的键值比较 如果不相等,表明有键值的变化,开始计时 if (key != 0 && time_4 == 0) { key_old = key; time_4 = 1; } if (key != 0 && time_4 >= 1 && time_4 <= 100) // 100ms { time_4++; // 时间记录器 } if (key == 0 && time_4 > 0 && time_4 < Key_Scan_Time) // 短按 { switch (key_old) { case KEY1_PRES: printf("Key1_Short\n"); break; case KEY2_PRES: printf("Key2_Short\n"); break; case KEY3_PRES: break; case KEY4_PRES: break; default: break; } time_4 = 0; } else if (key == 0 && time_4 >= Key_Scan_Time) // 长按 { switch (key_old) { case KEY1_PRES: printf("Key1_Long\n"); break; case KEY2_PRES: printf("Key2_Long\n"); break; case KEY3_PRES: break; case KEY4_PRES: break; default: break; } time_4 = 0; } return 1; } /*END OF FILE/ 

串口效果如下

该改进版本(1ms太繁琐了,我改成了25ms检测一次) 

主要代码如下

 1.定时器部分

讯享网#if GENERAL_TIM4 void TIM4_IRQHandler(void) // TIM4中断 { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) // 检查TIM3更新中断发生与否 { time_4++; if (time_4 % 25 == 0) { Check_Key_ON_OFF(); time_4 = 0; } TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // 清除TIMx更新中断标志 } } #endif

2.按键检测部分

Key_Scan_Time 检测次数我设置的为 10

// 检测按键是否按下 static U8 num_on = 0; static U8 key_old = 0; int Check_Key_ON_OFF() { U8 key; key = KEY_Scan(1); // 与上一次的键值比较 如果不相等,表明有键值的变化,开始计时 if (key != 0 && num_on == 0) { key_old = key; num_on = 1; } if (key != 0 && num_on >= 1 && num_on <= Key_Scan_Time) // 25*10ms { num_on++; // 时间记录器 } if (key == 0 && num_on > 0 && num_on < Key_Scan_Time) // 短按 { switch (key_old) { case KEY1_PRES: printf("Key1_Short\n"); break; case KEY2_PRES: printf("Key2_Short\n"); break; case KEY3_PRES: break; case KEY4_PRES: break; default: break; } num_on = 0; } else if (key == 0 && num_on >= Key_Scan_Time) // 长按 { switch (key_old) { case KEY1_PRES: printf("Key1_Long\n"); break; case KEY2_PRES: printf("Key2_Long\n"); break; case KEY3_PRES: break; case KEY4_PRES: break; default: break; } num_on = 0; } return 1; }

 五、参考

【stm32单片机基础】按键状态机实现长按和短按icon-default.png?t=N7T8https://blog.csdn.net/_/article/details/?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%9E%E7%8E%B0%E9%95%BF%E7%9F%AD%E6%8C%89&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-.nonecase&spm=1018.2226.3001.4450


完整代码请关注卫星公众号进行获取和咨询


小讯
上一篇 2025-01-17 10:59
下一篇 2025-02-09 16:27

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/54713.html