用逐行扫描法读取4X4键盘矩阵,不能扫描出第一列按键?


问题描述:
用逐行扫描法能够检测出按键2 3 4 ,6 7 8 ,10 11 12 ,14 15 16,不能检测出1 5 9 13,而且不能从1 5 9 13 中退出来,估计是进入了死循环,但是我找了好久,都没发现哪里有问题,求帮忙看下一下。
以下是C语言程序:
/* 逐行扫描法在数码管上显示相应按键编号 */
#include <reg51.h>
#define uchar unsigned char
#define DIG P0 //数码管对应接口
uchar num,temp; //两个全局变量,数码管显示数字用num,temp是扫描控制变量
uchar code DIG_NUM[16]={~0x3f,~0x06,~0x5b,~0x4f,~0x66,~0x6d,~0x7d,~0x07,~0x7f,~0x6f,
~0x77,~0x7c,~0x39,~0x5e,~0x79,~0x71};//0~F
void display();//按键扫描函数
void delay();//延时函数
void main()
{
DIG=0xff; //数码管全灭
while(1)
{
display();
}
}
void display() //逐行扫描法
{
P3=0xfe; //扫描第一行
temp=P3&0xf0;
if(temp!=0xf0)
{
delay();
temp=P3&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee: num=1;break;
case 0xde: num=2;break;
case 0xbe: num=3;break;
case 0x7e: num=4;break;
}
while(temp!=0xf0)
{
temp=P3&0xf0;
}
DIG = DIG_NUM[num-1];
}
}
P3=0xfd; //扫描第二行
temp=P3&0xf0;
if(temp!=0xf0)
{
delay();
temp=P3&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed: num=5;break;
case 0xdd: num=6;break;
case 0xbd: num=7;break;
case 0x7d: num=8;break;
}
while(temp!=0xf0)
{
temp=P3&0xf0;
}
DIG = DIG_NUM[num-1];
}
}
P3=0xfb; //扫描第三行
temp=P3&0xf0;
if(temp!=0xf0)
{
delay();
temp=P3&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb: num=9;break;
case 0xdb: num=10;break;
case 0xbb: num=11;break;
case 0x7b: num=12;break;
}
while(temp!=0xf0)
{
temp=P3&0xf0;
}
DIG = DIG_NUM[num-1];
}
}
P3=0xf7; //扫描第四行
temp=P3&0xf0;
if(temp!=0xf0)
{
delay();
temp=P3&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xe7: num=13;break;
case 0xd7: num=14;break;
case 0xb7: num=15;break;
case 0x77: num=16;break;
}
while(temp!=0xf0)
{
temp=P3&0xf0;
}
DIG = DIG_NUM[num-1];
}
}
}
void delay()
{
uchar a;
for(a=500;a>0;a--);
}
各位前辈,再次谢了!
建议找成熟稳定的程序抄。以下程序来自好书《51单片机轻松入门-基于STC15W4K系列》
#include "STC15W4K.H" // 注意宏定义后面没分号
#include "KEY.H"
#include "Dynamic_Display.H" // 动态显示相关
extern unsigned char DispBuf[6]; // 6字节的显示缓冲区,DispBuf[0]是最低位;
#define Hidden 16 // 高位消隐码在数据表中的位置,DispTab[15]=0xff
void delay10ms(void) // 22.1184MHz
{
unsigned char i,j,k;
for(i=2;i>0;i--) // 注意后面没分号
for(j=114;j>0;j--) // 注意后面没分号
for(k=241;k>0;k--); // 注意后面有分号
}
unsigned char KeyScan()
{
unsigned char key=0xff; // 无键按下时,key=0xff;
P2=0x0f; // 在IO口由输出方式变为输入方式时要延迟一个时钟周期。
P2=0x0f; // 采取写2次的方法延时。
if (P2!=0x0f)
{
delay10ms(); // 键盘消抖,延时10MS
if (P2!=0x0f) // 有键按下
{
P2=0xef; // 扫描第1列(逐列扫描开始)
P2=0xef;
switch (P2)
{
case 0xe7:key=0x00;break;
case 0xeb:key=0x04;break;
case 0xed:key=0x08;break;
case 0xee:key=0x0c;break;
}
//说明:本switch语句执行结束后会接着执行下面的语句,但由于P2口输出的扫描码
// 发生变化,程序不会进入后面的switch语句,也就不会多次修改key值。
P2=0xdf; // 扫描第2列(逐列扫描开始)
P2=0xdf;
switch (P2)
{
case 0xd7:key=0x01;break;
case 0xdb:key=0x05;break;
case 0xdd:key=0x09;break;
case 0xde:key=0x0d;break;
}
P2=0xbf; // 扫描第3列(逐列扫描开始)
P2=0xbf;
switch (P2)
{
case 0xb7:key=0x02;break;
case 0xbb:key=0x06;break;
case 0xbd:key=0x0a;break;
case 0xbe:key=0x0e;break;
}
P2=0x7f; // 扫描第4列(逐列扫描开始)
P2=0x7f;
switch (P2)
{
case 0x77:key=0x03;break;
case 0x7b:key=0x07;break;
case 0x7d:key=0x0b;break;
case 0x7e:key=0x0f;break;
}
P2=0x0f; // 在IO口由输出方式变为输入方式时要延迟一个时钟周期。
P2=0x0f; // 采取写2次的方法延时。
while(P2!=0x0f); // 等待按键释放
}
}
return(key);
}
void KeyHandle(unsigned char KeyValue)
{
DispBuf[0] = KeyValue; // 个位显示按键值
DispBuf[5] = Hidden; // 十万位消隐
DispBuf[4] = Hidden; // 万位消隐
DispBuf[3] = Hidden; // 千位消隐
DispBuf[2] = Hidden; // 百位消隐
DispBuf[1] = Hidden; // 十位消隐
}
谢谢,按书中列扫描的方式问题可以解决!但是按行扫描还是不行,依旧是第一列不能扫描,请问这是什么原因呢?
以下是两种扫描方式的C语言源程序:
/* 逐行扫描法(按列扫描)在数码管上显示相应按键编号 */
#include <reg51.h>
#define uchar unsigned char
#define DIG P0 //数码管对应接口
uchar code DIG_NUM[]={~0x3f,~0x06,~0x5b,~0x4f,~0x66,~0x6d,~0x7d,~0x07,~0x7f,~0x6f,
~0x77,~0x7c,~0x39,~0x5e,~0x79,~0x71,~0x00};//0~F 最后一个不显示
uchar display(void);//按键扫描函数
void delay();//延时函数
uchar num;
void main()
{
num=17; //无按键按下不显示
while(1)
{
DIG=DIG_NUM[display()-1];
}
}
uchar display(void) //逐行扫描法
{
P3=0x0f;
P3=0x0f;
if(P3!=0x0f)
{
delay();
if(P3!=0x0f)
{
P3=0xef;//扫描第一列
P3=0xef;
switch(P3)
{
case 0xee: num=1;break;
case 0xed: num=5;break;
case 0xeb: num=9;break;
case 0xe7: num=13;break;
}
P3=0xdf;//扫描第二列
P3=0xdf;
switch(P3)
{
case 0xde: num=2;break;
case 0xdd: num=6;break;
case 0xdb: num=10;break;
case 0xd7: num=14;break;
}
P3=0xbf;//扫描第三列
P3=0xbf;
switch(P3)
{
case 0xbe: num=3;break;
case 0xbd: num=7;break;
case 0xbb: num=11;break;
case 0xb7: num=15;break;
}
P3=0x7f;//扫描第四列
P3=0x7f;
switch(P3)
{
case 0x7e: num=4;break;
case 0x7d: num=8;break;
case 0x7b: num=12;break;
case 0x77: num=16;break;
}
P3=0x0f;
P3=0x0f;
while(P3!=0x0f);//检测按键是否松开
}
}
return num;
}
void delay()
{
uchar a;
for(a=100;a>0;a--);
}
/* 逐行扫描法(按行扫描)在数码管上显示相应按键编号 */
#include <reg51.h>
#define uchar unsigned char
#define DIG P0 //数码管对应接口
uchar code DIG_NUM[]={~0x3f,~0x06,~0x5b,~0x4f,~0x66,~0x6d,~0x7d,~0x07,~0x7f,~0x6f,
~0x77,~0x7c,~0x39,~0x5e,~0x79,~0x71,~0x00};//0~F 最后一个不显示
uchar display(void);//按键扫描函数
void delay();//延时函数
uchar num;
void main()
{
num=17; //无按键按下不显示
while(1)
{
DIG=DIG_NUM[display()-1];
}
}
uchar display(void) //逐行扫描法
{
P3=0xf0;
P3=0xf0;
if(P3!=0xf0)
{
delay();
if(P3!=0xf0)
{
P3=0xfe;//扫描第一行
P3=0xfe;
switch(P3)
{
case 0xee: num=1;break;
case 0xde: num=2;break;
case 0xbe: num=3;break;
case 0x7e: num=4;break;
}
P3=0xfd;//扫描第二行
P3=0xfd;
switch(P3)
{
case 0xed: num=5;break;
case 0xdd: num=6;break;
case 0xbd: num=7;break;
case 0x7d: num=8;break;
}
P3=0xfb;//扫描第三行
P3=0xfb;
switch(P3)
{
case 0xeb: num=9;break;
case 0xdb: num=10;break;
case 0xbb: num=11;break;
case 0x7b: num=12;break;
}
P3=0xf7;//扫描第四行
P3=0xf7;
switch(P3)
{
case 0xe7: num=13;break;
case 0xd7: num=14;break;
case 0xb7: num=15;break;
case 0x77: num=16;break;
}
P3=0xf0;
P3=0xf0;
while(P3!=0xf0);//检测按键是否松开
}
}
return num;
}
void delay()
{
uchar a;
for(a=100;a>0;a--);
}
我想问,根据你的电路连接,P3开始赋值0xfe。你觉得此时是选中第一行还是第四列?我怎么感觉你的应该是0x7f才能确保选择第一行
P3=0xfe是第一行(1 2 3 4),P3=0x7f第四列(4 8 12 16)啊,这个应该没什么问题,按行扫描不了我觉得是程序进入死循环无法退出,但是还找不出在哪里。
请注意,你的管脚是从下往上的,0Xfe说明,你的第一个1管脚处输出低电平,此时相当于选中最后一列,然后最后一列的哪个按键按下,例如S4按下,则8管脚点位等于1管脚等电位(低电平),于是你可以判断P3==0x7e,对应按键4。例如s8按下,则7管脚与1管脚等电位(低电平),于是你可以判断P3==0xbe,对应按键为8。以此类推
可以用temp=KEY2&0xf0,用&来判断第几行
受益匪浅受益匪浅受益匪浅
