单片机
返回首页

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

2023-01-05 来源:zhihu

以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. }


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 红外线探测报警器

  • 用NE555制作定时器

  • 带有短路保护系统的5V直流稳压电源电路图

  • 如何构建一个触摸传感器电路

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章