本帖最后由 tiankai001 于 2019-1-7 08:19 编辑
此内容由EEWORLD论坛网友tiankai001原创,如需转载或用于商业用途需征得作者同意并注明出处
一、任务要求将4X4矩阵式键盘编号,如果其中一个按键闭合,则在LED数码管上显示相应的按键编号。
二、分析说明4X4矩阵键盘只需要占用一个8位的I/O端口,硬件电路的设计较为简单,重点在于如何在程序中判断矩阵键盘的按键位置。
三、硬件电路硬件电路如下图所示。
选取MSP430F247单片机的P4端口连接数码管,用以显示按键编号,P5端口8个引脚分别和矩阵式键盘的行线和列线连接,硬件电路如下图所示。
选取MSP430F247单片机的P4端口连接数码管,用以显示按键编号,P5端口8个引脚分别和矩阵式键盘的行线和列线连接,硬件电路如下图所示。
电路图中,列线P5.4~P5.7通过上拉电阻连接电源,处于输入状态;行线P5.0~P5.3为输出状态。键盘上没有按键闭合时,所有列线P5.4~P5.7的输入全部为高电平。当键盘上某个按键闭合时,则对应的行线和列线短接。
在检测是否有键闭合时,先使4条行线全部输出低电平,然后读取4条列线的状态,如果全部为高电平则表示没有任何按键闭合,如果有任一键闭合,由于列线上是上拉电阻,则行线上读到的将是一个非全“1”的值。
四、程序设计确定矩阵式键盘上那个按键闭合通常采用行扫描法,又称为逐行(或列)扫描查询法,其软件主要基于扫描方式完成。
关于键盘扫描查询法的程序大致可以分为以下几个步骤:
1、检测当前是否有键闭合
首先看输入的列线,假设4条行线都输出低电平,4条列线上都是上拉电阻,在没有任何键闭合时,4条列线输入都为1,但当与某一条行线相连的4个键中的任何一个闭合时,这条列线将输入低电平,即当某条列线输入低电平时,必定是连接在这条裂线上的某个键闭合了。
2、去除按键抖动当检测到有按键闭合后,延长一段时间再做下一步的检测判断。
3、若有键闭合,检测出是哪一个键闭合
逐行扫描方式:在4条行线上分别输出0信号。第一次,在第一条行线上输出低电平,其它行线上输出高电平,第二次,在第二条行线上输出低电平,其它行线上输出高电平,,第三次,在第三条行线上输出低电平,其它行线上输出高电平,,第四次,在第四条行线上输出低电平,其它行线上输出高电平,当某一行线上输出低电平时,此时如果此行上有键被按下,那么相应键的列线上就会读到0,于是可以唯一的确定是那个那键被按下。
- //main.c
- #include "msp430f247.h"
- #include "stdlib.h"
- #include "string.h"
- /*****************************************软件延时,主频1M*******************/
- #define CPU_F1 ((double)1000000)
- #define delay_us1M(x) __delay_cycles((long)(CPU_F1*(double)x/1000000.0))
- #define delay_ms1M(x) __delay_cycles((long)(CPU_F1*(double)x/1000.0))
- /****************************************************************************/
- //共阳极数码管段码表
- unsigned char const Led_Tab1[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
- 0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
- //共阴极数码管段码表
- unsigned char const Led_Tab2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,
- 0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
- static unsigned char key;
- unsigned char KeyScan(void);//
- unsigned char GetKeyValue(unsigned char keycode);//
- /************************************************
- 函数名称:主函数
- 函数功能:矩阵键盘编号显示
- 入口参数:无
- 出口参数:无
- 描述:
- 作者:老马识途单片机
- 日期:2018年1月7日
- ************************************************/
- main()
- {
- unsigned char key;
- WDTCTL = WDTPW + WDTHOLD;//关看门狗
- P4DIR=0xff;//端口初始化
- P4OUT=0x00;//端口初始化
- //行线P5.0~P5.3为输出状态。列线P5.4~P5.7为输入
- P5DIR=0x0f;//
- while(1)
- {
- key=GetKeyValue(KeyScan());//显示扫描
- if(key != 255)
- {
- P4OUT=Led_Tab2[key];
- }
- }
- }
- unsigned char KeyScan(void)
- {
- unsigned char ScCode,ReCode;
- P5OUT=0x00;
- if((P5IN & 0xf0) != 0xf0)//判断是否有键闭合
- {
- delay_ms1M(40);
- if((P5IN & 0xf0) != 0xf0)//延时后再次判断是否有按键闭合,
- {
- ScCode=0xfe;//逐行扫描初值,先扫描第一行
- while((ScCode&0x0f) != 0x0f)//行扫描完成
- {
- P5OUT=ScCode;//输出行扫描码
- if((P5IN & 0xf0) != 0xf0)//当前行有键闭合
- {
- ReCode=(P5IN & 0xf0) | 0x0f;//读取高4位列值,低4位置1
- key=(ScCode & ReCode);//行和列组合得到键盘编码
- return key;
- }
- else//所扫描行没有按键闭合,则扫描下一行
- {
- ScCode=(ScCode<<1)|0x01;//行扫描码左移一位
- }
- }
- }
- return 0xff;//无键按下
- }
- return 0xff;//无键按下
- }
- unsigned char GetKeyValue(unsigned char keycode)
- {
- unsigned char keyval;
- switch(keycode)
- {
- case 0x77:
- keyval=0;
- break;
- case 0x7b:
- keyval=1;
- break;
- case 0x7d:
- keyval=2;
- break;
- case 0x7e:
- keyval=3;
- break;
- case 0xb7:
- keyval=4;
- break;
- case 0xbb:
- keyval=5;
- break;
- case 0xbd:
- keyval=6;
- break;
- case 0xbe:
- keyval=7;
- break;
- case 0xd7:
- keyval=8;
- break;
- case 0xdb:
- keyval=9;
- break;
- case 0xdd:
- keyval=10;
- break;
- case 0xde:
- keyval=11;
- break;
- case 0xe7:
- keyval=12;
- break;
- case 0xeb:
- keyval=13;
- break;
- case 0xed:
- keyval=14;
- break;
- case 0xee:
- keyval=15;
- break;
- default:
- keyval=255;
- }
- return keyval;
- }
复制代码
五、程序说明主程序通过调用键盘扫描程序获取键值,并通过数码管显示出键盘编号。键盘扫描首先通过读取列线输入,如果不全是1,则延迟一定时间后再次判断列线是否全为1,如果依旧不全为1,则可以确定有稳定的按键动作,通过逐行扫描的方式得到按键的位置。
从程序上看还存在两个问题,一是按键扫描中延迟去抖需要一定的时间,浪费了单片机的运算资源;二是在扫描得到按键后如果按键闭合不动,主程序会得到多个相同的键值,即重复按键,这种情况可以通过判断按键弹起的动作来解决
六、仿真结果与分析在proteus中画好电路图后,双击单片机,将可执行文件装载到单片机内,点击运行,观察数码管的显示情况。如下图所示。