关于独立按键扫描程序的思考(整合两种算法)

转自关于独立按键扫描程序的思考(整合两种算法)


源程序主要部分如下:

  1. volatile unsigned char Trg;    
  2. volatile unsigned char Cont;    
  3. volatile unsigned char Release;    
  4.   
  5. void KeyRead( void )    
  6. {    
  7.   unsigned char ReadData = PINB^0xff;      // 1  读键值   
  8.   Trg = ReadData & (ReadData ^ Cont);      // 2  得到按下触发值   
  9.   Release=  (ReadData ^ Trg ^ Cont);       // 3  得到释放触发值   
  10.   Cont = ReadData;                         // 4  得到所有未释放的键值   
  11. }    
首先感觉程序的逻辑可以优化一下,因此就自作主张用了数字电路上逻辑优化方法
  1.  void KeyRead( void )    
  2. {    
  3.   unsigned char ReadData = (~PINB)&0xff;      // 1  读键值   
  4.   Trg = (~Cont) & ReadData;      // 2  得到按下触发值   
  5.   Release= Cont &(~ReadData);       // 3  得到释放触发值   
  6.   Cont = ReadData;                         // 4  得到所有未释放的键值   
  7. }    

这样就好更理解一些Trg、Release、cont的含义。以Trg为例,只有在Cont=0且ReadData=1的情况下,才为1,这正好对应的是ReadData波形的上升沿(由于ReadData做了取反操作,该上升沿对应按键按下瞬间IO口波形的下降沿)。

由于是要用到51板子上,所以在程序上也做了相应的修改。这里用了4个独立按键,分别对应P1口的P1.0-P1.3。同时为了方便理解,改动了变量的名字。

Trig---KeyPressDown  代表按键按下对应的边沿,

ReadData---CurrKey  当前读取的键值,进行了取反操作,这样当按键按下时,相应的位为1。

Cont -----LastKey     上次的按键值

Release-----KeyRelease    按键释放时对应的边沿


修改到51后的程序为

  1. #define KEYMASK  0x0f  
  2. //按键变量  
  3. unsigned char   KeyPressDown=0x00;  
  4. unsigned char   KeyRelease=0x00;  
  5. unsigned char   LastKey=0x00;  
  6.   
  7. //按键扫描,定时10ms执行一次  
  8. void KeyScan(void)  
  9. {     
  10.     //当前读取的键值  
  11.     unsigned char CurrKey;  
  12.     P1|=KEYMASK;  //将按键对应的IO设置为输入状态  
  13.     CurrKey=(~P1)&KEYMASK;  
  14.   
  15.     KeyPressDown=(~LastKey)&CurrKey;  
  16.     KeyRelease=LastKey&(~CurrKey);  
  17.   
  18.     LastKey=CurrKey;  
  19. }  

为了测试该程序,没想到好的办法,闭着眼数数按键,对KeyPressDown进行计数,然后用数码管显示该计数值。如果我按100下,那么数码管应该也显示的是100。但是四个按键都试了下,都比100多了好几个,用示波器看了下IO口的波形,波形大部分情况下是个矩形波,只是在个别情况下,会在按下或释放瞬间出来个干扰脉冲(也就是抖动),而程序采用10ms定时采样,由于两者间没有相关性,如果在刚好在干扰脉冲瞬间采样的话,这样有可能导致按键在一次按键中,多次触发事件。

如果按键IO口受干扰比较严重或按键接触不良的情况下,可能在按键没有按下时也会产生一个负脉冲,或在按键按下稳定的过程中出现一个正脉冲。这样的脉冲可能不是很宽,但是一旦被检测被程序的10ms定时采样采到,就会引起一个误触发操作,增大采样时间可以减小几率,但不能避免。

为了完善该算法,解决干扰脉冲可能带来的误触发的问题。在网上找了好久,终于找到一篇博文《一种软件去除键抖动的方法,就借鉴了过来

核心算法是

Keradyn=Ktemp Kinput+Kreadyn-1 (Ktemp ⊙Kinput)    (1)

Ktemp=Kinput    (2)

为了方便理解,再次对变量名作了修改,并且增加了变量, 

LastReadKey代表上次KeyScan()采样读取的键值

CurrReadKey; //记录本次KeyScan()采样读取的键值

LastKey 代表上次的有效判定的键值(经过逻辑判断,或者经过校正的实际键值)

CurrKey代表本次的有效判定键值。

按照逻辑式

CurrKey=(CurrReadKey&LastReadKey)|LastKey&(CurrReadKey^LastReadKey)  (1)

对CurrKey进行判定:如果上次LastReadKey和当前CurReadKey读取的键值都为1,那么当前有效键值CurrKey一定为1;如果上次和当前读取的键值不一样的话,则与上次有效键值LastKey保持一致。就是利用这种方式,来消除干扰脉冲。

该算法要求干扰脉冲的宽度必须要小于程序定时采样周期,如果程序定时20ms采样的话,那么干扰脉冲宽度必须小于20ms。实际上,如果IO键干扰脉冲大于20ms,就该考虑是否硬件设计存在问题,或者应该在按键IO口并上滤波电容。

最后给出两种算法整合后的程序

  1. //按键变量  
  2. unsigned char   KeyPressDown=0x00;  
  3. unsigned char   KeyRelease=0x00;  
  4. unsigned char   LastKey=0x00;  
  5.   
  6. //按键扫描,定时10ms执行一次  
  7. void KeyScan(void)  
  8. {     
  9.     static unsigned char LastReadKey=0x00;  //记录上次KeyScan()读取的IO口键值  
  10.     unsigned char CurrReadKey;  //记录本次KeyScan()读取的IO口键值  
  11.     unsigned char CurrKey;      //记录本次经过消抖处理后的有效按键值   
  12.   
  13.     P1|=KEYMASK;  //将按键对应的IO设置为输入状态  
  14.     CurrReadKey=(~P1)&KEYMASK; //取反  
  15.       
  16.     //消抖原理很简单:  
  17.     //如果上次LastReadKey和当前CurReadKey读取的键值都为1,那么当前有效键值CurrKey一定为1  
  18.     //如果上次和当前读取的键值不一样的话,则与上次有效键值LastKey保持一致。  
  19.     //通过这种方式来进行消抖,抖动时间必须小于keyscan()定时扫描周期,否则会出现错误。  
  20.     CurrKey=(CurrReadKey&LastReadKey)|LastKey&(CurrReadKey^LastReadKey);  
  21.     //记录按键按下及释放  
  22.     KeyPressDown=(~LastKey)&CurrKey;  
  23.     KeyRelease=LastKey&(~CurrKey);  
  24.   
  25.     LastReadKey=CurrReadKey;  
  26.     LastKey=CurrKey;  
  27. }  
下载到单片机后,还是老办法测试,这次按100下,数码管就显示100。相对于以前是稳定多了。至于该按键扫描程序的性能,还要在将来的应用中得到证明。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于您的问题,C语言实现2×2矩阵按键扫描算法的基本思路如下: 1. 定义一个二维数组,用于表示矩阵中各个按键的状态; 2. 循环扫描矩阵中的元素,检测按键的状态; 3. 在检测到按键状态变化时,执行相应的操作。 下面是实现代码: ```c #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <windows.h> // 定义矩阵大小和扫描频率 #define ROWS 2 #define COLS 2 #define SCAN_RATE 100 // 定义按键状态枚举类型 enum KEY_STATE { KEY_UP, KEY_DOWN }; // 定义矩阵 int matrix[ROWS][COLS] = { {VK_UP, VK_DOWN}, {VK_LEFT, VK_RIGHT} }; // 定义按键状态数组 enum KEY_STATE key_state[ROWS][COLS] = { {KEY_UP, KEY_UP}, {KEY_UP, KEY_UP} }; // 定义按键操作函数 void do_key_event(int key, int state) { // 根据按键和状态执行相应的操作 printf("key:%d, state:%d\n", key, state); } // 执行矩阵按键扫描操作 void scan_key_matrix() { int i, j; int curr_key_state; enum KEY_STATE prev_key_state; // 循环扫描矩阵元素 for (i = 0; i < ROWS; i++) { for (j = 0; j < COLS; j++) { // 获取当前按键状态 curr_key_state = (GetKeyState(matrix[i][j]) < 0)? KEY_DOWN : KEY_UP; prev_key_state = key_state[i][j]; // 检测按键状态是否改变 if (prev_key_state != curr_key_state) { // 更新按键状态 key_state[i][j] = curr_key_state; // 执行相应的按键操作 do_key_event(matrix[i][j], curr_key_state); } } } } int main() { while (1) { scan_key_matrix(); Sleep(SCAN_RATE); } return 0; } ``` 希望能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值