在MS8006示例代码库中有一个mculib_key.c文件中主要是按键实现的相关函数,mculib_key_init是作为MS8006开发平台的IO口初始化配置,_do_key_scan()函数是按键扫描函数,是一个很经典的按键扫描算法,这个函数可实现按键的长按短按功能,并且可配置按键IO口的位操作或者是总线操作。原理是检测按下的瞬间和弹起的瞬间。可以从以下示例代码逐步分析:
VOID _do_key_scan()
{
UINT8 u8keyVal,PortB_Val;
//先定义两个变量
PortB_Val=0xf0 | PIN_KEY4 << 3 | PIN_KEY3 << 2 | PIN_KEY2 << 1 | PIN_KEY1;
//这里按键IO使用的位操作,利用移位将4个位整合成一组数据
u8keyVal=PortB_Val & KEY_GLOBAL_SWITCH;
//按键可配置成共阴接法或是共阳接法,这里使用的共阴接法,即低电平触发,在mculib_key.h中可知KEY_GLOBAL_SWITCH为0xff,举个例子说明,假设此时按下了1按键即PortB_Val=0xfe,则u8keyVal=0xfe
g_u8keyTrigger=u8keyVal & (u8keyVal ^ g_u8keyCount);
// g_u8keyCount代表连续按下的变量,这里第一次默认为0xff,与u8keyVal进行异或运算,所以
(1)、如果按键1是短按的情况为:按下后短时间弹起说明此函数还能检测到弹起瞬间的,而物理按键按下时间必然大于函数运行时间,所以弹起瞬间时g_u8keyCount已为0xfe,但是由于已弹起u8keyVal变成0xff,计算下式后又将值赋给g_u8keyCount,所以g_u8keyCount参与算法运算后又变成0xff。
g_u8keyTrigger=0xff&(0xff^0xfe)计算后 g_u8keyTrigger=0x01
此时g_u8keyTrigger=0x01
g_u8keyCount=0xff
(2)、如果按键1是长按的情况为:按下后开始定时器计算低电平时间,所以是检测不到弹起瞬间的,此时按键处于低电平状态此时由于是连续按下g_u8keyCount已被赋值为0xfe,而且u8keyVal此时也为0xfe,那么和进行异或运算后得
g_u8keyTrigger=0xfe&(0xfe^0xfe)计算后g_u8keyTrigger=0
此时 g_u8keyTrigger=0, g_u8keyCount=0xfe
g_u8keyCount =u8keyVal;
//这是长按的情况将当前的u8keyVal赋给g_u8keyCount
}
接下来在mculib_key_detect()函数中实现长按短按各返回的值,因为在文件开头已经宏定义了
#define KEY_RESCAN_LONG (60) 按键循环检测时间为50ms,这里设置60即长按时间为60*50ms=1s
#define KEY_SWITCH1 (0X01)
#define KEY_SWITCH2 (0X02)
#define KEY_SWITCH3 (0X04)
#define KEY_SWITCH4 (0X08)
#define KEY_GLOBAL_SWITCH (0xff)
所以在mculib_key_detect()中再利用UINT8 mculib_key_detect(VOID)返回值函数,返回在主函数中加以应用
UINT8 mculib_key_detect(VOID)
{
UINT8 u8keyPressed = KEY_NONE;
_do_key_scan(); //运行上面的按键扫面函数
if (g_u8keyTrigger & KEY_SWITCH1) //这里只是列举了按键1按下的情况,由于此时是短按功能,所以由_do_key_scan();可得g_u8keyTrigger=0x01进行按位与后得0x01,if语句为真,即可运行
{
if (g_bAntiShakeFlag)
{
u8keyPressed = KEY_MAP_1; //将变量赋值返回函数以在主函数中应用
}
g_bAntiShakeFlag = TRUE;
}
//reset long key time count
if (u8keyPressed != KEY_NONE)
{
g_u8keyTimes = 0;
}
#if 1 //if support long key
// key1 long pressed sample
if ((g_u8keyCount & KEY_SWITCH1) == 0 ) //这是按键长按时有上面计算后得到g_u8keyCount此时为0xfe,所以满足if条件
{
g_u8keyTimes ++; //进入低电平后开始计时
if (g_u8keyTimes > KEY_RESCAN_LONG) //当计时满足所设要求后进入
{
u8keyPressed = KEY_MAP_LONG1; //赋长按变量返回函数后应用
g_u8keyTimes = 0; //将计时清零
g_bAntiShakeFlag = FALSE;
}
}
#endif
return u8keyPressed;
}
下图是对按键动作过程的解析: