[麦麦茶水间] 【每周分享】下面这段矩阵按键扫描程序有木有什么隐患?

[复制链接]
145|2
dffzh 发表于 2026-4-16 19:47 | 显示全部楼层 |阅读模式
本帖最后由 dffzh 于 2026-4-16 19:48 编辑

最近在做基于MCU+矩阵按键的USB键盘,程序上不知道用什么样的框架会比较稳妥,以及扩展性好;然后参考了AI代码,加上自己添加的,实现了下面这么一段矩阵按键扫描功能,当然支持多个按键的组合按键功能:
  1. uint8_t  key_buff[KEY_BUFF_MAX]; // 1个shift键+1个ctrl键+1个alt键+普通按键7个
  2. uint8_t  last_key_buff[KEY_BUFF_MAX];

  3. /*延时函数(消抖用)*/
  4. static void matrix_key_delay_ms(uint16_t ms)
  5. {
  6.     // 根据你的单片机主频修改
  7.     // HAL_Delay(ms);  // STM32示例
  8.     uint16_t i, j;
  9.     for(i = 0; i < ms; i++)
  10.     {
  11.         for(j = 0; j < 120; j++);
  12.     }
  13. }

  14. void matrix_key_init(void)
  15. {
  16.         ROW_HIGH(ROW0_PIN);
  17.     ROW_HIGH(ROW1_PIN);
  18.     ROW_HIGH(ROW2_PIN);
  19.     ROW_HIGH(ROW3_PIN);
  20.     ROW_HIGH(ROW4_PIN);
  21.     ROW_HIGH(ROW5_PIN);
  22.     ROW_HIGH(ROW6_PIN);
  23.     ROW_HIGH(ROW7_PIN);
  24. }

  25. /*读取9列状态*/
  26. static uint16_t Key_ReadCol(void)
  27. {
  28.     uint16_t col = 0;

  29.     if(COL_READ(COL0_PIN) == 0) col |= (1<<0);
  30.     if(COL_READ(COL1_PIN) == 0) col |= (1<<1);
  31.     if(COL_READ(COL2_PIN) == 0) col |= (1<<2);
  32.     if(COL_READ(COL3_PIN) == 0) col |= (1<<3);
  33.     if(COL_READ(COL4_PIN) == 0) col |= (1<<4);
  34.     if(COL_READ(COL5_PIN) == 0) col |= (1<<5);
  35.     if(COL_READ(COL6_PIN) == 0) col |= (1<<6);
  36.     if(COL_READ(COL7_PIN) == 0) col |= (1<<7);
  37.     if(COL_READ(COL8_PIN) == 0) col |= (1<<8);

  38.     return col;
  39. }

  40. /*8×9矩阵按键扫描核心函数*/
  41. // 返回值:0xFF = 无按键
  42. // 0~71 = 对应按键编号(8行×9列)
  43. static uint16_t MatrixKey_Scan(void)
  44. {
  45.     //uint16_t col_val;
  46.     uint8_t row_index = 0;
  47.         uint8_t col_index = 0;
  48.     uint8_t key_val = 0xFF; // 默认无按键, 键值 = 行号 × 9 + 列号
  49.     uint8_t current_key = 0;
  50.         uint8_t  tmp_key_buff[KEY_BUFF_MAX] = {0x00,};
  51.         uint8_t key_num = 3; //front 3 is ModifierKey

  52.     //第一步:所有行输出高电平
  53.     ROW_HIGH(ROW0_PIN);
  54.     ROW_HIGH(ROW1_PIN);
  55.     ROW_HIGH(ROW2_PIN);
  56.     ROW_HIGH(ROW3_PIN);
  57.     ROW_HIGH(ROW4_PIN);
  58.     ROW_HIGH(ROW5_PIN);
  59.     ROW_HIGH(ROW6_PIN);
  60.     ROW_HIGH(ROW7_PIN);

  61.     //第二步逐行拉高扫描
  62.     for(row_index = 0; row_index < MATRIX_KEY_ROW_NUM; row_index++)
  63.     {
  64.         // 所有行先拉高
  65.         ROW_HIGH(ROW0_PIN);
  66.         ROW_HIGH(ROW1_PIN);
  67.         ROW_HIGH(ROW2_PIN);
  68.         ROW_HIGH(ROW3_PIN);
  69.         ROW_HIGH(ROW4_PIN);
  70.         ROW_HIGH(ROW5_PIN);
  71.         ROW_HIGH(ROW6_PIN);
  72.         ROW_HIGH(ROW7_PIN);

  73.         // 当前行拉低
  74.         switch(row_index)
  75.         {
  76.             case 0: ROW_LOW(ROW0_PIN); break;
  77.             case 1: ROW_LOW(ROW1_PIN); break;
  78.             case 2: ROW_LOW(ROW2_PIN); break;
  79.             case 3: ROW_LOW(ROW3_PIN); break;
  80.             case 4: ROW_LOW(ROW4_PIN); break;
  81.             case 5: ROW_LOW(ROW5_PIN); break;
  82.             case 6: ROW_LOW(ROW6_PIN); break;
  83.             case 7: ROW_LOW(ROW7_PIN); break;
  84.         }

  85.         matrix_key_delay_ms(10); // 消抖
  86.         col_val[row_index] = Key_ReadCol(); // 读取9列

  87.         if(col_val[row_index] != 0) // 有按键按下
  88.         {
  89.             // 判断是哪一列
  90.             for(col_index = 0; col_index < MATRIX_KEY_COL_NUM; col_index++)
  91.             {
  92.                 if((col_val[row_index] & (1 << col_index)))
  93.                 {
  94.                     key_val = row_index * 9 + col_index; // 计算键值 0~71
  95.                     
  96.                     current_key = matrix_key_map[row_index][col_index];
  97.                                         switch(current_key)
  98.                     {
  99.                         case SW14_KEY_SHIFT_VALUE:
  100.                                                         tmp_key_buff[0] = current_key;
  101.                                                         break;
  102.                         case SW22_KEY_CTRL_VALUE:
  103.                                                         tmp_key_buff[1] = current_key;
  104.                                                         break;
  105.                         case SW30_KEY_ALT_VALUE:  
  106.                                                         tmp_key_buff[2] = current_key;
  107.                                                         break;
  108.                                                 default: //else normal key
  109.                                                     tmp_key_buff[key_num++] = current_key;
  110.                                                         break;
  111.                     }
  112.                 }
  113.             }
  114.         }
  115.     }

  116.     //update current key value
  117.     memcpy(key_buff, tmp_key_buff, KEY_BUFF_MAX);
  118.         //update last key value when key state has changed
  119.     if(memcmp(last_key_buff, key_buff, KEY_BUFF_MAX))
  120.     {
  121.                 memcpy(last_key_buff, tmp_key_buff, KEY_BUFF_MAX);
  122.                 #ifdef PRINT_ENABLE
  123.                 printf("key buff: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\r\n", \
  124.                                                     tmp_key_buff[0],  \
  125.                                                     tmp_key_buff[1],  \
  126.                                                     tmp_key_buff[2],  \
  127.                                                     tmp_key_buff[3],  \
  128.                                                     tmp_key_buff[4],  \
  129.                                                     tmp_key_buff[5],  \
  130.                                                     tmp_key_buff[6],  \
  131.                                                     tmp_key_buff[7]);
  132.                 #endif
  133.     }

  134.     return 0;
  135. }
主函数接口是MatrixKey_Scan,key_buff数组存储按键值,前面三个元素存储修饰按键值(Shift,Ctrl,Alt),后面几个元素存储普通按键值,支持多个组合按键。
二维数组matrix_key_map存储与硬件对应的按键码值,8行9列的按键矩阵。
当按键状态,也就是key_buff数组的值发生任何改变后,更新上一次值,并且通过串口打印出来。
做了简单测试,倒是能实现矩阵按键扫描功能,串口打印也比较正常:

但不确定是否存在某些特殊情况下的异常?没做过USB键盘,是否有做过的,欢迎提建议。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
yzdel7 发表于 2026-4-16 21:09 来自手机 | 显示全部楼层
这问题是,你用delay的话整个程序就阻塞了,建议用定时器扫描,也比较灵活。

评论

好的,谢谢  发表于 2026-4-17 08:53
您需要登录后才可以回帖 登录 | 注册

本版积分规则

562

主题

4018

帖子

27

粉丝
快速回复 在线客服 返回列表 返回顶部
0