【按键扫描】独立按键与矩阵按键

按键介绍

我们通常提到按键,一般是指按键开关,也称为轻触开关。轻触开关是最常用的几种电子元器件之一,被各种电子产品广泛使用。

轻触开关与普通开关类似,但又略有不同。普通开关有闭合与断开两种状态,切换后状态会锁定,直到下次操作前不改变;而轻触开关内部有弹簧,弹起时为断开状态,在施加一定压力后会闭合短路,而松开后又会自动弹起,重新恢复断开状态。
所以轻触开关除了可以检测按下与抬起外,还可以检测长按与短按,从而单按键开关可实现多种复合功能。

最常见的轻触开关为四脚轻触开关,如下图

轻触开关规格书
四脚轻触开关实际内部是两两相连,设计为四脚一方面是为了稳定性(四脚固定在按动时会比两脚更稳),另一方面也是为了硬件布线可以更方便。
根据规格书的描述,内部1与2,3与4直接相连,在按下时,1/2 才会和 3/4闭合,接线时需要注意,不要弄错引脚,导致按键为常闭状态。

按键抖动

按键开关一般是由弹性金属制成,在按键按下后会经历波动过程,然后再稳定;弹起时也会产生类似情况。通常我们称此现象为按键抖动,也被称为接触弹跳(Contant Bounce)。

接触弹跳-按键弹起
由于按键检测芯片频率都比较高,而接触弹跳的存在,会导致软件在检测按键按下/抬起时,会发生多次误检测的异常现象。
所以在实际使用按键扫描时,需要进行消抖(Debounce)才能正常使用。

按键消抖分为两种:硬件消抖,软件消抖。

硬件消抖

硬件的滤波电路或者其他电路实现,此处不做讨论

软件消抖

根据抖动的特征,软件进行消除。

参考按键规格书中的描述,抖动分为"按下抖动"和"抬起抖动",一般都在 10ms以下。简单的消抖方法为延时消抖,即检测到状态变化后,延时10ms,再次确认状态,如果相同才识别为有效。

按键规格书

// 示例伪代码
// 判断按键为按下状态
if scan_status == KEY_PRESSED
    // 延时 10ms
    delay_10ms
    // 再次检测按键状态如果为按下
    if scan_status == KEY_PRESSED
        // 确认当前按键状态为按下
        key_status = scan_status
// 结束

按键扫描

按键扫描按照接线方式的不同,可分为独立按键接线与矩阵按键接线。

独立按键

独立按键,即单个按键独立的接到单片机的 IO口上,读取对应IO状态,例如高电平为按键抬起,低电平为按键按下。
此种方式结构简单,软件简单。实际开发中,单片机IO资源有限,除非按键很少,否则不会使用。

示例流程如下

  1. 按键单独连接到IO口,另一端接地
  2. 配置对应IO口为输入状态,且为内部上拉
  3. 读取IO口电平,为高时按键抬起,为低时为按键按下
  4. 消抖后判定状态是否有效,确认按键状态

独立按键状态变化

// 独立按键检测
void stand_alone_scan(void)
{
    // 检测 P2.0 IO口, 按键状态变化
    if (stand_alone_io != stand_alone_last)
    {
        // 延时 10ms 消抖
        delay_10us(1000);
        // 再次检测 IO 口确认按键状态
        if (stand_alone_io != stand_alone_last)
        {
            printf("stand alone - %s\r\n",
                   stand_alone_io == KEY_UNPRESSED ? "unpressed" : "pressed");
            stand_alone_last = stand_alone_io;
        }
    }
}

矩阵按键

矩阵键盘,顾名思义采用行列式连线进行布局,按键的两端分别接到矩阵的行线与列线。按键按下时,对应按键的行和列短路,检测确定行和列,即可确认按键位置。
以 4x4 矩阵键盘为例,共16个按键,仅需要8个IO口,4个IO口连接按键矩阵行,4个IO口连接按键矩阵列。
矩阵键盘识别比独立按键略微复杂,但是相比独立按键可节省大量IO口,实际开发中应用较多。

矩阵键盘连线

常用的矩阵扫描方法有三种:逐行扫描,逐列扫描,行列反转扫描。

逐行扫描

按键以矩阵方式连接,P1.0~P1.3 为行线,P1.4~P1.7为列线
设置全部行线为输出线,行线全部设置为高电平,全部列线为输入线,检测列线全部为高时,则代表无按键按下
逐行设置行线为低电平,检测每行所有列线,如果识别到低电平,则可判定对应行列按键被按下
如下图所示,行1设置为低电平,检测到列2为低电平,则判定行1列2对应按键被按下

行1为低,按下列2后的状态变化

// 逐行检测代码示例
void matrix_scan(void)
{
    unsigned char row, col;

    for (row = 0; row < MATRIX_ROW_MAX; row++)
    {
        // 逐行选中
        P1 &= ~(1 << row);
        for (col = 0; col < MATRIX_COL_MAX; col++)
        {
            // 在选中行检查每一列
            if (((P1 >> (MATRIX_ROW_MAX + col)) & 0x01) == KEY_PRESSED)
            {
                // 消抖
                delay_10us(1000);
                // 再次检测 IO 口确认按键状态
                if (((P1 >> (MATRIX_ROW_MAX + col)) & 0x01) == KEY_PRESSED)
                {
                    if (matrix_status == KEY_UNPRESSED) 
                    {
                        printf("matrix - row %d col %d pressed\r\n", (int)row, (int)col);
                        pressed_row = row;
                        pressed_col = col;
                        matrix_status = KEY_PRESSED;
                    }
                    break;
                }
            }
            if (((row == pressed_row) && (col == pressed_col)))
            {
                printf("matrix - row %d col %d unpressed\r\n", (int)row, (int)col);
                pressed_row = MATRIX_UNKNOWN;
                pressed_col = MATRIX_UNKNOWN;
                matrix_status = KEY_UNPRESSED;
            }
        }
        // 清除选中行
        P1 |= (1 << row);
    }
}
逐列扫描

与逐行扫描类似,变为逐列设置列线为0,检测每列对应行线,确定按键位置。

行列反转扫描

行先全部设置为高,列全部设置为低,如有按键按下,检测到对应行线变低,确认行;
反向设置,列为高,行为低,检测对应变低的列,确认列;
根据对应行列,即可确认到对应被按下按键位置。

总结

实际按键使用中,使用独立按键与矩阵按键,可覆盖大部分应用场景,针对一些特殊场景,例如多按键按下,会在后续继续讨论优化。


  • 14
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值