【按键】短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动)--- 矩阵键盘

请先阅读上篇:
短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动)

上面的程序适用于单个按键,那是不是也可以适用于矩阵键盘呢?
答案是肯定的。

接下来在这里做一个简单的扩展,具体框架不用改变,所以具体的框架内容和思路在这里不详述了,自行参考上篇文章,这里就说说扩展矩阵键盘的需要改动的地方。

重点:
对于此按键盘函数的框架来说,其实单个按键与矩阵键盘程序的差别仅仅在于读取按键的方式不同。所以这里的程序只需要更改读取按键值的部分即可。

1.单个按键的读取按键值程序

CurrReadKey = P0 & 0x00ff;                //获取当前的键值

2.矩阵键盘的读取按键值程序
这里以4*4键盘为例
原理图如下:

矩阵键盘原理图
将原理图中的P1看成是P0(由于赖所以直接从度娘那里盗的图),下面程序是以P0为例。

//读取按键的行数
P0 = 0xf0;
KeyRow = P0;

//读取按键的列数
P0 = 0x0f;
KeyColumn = P0;

//读取行列数,就是当前键值了
CurrReadKey = Key_Row|Key_Column;

3.只需要将单个按键的读取键值程序改为矩阵键盘的读取键值的程序即可,程序如下:

KeyScan.c文件如下:

//======================================================
//KeyScan.c
//======================================================
//注意:该宏定义,定义在keyscan.h文件中
//#define KEYDEBOUNCE 0x05             //消抖动,按键扫描次数。如果连续5次都是扫描的都是相同键值,则认为是有效键值,否则是误触发

unsigned int g_uiCurrKey;            //当前按键值
unsigned int g_uiLastKey;            //上次按键值
unsigned int g_uiKeyScanCount;       //按键扫描计数,作用:消抖动

unsigned int g_uiKeyDown;            //键被按下,返回的键值。       作用:单次按键,单次返回有效键值;按住不放,也只返回被按下的一个键值
unsigned int g_uiKeyRelease;         //键被释放后,返回的键值。     作用:只有按下的按键被释放后,才返回按下的键值
unsigned int g_uiKeyContinue;        //键连续按键,重复返回的键值。 作用:只要按住不放,就会重复地返回相同键值

//P0口的低八位作为按键
//没有按键时,返回的是0xff,
void Int_Key_Scan(void)
{
    static unsigned short LastReadKey;        //上次从IO口读取的键值 ,注意是静态变量
    unsigned short CurrReadKey;               //当前从IO口读取的键值

//---------------------- 需要修改的地方 --------------------------------------//
    unsigned short KeyRow,KeyColumn;

    //读取按键的行数
    P0 = 0xf0;
    KeyRow = P0;

    //读取按键的列数
    P0 = 0x0f;
    KeyColumn = P0;

    //读取行列数,就是当前键值了
    CurrReadKey = Key_Row|Key_Column;         //获取当前的键值        
//-----------------------------------------------------------------------//

    if(CurrReadKey == LastReadKey)            //如果当前读取的键值与上次从IO口读取的键值相同
    {
        if(g_uiKeyScanCount >= KEYDEBOUNCE)   //首先判断是否大于等于debounce的设定值(即是,是否大于等于设定的采样次数)
        {
            //按住不放,多次响应
            g_uiCurrKey = CurrReadKey;                //如果是,将当前的读取值判定为有效按键值(如果是,在采样周期中,都是这个值,则判定为有效按键值)

            //按住不放只响应一次
            if(g_uiPreKeyValue == g_uiCurrKey)
            {
                g_uiKeyDown = 0xff;                    //没有键值
            }
            else
            {
                g_uiKeyDown = g_uiCurrKey;             //如果不同,按键有效,(就是第一次有效值时)
            }

            //按键释放时,按键值才有效     
            if(g_uiCurrKey == 0xff)                  //当有效按键值从非0到0的状态时(即是,从有按键到无按键,表示已经释放了),表示之前按键已经释放了
            {
                g_uiKeyRelease = g_uiPreKeyValue;
            }

            g_uiLastKey = g_uiCurrKey;               //记录上次有效按键值
        }
        else                                       //如果否,则debounce加一(如果否,则继续采样键值)
        {
            g_uiKeyScanCount++;
        }
    }
    else                                          //如果当前读取的键值与上次从IO口读取的键值不同,说明按键已经变化
    {
        g_uiKeyDown = 0xff;                       //放开按键后第一次进入扫描程序,清零g_uiKeyDown.作用:消除一个BUG(你猜BUG是什么?)
        g_uiKeyScanCount = 0;                     //清零之前的按键的debounce计数
        LastReadKey = CurrReadKey;                //将当前读取的键值记录为上次读取的按键值
    }   
}

KeyScan.h文件如下:

//======================================================
//KeyScan.h
//======================================================

//宏定义
#define KEYDEBOUNCE 0x05             //消抖动,按键扫描次数。如果连续5次都是扫描的都是相同键值,则认为是有效键值,否则是误触发

//声明变量
extern unsigned int g_uiCurrKey;            //当前按键值
extern unsigned int g_uiLastKey;            //上次按键值
extern unsigned int g_uiKeyScanCount;       //按键扫描计数,作用:消抖动

extern unsigned int g_uiKeyDown;            //键被按下,返回的键值。       作用:单次按键,单次返回有效键值;按住不放,也只返回被按下的一个键值
extern unsigned int g_uiKeyRelease;         //键被释放后,返回的键值。     作用:只有按下的按键被释放后,才返回按下的键值
extern unsigned int g_uiKeyContinue;        //键连续按键,重复返回的键值。 作用:只要按住不放,就会重复地返回相同键值

//函数声明
void Int_Key_Scan(void);

4.实例程序
这里就以单次按键有效为例,按键释放有效与长按多次有效 写法一样。
仅仅修改switch中的case值即可。

main.c如下所示:

//======================================================
//main.c
//======================================================
#include "reg51.h"
#include "KeyScan.h"

sbit LED = P1.0;       //定义LEDIO口

void main(void)
{
    P0 = 0xff;
    LED = 0;           //点亮LED

    while(1)
    {
        Int_Key_Scan();      //按键扫描程序

        //第一种:KeyDown的使
        //单按时和长按时,都只返回一次有效键值(无需等到按键释放,就可以返回有效键值)
        switch(g_uiKeyDown)
        {
            case 0xEE:
                        //P0.0按键程序
                        LED = !LED; 
                        break;

            case 0xED:
                        //P0.1按键程序 
                        //...
                        break;

            case 0xEB:
                        //P0.2按键程序 
                        //...
                        break;

            case 0xE7:
                        break;

            case 0xDE:
                        break;

            case 0xDD:
                        break;

            case 0xDB:
                        break;

            case 0xD7:
                        break;   

            case 0xBE:
                       break;

            case 0xBD:
                       break;

            case 0xBB:
                       break;

            case 0xB7:
                       break; 

            case 0x7E:
                       break;

            case 0x7D:
                       break;

            case 0x7B:
                        break;

            case 0x77:
                        break;        

            case 0xFF:
                        //没有按键程序
                        //...
                        break;
        }
    }
}

Pillar Peng
Friday, February 26, 2016 11:48


友情链接:

【按键】短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动)

  • 4
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
好的,我可以为您提供一个4个独立按键按键扫描程序,以下是程序的代码: ```c #include <avr/io.h> #include <util/delay.h> #define KEY_DDR DDRD #define KEY_PORT PORTD #define KEY_PIN PIND #define KEY1 PD0 #define KEY2 PD1 #define KEY3 PD2 #define KEY4 PD3 #define LONG_PRESS_TIME_MS 1000 #define DEBOUNCE_DELAY_MS 10 uint8_t key_pressed = 0; uint8_t key_hold = 0; uint8_t key_hold_timer = 0; void init_keypad(void) { KEY_DDR &= ~(1 << KEY1 | 1 << KEY2 | 1 << KEY3 | 1 << KEY4); KEY_PORT |= (1 << KEY1 | 1 << KEY2 | 1 << KEY3 | 1 << KEY4); } uint8_t scan_key(void) { uint8_t key_press = 0; if (!(KEY_PIN & (1 << KEY1))) { key_press = 1; } else if (!(KEY_PIN & (1 << KEY2))) { key_press = 2; } else if (!(KEY_PIN & (1 << KEY3))) { key_press = 3; } else if (!(KEY_PIN & (1 << KEY4))) { key_press = 4; } return key_press; } uint8_t is_key_pressed(uint8_t key) { uint8_t key_press = 0; if (key_pressed == key) { key_press = 1; } return key_press; } uint8_t is_key_hold(uint8_t key) { uint8_t key_hold_flag = 0; if (key_hold && (key_pressed == key)) { key_hold_flag = 1; } return key_hold_flag; } uint8_t is_key_combo(uint8_t key1, uint8_t key2) { uint8_t key_combo_flag = 0; if (key_pressed == key1 && key_hold && (key_hold_timer > DEBOUNCE_DELAY_MS) && is_key_pressed(key2)) { key_combo_flag = 1; } return key_combo_flag; } void check_key_press(void) { uint8_t key_press = scan_key(); if (key_press && (key_pressed != key_press)) { key_pressed = key_press; key_hold_timer = 0; key_hold = 0; } else if (key_press && (key_pressed == key_press)) { key_hold_timer++; if (key_hold_timer >= LONG_PRESS_TIME_MS / DEBOUNCE_DELAY_MS) { key_hold = 1; } } else if (!key_press) { key_pressed = 0; key_hold_timer = 0; key_hold = 0; } } int main(void) { init_keypad(); while (1) { check_key_press(); if (is_key_pressed(KEY1)) { // KEY1 is pressed } if (is_key_hold(KEY1)) { // KEY1 is held down } if (is_key_combo(KEY1, KEY2)) { // KEY1 and KEY2 are pressed together } _delay_ms(DEBOUNCE_DELAY_MS); } } ``` 该程序使用了AVR单片机,定义了4个按键分别为KEY1,KEY2,KEY3,KEY4。程序支持短按区分,同时支持组合键。 在程序中,我们首先初始化了按键的IO口,并定义了KEY1,KEY2,KEY3,KEY4的输入端口及状态。接下来是程序的主要部分,包括按键扫描按键状态判断等。在程序的主循环中,我们通过调用check_key_press()函数对按键进行扫描,并根据按键的状态进行相应的处理。 如果键被按下,我们可以通过调用is_key_pressed(KEYn)函数来检查该键是否被按下。如果键被按,我们可以通过调用is_key_hold(KEYn)函数来检查该键是否被按。如果我们需要检查组合键是否被按下,我们可以通过调用is_key_combo(KEY1, KEY2)函数来检查KEY1和KEY2是否同时被按下。 需要注意的是,程序中使用了一个延时函数_delay_ms(),以避免按键抖动。如果您需要更加准确的按键检测,可以使用外部中断来代替延时函数。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PillarPeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值