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

发布时间:2023-01-05  

以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. Data=Key_Phy_Num;

  117. for(i=0;i<16;i++)

  118. {

  119. if(Data&0x8000)

  120. {

  121. j++;

  122. }

  123. Data<<=1;

  124. }

  125. /*发现多个bit为1,那指定多个按键按下了*/

  126. if(j>1)

  127. {

  128. KeyCom=1;

  129. }

  130. /*一切归于平静,又是一个因果循环*/

  131. if(Key_Phy_Num==0x0)

  132. {

  133. KeyCom=0;

  134. }

  135. }

  136. /********************************************************/

  137. void Key_Scan(void)

  138. {

  139. static uint8_t ScanCount=0;


  140. Recognition_KeyCombination();

  141. switch(ScanCount)

  142. {

  143. case 0:

  144. {

  145. KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;KEY_LON4=1;

  146. Key_Num_Read(&Key_1,(uint16_t)0x0001 ,1,KEY_HOR1);

  147. Key_Num_Read(&Key_2,(uint16_t)0x0001<<1,2,KEY_HOR2);

  148. Key_Num_Read(&Key_3,(uint16_t)0x0001<<2,3,KEY_HOR3);

  149. Key_Num_Read(&Key_4,(uint16_t)0x0001<<3,4,KEY_HOR4);

  150. KEY_LON1=1;KEY_LON2=0;KEY_LON3=1;KEY_LON4=1;

  151. ScanCount++;

  152. }break;

  153. case 1:

  154. {

  155. KEY_LON1=1;KEY_LON2=0;KEY_LON3=1;KEY_LON4=1;

  156. Key_Num_Read(&Key_5,(uint16_t)0x0001<<4,5,KEY_HOR1);

  157. Key_Num_Read(&Key_6,(uint16_t)0x0001<<5,6,KEY_HOR2);

  158. Key_Num_Read(&Key_7,(uint16_t)0x0001<<6,7,KEY_HOR3);

  159. Key_Num_Read(&Key_8,(uint16_t)0x0001<<7,8,KEY_HOR4);

  160. KEY_LON1=1;KEY_LON2=1;KEY_LON3=0;KEY_LON4=1;

  161. ScanCount++;

  162. }break;

  163. case 2:

  164. {

  165. KEY_LON1=1;KEY_LON2=1;KEY_LON3=0;KEY_LON4=1;

  166. Key_Num_Read(&Key_9 ,(uint16_t)0x0001<<8 , 9,KEY_HOR1);

  167. Key_Num_Read(&Key_10,(uint16_t)0x0001<<9 ,10,KEY_HOR2);

  168. Key_Num_Read(&Key_11,(uint16_t)0x0001<<10,11,KEY_HOR3);

  169. Key_Num_Read(&Key_12,(uint16_t)0x0001<<11,12,KEY_HOR4);

  170. KEY_LON1=1;KEY_LON2=1;KEY_LON3=1;KEY_LON4=0;

  171. ScanCount++;

  172. }break;

  173. case 3:

  174. {

  175. KEY_LON1=1;KEY_LON2=1;KEY_LON3=1;KEY_LON4=0;

  176. Key_Num_Read(&Key_13,(uint16_t)0x0001<<12,13,KEY_HOR1);

  177. Key_Num_Read(&Key_14,(uint16_t)0x0001<<13,14,KEY_HOR2);

  178. Key_Num_Read(&Key_15,(uint16_t)0x0001<<14,15,KEY_HOR3);

  179. Key_Num_Read(&Key_16,(uint16_t)0x0001<<15,16,KEY_HOR4);

  180. KEY_LON1=0;KEY_LON2=1;KEY_LON3=1;KEY_LON4=1;

  181. ScanCount=0;

  182. }break;

  183. default:

  184. {

  185. ScanCount=0;

  186. }break;

  187. }

  188. }


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    仅执行一次按键功能程序。 c.准确输出按键值(或键号),以满足跳转指令要求。 独立按键与矩阵键盘 (1)独立按键 单片机控制系统中,如果只需要几个功能键,此时,可采用独立式按键结构。 独立按键式直接用I/O口线构成的单个按键电路......
    3x3矩阵键盘扫描原理及扫描程序;3x3矩阵键盘简介 矩阵键盘,3×3矩阵键盘,数码管显示对应按键,动态扫描,中断实现。 其中上拉电阻属性需设置为“digital”。 3x3矩阵键盘......
    基于51单片机的简易电子琴设计;一.系统概述 系统使用的模块有AT89C51单片机+8位共阳数码管+矩阵键盘+小灯+按键+蜂鸣器。 本次设计的电子琴系统以AT89C51单片机为控制核心,使用......
    机为主控模块,运用 AT24C02存储芯片、ESP8266WiFi、TFT 彩屏、4×4 矩阵键盘、继电器和电磁锁等电路矩阵键盘作为输入,任意 6 位数字作为密码,用 C 语言......
    基于STC51单片机的计数器;设计要求:# 写出矩阵键盘的驱动 分别按下对应的键。数码管会将键盘对应的值累加并显示 设计概述:#   按照设计要求,所需要的单片机芯片为STC89C52,所用......
    NO.9 矩阵键盘和MSP432实例——控制MSP开发板板载LED;  上一节我们介绍了矩阵键盘的具体原理,这次我们来将实际操作。   首先,在MSP432中,对于按键的输入就是检测端口的高低电平来判断矩阵键盘......
    用了FT24C128A存储器,是一种电可擦除PROM,电压可允许低至1.8V,待机电流和工作电流分别为1μA和1mA,具有页写能力。IR01 红外遥控学习模块连接电路如下图4-2所示: 矩阵键盘电路......
    S3C6410(Real6410) Linux 6x6矩阵键盘驱动移植;Real6410默认的键盘驱动是gpio键盘驱动,现在要取消gpio键盘,实现矩阵键盘。6410的矩阵键盘......
    所示。 图1 自锁按键 图2 非自锁按键 非编码键盘通常又分为独立键盘和矩阵键盘两大类。所谓独立键盘是指按键在与单片机相连时,每一个按键都与一个单片机单片机的I/O口相连,如图3所示;而对......
    针对现有售货机存在的缺陷,设计了一款基于STM32的无人售货机系统。该系统采用STM32作为主控芯片,使用液晶屏显示各种商品库存与售价,用户按下对应按键选择购买指定商品,在矩阵键盘输入账号密码付款。若付......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>