学习目标

  1. 了解矩阵键盘的原理和基本结构
  2. 学会矩阵键盘的按键扫描方法
  3. 学会矩阵键盘的编程实现

    学习内容

    矩阵按键

    矩阵键盘是一种常见的数字输入设备,由多行多列的按键组成。每个按键都有一个唯一的行列坐标,通过行列坐标可以确定按键的编号,从而实现对数字或字母的输入。

    原理图

    image.png
    矩阵键盘的基本结构包括按键、行引脚和列引脚。按键一般是机械按键或触摸按键,行引脚和列引脚分别与矩阵键盘的行和列相连,用于检测按键的输入状态。

    按键状态检测

    单个按键状态检测

    111.png
  • 输出端的电平
  • 输入端的状态
  • 按键抬起

通过按键抬起时的状态,我们分析输入端的电平信号,来确定抬起时输入端的默认电平状态。
112.png
通过按键按下时的状态,我们分析输入端的电平信号,来确定按下时输入端的默认电平状态。
通过分析确认,默认输出端和输入端都是高电平;

  • 当输出端输出低电平时,输入端为高电平,则开关为抬起状态;
  • 当输出端输出低电平时,输入端为低电平,则开关为按下状态;

单行按键状态检测

113.png
通过逐一检测输入端的状态,来判断按键是否按下。

多行按键状态检测

114.png
首先,将第一行输出低电平,其余行设置为高电平。目的是为了只测试第一行的按键状态。
115.png
然后,将第二行输出低电平,其余行设置为高电平。目的是为了只测试第二行的按键状态。
116.png
接着,将第三行输出低电平,其余行设置为高电平。目的是为了只测试第三行的按键状态。
117.png
最后,将第四行输出低电平,其余行设置为高电平。目的是为了只测试第四行的按键状态。

状态记录

通过自定义状态来记录按键状态

  1. // 记录16个按键状态,0为按下,1为抬起
  2. u16 key_state = 0xFFFF;
  3. ......
  4. void scan() {
  5. // 初始都是 高电平
  6. ROW_COL_RESET();
  7. NOP1();
  8. // ROW1
  9. // 给 row1 低电平,读取COL1的值
  10. ROW1 = 0;
  11. NOP1();
  12. // 当前是UP,当之前是DOWN,则为UP
  13. // 当前是DOWN,当之前是UP,则为DOWN
  14. if(COL1 != (key_state & 0x01) >> 0) {
  15. if(COL1) {
  16. // 修改当前状态为UP
  17. key_state |= 0x01;
  18. printf("K1 Up\r\n");
  19. } else {
  20. // 修改当前状态为DOWN
  21. key_state &= ~0x01;
  22. printf("K1 Down\r\n");
  23. }
  24. }
  25. ......
  26. }

状态优化

通过define优化一些数值的操作,方便在后续看代码时方便理解,提高代码的阅读性。

  1. // 记录16个按键状态,0为按下,1为抬起
  2. u16 key_state = 0xFFFF;
  3. #define KEY_UP 1
  4. #define KEY_DOWN 0
  5. // 第n个按键的状态
  6. #define KEY_STATE(n) ((key_state & (1 << n)) >> n)
  7. #define SET_KEY_UP(n) (key_state |= (1 << n))
  8. #define SET_KEY_DOWN(n) (key_state &= ~(1 << n))
  9. #define ROW_COL_RESET() {ROW1=1,ROW2=1,ROW3=1,ROW4=1;COL1=1,COL2=1,COL3=1,COL4=1;}
  10. ......
  11. void scan() {
  12. // 初始都是 高电平
  13. ROW_COL_RESET();
  14. NOP1();
  15. // ROW1
  16. // 给 row1 低电平,读取COL1的值
  17. ROW1 = 0;
  18. NOP1();
  19. // 当前是UP,当之前是DOWN,则为UP
  20. // 当前是DOWN,当之前是UP,则为DOWN
  21. if(COL1 != KEY_STATE(0)) {
  22. if(COL1) {
  23. // 修改当前状态为UP
  24. SET_KEY_UP(0);
  25. printf("K1 Up\r\n");
  26. } else {
  27. // 修改当前状态为DOWN
  28. SET_KEY_DOWN(0);
  29. printf("K1 Down\r\n");
  30. }
  31. }
  32. ......
  33. }

循环优化

操作的按键众多,通过循环的方式来操控每一个按键,减少代码量,方便维护。

  1. #define ROW 4
  2. #define COL 4
  3. // 记录16个按键状态,0为按下,1为抬起
  4. u16 key_state = 0xFFFF;
  5. #define KEY_UP 1
  6. #define KEY_DOWN 0
  7. // 第n个按键的状态
  8. #define KEY_STATE(r, c) ((key_state & (1 << (r * ROW + c))) >> (r * ROW + c))
  9. #define SET_KEY_UP(r, c) (key_state |= (1 << (r * ROW + c)))
  10. #define SET_KEY_DOWN(r, c) (key_state &= ~(1 << (r * ROW + c)))
  11. #define ROW_COL_RESET() {ROW1=1,ROW2=1,ROW3=1,ROW4=1;COL1=1,COL2=1,COL3=1,COL4=1;}
  12. void scan() {
  13. u8 i, j;
  14. for(i = 0; i < ROW; i++) {
  15. // 初始都是 高电平
  16. ROW_COL_RESET();
  17. NOP1();
  18. ROW_ON(i);
  19. for(j = 0; j < COL; j++) {
  20. // 当前是UP,当之前是DOWN,则为UP
  21. // 当前是DOWN,当之前是UP,则为DOWN
  22. if(COL_STATE(j) != KEY_STATE(i, j)) {
  23. if(COL_STATE(j)) {
  24. // 修改当前状态为UP
  25. SET_KEY_UP(i, j);
  26. printf("(%d, %d) Up\r\n", (int)i, (int)j);
  27. } else {
  28. // 修改当前状态为DOWN
  29. SET_KEY_DOWN(i, j);
  30. printf("(%d, %d) Down\r\n", (int)i, (int)j);
  31. }
  32. }
  33. }
  34. }
  35. }

练习题

  1. 按键扫描检测
  2. 优化按键状态扫描
  3. 驱动封装