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

发布者:MoonlightStar最新更新时间: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. }


关键字:单片机  矩阵键盘  扫描驱动 引用地址:单片机矩阵键盘扫描驱动程序与电路分析

上一篇:8051单片机快速入门--我的第一盏灯
下一篇:at89c51 8个LED 如何循环亮灭?

推荐阅读最新更新时间:2024-11-06 01:33

L297A+L298N步进电机驱动板电路原理图PCB与单片机控制源程序
Altium Designer画的基于L297A+L298N芯片步进电机驱动模块的电路原理图和PCB图如下:(51hei附件中可下载工程文件) 驱动模块的实物图: 接上步进电机后的图片: L297是步进电机专用控制器,它能产生4相控制信号, 可用于计算机控制的两相双极和四相单相步进电机,能够用单四拍、双四拍、四相八拍方式 控制步进电机。芯片内的PWM 斩波器电路可开关模式下调节步进电机绕组中的电流。 L298N 是一种双H桥电机驱动芯片,其中每个H桥可以提供2A的电流,功率部分的供电电压范围是2.5-48v,逻辑部分5v供电,接受5vTTL电平。一般情况下,功率部分的电压应大于6V否则芯片可能不能正常工作。 步进电机的
[单片机]
L297A+L298N步进电机<font color='red'>驱动</font>板电路原理图PCB与<font color='red'>单片机</font>控制源程序
基于单片机的计算器设计(程序)
#include reg51.h #define DIG P0 #define KEY P1sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; unsigned long int count=0, sum=1; int a ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00}; unsigned char KeyDate; unsigned char symbol; void delay(); void KeyDown(); void DigDisplay(); void conversion(); void DigDisp
[单片机]
51单片机扩展中断的方法
一、采用硬件请求和软件查询的方法: 这种方法是:把各个中断源通过硬件“或非(高有效,如CD4002)”(与,低有效)门引入到单片机外部中断源输入端(INT0或INT1),同时再把外部中断源送到单片机的某个输入输出端口,这样当外部中断时,通过“或非”(与)门引起单片机中断,在中断服务程序中再通过软件查询,进而转相应的中断服务程序。显然,这种方法的中断优先级取决于软件查询的次序。其硬件连接和软件编程如下: Void zhongduan (void) interrupt 0 using 3 //中断函数 { EX0=0;//关中断 If(P0_0=1) { *****}//中断查询 If(P0_1=1) { ****
[单片机]
51<font color='red'>单片机</font>扩展中断的方法
6502单片机编程--伪指令2
** 宏指令 ** 令我感到极度兴奋的是 6502_Macroassembler &Simulator 有强大的宏功能 . 这无疑使程序 的编写又简单方便许多了 . 我们先说说宏的格式 宏名 :.MACRO 宏虚参数 1, 宏虚参数 2, 宏虚参数 3..... 宏体 .ENDM 这里 .MACRO 和 .ENDM 必须成对出现 宏调用的格式 : 宏名宏实参数 1, 宏实参数 2, 宏参数 3 具有宏调用的源程序被编译时 , 每个宏调用将被编译程序展开 , 宏展开实际上是宏定义时设计的宏体去代替相应的宏指令名 , 并且用实际参数去取代虚参数 , 以形成符合功能并可以被执行的实际代
[单片机]
用PROTEUS仿真单片机与LCD接口项目
设计要求: 在上一个项目中,介绍了如何使用8051微控制器进行串行通信,这是一个非常基础的项目,不需要太多的硬件。接下来将实现LCD与8051微控制器的接口。经常将LCD作为硬件工程项目的调试工具。使用LCD来显示不同的值。例如, ATM机,手机也都配有LCD。该项目将使用通常称为1602 的LCD。因此,我们使用8051单片机同LCD的连接。 还是放一下图和代码吧 #include reg51.h #define uchar unsigned char #define uint unsigned int sbit RS = P1^0; sbit RW = P1^2; sbit EN = P1^1; voi
[单片机]
用PROTEUS仿真<font color='red'>单片机</font>与LCD接口项目
Atmel推出基于ARM技术的32位微控制器
在本届“国际集成电路研讨会暨展览会/嵌入式系统研讨会”(IIC/ESC China 2006)上,Atmel公司重点介绍了AT91SAM9261微控制器。该产品是Atmel基于ARM技术的32位微控制器系列中的最新型号。此外,Atmel公司还向与会者展示同系列的下一代型号——AT91SAM9260。 AT91SAM9261 AT91SAM9261是一款以ARM926EJ-S处理器为核心的超低功耗“确定过程式”(deterministic)微控制器。该型号专为功耗低、数据吞吐量大的无线手持式应用(例如无线销售终端(point-of-sale, PoS)设备)市场而开发的,其待机电流仅为2.5uA;工作频率500Hz时,电流消耗则
[新品]
单片机驱动4X4矩阵式键盘输入程序
用AT89S51单片机的并行口P1接4×4矩阵键盘,以P1.0-P1.3作输入线,以P1.4-P1.7作输出线;在数码管上显示每个按键的“0-F”序号。 实现键盘输入的识别。 我将给大家提供c和汇编两个版本的4X4矩阵式键盘输入程序。如汇编语言源程序: KEYBUF EQU 30H ORG 00H START: MOV KEYBUF,#2 WAIT: MOV P3,#0FFH CLR P3.4 MOV A,P3 ANL A,#0FH XRL A,#0FH JZ NOKEY1 LCALL DELY10MS MOV
[单片机]
单片机电源可靠性设计
影响单片机系统运行稳定性的因素可大体分为外因和内因两部分: 1、外因 射频干扰,它是以空间电磁场的形式传递 在机器内部的导体(引线或零件引脚)感生出相应的干扰,可通过电磁屏蔽和合理的布线/器件布局衰减该类干扰; 电源线或电源内部产生的干扰,它是通过电源线或电源内的部件耦合或直接传导,可通过电源滤波、隔离等措施来衰减该类干扰 。 2、内因 振荡源的稳定性,主要由起振时间 频率稳定度和占空比稳定度决定 起振时间可由电路参数整定 稳定度受振荡器类型 温度和电压等参数影响复位电路的可靠性。 二、复位电路的可靠性设计 1、基本复位电路 复位电路的基本功能是:系统上电时提供复位信号,直至系统电源稳定后,撤销复位信号。为
[单片机]
<font color='red'>单片机</font>电源可靠性设计
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved