按键检测
按键检测步骤:
- 判断按下
- 消抖(一般为5ms~10ms)
- 等待按键松开(可略)
- 检测松开
传统法
if 检测按键的按下, delay 来消抖, while 等待按键松开 。
程序:
sbit s4 = P3^3; //单独使用一个 I/O 口,则需要使用 sbit 进行位定义
void main() {
while(1)
{
if(s4 == 0) //检测是否有键按下
{
delay10ms(); //经过10ms的延时函数进行消抖
if(s4 == 0) //若P3^3仍然为低电平,则确实有键(s4)按下
{
dspbuf[0]++; //第一位数码管的段码值 +1
while(!s4); //等待按键松开,等价于while(s4 == 0)
}
}
display(); //数码管显示函数
} }
//源码:https://blog.csdn.net/xiaocheng_sky/article/details/52124568
传统法使用了 whlie() 来等待按键松开,容易卡死程序,使后面的代码无法执行。
带有标志位的按键识别法
先定义一个 key_flag 作为按键标志位, if 检测按键的按下(当前按键与标识位 key_flag 对比), delay 来消抖,取反标识位 key_flag 的值,if 再次确认当前按键, 进行事件处理 , 通过mian函数的循环来检测按键的松开。
其中: 代码“key_flag = 1”的作用是:下次即便按键没有松手,程序跑完一圈之后,也不会再满足if((s4 == 0) && (!key_flag))的条件;同样,亦不会满足else if(s4 != 0)的条件,那么key_flag 不会被赋为0。综合以上情况,一次按键只会进行一次处理。当按键被释放后,以后的扫描则会满足else if(s4 != 0)的条件,那么key_flag 会被赋为0,则可以进行接下来的按键扫描了,如此反复……
程序:
sbit s4 = P3^3;
uchar key_flag = 0; //首先定义按键的标志位,并初始化为0
void key_scan() //按键扫描函数
{
if((s4 == 0) && (!key_flag)) //如果有键按下,则条件成立(有键按下,则s4为0;而 !key_flag为1)
{
delay10ms(); //延时消抖
key_flag = 1; //把标志位置为1
if(s4 == 0) //如果确定有键按下
{
dspbuf[0]++; //进行事件处理(数码管显示值加1)
}
}
else if(s4 != 0) //未按下按键
{
key_flag = 0;
}
}
//源码:https://blog.csdn.net/xiaocheng_sky/article/details/52139480
以上可以避免使用 while 函数造成的卡程序,但依旧使用了 delay 函数进行延时消抖,不够高效。
快速识别法
知识点:符号“^”为“异或”的计算机语言。
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
先定义三个标志位: key_now 、 key_flag 、 key_old 。
然后保证三个值的初始值与三个值的初始化状态相同。
下面程序中,当按键没按下时,now为1;flag为0;old为1。
当按键按下时,now为0;flag为(0 ^ 1) ^ 0 = 1;old为0;与没按下时不同。
当按键释放后,now为1;flag为(1 ^ 1) ^ 0 = 0;old为0;
uchar key_now = 1; //用处存储按键本次的状态(0表示按下,1表示未按下)
uchar key_flag = 0; //按键是否按下的标志(0表示按键未被按下,1表示按键被按下)
uchar key_old = 1; //用于存储按键上次的状态(0表示上次按下,1表示上次未按下)
sbit s4 = P3^3;
void key_scan()
{
key_now = s4;
key_flag = (key_now ^ key_old) ^ key_flag;
key_old = key_now;
}
//计算key_flag为1的次数,消抖
//源码:https://blog.csdn.net/xiaocheng_sky/article/details/52146083
简洁快速识别法(可识别长按)|建议使用
知识点1:c语言中&与&&的区别
&:按照位与操作,例如:0010&1101,结果为0000
&是java中的位逻辑运算:
eg: 2&3=2;
分析如下: 2的二进制为10 ;3的二进制为11 ; 逻辑&之后为10
&&:短路与,表示如果两个条件都成立则执行之后的逻辑;
例如:if(a0&&b0),意思就是if a为0并且b为0的时候,进行下一步操作。
|| 短路或。 A||B,只有当A为假的时候才会对B进行判断。若A为真,则不继续对B进行判断。
| 逻辑或。 A|B,会对A, B都进行判断
! 逻辑非,当操作数为true时,返回false,否则返回true。
知识点2:位异或运算:A ^ 0xff。代表操作数A逐位和0xff异或,它的结果是~A。
任何数与全0异或,值不变。
任何数与全1异或,值取反。
任何数与自己异或,值为0。
先定义两个标志位: Trg 、 Cont 。
保证三个值的初始值与三个值的初始化状态相同。
下面程序中,当按键没按下时,ReadData = 1 ^ 0xff = 0x00 ; Trg = 0 & (0 ^ 0)= 0 ; Cont = 0。三值分别为0,0,0。
当按键按下时,ReadData = 0 ^ 0xff = 1 ;Trg = 1 & (1 ^ 0) = 1 ; Cont = 1。 三值分别为1,1,1。
当按键长按时,ReadData = 0 ^ 0xff = 1 ; Trg = 1 & (1 ^ 1) = 0; Cont = 1。 三值一直保持为1,0,1。
当按键释放后,ReadData = 1 ^ 0xff = 0 ;Trg = 0 & (0 ^ 0) = 0 ; Cont = 0。 三值分别为0,0,0。
unsigned char Trg;
unsigned char Cont;
void KeyRead( void )
{
unsigned char ReadData = PINB ^ 0xff; // 1
Trg = ReadData & (ReadData ^ Cont); // 2
Cont = ReadData; // 3
}