欢迎光临
我们一直在努力

4x4矩阵键盘扫描与按键消抖——西风模板的三态检测算法解析

CT107D上的键盘是4×4矩阵布局,总共16个按键(S4~S19),但实际代码里通常只用其中一部分。这一篇结合项目中的实际代码,把键盘扫描的原理、消抖策略和按键处理讲清楚。


硬件原理

CT107D的矩阵键盘布局:

P3.3 P3.2 P3.1 P3.0
| | | |
[S4] [S8] [S12] [S16] ← P4.4 控制(列1)
| | | |
[S5] [S9] [S13] [S17] ← P4.2 控制(列2)
| | | |
[S6] [S10] [S14] [S18] ← P3.5 控制(列3)
| | | |
[S7] [S11] [S15] [S19] ← P3.4 控制(列4)

扫描原理就是逐列拉低,逐行检测:

  • 把某一列的IO拉低(比如P4.4=0),其余列保持高电平
  • 检查P3.3/P3.2/P3.1/P3.0哪个被拉低了
  • 如果P3.3=0,说明是S4被按下;P3.2=0说明是S5被按下,以此类推
  • 有一个需要注意的地方:P3.4跟NE555频率发生器共用一个引脚。如果题目考到了NE555频率测量,就不能扫描第四列了。代码里的注释也说了:

    // 若考到ne555 把P34全注释掉
    P44 = 1; P42 = 1; P35 = 1; P34 = 1;

    键盘扫描函数(项目实际代码)

    以下是 Driver/Key.c 中的完整扫描代码(西风模板版本):

    unsigned char Key_Read() {
    unsigned char Temp = 0; // 一定要赋初值0!

    /* 第一列扫描(P4.4=0)*/
    P44 = 0; P42 = 1; P35 = 1; P34 = 1;
    if(P33 == 0) Temp = 4; // S4
    if(P32 == 0) Temp = 5; // S5
    if(P31 == 0) Temp = 6; // S6
    if(P30 == 0) Temp = 7; // S7

    /* 第二列扫描(P4.2=0)*/
    P44 = 1; P42 = 0; P35 = 1; P34 = 1;
    if(P33 == 0) Temp = 8; // S8
    if(P32 == 0) Temp = 9; // S9
    if(P31 == 0) Temp = 10; // S10
    if(P30 == 0) Temp = 11; // S11

    /* 第三列扫描(P3.5=0)*/
    P44 = 1; P42 = 1; P35 = 0; P34 = 1;
    if(P33 == 0) Temp = 12; // S12
    if(P32 == 0) Temp = 13; // S13
    if(P31 == 0) Temp = 14; // S14
    if(P30 == 0) Temp = 15; // S15

    /* 第四列扫描(P3.4=0)—— NE555时需要注释掉!*/
    P44 = 1; P42 = 1; P35 = 1; P34 = 0;
    if(P33 == 0) Temp = 16; // S16
    if(P32 == 0) Temp = 17; // S17
    if(P31 == 0) Temp = 18; // S18
    if(P30 == 0) Temp = 19; // S19

    return Temp;
    }

    两个关键细节:

    • Temp 必须初始化为0。如果没有按键按下,函数返回0。如果忘了初始化,返回值就是不确定的,按键处理会出错。
    • 每扫描完一列后恢复所有列为高电平(P44=1; P42=1; P35=1; P34=1;),防止列之间互相干扰。

    三态检测算法

    西风模板里处理按键的核心逻辑:

    idata uc Key_Val, Key_Old, Key_Down, Key_Up;

    void Key_Proc() {
    Key_Val = Key_Read();

    Key_Down = Key_Val & (Key_Val ^ Key_Old); // 按下检测
    Key_Up = ~Key_Val & (Key_Val ^ Key_Old); // 释放检测
    Key_Old = Key_Val; // 更新旧值

    switch(Key_Down) {
    case 4: // S4按下
    // …
    break;
    case 8: // S8按下
    // …
    break;
    }
    }

    这个算法的好处在于:

  • 一行代码检测按下和释放,不需要一堆if-else
  • 天然消抖,配合10ms调度周期,按键抖动会被过滤
  • 支持双按键,比如S8和S9同时按下
  • 消抖的原理:机械按键按下时会有5~20ms的抖动期,电平会来回跳变。但因为Key_Proc在调度器里每10ms才执行一次,这10ms内不管抖动多少次都只读一次。

    实际竞赛中常用的按键分配:

    按键常用功能
    S4 (case 4) 模式切换/启动/确认
    S5 (case 5) 复位/清除
    S8 (case 8) 界面切换
    S9 (case 9) 参数位置切换
    S12 (case 12) 参数增加
    S13 (case 13) 参数减少
    S8+S9 (case 89) 双按键(特殊操作)

    参数调节的边界保护

    按键调参数时必须加边界保护,否则调过头会导致数据溢出或功能异常。第15届国赛中的例子:

    case 12: // 增加
    if(Seg_Disp_Mode == 2) {
    if(Param_Set_Pos == 0) { // R参数(范围10~20)
    if(++Param_R_10x_Show > 20)
    Param_R_10x_Show = 20; // 到上限就停
    } else { // B参数(范围-90~90)
    Param_B_Show += 5;
    if(Param_B_Show > 90)
    Param_B_Show = 90;
    }
    }
    break;

    case 13: // 减少
    if(Seg_Disp_Mode == 2) {
    if(Param_Set_Pos == 0) {
    if(–Param_R_10x_Show < 10)
    Param_R_10x_Show = 10; // 到下限就停
    } else {
    Param_B_Show -= 5;
    if(Param_B_Show < -90)
    Param_B_Show = -90;
    }
    }
    break;

    ++ 在条件判断之前执行,所以用 > 20 而不是 > 19。这种"先变后判"的写法很常见。


    第16届省赛的状态互锁设计

    第16届省赛的按键处理有一个很好的设计——累加测量进行中时禁止某些操作:

    case 8: // 界面切换
    if(measure_start_flag == 0 && ++seg_display_mode == 4)
    seg_display_mode = 1;
    break;
    // 累加测量中(measure_start_flag=1)不允许切换界面

    case 12: // 清除
    if(measure_start_flag == 0) {
    measure_mode = 0;
    seg_display_mode = 1;
    quench_flag = 1;
    distance = 0;
    }
    break;
    // 累加测量中不允许清除

    这种"条件限制"的写法在竞赛中非常实用,可以防止用户在不合适的时候执行操作导致系统状态混乱。

    赞(0)
    未经允许不得转载:171主机测评 » 4x4矩阵键盘扫描与按键消抖——西风模板的三态检测算法解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址