找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7393|回复: 8
打印 上一主题 下一主题
收起左侧

单片机矩阵键盘扫描驱动程序与电路分析

  [复制链接]
跳转到指定楼层
楼主
ID:446156 发表于 2021-11-15 14:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
矩阵键盘的扫描原理很多教材都介绍过,在这里再赘述一遍。
以4X4键盘为例,首先按照下图制作电路。

然后将HOR1-HOR4连接到单片机的输入引脚上去;LON1-LON4连接到单片机的开漏输出引脚上去,注意这4个引脚必须设置为开漏模式!
程序上首先将LON1所连接的IO输出低电平其余3个IO输出高电平,同时检测HOR1-HOR4的电平来获取K1-K4的按键状态;然后将LON2所连接的IO输出低电平其余3个IO输出高电平,同时检测HOR1-HOR4的电平来获取K5-K8的按键状态;依次类推。
但是这个电路是有BUG的,比如同时按下K1、K5和K6,当LON1为低电平的时HOR1检测到是低电平没有问题;因为K2没有被按下所以我们希望HOR2是高电平,但是由于K1、K5、K6同时按下电流从VCC通过R2再通过K6再通过K5再通过K1流到LON1,所以实际上HOR2也是低电平这时候程序就认为K2被按下了导致出错。


解决这个问题很简单只需要在合适的位置加一个二极管,利用其单向导电性阻挡电流跨列流动就行了。


好了,接下来是单片机代码时间
  1. #ifndef __Key_matrix_H
  2. #define __Key_matrix_H

  3. #include "gpio.h"
  4. #include "gpio_bool.h"

  5. /*务必把这4个输出IO设置为上拉输入*/
  6. #define KEY_HOR1 PAin(7)
  7. #define KEY_HOR2 PAin(6)
  8. #define KEY_HOR3 PAin(5)
  9. #define KEY_HOR4 PAin(4)
  10. /*务必把这4个输出IO设置为开漏*/
  11. #define KEY_LON1 PBout(0)
  12. #define KEY_LON2 PCout(5)
  13. #define KEY_LON3 PCout(4)
  14. #define KEY_LON4 PCout(3)

  15. #define KEY_PRESS_TIME 20//消抖常数
  16. #define KEY_LONG_PRESS_TIME 3000//单个按键长按阈值3s
  17. /*通过读取(只读)这三个变量即可获得按键的单按、长按和组合键信息*/
  18. extern volatile uint16_t Key_Phy_Num;
  19. extern volatile uint8_t Key_Pulse_Num;
  20. extern volatile uint16_t Key_LP_Num;

  21. typedef enum
  22. {
  23.         KPL_DISABLE=0,
  24.         KPL_ENABLE
  25. }K_L_P;//按键的长按状态
  26. typedef struct
  27. {
  28.         K_L_P KEY_LONG_PRESS;
  29.         uint16_t KeyOpenCount;
  30.         uint8_t KOC_EN;
  31.         uint16_t KeyCloseCount;
  32.         uint8_t KCC_EN;
  33. }Key_Para;

  34. exter Key_Par Key_1,Key_2,Key_3,Key_4,Key_5,Key_6,Key_7,Key_8,Key_9,Key_10,Key_11,Key_12,Key_13,Key_14,Key_15,Key_16;

  35. void Clear_Key_Pulse_Num(void);//当读取完Key_Pulse_Num后调用
  36. void KeyCount_Run(void);//在1ms滴答里调用
  37. void Key_Scan(void);//大循环或者滴答里边都行

  38. #endif
复制代码
  1. #include "Key_matrix.h"

  2. Key_Par Key_1,Key_2,Key_3,Key_4,Key_5,Key_6,Key_7,Key_8,Key_9,Key_10,Key_11,Key_12,Key_13,Key_14,Key_15,Key_16;
  3. volatile uint16_t Key_Phy_Num=0;  //Key_Phy_Num每一个bit代表一个按键的状态
  4. volatile uint8_t Key_Pulse_Num=0;//当某一个按键从按下到弹起的过程中(非长按)始终只有该按键被操作,则Key_Pulse_Num被修改为该键的序号
  5. volatile uint16_t Key_LP_Num=0;   //Key_LP_Num每一个bit代表一个按键的长按状态
  6. uint8_t KeyCom=0;//组合键是否出现

  7. static void Key_Num_Read(Key_Para* Key,uint16_t KPN,uint8_t Pulse,uint8_t Key_Hor)
  8. {
  9.         if(Key_Hor == 0)
  10.         {
  11.                 Key->KOC_EN=0;//按键按下立即清除(松开)计数
  12.                 if(Key->KeyCloseCount > KEY_PRESS_TIME)
  13.                 {
  14.                         /*消抖方法为检测到按键被(持续)按下超过20ms*/
  15.                         Key_Phy_Num|=KPN;//消抖完毕后记录被按下的按键的键值
  16.                         if(Key->KeyCloseCount > KEY_LONG_PRESS_TIME)
  17.                         {
  18.                                 /*检测到按键被(持续)按下超过3秒*/
  19.                                 Key->KEY_LONG_PRESS=KPL_ENABLE;
  20.                                 Key_LP_Num|=KPN;
  21.                                 Key->KCC_EN=0;
  22.                         }
  23.                         else
  24.                         {
  25.                                 /*时间不够启动计数*/
  26.                                 Key->KCC_EN=1;
  27.                         }
  28.                 }
  29.                 else
  30.                 {
  31.                         /*时间不够启动计数*/
  32.                         Key->KCC_EN=1;
  33.                 }
  34.         }
  35.         else
  36.         {
  37.                 Key->KCC_EN=0;//按键松开立即清除(按下)计数
  38.                 if(Key->KeyOpenCount > KEY_PRESS_TIME)
  39.                 {
  40.                         if((Key_Phy_Num==KPN)&&(KeyCom==0)&&(Key->KEY_LONG_PRESS!=KPL_ENABLE))
  41.                         {
  42.                                 //按键被按下过&&非长按&&不是在组合键周期,该按键释放时发出生命周期为直到被读取或者直到有新按键被按下的脉冲
  43.                                 Key_Pulse_Num=Pulse;
  44.                         }
  45.                          //清除该位
  46.                          Key_Phy_Num&=(~KPN);
  47.                          Key_LP_Num&=(~KPN);
  48.                         /*检测到(持续)松开20ms*/
  49.                         Key->KEY_LONG_PRESS=KPL_DISABLE;
  50.                         Key->KOC_EN=0;
  51.                 }
  52.                 else
  53.                 {
  54.                         Key->KOC_EN=1;
  55.                 }
  56.         }
  57. }
  58. /********************************************************/
  59. static void Key_Count(Key_Para *Key)
  60. {
  61.         if(Key->KOC_EN==0)
  62.         {
  63.                 Key->KeyOpenCount=0;
  64.         }
  65.         else if(Key->KeyOpenCount>=50000)
  66.         {
  67.                 Key->KeyOpenCount=50000;
  68.         }
  69.         else
  70.         {
  71.                 Key->KeyOpenCount++;
  72.         }

  73.         if(Key->KCC_EN==0)
  74.         {
  75.                 Key->KeyCloseCount=0;
  76.         }
  77.         else if(Key->KeyCloseCount>=50000)
  78.         {
  79.                 Key->KeyCloseCount=50000;
  80.         }
  81.         else
  82.         {
  83.                 Key->KeyCloseCount++;
  84.         }
  85. }
  86. /********************************************************/
  87. void Clear_Key_Pulse_Num(void)
  88. {
  89.         Key_Pulse_Num=0;
  90. }
  91. /********************************************************/
  92. void KeyCount_Run(void)
  93. {
  94.         Key_Count(&Key_1);
  95.         Key_Count(&Key_2);
  96.         Key_Count(&Key_3);
  97.         Key_Count(&Key_4);
  98.         Key_Count(&Key_5);
  99.         Key_Count(&Key_6);
  100.         Key_Count(&Key_7);
  101.         Key_Count(&Key_8);
  102.         Key_Count(&Key_9);
  103.         Key_Count(&Key_10);
  104.         Key_Count(&Key_11);
  105.         Key_Count(&Key_12);
  106.         Key_Count(&Key_13);
  107.         Key_Count(&Key_14);
  108.         Key_Count(&Key_15);
  109.         Key_Count(&Key_16);
  110. }
  111. /********************************************************/
  112. static void Recognition_KeyCombination(void)
  113. {
  114.         uint8_t i=0,j=0;
  115.         uint16_t Data=0;
  116.         
  117.         Data=Key_Phy_Num;
  118.         for(i=0;i<16;i++)
  119.         {
  120.                 if(Data&0x8000)
  121.                 {
  122.                         j++;
  123.                 }
  124.                 Data<<=1;
  125.         }
  126.         /*发现多个bit为1,那指定多个按键按下了*/
  127.         if(j>1)
  128.         {
  129.                 KeyCom=1;
  130.         }
  131.         /*一切归于平静,又是一个因果循环*/
  132.         if(Key_Phy_Num==0x0)
  133.         {
  134.                 KeyCom=0;
  135.         }
  136. }
  137. /********************************************************/
  138. void Key_Scan(void)
  139. {        
  140.   static uint8_t ScanCount=0;
  141.   
  142.   Recognition_KeyCombination();
  143.   switch(ScanCount)
  144.   {
  145.     case 0:
  146.     {
  147.       KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;KEY_LON4=1;
  148.       Key_Num_Read(&Key_1,(uint16_t)0x0001   ,1,KEY_HOR1);
  149.       Key_Num_Read(&Key_2,(uint16_t)0x0001<<1,2,KEY_HOR2);
  150.       Key_Num_Read(&Key_3,(uint16_t)0x0001<<2,3,KEY_HOR3);
  151.       Key_Num_Read(&Key_4,(uint16_t)0x0001<<3,4,KEY_HOR4);
  152.       KEY_LON1=1;KEY_LON2=0;KEY_LON3=1;KEY_LON4=1;
  153.       ScanCount++;
  154.     }break;
  155.     case 1:
  156.     {
  157.       KEY_LON1=1;KEY_LON2=0;KEY_LON3=1;KEY_LON4=1;
  158.       Key_Num_Read(&Key_5,(uint16_t)0x0001<<4,5,KEY_HOR1);
  159.       Key_Num_Read(&Key_6,(uint16_t)0x0001<<5,6,KEY_HOR2);
  160.       Key_Num_Read(&Key_7,(uint16_t)0x0001<<6,7,KEY_HOR3);
  161.       Key_Num_Read(&Key_8,(uint16_t)0x0001<<7,8,KEY_HOR4);
  162.       KEY_LON1=1;KEY_LON2=1;KEY_LON3=0;KEY_LON4=1;
  163.       ScanCount++;
  164.     }break;
  165.     case 2:
  166.     {
  167.       KEY_LON1=1;KEY_LON2=1;KEY_LON3=0;KEY_LON4=1;
  168.       Key_Num_Read(&Key_9 ,(uint16_t)0x0001<<8 , 9,KEY_HOR1);
  169.       Key_Num_Read(&Key_10,(uint16_t)0x0001<<9 ,10,KEY_HOR2);
  170.       Key_Num_Read(&Key_11,(uint16_t)0x0001<<10,11,KEY_HOR3);
  171.       Key_Num_Read(&Key_12,(uint16_t)0x0001<<11,12,KEY_HOR4);
  172.       KEY_LON1=1;KEY_LON2=1;KEY_LON3=1;KEY_LON4=0;
  173.       ScanCount++;
  174.     }break;
  175.     case 3:
  176.     {
  177.       KEY_LON1=1;KEY_LON2=1;KEY_LON3=1;KEY_LON4=0;
  178.       Key_Num_Read(&Key_13,(uint16_t)0x0001<<12,13,KEY_HOR1);
  179.       Key_Num_Read(&Key_14,(uint16_t)0x0001<<13,14,KEY_HOR2);
  180.       Key_Num_Read(&Key_15,(uint16_t)0x0001<<14,15,KEY_HOR3);
  181.       Key_Num_Read(&Key_16,(uint16_t)0x0001<<15,16,KEY_HOR4);
  182.       KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;KEY_LON4=1;
  183.       ScanCount=0;
  184.     }break;
  185.     default:
  186.     {
  187.       ScanCount=0;
  188.     }break;
  189.   }
  190. }
复制代码




评分

参与人数 1黑币 +80 收起 理由
admin + 80 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏17 分享淘帖 顶9 踩1
回复

使用道具 举报

沙发
ID:859897 发表于 2021-11-24 09:53 | 只看该作者
优秀啊
回复

使用道具 举报

板凳
ID:989232 发表于 2021-12-14 21:09 来自手机 | 只看该作者
牛啊,确实不错
回复

使用道具 举报

地板
ID:872412 发表于 2022-3-1 11:54 | 只看该作者
矩阵代码需要写这么多么,写过一次记得没几句
回复

使用道具 举报

5#
ID:214914 发表于 2022-3-20 20:11 | 只看该作者
讲的很好,通俗易懂
回复

使用道具 举报

6#
ID:1014835 发表于 2022-4-2 11:52 | 只看该作者
矩阵代码需要写这么多么,写过一次记得没几句
回复

使用道具 举报

7#
ID:1015352 发表于 2022-4-3 11:17 | 只看该作者
so nice
回复

使用道具 举报

8#
ID:1064915 发表于 2023-8-28 15:04 | 只看该作者
在51用,程序怎么改
回复

使用道具 举报

9#
ID:371734 发表于 2024-3-15 21:35 | 只看该作者
牛啊,确实不错
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表