版权声明:本文为博主原创文章,转载请附上原文出处链接。
今天介绍下STC8A8K64S4A12系列单片机4x4矩阵按键检测电路的工作原理、4x4矩阵按键检测的程序设计。
在讲解输入按键检测时,总结了按键检测常见的有GPIO高低电平检测和ADC电阻分压检测。分压式接法,使用的单片机引脚须具有ADC功能,根据检测口测得不同的电压值来识别是哪个按键被按下。如下图。
ADC电阻分压检测按键的方法适合单片机IO口资源非常紧张的场合,如一些电磁炉的按键使用的就是这种方法。高低电平式接法检测按键需要用到的单片机IO口较多,而高低电平式接法又可分为两种:独立式接法和行列式接法。
行列式接法是利用单片机的 GPIO口组成行与列,在行与列的每一个交点处连接按键。 故也称为矩阵式按键,该接线方法最大的优势是可以使用较少的GPIO口实现较多按键的检测。根据行和列的不同可以有很多种矩阵按键组合,比如2x3矩阵按键、2x4矩阵按键、3x3矩阵按键、4x4矩阵按键等等。
☆注:行的英文是Row,列的英文是Line。
上述矩阵按键原理图中没有加任何保护电阻,市场上很多矩阵按键检测模块,都是参考上图来的。这样的设计可以实现矩阵检测,但是并不是最规范的。
STC8A8K64S4A12开发板上设计了4x4矩阵按键检测电路,该检测电路在行检测和列检测连接单片机IO上串联了220Ω电阻,并且把行检测用IO口加了10K上拉电阻,如图。
★ 上图开发板板载矩阵按键电路的RN3和RN4都是串在单片机GPIO口引脚的220Ω电阻,作用为:
★ 上图开发板板载矩阵按键电路的RN2作用:保证行检测用单片机GPIO口在按键没有按下时为有效高电平。
★ 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上。
矩阵按键检测的工作原理:按键设置在行、列线交点上,行、列线分别连接到按键开关的两端。行线通过上拉电阻接到VCC电源上。无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平由与此行线相连的列线电平决定。
矩阵按键检测方法有多种,这里介绍下比较常用的行列扫描法。原理解析如下:
☆注:m代表矩阵按键检测电路的行数,n代表矩阵按键检测电路的列数。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | led | .c | 用户LED有关的用户自定义函数。 |
2 | key_4x4 | .c | 4x4矩阵按键有关的用户自定义函数。 |
3 | delay | .c | 包含用户自定义延时函数。 |
■ 需要引用的头文件
#include " led.h"
#include " delay.h"
#include " key_4x4.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | led.h,key_4x4.h和delay.h头文件在该路径,所以要包含。 |
2 | …\User | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在key_4x4.c文件中对4x4矩阵按键进行扫描,并返回键值。该KeyScan函数代码如下。
程序清单:4x4矩阵扫描函数
/*****************************************************************************
* 描 述 : 4x4矩阵扫描函数
* 入 参 : 无
* 返回值 : 哪个按键按下的对应值
*****************************************************************************/
uint8 KeyScan(void)
{
uint8 X_temp,Y_temp,temp;
X_temp=0xF0; //列值赋初值
Y_temp=0x0F; //行值赋初值
P1M1 &= 0x3F; P1M0 |= 0xC0; //设置P1.6~P1.7为强推挽输出
P2M1 &= 0x3F; P2M0 |= 0xC0; //设置P2.6~P2.7为强推挽输出
P0M1 &= 0xF0; P0M0 |= 0x0F; //设置P0.0~P0.3为强推挽输出
ROW1=1;ROW2=1;ROW3=1;ROW4=1; //行置高
COL1=0;COL2=0;COL3=0;COL4=0; //列置低
//所用到行IO口配置为输入,进行检测
delay_ms(15);
P2M1 &= 0x3F; P2M0 &= 0x3F; //设置P2.6~P2.7为准双向口
P0M1 &= 0xF6; P0M0 &= 0xF6; //设置P0.0,P0.3为准双向口
delay_ms(15);
if(ROW1 == 0) //检测行1电平是否为低电平
{
delay_ms(DELAY_TIME);
if(ROW1 == 0)
Y_temp &= 0x0E;
}
if(ROW2 == 0) //检测行2电平是否为低电平
{
delay_ms(DELAY_TIME);
if(ROW2 == 0)
Y_temp &= 0x0D;
}
if(ROW3 == 0) //检测行3电平是否为低电平
{
delay_ms(DELAY_TIME);
if(ROW3 == 0)
Y_temp &= 0x0B;
}
if(ROW4 == 0) //检测行4电平是否为低电平
{
delay_ms(DELAY_TIME);
if(ROW4 == 0)
Y_temp &= 0x07;
}
P1M1 &= 0x3F; P1M0 |= 0xC0; //设置P1.6~P1.7为强推挽输出
P2M1 &= 0x3F; P2M0 |= 0xC0; //设置P2.6~P2.7为强推挽输出
P0M1 &= 0xF0; P0M0 |= 0x0F; //设置P0.0~P0.3为强推挽输出
ROW1=0;ROW2=0;ROW3=0;ROW4=0; //行置低
COL1=1;COL2=1;COL3=1;COL4=1; //列置高
//所用到列IO口配置为输入,进行检测
delay_ms(15);
P1M1 &= 0x3F; P1M0 &= 0x3F; //设置P1.6~P1.7为准双向口
P0M1 &= 0xF9; P0M0 &= 0xF9; //设置P0.1,P0.2为准双向口
delay_ms(15);
if(COL1 == 0) //检测列1电平是否为低电平
{
delay_ms(DELAY_TIME);
if(COL1 == 0)
X_temp &= 0xE0;
}
if(COL2 == 0) //检测列2电平是否为低电平
{
delay_ms(DELAY_TIME);
if(COL2 == 0)
X_temp &= 0xD0;
}
if(COL3 == 0) //检测列3电平是否为低电平
{
delay_ms(DELAY_TIME);
if(COL3 == 0)
X_temp &= 0xB0;
}
if(COL4 == 0) //检测列4电平是否为低电平
{
delay_ms(DELAY_TIME);
if(COL4 == 0)
X_temp &= 0x70;
}
//将行值和列值合并,得到按键对应的编码值,该值与16个按键一一对应
temp = X_temp|Y_temp;
temp = ~temp;
//将按键检测的原始编码值解析对应按键值信息
switch (temp)
{
case 0x11:return 1; //1
case 0x21:return 2; //2
case 0x41:return 3; //3
case 0x81:return 4; //4
case 0x12:return 5; //5
case 0x22:return 6; //6
case 0x42:return 7; //7
case 0x82:return 8; //8
case 0x14:return 9; //9
case 0x24:return 10; //0
case 0x44:return 11; //a
case 0x84:return 12; //b
case 0x18:return 13; //c
case 0x28:return 14; //d
case 0x48:return 15; //e
case 0x88:return 16; //f
default: return 0;
}
}
然后,主函数中通过检测按键扫描的键值来控制用户指示灯闪烁不同的次数。具体代码如下。
代码清单:主函数
int main(void)
{
uint8 temp;
while(1)
{
temp=KeyScan(); //得到键值
if(temp)
{
switch(temp)
{
case 1: //按下按键S3
LED_FLI_D3(1); //用户指示灯D3闪烁1次
break;
case 2: //按下按键S4
LED_FLI_D3(2); //用户指示灯D3闪烁2次
break;
case 3: //按下按键S5
LED_FLI_D3(3); //用户指示灯D3闪烁3次
break;
case 4: //按下按键S6
LED_FLI_D3(4); //用户指示灯D3闪烁4次
break;
case 5: //按下按键S7
LED_FLI_D3(5); //用户指示灯D3闪烁5次
break;
case 6: //按下按键S8
LED_FLI_D3(6); //用户指示灯D3闪烁6次
break;
case 7: //按下按键S9
LED_FLI_D3(7); //用户指示灯D3闪烁7次
break;
case 8: //按下按键S10
LED_FLI_D3(8); //用户指示灯D3闪烁8次
break;
case 9: //按下按键S11
LED_FLI_D4(1); //用户指示灯D4闪烁1次
break;
case 10: //按下按键S12
LED_FLI_D4(2); //用户指示灯D4闪烁2次
break;
case 11: //按下按键S13
LED_FLI_D4(3); //用户指示灯D4闪烁3次
break;
case 12: //按下按键S14
LED_FLI_D4(4); //用户指示灯D4闪烁4次
break;
case 13: //按下按键S15
LED_FLI_D4(5); //用户指示灯D4闪烁5次
break;
case 14: //按下按键S16
LED_FLI_D4(6); //用户指示灯D4闪烁6次
break;
case 15: //按下按键S17
LED_FLI_D4(7); //用户指示灯D4闪烁7次
break;
case 16: //按下按键S18
LED_FLI_D4(8); //用户指示灯D4闪烁8次
break;
}
}
}
}
本矩阵按键实验需要用到P26和P27,所以J29的短路帽需去掉。矩阵按键检测用到8个GPIO,所以J30和J31端子均需短路帽短接,实验连接图如下。
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-18-1:4x4矩阵按键扫描实验 - 指示灯闪烁”部分。
首先,在key_4x4.c文件中对4x4矩阵按键进行扫描,并返回键值。该KeyScan函数代码请参考“实验2-18-1:4x4矩阵按键扫描实验 - 指示灯闪烁”部分。
然后,在主函数中对串口1进行初始化,并通过串口1发送按键扫描的键值。具体代码如下:
代码清单:主函数
int main(void)
{
uint16 temp;
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while(1)
{
temp=(uint16)KeyScan(); //得到键值
if(temp)
{
printf("\r\n 4x4矩阵按键键值: %d\r\n",temp); //串口打印上传的采集的原始值
temp = 0; //清零
}
}
}
本矩阵按键实验需要用到P26和P27,所以J29的短路帽需去掉。矩阵按键检测用到8个GPIO,所以J30和J31端子均需短路帽短接。另外,需使用USB线连接开发板USB口至电脑,以串口调试助手接收数据。
以上就是今天要讲的内容,希望对你有帮助!