用中断的方式,有键按下进中断扫描键盘。P1口先置为0x0f,为中断准备。中断后P1口置0xff(关键是这里,中断进去后没用,不知道怎么办),然后扫描键盘。
下面是程序:
#include<reg52.h>
#include<absacc.h>
#include <string.h>
#include <intrins.h>
#define COM8155 XBYTE[0xff20] //8155控制口地址
#define PA XBYTE[0xff21] //8155A口地址
#define PB XBYTE[0xff22] //8155B口地址
sbit LED=P3^0;
sbit LOUD=P3^5; //报警器脉冲
unsigned code seg[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //显示段位
unsigned dispbuf[8]={0}; //显示缓存
unsigned dispbit[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //数位扫描
unsigned code Pwd[8]={0};
unsigned int i=0; //第几次按键
unsigned char key,k=0,dispcount=0;
/****************************************************/
void led()
{
PA=seg[key];
PB=dispbit[k];
k++;
if (k==7)
k=0;
dispcount++;
}
/*******************************************************************
/*函数名:bi
/*功 能:喇叭发声
/*参 数:t
******************************************************************/
void bi(unsigned long t)
{
unsigned long c;
unsigned int n;
for(c=0;c<t;c++)
{
for(n=0;n<50;n++); //延时
LOUD=~LOUD; //取反输出到喇叭的信号
}
}
/******************************************************************
/*函数名:lamp
******************************************************************/
void lamp(unsigned long l)
{
unsigned char c;
for(c=0;c<l;c++); //延时
LED=1;
}
/*****************************************************************
/*函数名:KeyInt
/*功 能:4*4键盘扫描
*****************************************************************/
void KeyInt() interrupt 0 using 0
{
int t; //延时循环变量
char keycode; //最终键盘码
char scancode; //扫描码
char temp;
char flag=0xff; //看键是否释放
t=5000;
while(t--); //延时10ms
if (INT0==1)
return;
P1=0xff;
EX0=0; //关闭键盘中断
scancode=0xef; //键盘扫描码,采用逐列扫描的方式
while(scancode!=0xff)
{
P1=scancode; //输入扫描码
temp=scancode;
keycode=P1; //读出数据,看是否此列上有键按下
if ((keycode&0x0f)!=0x0f)
break; //扫描到有键按下,退出
scancode=(keycode<<1)|0x0f; //否则更新扫描码继续扫描
}
/*keycode=temp|keycode; */
keycode=~keycode;
if (keycode==0x11) //S11,数字“1”
{
dispbuf[dispcount]=seg[1];
}
if (keycode==0x21) //S21,数字“4”
{
dispbuf[dispcount]=seg[4];
}
if (keycode==0x41) //S31,数字“7”
{
dispbuf[dispcount]=seg[7];
}
if (keycode==0x81) //S41,数字“0”
{
dispbuf[dispcount]=seg[0];
}
if (keycode==0x12) //S12,数字“2”
{
dispbuf[dispcount]=seg[2];
}
if (keycode==0x22) //S22,数字“5”
{
dispbuf[dispcount]=seg[5];
}
if (keycode==0x42) //S32,数字“8”
{
dispbuf[dispcount]=seg[8];
}
/*if (keycode==0x82) //S42,CHG,更换密码
{
key=0;
}*/
if (keycode==0x14) //S13,数字“3”
{
dispbuf[dispcount]=seg[3];
}
if (keycode==0x24) //S23,数字“6”
{
dispbuf[dispcount]=seg[6];
}
if (keycode==0x44) //S33,数字“9”
{
dispbuf[dispcount]=seg[9];
}
/*if (keycode==0x84) //S43,ENTR,确认
{
if(strcmp(dispbuf,Pwd)==0)
{
lamp(100);
}
else bi(100);
} */
P1=0x0f;
while(1) //看键是否释放
{
if(INT0==1)
{
flag=~flag;
if (flag==0)
break;
}
t=1000;
while(t--);
}
EX0=1; //打开键盘中断
dispcount++;
return;
}
void main()
{
void lamp();
void bi();
LED=0;
LOUD=0;
COM8155=0x03; //设置8155工作方式,PA,PB均为输出口
EA=1; //开中断
EX0=1; //开外部中断0
IT0=0;
P1=0x0f;
PA=0;
while(1)
{
PA=seg[dispbuf[k]];
PB=dispbit[k];
k++;
if (k==dispcount)
k=0;
}
EA=0;
}

Snap1.jpg

最新回复
leolle (2008-7-19 09:22:17)
panhearii (2008-7-19 09:33:16)
第一 扔掉8155 8155是用来给各种书充页数的 哪个产品里要是用8155 那准得让人笑到死
第二 if串改用 else if 或switch 运行时间会少很多 很多
第三 大家愿意帮助新手 但是串口终端的矩阵键盘已经有异常多的例子了 要是翻页都懒得翻
那不如再多懒一点 把钱汇过来 我三包好了
leolle (2008-7-19 09:46:24)
我用的是中断的方式,这种扫描方法在网上并不多见,其中的问题当然也没提。
我遇到的问题是中断进去后P1口不能对其赋值。
其它方法很多当然也很简单,但是遇到问题就逃避岂不是有违精神?
leolle (2008-7-19 10:12:33)
budhy (2008-7-19 10:28:07)
QUOTE:
我很赞成楼3#的意见,8155; 8255 是老古董,别学它也罢。需要用心学的新芯片多得很。。。
- 我说你的中断的方式是行不通。中断程序不是那么写的。(看下面的程序)
- 写错了,当然P1口不能对其赋值。
- 是你把简单的方法复杂化。
=====================================================================要是把你这一段程序改成这样,程序变得简单,更好读,运行时间也更快
CODE:
/*keycode=temp|keycode; */在写中断服务程序时,要特别注意的一点是:《越简单越好》,运行时间越短越好。在你的程序里,这一段是致命的:keycode=!keycode;
switch (keycode){
case 0x11 : dispbuf[dispcount]=seg[1]; break;
case 0x21 : dispbuf[dispcount]=seg[4]; break;
case 0x41 : dispbuf[dispcount]=seg[7]; break;
case 0x81 : dispbuf[dispcount]=seg[0]; break;
case 0x12 : dispbuf[dispcount]=seg[2]; break;
case 0x22 : dispbuf[dispcount]=seg[5]; break;
case 0x42 : dispbuf[dispcount]=seg[8]; break;
case 0x14 : dispbuf[dispcount]=seg[3]; break;
case 0x24 : dispbuf[dispcount]=seg[6]; break;
case 0x44 : dispbuf[dispcount]=seg[9]; break;
}
CODE:
P1=0x0f;while(1) //看键是否释放
{
if(INT0==1)
{
flag=~flag;
if (flag==0)
break;
}
t=1000;
while(t--);
}
panhearii (2008-7-19 14:43:33)
有个不错的例子
[ 本帖最后由 panhearii 于 2008-7-19 15:16 编辑 ]
leolle (2008-7-19 15:08:31)
panhearii (2008-7-19 15:08:50)
本论坛置顶贴里的一个例子
这个键盘扫描做得很漂亮 打个90分一点也不过
(100分的程序 和100分作文一样。。。不敢想象)
唯一的缺点:不是我写的。。。。。。。。。。。。
//*************************************************************************************************
//*************************************************************************************************
//**<程序名>:键盘扫描子程序 **
//**<功能>:当有外部中断0时调用此函数,使用列扫描的方式获取键码,键码由2位数字组成。高位为行号 **
//** 低位为列号。 **
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
//* *
//* ******************************头文件及宏定义************************** *
//* *
//*************************************************************************************************
#include <at89x51.h>
#define SCANPORT P1 //4×4键盘扫描端口,低4位是行线,高4位是列线。
//采用逐列扫描的方法,无按键时,低4位输出1,高4位输出0。
//当有按键时,高4位输出扫描电位,低4位输入扫描结果。
//*************************************************************************************************
//* *
//* ********************************全局变量****************************** *
//* *
//*************************************************************************************************
unsigned char uca_LineScan[4]={0xEF,0xDF,0xBF,0x7F}; //列线扫描电压,分为第1,2,3,4根列线
//为低电平,其他为高电平。
//*************************************************************************************************
//* *
//* ********************************函数实现****************************** *
//* *
//*************************************************************************************************
unsigned char ucKeyScan()
{
unsigned char ucTemp=0; //扫描状态暂存。
unsigned char ucRow=0,ucLine=0; //行号,列号。
EX0=0; //在键扫描处理时,关闭外部中断0,防抖动。
for(ucLine=0;ucLine<4;ucLine++) //列扫描
{
SCANPORT=uca_LineScan[ucLine]; //输出扫描电位。
ucTemp=SCANPORT&0x0F; //输入扫描电位,并屏蔽高4位。
if(ucTemp!=0x0F)
{ //判断该列是否有按键按下。
switch(ucTemp)
{
case 0x0E: ucRow=10;break; //如果有,则判断行号。
case 0x0D: ucRow=20;break;
case 0x0B: ucRow=30;break;
case 0x07: ucRow=40;break;
default: ucRow=50;break;
}
break;
}
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<恢复键扫描处理前初始状态>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
SCANPORT=0x0F; //恢复P2口
EX0=1; //恢复按键中断。
return ucRow+ucLine+1; //返回按键编码。格式为2位数,高位为行号,低位为列号。
}
panhearii (2008-7-19 15:11:02)
这个子程序呢 有个非常大的缺点:为什么不是我写的。。。
void vINT0(void) interrupt 0
{
if(b_KeyShock==0)
{
if(b_Introduce==0) //此判断是为了屏蔽从介绍返回字符输入时的键盘扫描。
{
b_KeyScan=1; //开启键盘扫描标志。
b_KeyShock=1; //设置防抖动标志。
}
}
else b_KeyShock=0; //如果有抖动则不执行键扫描,恢复防抖动标志。
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<设置防抖动清除标志位 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if(b_KeyShock==1)
b_KillShock=1; //如果防抖动标志位开启则开启防抖动标志清除位,
//300ms后清除防抖动标志。
b_Introduce=0; //用于关闭介绍,在显示介绍时如果有案件按下,产生的中断会
}
budhy (2008-7-19 15:16:11)
leolle (2008-7-19 15:22:11)
总工程师:
为什么我判断键是否释放的部分是关键问题所在??
[ 本帖最后由 leolle 于 2008-7-19 15:23 编辑 ]
leolle (2008-7-19 15:25:31)
QUOTE:
P1口不能对其赋值的问题是什么意思?是我程序写错了还是别的原因??
panhearii (2008-7-19 15:28:25)
QUOTE:
多找找 不过人家的作品不只是4*4键盘 好像是4*4键盘+LCD什么的 我是从我硬盘上翻出来的 具体哪个不记得了panhearii (2008-7-19 15:48:05)
只在switch语句里写BREAK
只在跳转到错误(故障)处理程序时用GOTO
如果要延迟 做一个函数void vDelay(unsigned int ucTemp)
这些都是好习惯
leolle (2008-7-20 19:43:09)
tssing (2008-7-20 20:05:10)