按键扫描:双击、短按、长按(非阻塞)
创作时间:
作者:
@小白创作中心
按键扫描:双击、短按、长按(非阻塞)
引用
CSDN
1.
https://m.blog.csdn.net/l20190411w/article/details/138162665
本文介绍了一种在单片机中实现非阻塞式按键扫描的软件处理方法,详细描述了短按、长按和双击的识别算法。通过使用定时器进行分时调度,可以有效避免按键抖动问题,并实现对不同按键操作的准确识别。
引言
按键防抖大多使用延时来处理,一般延时阻塞10ms,就算对于51单片机的运行速度来说,10ms也能做很多很多任务,10ms的时间太浪费资源。
按键电路
使用最简单的按键电路,组件仅包含一个单片机IO和一个按键。由于STC15W4K32S4的P7口内部有上拉电阻,所以也不需要在外面加什么东西,K1按下的时候单片机P7.0的电平是低电平,没按下P7.0就是高电平。
1、按键识别算法的作用
一般看来按键至少有3种状态:短按、长按、双击(或多次点击)
- 短按:一般定义为按键按下并快速释放的操作,持续时间较短,按下时不会立即触动动作,而是过一个较短的时间才触发动作。
- 长按:通常是指按键保持按压状态超过短按时间阈值的操作,持续时间较长,松开时触发动作。
- 双击:较短的时间内,连续按下两次才会触发动作。
2、按键机械抖动
我们将使用非阻塞式按键检测来过滤前后沿抖动。
3、短按 长按 双击定义
- 短按:如果检测出按键按下的时间>=90ms且<=250ms时,则会被认定为按键短按。程序则是进入短按的逻辑处理状态。
- 长按:经过网上大量的资料查阅和实际按下的情况对比,长按的时间一般大于500ms,因此在本文的按键识别算法中,将临界时间设定为500ms。
- 双击:双击的第一次按下松开后到第二次按下开始的时间间隔较多分布在200ms以内,而考虑到一些用户操作动作较为缓慢,及误触发的风险、硬件和软件的响应能力。因此将双击检测时间设置为了250ms,如果250ms内没有检测到按下第二次短按,则是会判断为单击,否则就是触发双击。
4、实现代码
注:使用定时器进行分时调度
/* Max compare value: u8/2 */
#define tim_cmp_uint8(cur, last) (uint8_t)((s8)(cur) - (s8)(last))
/* Max compare value: uint16_t/2 */
#define tim_cmp_uint16(cur, last) (uint16_t)((s16)(cur) - (s16)(last))
/* Max compare value: uint32_t/2 */
#define tim_cmp_uint32(cur, last) (uint32_t)((s32)(cur) - (s32)(last))
static uint8_t time0_count;
static uint8_t led_count;
void main()
{
clk_enable_timer0();
timer0_set_overflow(249);//1 / (8000000 / 32) * 250 = 0.001 1ms
timer0_enable_it();
timer0_control(TIMER0_CTL_PSC_32 | TIMER0_CTL_START);
while(1)
{
if ( tim_cmp_uint8(time0_count, led_count) >= 50 )//50ms进入一次
{
led_count = time0_count;
led_Display();
.......略
}
.......略
}
}
void isr() __interrupt
{
if(IF0_BIT_T0F){
time0_count +=1;
IF0_BIT_T0F = 0;
}
}
1.定义各个状态:
typedef enum {
KeyState_IDLE, //空闲状态。
KeyState_CUT, //消抖状态。
KeyState_PRESSED, //按下状态。
KeyState_WAIT_DOUBLE_CLICK, //等待双击状态。
KeyState_DOUBLE_CLICKED, //双击状态。
KeyState_LONG_PRESSED //长按状态。
}KeyState;
2.定义按键结构体:
typedef struct key_state{
uint8_t key_flag; //按键标志
bool level_state; //电平状态
uint16_t start_time; //按下时间
uint16_t end_time; //松开时间
uint8_t key_cut; //按键消抖
uint8_t key_last; //按键最终状态
}key_state_t;
3.按键处理函数:
#define KEY_SCAN_CNT (10-1) //消抖 10ms
key_state_t g_key[4]; //按键状态全局结构体
//按键处理函数
void key_timer_callback(void)//注意该函数以每1ms 调用一次
{
g_key[0].level_state = GET_KEY_STATE(KEY0_GPIO_PIN);
g_key[1].level_state = GET_KEY_STATE(KEY1_GPIO_PIN);
g_key[2].level_state = GET_KEY_STATE(KEY2_GPIO_PIN);
g_key[3].level_state = GET_KEY_STATE(KEY3_GPIO_PIN);
for(int i=0; i<4; i++)
{
switch(g_key[i].key_flag)
{
case KeyState_IDLE: //空闲状态
if(g_key[i].level_state == GPIO_PIN_RESET) //按下
{
g_key[i].key_flag = KeyState_CUT; //将状态定义为消抖状态,在下次进入时开始消抖
g_key[i].key_cut = KEY_SCAN_CNT; //消抖
}
break;
case KeyState_CUT: //消抖状态
if ( g_key[i].key_cut == 0 )
{
if(g_key[i].level_state == GPIO_PIN_RESET) //10ms消抖后按键状态为按下
g_key[i].key_flag = KeyState_PRESSED; //将状态定义为按下状态
else //消抖后按键未按下认为是误触
g_key[i].key_flag = KeyState_IDLE;
}
else
g_key[i].key_cut--;
break;
case KeyState_PRESSED: //按下状态
if ( g_key[i].level_state == GPIO_PIN_RESET ) //按键按下
{
g_key[i].press_time++; //按下时间加1
if ( g_key[i].press_time > (500-1) ) //避免按键长时间触发 由于16位最大65535,以免溢出导致错误
{
g_key[i].key_last = KeyState_LONG_PRESSED; //如果长时间按下将一直为长按状态,可以选择其他方式优化
g_key[i].press_time = 0;
}
}
else //按键松开
{
if ( g_key[i].press_time > (500-1) ) //长按(按下持续时间 >= 500ms)
{
g_key[i].key_flag = KeyState_IDLE; //恢复到空闲状态
g_key[i].key_last = KeyState_LONG_PRESSED; //长按
g_key[i].press_time = 0;
}else if(g_key[i].press_time > (90-1)) //短按(持续时间>=90ms)
{
g_key[i].key_flag = KeyState_WAIT_DOUBLE_CLICK; //等待双击
g_key[i].press_time = 0;
g_key[i].key_cut = KEY_SCAN_CNT; //释放消抖
}
}
break;
case KeyState_WAIT_DOUBLE_CLICK: //释放按键消抖,只在双击时释放消抖
if ( g_key[i].key_cut == 0 )
{
g_key[i].end_time++; //等待双击
if(g_key[i].end_time > (250-1)) //超出等待双击时间
{
g_key[i].key_last = KeyState_PRESSED; //按键状态最终为短按
g_key[i].key_flag = KeyState_IDLE; //空闲
g_key[i].end_time = 0;
}
else
{
if(g_key[i].level_state == GPIO_PIN_RESET) //按键按下
{
g_key[i].key_flag = KeyState_DOUBLE_CLICKED;//双击状态
g_key[i].end_time = 0;
g_key[i].key_cut = KEY_SCAN_CNT; //按下消抖
}
}
}
else
{
g_key[i].key_cut--;
}
break;
case KeyState_DOUBLE_CLICKED:
if ( g_key[i].key_cut == 0 ) //双击消抖
{
if(g_key[i].level_state == GPIO_PIN_RESET) //按键按下
{
g_key[i].press_time++;
}
else
{
g_key[i].key_last = KeyState_DOUBLE_CLICKED; //双击
g_key[i].key_flag = KeyState_IDLE;
g_key[i].press_time = 0;
g_key[i].end_time = 0;
}
}
else
g_key[i].key_cut--;
}
}
}
热门推荐
预订酒店难“取消”,规则谁定?多职能部位参与督促平台、酒店优化退订机制
怎么做俯卧撑不伤手腕
特朗普和马斯克的奇特互动:一个反电车,一个卖电车
企业如何通过优化供应链管理流程来降低运营成本?
宝宝手掌心发红发热,是生病了吗?什么情况下要去看医生?
放疗化疗期间,每个阶段的饮食都有讲究
CRM系统如何支持多渠道客户互动
哪些因素影响管理规章制度建设情况的优化?
国家开放大学毕业证如何认证?学信网可查吗?
阿奇霉素与罗红霉素该如何选用?别再犹豫不决了!
比特币合约交易主要看哪些技术指标比较好?
如何修改HTML中的字体大小
量子世界充满了种种神秘,但最大的神秘已经被我们解开?
公司如何选择基金进行投资?这些要点需谨记
揭秘双眼皮:美的秘密与科学的解释
宽带提速降费或影响三大运营商收入 方案尚未落地
冬天洗澡时间太长?专家教你如何科学把控健康bathing时间
视角与镜头焦距换算
大学校园贴吧文化:机遇与挑战并存的网络亚文化
摄影新手必知的8个相机设置要点
超声检出颈动脉斑块,如何评估风险高低?
KVA与KW:深入理解电力设备选型与应用的关键指标
房子拆迁被弟弟占了怎么处理
中山大学是985还是211大学?
制造业回流趋势分析与未来发展展望
如何培养高中孩子的数学思维能力?有什么方法?
养成这14个习惯,你也可以变成易瘦体质
北水资金流入港股有何影响?
时隔13年,中美艺术家携手再现清唱剧《大风歌》
全面解读质量检验:从标准到实践,究竟有多少你不知的细节?