三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击 您所在的位置:网站首页 用0ld造句 三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击

三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击

#三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击| 来源: 网络整理| 查看: 265

九层妖塔 起于垒土 在这里插入图片描述

直接跳转到三行代码

三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击 一、基本理论0、按键的常见名词: ①按键抖动 ②按键稳定闭合时间 1、按键的扫描:2、按键的消抖: 二、常见的按键消抖算法1、普通软件延时2、标志位软件延时3、标志位定时器延时4、状态机5、三行代码 三行代码 1、按键的扫描①独立按键的扫描①矩阵键盘的扫描 2、按键的消抖3、相关变量值的变化4、按键处理函数①独立按键的处理函数②矩阵键盘的处理函数 5、长按长按一长按二 6、双击7、复合按键

● 改编自国信长天蓝桥杯官方蓝皮书例程,按照自己的习惯进行了补充和修改

链接 1、独立按键抖动产生原因;普通软件延时消抖;标志位软件延时消抖;标志位定时器延时消抖 2、矩阵键盘的识别与编码;查询扫描;定时扫描;中断扫描;使用定时器状态机的方法进行按键的扫描识别 3、定时器三行代码 矩阵键盘长短按的识别

一、基本理论 0、按键的常见名词:  ①按键抖动

 当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上就稳定的接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。

在这里插入图片描述 尝试用示波器的触发捕获按键抖动波形,只捕获到了比较明显的松手抖动。 在这里插入图片描述 下图是串口返回的IO口内部数学电平(不知道是采样频率的问题还是按键的问题,并没有观察到方波) 在这里插入图片描述

 ②按键稳定闭合时间

  按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为100ms 以上至数秒。

在这里插入图片描述

完整的按键识别分为两部分:按键的扫描和按键的消抖。

1、按键的扫描:

 按键的扫描就是通过程序判断与按键相连的IO口的电平状态。通过相关IO口的电平状态来判断按键的抬起和按下。  ①查询扫描   把按键扫描子程序和其他子程序并列在一起,单片机循环分时运行各个子程序。当按键按下并且单片机查询到的时候立即响应键盘输入操作。  ②定时扫描   定时器,灵活多样,方法较多。  ③中断扫描   依赖于硬件(如四路与门等),利用外部中断。

2、按键的消抖:

 由于上文所说的 按键抖动 ,需要对IO口电平进行消抖处理。消抖分为硬件消抖和软件消抖。常见的硬件消抖有 使用电容进行延时消抖 、S触发器进行硬件去抖等。  在此旨在介绍全面的软件消抖算法。

二、常见的按键消抖算法 1、普通软件延时

 ●消抖:软件延时。CPU在按键按下抖动时进行10ms的软件延时消抖,在数百ms的稳定期以及松手抖动时停滞在松手检测while(!KEY)中,占用CPU资源过多;也就是在整个按键检测期间内,没有再执行其他指令。  ●松手检查:while(!KEY)等待按键释放。 等按键确认释放后再去执行相应代码。防止重入。  ●按键检测时是借助while(1)死循环进行反复检测。  ●若是采用普通软件延时消抖10ms的软件延时和松手检测都会占用CPU资源,对其它同样借助while(1)死循环进行反复运行的模块代码产生了挤占影响,比如动态数码管。  ●稳定期取决于是否松手,若不松手,则数码管将一直保持进入按键检测前最后一次对数码管的操作。

最低级的方法,没有实用性。  ●流程图: 在这里插入图片描述  ●示例代码

while(1) { if(key==0) { Delay10ms(); if(key==0) { //按键按下执行相关功能代码 while(!key); } } } 2、标志位软件延时

 ●消抖:软件延时。  ●通过引入按下标志位,省去了while(!KEY)的松手检测,将按下时稳定期的数百ms释放出来,只占用了按下和松手时的共20ms的消抖时间。

标志位的使用使算法具有了一定的实用性,优点是不借助硬件资源  ●波形图: 在这里插入图片描述

3、标志位定时器延时

 ●消抖:定时器延时。使用定时器进行延时消抖。  ●使用标志位代替松手检测。

文前链接中有解释和程序,写的不太成熟,与状态机类似

4、状态机

 也是一种利用标志位定时器延时按键算法,但比较成熟,逻辑严谨。

状态机进行按键的扫描识别

bug最少,算法成熟,但程序量比较大,不太方便移植。

5、三行代码

目前已知最好的按键算法,将按键的识别与扫描完全分开,便于移植,拓展性好。

三行代码

完整的按键程序可以包括:按键的扫描 和 按键的消抖 两部分。 三行代码 属于 按键的消抖 部分的算法,与是独立按键还是矩阵键盘无关。 三行代码 其实就是一种时域滤波算法–消抖滤波法。

1、按键的扫描

按键的扫描 采用定时器进行扫描。10ms扫描一次IO口电平状态,即10ms读取一次键值。通过Key_Read()函数读取。

unsigned char Key_Read(void) { Key_Value = P01; //任意一个IO口的电平值保存在变量中 } ①独立按键的扫描

基于蓝桥杯单片机开发板

uchar Key4_Read(void) //独立按键扫描函数,读取键值 { uchar Key_temp; uchar Key_Value; P3 |= 0x0f; Key_temp = P3&0x0f; switch(Key_temp) { case 0x0e : Key_Value = 7; break; //S7 case 0x0d : Key_Value = 6; break; //S6 case 0x0b : Key_Value = 5; break; //S5 case 0x07 : Key_Value = 4; break; //S4 default: Key_Value = 0; } return Key_Value; }

Notes:  ●程序中P3 |= 0x0f; Key_temp = P3&0x0f;两行属于按键扫描的识别部分;  ●switch属于按键扫描的编码部分。

 ●对程序中P3 |= 0x0f; Key_temp = P3&0x0f;两行的解释:   ○P3 |= 0x0f; 将P3低四位即独立按键接口拉高置为1;Key_temp = P3&0x0f;读取P3低四位I/O口的电平。   ○重点是读取I/O口外部电平状态前,需要先将I/O口置为1。 STC15的芯片手册中有介绍:STC15F2系列单片机I/O口上电默认为准双向口,在324页有相关的介绍和使用说明。截去一段关于读取外部状态的使用说明: 在这里插入图片描述

再来看国信长天给的电子版例程: 在这里插入图片描述 emmm……似乎不太严谨…………

①矩阵键盘的扫描

基于蓝桥杯单片机开发板

uchar Key16_Read(void) //矩阵按键扫描函数,读取键值 { uint Key_temp; uchar Key_Value; //按键扫描的识别部分 P44=0; P42=1; P35=1; P34=1; P3|=0X0F; //第一列拉低,其余全为高 Key_temp = P3; //读取P3低四位I/O状态 P44=1; P42=0; P35=1; P34=1; P3|=0X0F; //第二列拉低,其余全为高 Key_temp = (Key_temp uchar Key_Temp,Key_Down,Key_Up; static uchar Key_Old = 0; if(uc_Key_flag) return; //10ms uc_Key_flag = 1; Key_Temp = Key4_Read(); Key_Down = Key_Temp & (Key_Old ^ Key_Temp); //按下为按键值,其它为0 Key_Up = ~Key_Temp & (Key_Old ^ Key_Temp); //松手为抬起前的按键值,其他为0 Key_Old = Key_Temp; if(Key_Down) { uc_Key_state = 'd'; uc_Key_Value = Key_Down; } if(Key_Up) { uc_Key_state = 'U'; } } ②矩阵键盘的处理函数 void Key16_Proc(void) //矩阵按键处理函数 { uchar Key_Value,Key_Down,Key_Up; static uchar Key_Old = 0; if(uc_Key_flag) return; //10ms uc_Key_flag = 1; Key_Value = Key16_Read(); Key_Down = Key_Value & (Key_Old ^ Key_Value); //按下为按键值,其它为0 Key_Up = ~Key_Value & (Key_Old ^ Key_Value); //松手为抬起前的按键值,其他为0 Key_Old = Key_Value; if(Key_Down) { uc_Key_state = 'd'; uc_Key_Value = Key_Down; } if(Key_Up) { uc_Key_state = 'U'; uc_Key_Value = Key_Up; } }

Notes:  ●观察独立按键/矩阵键盘的处理函数可知,定义了两个全局变量uc_Key_Value和uc_Key_state来分别向全局传递按键值和按键状态。  ●uc_Key_Value和uc_Key_state对比临时值Key_Down与Key_Up,可以理解为状态值,10ms刷新一次,可以在全局中传递。

5、长按 长按一

 顾名思义,长按 区别于 短按 在于时间,又因为STC15没有实时时钟,只能通过定时器来搭建全局时间。用定时器1作为1ms定时器,用作程序内的ms计时。

创建全局变量ul_ms,用作ms计时。

unsigned long ul_ms;

在定时器1的中断服务函数或者自定义的查询服务函数中:

ul_ms++; //ms计时

按键长按短按的实现只需在按键处理函数中增加代码:

//-----------------------------------------独立按键处理函数---------------// void Key4_Proc(void) //独立按键处理函数 { uchar Key_Temp,Key_Down,Key_Up; static uchar Key_Old = 0; static ulong ul_Key_Time_Down = 0; static ulong ul_Key_Time_Up = 0; if(uc_Key_flag) return; //10ms uc_Key_flag = 1; Key_Temp = Key4_Read(); Key_Down = Key_Temp & (Key_Old ^ Key_Temp); //按下为按键值,其它为0 Key_Up = ~Key_Temp & (Key_Old ^ Key_Temp); //松手为抬起前的按键值,其他为0 Key_Old = Key_Temp; if(Key_Down) { uc_Key_state = 'd'; ul_Key_Time_Down = ul_ms; //记录按键按下时的时刻 } if(Key_Up) { uc_Key_state = 'U'; ul_Key_Time_Up = ul_ms; //记录按键抬起时的时刻 switch(Key_Up) { case 7 : uc_Key_Value = 7; break; case 6 : uc_Key_Value = 6; break; case 5 : uc_Key_Value = 5; break; case 4 : uc_Key_Value = 4; break; } //长按判断 if(ul_Key_Time_Up - ul_Key_Time_Down >1000) { uc_Key_Value = uc_Key_Value +10; } } }

Notes:  ●有长按的要求,按键的功能最好放在按键抬起时刻。即在按键抬起时刻相应事件再响应。

长按二 static ulong ul_Key4_Presstime = 0; //计时按键按下状态的时间 static bit Key4_Down_flag = 0; //按按下标志位 static bit Key4_Up_Enable = 0; //抬起触发使能标志位 if(Key_Down) //下降沿检测 { switch(Key_Down) { case 4: { ul_Key4_Presstime = ul_time; //在按键按下的时刻 开始记录时间 Key4_Down_flag = 1; //按下标志位 置1 Key4_Up_Enable = 1; //使能抬起标志位 }break; ………… } } if(Key_Up) //上升沿检测 { switch(Key_Up) { case 4: { if(Key4_Up_Enable == 1) //抬起标志位已经使能 { Key4_Down_flag = 0; //清除按下标志位 Key4_Up_Enable = 0; //清除抬起标志位 uc_Key_Value = 40; //短按功能 } } } if( (( ul_time - ul_Key4_Presstime)>1000) && (Key4_Down_flag == 1) && (Key_Old == 4) && (ul_Key4_Presstime != 0)) { ul_Key4_Presstime = ul_time; //ul_Key4_Presstime = 0; Key4_Up_Enable = 0; uc_Key_Value += 1; } 6、双击 //-----------------------------------------独立按键处理函数---------------// void Key4_Proc(void) //独立按键处理函数 { uchar Key_Temp,Key_Down,Key_Up; static uchar Key_Old = 0; static ulong ul_Key_Time_Down_First = 0; //记录按键 第一次 按下时的时刻 static ulong ul_Key_Time_Down_Second = 0; //记录按键 第二次 按下时的时刻 static uchar Key_First = 0; //记录按键 第一次 按键值 static uchar Key_Second = 0; //记录按键 第二次 按键值 if(uc_Key_flag) return; //10ms uc_Key_flag = 1; Key_Temp = Key4_Read(); Key_Down = Key_Temp & (Key_Old ^ Key_Temp); //按下为按键值,其它为0 Key_Up = ~Key_Temp & (Key_Old ^ Key_Temp); //松手为抬起前的按键值,其他为0 Key_Old = Key_Temp; if(Key_Down) { uc_Key_state = 'd'; if( (Key_First == 0) && (Key_Second == 0) ) //未有按键按下 { ul_Key_Time_Down_First = ul_ms; //记录按键 第一次 按下时的时刻 Key_First = Key_Down; //记录按键 第一次 按键值 } else if( (Key_First != 0) && (Key_Second == 0) ) //已有一次按键按下,此次是第二次 { ul_Key_Time_Down_Second = ul_ms; //记录按键 第二次 按下时的时刻 Key_Second = Key_Down; //记录按键 第二次 按键值 } } if(Key_Up) { uc_Key_state = 'U'; } //双击、单击的判断 if( (Key_First != 0) && (ul_ms - ul_Key_Time_Down_First >500) ) //有第一次按键按下,并且时间>500ms { if(Key_Second == 0) //第二次没有按下, 单击 { switch(Key_First) { case 7 : uc_Key_Value = 7; break; case 6 : uc_Key_Value = 6; break; case 5 : uc_Key_Value = 5; break; case 4 : uc_Key_Value = 4; break; } Key_First = 0;Key_Second = 0; //清零 } else if(Key_Second == Key_First) //第二次有按下,且和第一次相等 ,双击 { uc_Key_Value = Key_Second +10; Key_First = 0;Key_Second = 0; //清零 } else if( (Key_Second != 0) && (Key_Second != Key_First) ) //第二次有按下,但和第一次不相等 ,错误 { uc_Key_Value = 'E'; Key_First = 0;Key_Second = 0; //清零 } } }

Notes:  ●按键双击的判断周期设为500ms,即按键500ms响应一次,判断是单击、双击还是错误。

7、复合按键

 类似于Ctrl+C 和 Ctrl+V 在长按一个的情况下,再单击另一个。  复合按键 是在按键扫描函数中进行修改。

uchar Key4_Read(void) //独立按键扫描函数,读取键值 { uchar Key_temp; uchar Key_Value; P3 |= 0x0f; Key_temp = P3&0x0f; switch(Key_temp) { case 0x0e : Key_Value = 7; break; //S7 case 0x0d : Key_Value = 6; break; //S6 case 0x0b : Key_Value = 5; break; //S5 case 0x07 : Key_Value = 4; break; //S4 //S4的复合按键部分 case 0x06 : Key_Value = 47; break; //S4 + S7 case 0x05 : Key_Value = 46; break; //S4 + S6 case 0x03 : Key_Value = 45; break; //S4 + S5 default: Key_Value = 0; } return Key_Value; }

            彩 蛋



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有