在单片机IO口不够用、又需要较多的按键时,我们经常采取的解决办法是矩阵扫描按键,从而提高按键的数量,但有些时候我们想既要实现短按,又要实现长按,碍于是矩阵轮询扫描的方式,这就有点麻烦了,因为这两个键值比较容易冲突。
网上关于矩阵按键的教程有很多,关于长按短按识别也很多,但是关于矩阵按键扫描并且识长按与短按的例子比较少,所以我决定再次贴上自己的代码,该代码是从网上找来的程序基础上修改而来的,经测试可正常使用。
(如有更优秀的方法,欢迎讨论和指正)
注:该代码所扫描的按键是7行*3列,常驻高电平,按下为低电平。
//长短键判断函数,调用按键扫描函数
uint8 Discern_KeyScan(void)
{
static uint8 Old_key = 0;//上次按键值
static uint8 Key_buf = 0;//按键有效保存
static uint8 Press_CNT = 0;//按键按下时间,根据调用时间间隔确定
static uint8 longkey = 0;//记录长按键值
uint8 New_key = 0;//当前按键值
uint8 KEY = 0;//待返回的按键值
New_key = KeyScan(); //扫描一次键盘状态
if((s_TimerATCB.Delay10MS_Flag == true)) //10mS消抖
{
s_TimerATCB.Delay10MS_Flag = false; //按下后10MS标志位清零
if(New_key == Old_key)
{
if(New_key != 0) //建议keyscan返回0的那个值改为其他值,方便这里判断
{
Key_buf = New_key;
if(Press_CNT < 0xff)
{
Press_CNT ++;
}
if(Press_CNT >= 100)//调用时间间隔确定 * 100为长按时间,
{
KEY = Key_buf + 100; //将短键+100作长键区分
if(KEY==longkey)return 0; //连续长按只执行一次长按
longkey=KEY; //记录长按键值
Press_CNT = 0; //按键时间清0
Key_buf = 0; //按键缓冲清0
}
else if(Press_CNT < 100) //短按弹起
{
if(KeyScan()==0) //如果按键已经松开
{
KEY = Key_buf; //短按值
Key_buf = 0; //清按键缓冲
longkey = 0; //清除上一次长按
Press_CNT = 0; //按键弹起或按其他按键,将长按时间清0
}
}
}
else //无按键操作或长按弹起
{
if(Key_buf==longkey-100)
{
Press_CNT = 0; //按键弹起或按其他按键,将长按时间清0
Key_buf = 0; //清按键缓冲
return 0; //防止长按后又执行一次短按
}
if(Key_buf)
{
KEY = Key_buf; //将缓冲值送将返回的值
Key_buf = 0; //清按键缓冲
}
longkey = 0; //清除上一次长按
Press_CNT = 0; //按键弹起或按其他按键,将长按时间清0
}
}
Old_key = New_key; //将新按键赋给下一次的旧按键值
}
return KEY; //0代表无按键
}
//按键状态扫描,返回键值
uint8 KeyScan(void)
{
uint8 i;
uint8 keynum=0;
for(i=0;i<=6;i++) //扫描7行按键
{
RowLow(i); //循环拉低一行
if(line1==1) //扫描第一列按键
{
switch(i)
{
case 0: keynum=3; break; //返回键值
case 1: keynum=6; break; //返回键值
case 2: keynum=9; break; //返回键值
case 3: keynum=12; break; //返回键值
case 4: keynum=15; break; //返回键值
case 5: keynum=18; break; //返回键值
case 6: keynum=21; break; //返回键值
}
}
else if(line2==1)//扫描第二列按键
{
switch(i)
{
case 0: keynum=2; break; //返回键值
case 1: keynum=5; break; //返回键值
case 2: keynum=8; break; //返回键值
case 3: keynum=11; break; //返回键值
case 4: keynum=14; break; //返回键值
case 5: keynum=17; break; //返回键值
case 6: keynum=20; break; //返回键值
}
}
else if(line3==1)//扫描第三列按键
{
switch(i)
{
case 0: keynum=1; break; //返回键值
case 1: keynum=4; break; //返回键值
case 2: keynum=7; break; //返回键值
case 3: keynum=10; break; //返回键值
case 4: keynum=13; break; //返回键值
case 5: keynum=16; break; //返回键值
case 6: keynum=19; break; //返回键值
}
}
}
return keynum;
}
//置一行按键为低电平
void RowLow(uint8 num)
{
switch(num)
{
case 0:
row1_L; row2_H; row3_H; row4_H;
row5_H; row6_H; row7_H; break;
case 1:
row1_H; row2_L; row3_H; row4_H;
row5_H; row6_H; row7_H; break;
case 2:
row1_H; row2_H; row3_L; row4_H;
row5_H; row6_H; row7_H; break;
case 3:
row1_H; row2_H; row3_H; row4_L;
row5_H; row6_H; row7_H; break;
case 4:
row1_H; row2_H; row3_H; row4_H;
row5_L; row6_H; row7_H; break;
case 5:
row1_H; row2_H; row3_H; row4_H;
row5_H; row6_L; row7_H; break;
case 6:
row1_H; row2_H; row3_H; row4_H;
row5_H; row6_H; row7_L; break;
}
}