嵌入式开发领域,按键检测技术持续进步,这无疑会提升用户体验。然而,之前的状态图存在缺陷,现在我们将对其进行修正,并增加新的功能。
引出主题
嵌入式系统中,按键检测扮演着关键角色。先前已阐述过在按键检测中引入长按功能,并对抖动现象进行了特别处理。然而,研究发现原始状态图存在缺陷。本文旨在解决这些问题,并增加按键双击的检测,以使按键检测功能更加完善。
增加双击检测
为了实现按键的双击识别功能,我们需要引入两个新的状态。同时,原来的“短按”和“长按”状态将被更名为“确认按下”和“确认长按”。这两个新状态和名称的变更,都是为了更精确地区分按键的不同操作方式。
状态图修改
新状态图包含了许多关键信息。与之前相比,它变得更加复杂,但同时也更加精确,能够精确描述按键在不同操作中的状态变化。深入研究这个新状态图,可以发现状态之间的转换关系更加明确,这对于后续的程序编写极为重要。
程序编写
根据最新的状态图表,我们需要对状态机的运作逻辑进行调整。调整后的程序使得按键检测环节更加符合新的工作流程。调试时,可以通过取消注释的打印指令来便于观察状态变动,这有助于我们找出问题并改进代码。
void key_status_check()
{
switch(g_keyStatus)
{
//按键释放(初始状态)
case KS_RELEASE:
{
//检测到低电平,先进行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_SHAKE;
}
}
break;
//抖动
case KS_SHAKE:
{
if (KEY0 == 1)
{
//从松开状态来的抖动
if (KS_RELEASE == g_lastKeyStatus)
{
g_keyStatus = KS_RELEASE;
}
//从等待再次按下状态来的抖动
else if (KS_WAIT_PRESS_AGAIN == g_lastKeyStatus)
{
g_keyStatus = KS_WAIT_PRESS_AGAIN;
}
//从确认按下状态来
else if (KS_AFFIRM_SHORT_PRESS == g_lastKeyStatus)
{
g_WaitPressAgainCnt = 0;
g_keyStatus = KS_WAIT_PRESS_AGAIN;
}
//从确认再次按下状态来
else if (KS_AFFIRM_PRESS_AGAIN == g_lastKeyStatus)
{
printf("=====> key double press\r\n");
g_keyStatus = KS_RELEASE;
}
//从确认长按状态来
else if (KS_AFFIRM_LONG_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_RELEASE;
}
else
{
printf("err!\r\n");
}
}
else
{
//从确认按下状态来的抖动
if (KS_AFFIRM_SHORT_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_AFFIRM_SHORT_PRESS;
}
//从第2次按下状态来的抖动
else if (KS_AFFIRM_PRESS_AGAIN == g_lastKeyStatus)
{
g_keyStatus = KS_AFFIRM_PRESS_AGAIN;
}
//从确认长按状态来的抖动
else if (KS_AFFIRM_LONG_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_AFFIRM_LONG_PRESS;
}
//从松开状态而来
else if (KS_RELEASE == g_lastKeyStatus)
{
g_PressTimeCnt = 0;
g_keyStatus = KS_AFFIRM_SHORT_PRESS;
//printf("=====> key short press\r\n");
}
//从等待再次看下(的松开)状态而来
else if (KS_WAIT_PRESS_AGAIN == g_lastKeyStatus)
{
g_Press2TimeCnt = 0;
g_keyStatus = KS_AFFIRM_PRESS_AGAIN;
}
else
{
printf("err!\r\n");
}
}
}
break;
//确认按下
case KS_AFFIRM_SHORT_PRESS:
{
//检测到高电平,先进行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
else
{
if (g_LongPressTimeCnt % 20 == 0) //每隔1000ms打印一次
{
printf("=====> key long press:%d\r\n", g_LongPressTimeCnt/20);
keyEvent = KE_LONG_PRESS;
}
g_LongPressTimeCnt++;
}
}
break;
//等待再次按下
case KS_WAIT_PRESS_AGAIN:
{
//检测到低电平,先进行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_SHAKE;
}
g_WaitPressAgainCnt++;
if (g_WaitPressAgainCnt == 4) //200ms没有再次按下
{
printf("=====> key single press\r\n");
g_keyStatus = KS_RELEASE;
}
}
break;
//确认第2次按下
case KS_AFFIRM_PRESS_AGAIN:
{
//检测到高电平,先进行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
g_Press2TimeCnt++;
if (g_Press2TimeCnt == 20) //1000ms
{
g_LongPressTimeCnt = 0;
g_keyStatus = KS_AFFIRM_LONG_PRESS;
}
}
break;
//确认长按
case KS_AFFIRM_LONG_PRESS:
{
//检测到高电平,先进行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
g_LongPressTimeCnt++;
if (g_LongPressTimeCnt % 20 == 0) //每隔1000ms打印一次
{
printf("=====> key long press:%d\r\n", g_LongPressTimeCnt/20);
}
}
break;
default:break;
}
if (g_keyStatus != g_nowKeyStatus)
{
g_lastKeyStatus = g_nowKeyStatus;
g_nowKeyStatus = g_keyStatus;
//printf("new key status:%d(%s)\r\n", g_keyStatus, key_status_name[g_keyStatus]);
}
}
功能测试
测试了按键检测功能的修改。短按、长按、双击都表现良好。但需注意,从第二次按下确认状态过渡到长按状态的部分,还需深入测试。目的是要确保在所有情况下,按键检测都能精确无误。
代码结构优化
先前代码在主函数里,每隔50毫秒就执行一次状态机循环,仅用于展示。在实际的开发过程中,按键检测的代码应当单独运行。比如在stm32裸机开发时,可以将按键的状态机置于定时器中断服务函数中,一旦检测到按键状态,便通知应用程序,这样能提高开发效率。
这篇文章对按键检测功能进行了全方位的优化,那么在真正的开发过程中,我们可能还会遇到哪些关于按键检测的挑战?不妨点个赞、转发这篇文章,然后在评论区告诉我们你的想法!