三行按键扫描基础
核心算法:
unsigned char trg;
unsigned char cont;
void KeyRead( void )
{
unsigned char readDate = P3^0xff; // 第一行
trg = readDate & (ReadData ^ cont); // 第二行
cont = readDate; // 第三行
}
用定时器每隔10ms
执行一次按键扫描读取函数
第一行:
读P3
端口数据并且取反,然后保存到ReadData
第二行:
用来计算触发变量的
第三行:
用来计算连续变量
基础知识
- 一位数x异或一用来取反一个数
x ^ 1 = ~x
1 ^ 1 = 0
0 ^ 1 = 1
- 一位数x异或零不变
x ^ 0 = x
1 ^ 0 = 1
0 ^ 0 = 0
- 一位数x与一用于判断x的值
if((x & 1) == 0) x为0
if((x & 1) == 1) x为1
- 一位数x与零用于清零
x & 0 = 0
1 & 0 = 0
0 & 0 = 0
- 一位数x或一用于置一
x | 1 = 1
1 | 1 = 1
0 | 1 = 1
- 一位数x或零用于判断x的值
if((x | 0) == 1) x为1
if((x | 0) == 0) x为0
模拟状态
初始时候
Cont
= 0x00
当没有按键
P3
为0xff
ReadData
读端口并且取反 等于0x00
Trg = ReadData & (ReadData ^ Cont)
Trg
= 0x00
& (0x00
^ 0x00
) = 0x00
Cont
等于ReadData
,为0x00
结果:
ReadData
= 0x00
Trg
= 0x00
Cont
= 0x00
当P30
按键按下
P3
为0xfe
ReadData
读端口并且取反 等于0x01
Trg = ReadData & (ReadData ^ Cont)
Trg
= 0x01
& (0x01
^0x00
) = 0x01
Cont
等于ReadData
,为0x01
结果:
ReadData
= 0x01
Trg
= 0x01
Trg
只会在这个时候对应位的值为1,其它时候都为0
Cont
= 0x01
当P30
按键按下不松开(长按)
P3
为0xfe
ReadData
读端口并且取反 等于0x01
Trg = ReadData & (ReadData ^ Cont)
Trg
= 0x01
& (0x01
^0x01
) = 0x00
Cont
等于ReadData
,为0x01
结果:
ReadData
= 0x01
;这个不会变,因为按键没有松开
Trg
= 0x00
只要按键没有松开,Trg
值永远为 0x00
Cont
= 0x01
只要按键没有松开,Cont
值永远为 0x01
当P30
按键按下松开
P3
为0xff
ReadData
读端口并且取反 等于0x00
Trg = ReadData & (ReadData ^ Cont)
Trg
= 0x00
& (0x00
^0x01
) = 0x00
Cont
等于ReadData
,为0x00
结果:回到初始状态
ReadData
= 0x00
Trg
= 0x00
Cont
= 0x00
总结
ReadData | Trg | Cont | ||
---|---|---|---|---|
无按下 | 0x00 | 0x00 | 0x00 | 都为0 |
第一次扫描P30 按下 | 0x01 | 0x01 | 0x01 | 都为0x01 |
之后扫描P30 按下 | 0x01 | 0x00 | 0x01 | |
之后扫描P30 松开 | 0x00 | 0x00 | 0x00 | 恢复初始 |
Trg(triger)
电平从1到0的跳变(下降沿),在对应按键相应位为一
补充一句如果想要判断上升沿 (~ReadData) & (ReadData^ Cont)
Cont(continue)
Cont代表的长按键,按着不放Cont的相应位为一
ReadData ^ Cont
解释:
这一次读取结果异或上之前读取结果
如果有变化相应位为一,如果没变化相应位为零
如果没有松手所有位都为零
Trg = ReadData & (0x00)
松手时
Trg = ReadData & (0x01)
这时候就要Trg
的值取决于ReadData
设计思路根据基础知识与1判0
只有当读取到按键按下Trg
才不等于0
三行按键扫描的改进
产生抖动情况分析
这里我们假设的是抖动时长不超过10ms
改进程序:
uchar cnt = 0,lastTrg = 0;
void KeyRead( void )
{
uchar trg = 0,readDate = 0;
//如果我们要读取的只有P30-P33--通过|0xF0消除P34-P37影响
readDate = (P3|0xF0)*Oxff;
trg = (readDate^cnt) & readDate;
cnt = readDate;
//判断上一次是否产生下降沿,并且当前状态是按键按下状态
//lastTrg^trg & lastTrg这样写也是为了排除其他位影响
//如果不是当前位,这个结果都为0
if((lastTrg^trg & lastTrg) && readDate)
{
//执行按键按下的程序
}
}
三行按键扫描的具体应用
单次触发
void KeyProc(void)
{
if ( trg & 相应位置一 ) // 与1判0
{
//处理函数
}
}
长按和连按
#define KEY 0x02 // 按键
void KeyProc(void)
{
static keyCnt = 0;
if (cont & KEY) // 如果按键被按着不放
{
//短按处理函数
//连按连加
keyCnt ++; // 计数加1
if (keyCnt > 100) // 10ms*100 = 1S
{
keyCnt = 0;
//长按处理函数
}
}
}