版权声明:本文为博主原创文章,转载请附上原文出处链接。
前言
今天介绍下STC8A8K64S4A12系列单片机4x4矩阵按键检测电路的工作原理、4x4矩阵按键检测的程序设计。
一、硬件电路设计
1.矩阵按键检测介绍
在讲解输入按键检测时,总结了按键检测常见的有GPIO高低电平检测和ADC电阻分压检测。分压式接法,使用的单片机引脚须具有ADC功能,根据检测口测得不同的电压值来识别是哪个按键被按下。如下图。
图1:分压式接法原理
ADC电阻分压检测按键的方法适合单片机IO口资源非常紧张的场合,如一些电磁炉的按键使用的就是这种方法。高低电平式接法检测按键需要用到的单片机IO口较多,而高低电平式接法又可分为两种:独立式接法和行列式接法。
行列式接法是利用单片机的 GPIO口组成行与列,在行与列的每一个交点处连接按键。 故也称为矩阵式按键,该接线方法最大的优势是可以使用较少的GPIO口实现较多按键的检测。根据行和列的不同可以有很多种矩阵按键组合,比如2x3矩阵按键、2x4矩阵按键、3x3矩阵按键、4x4矩阵按键等等。
图2:矩阵按键原理图
☆注:行的英文是Row,列的英文是Line。
上述矩阵按键原理图中没有加任何保护电阻,市场上很多矩阵按键检测模块,都是参考上图来的。这样的设计可以实现矩阵检测,但是并不是最规范的。
图3:4x4矩阵模块
STC8A8K64S4A12开发板上设计了4x4矩阵按键检测电路,该检测电路在行检测和列检测连接单片机IO上串联了220Ω电阻,并且把行检测用IO口加了10K上拉电阻,如图。
图4:开发板4x4矩阵按键检测部分
★ 上图开发板板载矩阵按键电路的RN3和RN4都是串在单片机GPIO口引脚的220Ω电阻,作用为:
- 保护单片机GPIO口。若本该作为输入用的GPIO口不小心被配置成了输出(输出了高电平或低电平),按下按键外部会直接给GPIO口一个高电平或低电平。如果没有串接电阻,则若单片机GPIO口输出的电平和按键按下给的电平不一致,会损坏GPIO口。
- 便于检测。串接220Ω电阻可有效降低按键按下时产生的抖动峰值电压。
★ 上图开发板板载矩阵按键电路的RN2作用:保证行检测用单片机GPIO口在按键没有按下时为有效高电平。
★ 4x4矩阵按键检测电路占用的单片机的引脚如下表:
表1:4x4矩阵按键检测电路引脚分配
☆注: 1)独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。 2)在使用开发板4x4矩阵按键功能时,不仅J30和J31端子需使用8个短路帽短接,还要去掉J29的2个短路帽,以将P2.6和P2.7与用户指示灯断开连接。 3)开发板外部晶振电路没有焊接,也就意味着P1.6和P1.7可以使用。而如果外部晶振焊接了,则一定注意使用杜邦线连接其他没有占用的GPIO到 J31端子的L3和L4上。
2.矩阵按键检测原理介绍
矩阵按键检测的工作原理:按键设置在行、列线交点上,行、列线分别连接到按键开关的两端。行线通过上拉电阻接到VCC电源上。无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平由与此行线相连的列线电平决定。
矩阵按键检测方法有多种,这里介绍下比较常用的行列扫描法。原理解析如下:
- 将行线(R1Rm)配置为输出口并控制输出为高电平,将列线(L1Ln)配置为输出口并控制输出为低电平。再配置行线(R1Rm)为输入口、列线(L1Ln)仍是输出口,检测行线(R1Rm)的GPIO口电平变化。如果有按键按下,按键按下的对应行线被拉低,否则所有的行线(R1Rm)都为高电平。
- 将列线(L1Ln)配置为输出口并控制输出为高电平,将行线(R1Rm)配置为输出口并控制输出为低电平。再配置列线(L1Ln)为输入口、行线(R1Rm)仍是输出口,检测列线(L1Ln)的GPIO口电平变化。如果有按键按下,按键按下的对应列线被拉低,否则所有的列线(L1Ln)都为高电平。
- 在第一步和第二步判断有键按下后,延时10ms消除机械抖动,再次读取相应的行值或列值。
- 开始扫描按键位置,采用行扫描,得到行值;采用列扫描,得到列值,再分别把行值和列值保存起来。
- 将保存的行值和列值合并,得到按键对应的编码值,通过对编码值的操作可得到对应的按键信息,完成对矩阵按键的检测。
☆注:m代表矩阵按键检测电路的行数,n代表矩阵按键检测电路的列数。
二、软件设计
1.矩阵按键扫描实验 – 指示灯闪烁
1.1.工程需要用到的c文件
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
表2:实验需要用到的c文件
序号 | 文件名 | 后缀 | 功能描述 |
---|
1 | led | .c | 用户LED有关的用户自定义函数。 | 2 | key_4x4 | .c | 4x4矩阵按键有关的用户自定义函数。 | 3 | delay | .c | 包含用户自定义延时函数。 |
1.2.头文件引用和路径设置
■ 需要引用的头文件
#include " led.h"
#include " delay.h"
#include " key_4x4.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
表3:头文件包含路径
序号 | 路径 | 描述 |
---|
1 | …\ Source | led.h,key_4x4.h和delay.h头文件在该路径,所以要包含。 | 2 | …\User | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
图5:添加头文件包含路径
1.3.编写代码
首先,在key_4x4.c文件中对4x4矩阵按键进行扫描,并返回键值。该KeyScan函数代码如下。
程序清单:4x4矩阵扫描函数
uint8 KeyScan(void)
{
uint8 X_temp,Y_temp,temp;
X_temp=0xF0;
Y_temp=0x0F;
P1M1 &= 0x3F; P1M0 |= 0xC0;
P2M1 &= 0x3F; P2M0 |= 0xC0;
P0M1 &= 0xF0; P0M0 |= 0x0F;
ROW1=1;ROW2=1;ROW3=1;ROW4=1;
COL1=0;COL2=0;COL3=0;COL4=0;
delay_ms(15);
P2M1 &= 0x3F; P2M0 &= 0x3F;
P0M1 &= 0xF6; P0M0 &= 0xF6;
delay_ms(15);
if(ROW1 == 0)
{
delay_ms(DELAY_TIME);
if(ROW1 == 0)
Y_temp &= 0x0E;
}
if(ROW2 == 0)
{
delay_ms(DELAY_TIME);
if(ROW2 == 0)
Y_temp &= 0x0D;
}
if(ROW3 == 0)
{
delay_ms(DELAY_TIME);
if(ROW3 == 0)
Y_temp &= 0x0B;
}
if(ROW4 == 0)
{
delay_ms(DELAY_TIME);
if(ROW4 == 0)
Y_temp &= 0x07;
}
P1M1 &= 0x3F; P1M0 |= 0xC0;
P2M1 &= 0x3F; P2M0 |= 0xC0;
P0M1 &= 0xF0; P0M0 |= 0x0F;
ROW1=0;ROW2=0;ROW3=0;ROW4=0;
COL1=1;COL2=1;COL3=1;COL4=1;
delay_ms(15);
P1M1 &= 0x3F; P1M0 &= 0x3F;
P0M1 &= 0xF9; P0M0 &= 0xF9;
delay_ms(15);
if(COL1 == 0)
{
delay_ms(DELAY_TIME);
if(COL1 == 0)
X_temp &= 0xE0;
}
if(COL2 == 0)
{
delay_ms(DELAY_TIME);
if(COL2 == 0)
X_temp &= 0xD0;
}
if(COL3 == 0)
{
delay_ms(DELAY_TIME);
if(COL3 == 0)
X_temp &= 0xB0;
}
if(COL4 == 0)
{
delay_ms(DELAY_TIME);
if(COL4 == 0)
X_temp &= 0x70;
}
temp = X_temp|Y_temp;
temp = ~temp;
switch (temp)
{
case 0x11:return 1;
case 0x21:return 2;
case 0x41:return 3;
case 0x81:return 4;
case 0x12:return 5;
case 0x22:return 6;
case 0x42:return 7;
case 0x82:return 8;
case 0x14:return 9;
case 0x24:return 10;
case 0x44:return 11;
case 0x84:return 12;
case 0x18:return 13;
case 0x28:return 14;
case 0x48:return 15;
case 0x88:return 16;
default: return 0;
}
}
然后,主函数中通过检测按键扫描的键值来控制用户指示灯闪烁不同的次数。具体代码如下。
代码清单:主函数
int main(void)
{
uint8 temp;
while(1)
{
temp=KeyScan();
if(temp)
{
switch(temp)
{
case 1:
LED_FLI_D3(1);
break;
case 2:
LED_FLI_D3(2);
break;
case 3:
LED_FLI_D3(3);
break;
case 4:
LED_FLI_D3(4);
break;
case 5:
LED_FLI_D3(5);
break;
case 6:
LED_FLI_D3(6);
break;
case 7:
LED_FLI_D3(7);
break;
case 8:
LED_FLI_D3(8);
break;
case 9:
LED_FLI_D4(1);
break;
case 10:
LED_FLI_D4(2);
break;
case 11:
LED_FLI_D4(3);
break;
case 12:
LED_FLI_D4(4);
break;
case 13:
LED_FLI_D4(5);
break;
case 14:
LED_FLI_D4(6);
break;
case 15:
LED_FLI_D4(7);
break;
case 16:
LED_FLI_D4(8);
break;
}
}
}
}
1.4.硬件连接
本矩阵按键实验需要用到P26和P27,所以J29的短路帽需去掉。矩阵按键检测用到8个GPIO,所以J30和J31端子均需短路帽短接,实验连接图如下。
图6:4x4矩阵按键检测实验连接图
2.矩阵按键扫描实验 – 串口调试助手
2.1.工程需要用到的c文件
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-18-1:4x4矩阵按键扫描实验 - 指示灯闪烁”部分。
2.2.编写代码
首先,在key_4x4.c文件中对4x4矩阵按键进行扫描,并返回键值。该KeyScan函数代码请参考“实验2-18-1:4x4矩阵按键扫描实验 - 指示灯闪烁”部分。
然后,在主函数中对串口1进行初始化,并通过串口1发送按键扫描的键值。具体代码如下:
代码清单:主函数
int main(void)
{
uint16 temp;
P3M1 &= 0xFE; P3M0 &= 0xFE;
P3M1 &= 0xFD; P3M0 |= 0x02;
Uart1_Init();
EA = 1;
delay_ms(10);
while(1)
{
temp=(uint16)KeyScan();
if(temp)
{
printf("\r\n 4x4矩阵按键键值: %d\r\n",temp);
temp = 0;
}
}
}
2.3.硬件连接
本矩阵按键实验需要用到P26和P27,所以J29的短路帽需去掉。矩阵按键检测用到8个GPIO,所以J30和J31端子均需短路帽短接。另外,需使用USB线连接开发板USB口至电脑,以串口调试助手接收数据。
图7:4x4矩阵按键检测实验连接图
总结
以上就是今天要讲的内容,希望对你有帮助!
|