4x2矩阵按键的短按和长按
前言
最近在项目中遇到一个4x2矩阵按键的问题,各种百度了好久,都是在说4x4的矩阵按键(当然,通过4x4来推4x2的矩阵按键也没毛病),但是我现在的需求是识别出具体某个按键被按下后,是短按还是长按。
于是,我又找了好久,终于在阿B上找到了能满足我的需求的视频,按照上面所讲的结合我自己写的矩阵按键,终于实现了这个功能,在这里做个笔记。下面这个是阿B上的视频标题,通过搜索这个就能找到。
一、识别矩阵按键的具体按键
这里我用的是行列扫描法,由于我的6个IO口不是同一P引脚,所以我这里写了一个函数,方便对IO口赋值和对按键状态编码。
1、首先初始化6个IO引脚的工作模式为准双向模式
下面依次这样接了KEY_SET和BT_KEY]
void Key_Init(void)
{
/***********************
| * 说明 *
| 0,0准双向口
| 0,1推挽输出
| 1,0高阻输入
| 1,1开漏
************************/
P6M1&=~(1<<0);P6M0&=~(1<<0); /* KEY_SET P00*/
P6M1&=~(1<<1);P6M0&=~(1<<1); /* BT_KEY P02*/
P6M1&=~(1<<2);P6M0&=~(1<<2); /* KEY0or1 P24*/
P6M1&=~(1<<3);P6M0&=~(1<<3); /* KEY_MOD P27*/
P6M1&=~(1<<4);P6M0|=1<<4; /* NetInto1 P37*/
P6M1&=~(1<<5);P6M0|=1<<5; /* NetInto2 P36*/
}
2、写一个方便给6个IO口引脚赋值的函数
/*************************************************************************
* 函 数 名: Key_Pin
* 函数功能: 给按键引脚赋值0 or 1
* 输 入: p1-p4为高四位赋值,n2,n1为低四位的3,4为赋值
* 返 回 值: Key_Value的高四位为行按键的引脚值,低四位中的3,4位为列按键的引脚值
*************************************************************************/
uchar Key_Pin(uchar p1,uchar p2,uchar p3,uchar p4,uchar n1,uchar n2)
{
uchar Key_Value=0x00,Key_PinValue[8]={0x00};
KEY0or1=p1; KEY_MOD=p2; KEY_SET=p3; BT_KEY=p4; NetInto1=n1; NetInto2=n2;
Key_PinValue[7]=KEY0or1; Key_PinValue[6]=KEY_MOD; Key_PinValue[5]=KEY_SET; Key_PinValue[4]=BT_KEY;
Key_PinValue[3]=NetInto1; Key_PinValue[2]=NetInto2;
Key_Value |= Key_PinValue[7]<<7;
Key_Value |= Key_PinValue[6]<<6;
Key_Value |= Key_PinValue[5]<<5;
Key_Value |= Key_PinValue[4]<<4;
Key_Value |= Key_PinValue[3]<<3;
Key_Value |= Key_PinValue[2]<<2;
Key_Value |= Key_PinValue[1]<<1;
Key_Value |= Key_PinValue[0]<<0;
return Key_Value;
}
3、按键扫描函数
/*************************************************************************
* 函 数 名: Key_Scan
* 函数功能: 检测有按键按下并读取键值
* 输 入: 无
* 返 回 值: KeyValue分别返回1~8
*************************************************************************/
uchar Key_Scan(void)
{
uchar Key_PinSet=0, Key_PinSet_H=0, Key_PinSet_L=0, KeyValue=0;
Key_PinSet = Key_Pin(0,0,0,0,1,1); //0x0c
if(Key_PinSet != 0x0c) //读取按键是否按下
{
if(Key_PinSet != 0x0c) //再次检测键盘是否按下
{
Key_PinSet_H = Key_Pin(0,0,0,0,1,1); //0x0c 行输入0,检测列值,先判断列
switch(Key_PinSet_H)
{
case(0X04): KeyValue=1;break; //4:0100
case(0X08): KeyValue=2;break; //8:1000
}
Key_PinSet_L = Key_Pin(1,1,1,1,0,0); //0xf0 列输入0,检测行值,判断行
switch(Key_PinSet_L)
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+2;break;
case(0Xd0): KeyValue=KeyValue+4;break;
case(0Xe0): KeyValue=KeyValue+6;break;
}
}
}
return KeyValue;
}
二、识别具体按键的短按和长按
具体原理我也不太说的明白,直接上代码,不明白的话去看上面那个视频
//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,
uint seg_num[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e,0x01,0x00};
uint ums,keytime;
uchar usec,keydly, keyold;
bit KEY_UP_Flag=0,KEY_MODE_Flag=0,KEY_RESET_Flag=0;
bit KEY_LANGUAGE_Flag=0,KEY_CUSTOM_Flag=0,KEY_TIME_Flag=0,KEY_DISANCE_Flag=0,KEY_RACESET_Flag=0;
bit KEY_UP_1S_Flag=0,KEY_RESET_1S_Flag=0;
//按键判断函数
void Key_Judgment(void)
{
uchar keyval, keydown, keyup; //keydown:按下标志, keyup:抬起标志
if(keydly)return; //按键判断函数10ms判断一次
keydly = 1;
keyval = Key_Scan();
keydown = keyval & (keyval^keyold);//按下的瞬间,keydown 有值
keyup = ~keyval & (keyval^keyold);//抬起的瞬间,keyup 有值
keyold = keyval; //一直按着,keyold 有值
if(keydown != 0) //按下后对长按开始计时
{
keytime=ums;
key_flag = 0;
}
if((keyup != 0)&&((ums-keytime)<=300)) //短按及抬起判断,按下后在300ms内若抬起,则视为短按
{
key_id = keyup;
}
if(key_flag==1 && keyup != 0) //长按抬起判断,若长按完后还一直按着,则没反应,直到抬起,时间更新,进入下一次长按识别
{
key_flag = 0;
keytime=ums;
}
switch(keyold) //长按标志,按下后一直到750-1000ms内不放手,则视为长按
{
case(2):
if((ums-keytime)>750 && (ums-keytime)<1000)
{
key_flag = 1;
key_id = 9;
}
break;
case(6):
if((ums-keytime)>750 && (ums-keytime)<1000)
{
key_flag = 1;
key_id = 10;
}
break;
default:
if((ums-keytime)>750 && (ums-keytime)<1000)
{
key_flag = 1;
key_id = 0;
}
break;
}
switch(key_id)
{
case(1): KEY_LANGUAGE_Flag=1;break; //1
case(2): KEY_UP_Flag=1;break; //8
case(3): KEY_CUSTOM_Flag=1;break; //2
case(4): KEY_MODE_Flag=1;break; //7
case(5): KEY_TIME_Flag=1;break; //3
case(6): KEY_RESET_Flag=1;break; //6
case(7): KEY_DISANCE_Flag=1;break; //4
case(8): KEY_RACESET_Flag=1;break; //5
case(9): KEY_UP_1S_Flag=1;break; //up_1s
case(10): KEY_RESET_1S_Flag=1;break;//reset_1s
default: break;
}
key_id = 0;
if(KEY_LANGUAGE_Flag == 1) //1
{
KEY_LANGUAGE_Flag = 0;
LED_FLI_D3(1); //用户指示灯D3闪烁1次
SEG_Write_Data(seg_num[1],0); //数码管显示
}
if(KEY_CUSTOM_Flag == 1) //2
{
KEY_CUSTOM_Flag = 0;
LED_FLI_D3(1);
SEG_Write_Data(seg_num[2],1); //数码管显示
}
if(KEY_TIME_Flag == 1) //3
{
KEY_TIME_Flag = 0;
LED_FLI_D3(1);
SEG_Write_Data(seg_num[3],2); //数码管显示
}
if(KEY_DISANCE_Flag == 1) //4
{
KEY_DISANCE_Flag = 0;
LED_FLI_D3(1);
SEG_Write_Data(seg_num[4],3); //数码管显示
}
if(KEY_RACESET_Flag== 1) //5
{
KEY_RACESET_Flag = 0;
LED_FLI_D3(1); //用户指示灯D4闪烁1次
SEG_Write_Data(seg_num[5],4); //数码管显示
}
if(KEY_RESET_Flag == 1) //6
{
KEY_RESET_Flag = 0;
LED_FLI_D3(1);
SEG_Write_Data(seg_num[6],5); //数码管显示
}
if(KEY_MODE_Flag == 1) //7
{
KEY_MODE_Flag = 0;
LED_FLI_D3(1);
SEG_Write_Data(seg_num[7],6); //数码管显示
}
if(KEY_UP_Flag == 1) //8
{
KEY_UP_Flag = 0;
LED_FLI_D3(1);
SEG_Write_Data(seg_num[8],7); //数码管显示
}
if(KEY_UP_1S_Flag == 1) //9
{
KEY_UP_1S_Flag = 0;
LED_FLI_D4(1);
SEG_Write_Data(seg_num[1],0); //数码管显示
}
if(KEY_RESET_1S_Flag == 1) //10
{
KEY_RESET_1S_Flag = 0;
LED_FLI_D4(1);
SEG_Write_Data(seg_num[1],2); //数码管显示
}
}
开定时器1,定时1ms
void Timer1_Init(void) //1毫秒@11.0592MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x66; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1=1;
}
void tml_isr() interrupt 3
{
if(ums < 65536)
ums++;
else
ums=0;
if(++keydly==10)keydly=0;
}
总结
后面应该还能判断是双击、三击等多击事件,但我的项目上暂时没这个需求,所以先就不写了。