设计中你是否遇到过这样的问题:你的产品上要求有几十个按键,处理器IO口很少,PCB的面积又有严格限制,或者你要严格控制成本,无法使用象7219那样的扩展芯片,怎么解决这个问题?
下面有个方法,大家都见过遥控器吧,上面不但有几十个按键,而且功能还挺多什么去抖动,同时按键处理都有了,最最重要的是控制芯片体积小,价格便宜(也就1,2块钱),外围器件少。。不过具体实现起来有点小麻烦,这类芯片的信号一般是PPM输出的,通常为了有更远的遥控距离,按键编码调制在一个38k左右的载波上。所以我们不得不再为这个方案多花上1块钱,加一个有烂运放做的低通滤波器,将载波滤除后在接到单片机的IO脚上,由于两个频率相差较多,这个滤波器并不难做。我使用LM324做的。其中有两级低通,一个比较器。
当你的示波器上出现一串可爱的几百赫兹的方波时,你的硬件就成功啦。既然只用一条IO就扩展了几十个按键,软件上自然会多费些事,此类芯片发码都是有引导头,同步部分,用户码,键码等部分组成,有三十多个位,具体可参照sc6121资料。下面时一个完整的接收程序,针对的芯片是sc6121,处理器89c51
///////////////////////////////////////////////////////////////////////////////
/*定时器1中断服务程序*/
/*
每100us中断一次,定时检测HangSignal线上的电平状态,根据6121的砝码格式译出用户码键码.\\
ib_KeyCode[0] 用户码低位
,ib_KeyCode[1]用户码高位
,ib_KeyCode[2]键码
,ib_KeyCode[3]键码的反码
*/
/////////////////////////////////////////////////////////////////////////////////////
void HandIn() interrupt 3 using 3 {
unsigned char tempbit=0;
bit Hbit;
Hbit=HandSignal;
/*采样信号线*/
if (NewKey==FALSE){
/*如果上一次按键事件已经北处理*/
switch (ib_HandState){
/*根据接收状态散转*/
case LEAD:
/*引导头接收情况*/
if (Hbit){
/*如果信号线是高电平*/
if
((ib_LowTime>MIN_LeadTime)&&(ib_LowTime<MAX_LeadTime)) /*判断低电平时间是否合乎要求*/
ib_HandState=START;
/*正确进入同步头接收状态*/
else ib_HandState=RESTART;
/*否则复位接收程序*/
ib_LowTime=0;
/*清除低电平时间计数*/
}
else{
/*如果信号是低电平*/
ib_LowTime++;
/*增加低电平时间计数器*/
if (ib_LowTime>MAX_LeadTime)
/*判断低电平时间是否超时*/
ib_HandState=RESTART;
/*是的话复位接收程序*/
}
break;
case START:
/*同步头接收情况*/
if(Hbit){
/*如果信号线是高电平*/
ib_HighTime++;
/*高电平时间计数器加一*/
if (ib_HighTime>MAX_SynTime)
/*如果高电平时间超长,复位接收程序*/
ib_HandState=RESTART;
}
else{
/*如果信号线是低电平*/
if
((ib_HighTime>MIN_SynTime)&&(ib_HighTime<MAX_SynTime)) /*如果高电平时间合乎要求,将状态设定为数据为高电平检测*/
ib_HandState=WAIT_HIGH;
else ib_HandState=RESTART;
/*否则复位接收程序*/
ib_HighTime=0;
}
break;
case WAIT_HIGH: /*等待数据位的上升沿的情况*/
if
(Hbit){
/*如果检测到一个上升沿*/
if
((ib_LowTime>MIN_L_Time)&&(ib_LowTime<MAX_L_Time))
{ /*如果低电平时间合乎要求,将状态设定为等待数据下降沿*/
ib_HandState=WAIT_LOW;
}
else ib_HandState=RESTART;
/*否则复位接收程序*/
ib_LowTime=0;
}
else{
/*如果仍为低电平*/
ib_LowTime++;
/*低电平时间计数器加一*/
if (ib_LowTime>MAX_L_Time)
/*如果低电平时间超长,则复位寄售程序*/
ib_HandState=RESTART;
}
break;
case WAIT_LOW:
/*等待数据下降沿的情况*/
if
(Hbit){
/*如果仍是高电平*/
ib_HighTime++;
/*高电平数据计数器加一*/
if (ib_HighTime>MAX_H_Time)
/*如果高电平实际超长,则复位接收程序*/
ib_HandState=RESTART;
}
else{
/*如果收到一个下降沿*/
ib_KeyCode[ib_KeyPoint]>>=1;
/*接收数据字节右移一位*/
if
((ib_HighTime>MIN_B1TIME)&&(ib_HighTime<MAX_B1TIME)) { /*如果高电平宽度符合逻辑1标准,接收字节高位置一*/
ib_KeyCode[ib_KeyPoint]+=0x80;
}
ib_BitCount++;
/*接收数据位计数器加一*/
if (ib_BitCount>7) {
/*如果接收满一个字节*/
ib_BitCount=0;
/*清空位计数器*/
ib_KeyPoint++;
/*指向下一个字节*/
if (ib_KeyPoint>3)
/*如果接收完整个数据*/
{
//unsigned char i;
NewKey=TRUE;
/*置有新键按下标志*/
//for (i=0;i<4;i++){
//TI=0;
//SBUF=ib_KeyCode;
//while(!TI);
//TI=0;
//}
//ib_HandState=RESTART;
}
}
ib_HighTime=0;
/*将高电平时间计数器清零*/
ib_HandState=WAIT_HIGH;
/*设定系统为等待上升沿状态*/
}
break;
default:break;
}
}
if (ib_HandState==RESTART){
/*如果程序处于复位状态*/
ib_LowTime=0;
/*复位各项参数*/
ib_HighTime=0;
ib_HandState=LEAD;
ib_KeyCode[0]=0; ib_KeyCode[1]=0;
ib_KeyCode[2]=0; ib_KeyCode[3]=0;
ib_KeyPoint=0;
ib_BitCount=0;
NewKey=FALSE;
}
}
好多遥控器(无线,红外)发射数据都是PPM调制的,以上程序稍微改动后可以应用于各种PPM调制信号的场合。
4×4矩阵式键盘识别技术
#include <AT89X51.H>
unsigned char ID;
void delay10ms(void)
{ unsigned char i,j;
for(i=20;i>0;i--)
for(j=248;j>0;j--);
}
void delay02s(void)
{ unsigned char i;
for(i=20;i>0;i--)
{delay10ms();}
}
void main(void)
{ while(1)
{ if(P3_7==0)
{delay10ms();if(P3_7==0)
{ID++;if(ID==4)
{ID=0;}
while(P3_7==0);}
}
switch(ID)
{ case 0:P1_0=~P1_0;delay02s();break;
case1:P1_1=~P1_1;delay02s();break;
case 2:P1_2=~P1_2;delay02s();break;
case 3:P1_3=~P1_3;delay02s();break;}
}
}
支持键盘双击事件的C程序设计!
/**********************************************************************************
KeyBoard Encode Program
This Program can encode press_key up to
128 and it can deal KB_DBClick Message
This is just a test proram and only use
2 key.If More Key need to Encode ,please
modify the function:KBKeyPress()
This Porgram use Message_Driver
method,the KB_Msg is used to record what KB_Msg has occured.
The meaning of 'SysKBMsg' is list as
following.
Program Design:LiBaizhan
Ver:1.10
Date:2003-3-16
************************************************************************************/
#include <reg51.h>
#include <intrins.h>
sbit Key1
= P1^0;
sbit Key2
= P1^1;
/*
Some System Var Pre_Definition
Crystal Frequence is 11.0592MHz
*/
#define TIME_2MS
0X74
#define TIME_20MS
0X043B
#define KB_INTERNAL
3 /*Key DBClk Detection Internal */
/*
SysKBMsg define Keyboard Message,it include Single_Click or
Double_Click
It's bit6~bit0 record key_encode and bit 7 record DBClk(1) or
SglClk(0)
It can record and encode up to 127(2^7-1) keys
No key is press when it is 0
This method did not deal key such as Ctrl_Key or Alt_Key or
Shift_Key...etc.
*/
/*_________________________________________________*/
data unsigned char SysKBMsg=0;
/*| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |*/
/*| D/S
| KB6 | KB5 | KB4 | KB3 | KB2 | KB1 | KB0 |*/
data unsigned char KBCounter=0;
bit KBCounterStart=0;
bit KBCounterStop=0;
/*if KeyBoard counter stop then this bit is 1*/
bit KBCounterFlag=0;
/*Current Counter is used by KeyBoard*/
void TimerInt0(void) interrupt 1 /*timer 0 interrupt
use to record how long key is press*/
{
TR0=0;
if(KBCounterFlag)
/*Current Counter is used by KeyBoard*/
{
if(KBCounter<KB_INTERNAL) /*if DBClk
Detection is not finish then detect continue*/
{
KBCounter++;
TR0=1;
}
else
{
KBCounter=0;
/*DBClk Detection is finish*/
KBCounterStop=1;
}
}
}
void DelayMS(unsigned int dltime);
void Init51(void);
unsigned char KBKeyPress(void); /*only
return what key is press*/
void KBKeyEncode(void);
/*encode which key is pressed and DBClk or SglClk*/
void KBStartTimer(unsigned char
CntH,unsigned char CntL); /*load counter initial value and start
timer*/
void KBStopTimer(void);
void KBDealPressKey(void);
/*deal key_press message*/
void main(void)
{
Init51();
while(1)
{
KBKeyEncode();
/*recored what KeyBoard Msg occure:which key
is press and single clk or db clk*/
KBDealPressKey();
}
}
/*
Delay Time is
20+17*dl_time)*12/CrystalFrequence(us)
*/
void DelayMS(unsigned int dltime)
{
unsigned int i;
for(i=0;i<dltime;i++);
}
void Init51(void)
{
SCON = 0x50;
/* mode 1: 8-bit
UART, enable receiver */
TMOD = 0x21;
/* timer 1 mode 2:
8-Bit reload */
/* timer 0 mode 1: 16-bit Timer
*/
TH1 = BAUD_4800;
/* reload value 9600 baud
*/
TR1 = 1;
/* timer 1 run
*/
IE = 0X12;
/* enable Serial
INT,Timer0 INT */
ES = 0;
/* disable Serial
INT*/
EA = 1;
/* Open INT
Ctrl
*/
}
void KBKeyEncode(void)
{
data unsigned char CurrPress=0,LastPress=0;
if((LastPress=KBKeyPress())!=0)
/*if some key is press then start encode*/
{
KBStartTimer(0,0);
/*some
key is press then start DBClk Detection Counter*/
SysKBMsg=LastPress;
/*record the key
that is pressed this time*/
while(!KBCounterStop)
{
if((CurrPress=KBKeyPress())!=0X0)
/*if some key is pressed during DBClk Detection then jump
out to see wether DBclk is occured*/
break;
}
if((KBCounterStop==0)&&(LastPress==CurrPress))
/*this key DBClk occured*/
SysKBMsg|=0X80;
KBStopTimer();
}
}
unsigned char KBKeyPress(void)
{
data unsigned char KeyPress=0;
if((P1&0X03)!=0X03)
{
DelayMS(TIME_20MS);
if((KeyPress=(P1&0X03))!=0X03)
/*some key is press*/
{
if(KBCounterStart)
TR0=0;
while((P1&0X03)!=0X03);
/*wait until key is free*/
DelayMS(TIME_20MS);
if(KBCounterStart)
TR0=1;
}
else
/*Key is not real press*/
{
KeyPress=0;
}
}
return KeyPress;
}
void KBStartTimer(unsigned char CntH,unsigned char CntL)
{
TR0=0;
TH0=CntH;
TR0=1;
/*Start Counter*/
TL0=CntL;
KBCounterFlag=1;
/*this counter is used by KeyBoard*/
KBCounterStart=1;
KBCounterStop=0;
}
void KBStopTimer(void)
{
TR0=0;
TH0=0;
TL0=0;
KBCounter=0;
KBCounterFlag=0;
KBCounterStart=0;
}
void KBDealPressKey(void)
{
data unsigned char tmp=0;
switch(SysKBMsg) /*here is
just a test program,test to deal Sgl_Clk and DB_Clk Msg*/
{
case 0X01:
tmp=0X01;break;
case 0X02:
tmp=0X02;break;
case 0X81:
tmp=0X81;break;
case 0X82:
tmp=0X82;break;
default :
break;
}
SysKBMsg=0;
/*this key msg has been done*/
96个key的零延时采集
HotPower 发表于 2003-11-5 18:04 侃单片机 ←返回版面
;-------96键演示程序-------------------------
;这是1个回复题中的应用示例,已通过软仿真“验证”
;这只是键扫描技术的1个“缩影”,方法实在太多.
;有“难看之处”,敬请高手们批评指教.
;HotPower将虚心接受,坚决改正.重新做人.
;发表目的: 在21IC中壮大游击队.
;----------------------------------------------------
;由于2051资源问题,本程序只取多任务键盘的压放键2个事件
;废除长压键(压键1段时间后才激活)事件
;废除长放键(放键1段时间后才激活)事件
;废除双击键事件
;废除任意组合键事件
按键扫描驱动程序
//按键扫描驱动程序
unsigned char key,key_h,kpush;
unsigned int key_l;
//按键连接到p1.0、p1.1、p1.2
void int_t0(void) interrupt 1 {
unsigned char dd,i;
TL0=TL0+30;TH0=0xfb; //800
/* 按键判别 */
if ((P1&0x7)==0x7) {
if ((key_l>30)&&(key_l<800)&&(key_h>30))
{ //释放按键,如果之前按键时间少于1秒,读入键值
key=kpush;
}
if ((++key_h)>200) key_h=200;
key_l=0;
if (key>=0x80) key=0; //如果之前的按键为长按1秒,清除键值
} else {
kpush=P1&0x7;
key_l++;
if ((key_l>800)&&(key_h>30)) {
//如果按键超过1秒,键值加0x80标志长按键
key=kpush|0x80;
key_h=0;
key_l=0;
}
}
}
void main(void) {
TMOD=0x1;TR0=1;ET0=1;EA=1;
while (1) {
while (!key) {}
switch (key) {
case 1:break;
case 2:break;
}
}
}
键盘接口软件设计
软件设计要考虑以下3部分内容:键盘缓冲区的设置;初始化程序;中断服务程序。
1、键盘缓冲区
键盘缓冲区地址为:30H-4FH,共32个字节。使用R1作为缓冲区放数指针,R7为取数指针。缓冲区为环形。放满后将R1置为30H,再从头放数;当放数指针追上取数指针时,停止放数。当取数指针追上放数指针时,停止取数。
2、初始化程序
包括8052的初始化和键盘的初始化。
// AT89C51 C语言程序
//Web:http//winchiphead.com
//Author: yhw 2003.7
#define CH451_RESET 0x0201
//复位
#define CH451_LEFTMOV 0x0300
//设置移动方式-作移
#define CH451_LEFTCYC 0x0301
//设置移动方式-左循
#define CH451_RIGHTMOV 0x0302
//设置移动方式-右移
#define CH451_RIGHTCYC 0x0303
//设置移动方式-右循
#define CH451_SYSOFF 0x0400
//关显示、键盘、看门狗
#define CH451_SYSON1 0x0401
//开显示
#define CH451_SYSON2 0x0403
//开显示、键盘
#define CH451_SYSON3 0x0407
//开显示、键盘、看门狗功能
#define CH451_DSP 0x0500
//设置默认显示方式
#define CH451_BCD 0x0580
//设置BCD译码方式
#define CH451_TWINKLE 0x0600
//设置闪烁控制
#define CH451_DIG0 0x0800
//数码管位0显示
#define CH451_DIG1 0x0900
//数码管位1显示
#define CH451_DIG2 0x0a00
//数码管位2显示
#define CH451_DIG3 0x0b00
//数码管位3显示
#define CH451_DIG4 0x0c00
//数码管位4显示
#define CH451_DIG5 0x0d00
//数码管位5显示
#define CH451_DIG6 0x0e00
//数码管位6显示
#define CH451_DIG7 0x0f00
//数码管位7显示
//须主程序定义的参数
// sbit ch451_dclk=P1^7;
//串行数据时钟上升延激活
// sbit ch451_din=P1^6;
// 串行数据输出,接CH451的数据输入
// sbit ch451_load=P1^5;
//串行命令加载,上升延激活
// sbit ch451_dout=P3^3;
//INT1,键盘中断和键值数据输入,接CH451的数据输出
// uchar ch451_key;
// 存放键盘中断中读取的键值
//********************************************
//初始化子程序
void ch451_init()
{
ch451_din=0;
//先低后高,选择4线输入
ch451_din=1;
#ifdef USE_KEY
IT1=0;
//设置下降沿触发
IE1=0;
//清中断标志
PX1=0;
//设置低优先级
EX1=1;
//开中断
#endif
}
//*****************************************************
//输出命令子程序
//定义一无符号整型变量存储12字节的命令字。
void ch451_write(unsigned int command)
{
unsigned char i;
#ifdef USE_KEY
EX1=0;
//禁止键盘中断
#endif
ch451_load=0;
//命令开始
for(i=0;i<12;i++){
//送入12位数据,低位在前
ch451_din=command&1;
ch451_dclk=0;
command>>=1;
ch451_dclk=1;
//上升沿有效
}
ch451_load=1;
//加载数据
#ifdef USE_KEY
EX1=1;
#endif
}
#ifdef USE_KEY
//*************************************************
//输入命令子程序,MCU从451读一字节
unsigned char ch451_read()
{
unsigned char i;
unsigned char command,keycode; //定义命令字,和数据存储器
EX1=0;
//关中段
command=0x07;
//输入读451命令字
ch451_load=0;
for(i=0;i<4;i++){
ch451_din=command&1; //送入最低位
ch451_dclk=0;
command>>=1; //往右移一位
ch451_dclk=1; //产生时钟上升沿锁通知CH451输入位数据
}
ch451_load=1; //产生加载上升沿通知CH451处理命令数据
keycode=0; //清除keycode
for(i=0;i<7;i++){
keycode<<=1; //数据移入keycode,高位在前,低位在后
keycode|=ch451_dout; //从高到低读入451的数据
ch451_dclk=0; //产生时钟下升沿通知CH451输出下一位
ch451_dclk=1;
}
IE1=0; //清中断标志
EX1=1;
return(keycode); //反回键值
}
//*************************************************
//中断子程序 使用中断2,寄存器组1
void ch451_inter() interrupt 2 using 1
{
unsigned char i; //定义循环变量
unsigned char command,keycode; //定义控制字寄存器,和中间变量定时器
command=0x07; //读取键值命令的高4位0111B
ch451_load=0; //命令开始
for(i=0;i<4;i++){
ch451_din=command&1; //低位在前,高位在后
ch451_dclk=0;
command>>=1; //右移一位
ch451_dclk=1; //产生时钟上升沿锁通知CH451输入位数据
}
ch451_load=1; //产生加载上升沿通知CH451处理命令数据
keycode=0; //清除keycode
for(i=0;i<7;i++){
keycode<<=1; //数据作移一位,高位在前,低位在后
keycode|=ch451_dout; //从高到低读入451的数据
ch451_dclk=0; //产生时钟下升沿通知CH451输出下一位
ch451_dclk=1;
}
ch451_key=keycode; //保存上次的键值
IE1=0; //清中断标志
}
//***********************************************
#endif
8个IO接36个按键(没有用二极管)--键扫描程序
#include "REG52.h"//不知为何发帖不能用尖括弧?????
#include "intrins.h"//不知为何发帖不能用尖括弧?????
sbit Row0 = P1^0;
sbit Row1 = P1^1;
sbit Row2 = P1^2;
sbit Row3 = P1^3;
sbit Col0 = P1^4;
sbit Col1 = P1^5;
sbit Col2 = P1^6;
sbit Col3 = P1^7;
unsigned char KeyScan(void);
void main(void)
{
KeyScan();
while(1);
}
unsigned char KeyScan(void)
{
unsigned char key, temp;
unsigned char code keytab[] = {//键码表
//键值,//点,键名 键号
0x81,//07,key1 00
0x41,//06,key2 01
0x21,//05,key3 02
0x11,//04,key4 03
0x82,//17,key5 04
0x42,//16,key6 05
0x22,//15,key7 06
0x12,//14,key8 07
0x84,//27,key9 08
0x44,//26,key10 09
0x24,//25,key11 0A
0x14,//24,key12 0B
0x88,//37,key13 0C
0x48,//36,key14 0D
0x28,//35,key15 0E
0x18,//34,key16 0F
0x03,//01,key17 10
0x06,//12,key18 11
0x0c,//23,key19 12
0x05,//02,key20 13
0x09,//03,key21 14
0x0d,//13,key22 15
0xc0,//67,key23 16
0x60,//56,key24 17
0x30,//45,key25 18
0xa0,//57,key26 19
0x50,//46,key27 1A
0x90,//47,key28 1B
/*
0x01,//G0,key29 1C
0x02,//G1,key30 1D
0x04,//G2,key31 1E
0x08,//G3,key32 1F
0x10,//G4,key33 20
0x20,//G5,key34 21
0x40,//G6,key35 22
0x80,//G7,key36 23
*/
0//退出
};
P1 = 0xff;//释放键盘
_nop_();//延时
key = P1;//测试独占键(与GND连接)
key = ~key;//取反,变为正逻辑
if (key) {//有独占键压下,键码28~35,键key29..key36
temp = 35;//最后一个独占键key36
do {
if (key >= 0x80) break;//有独占键压下,退出测试
key <<= 1;//测试下一独占键
temp --;//键号-1
}
while(key);//未测完继续
if (key != 0x80) key = 0xff;//多个独占键压下,出错键码0xff
else key = temp;//得到键码28~35,键号key29~key36.
}
else {//测试组合键,键码0~27
temp = 0x01;//实为从P1_1测起,到P1_7测完
do {//只需扫描7次!!!
temp <<= 1;//继续扫描下一位
P1 = ~temp;//发送某位低电平
_nop_();//延时
key = P1;//接收键盘数据
key = ~key;//取反,变为正逻辑
}
while((temp < 0x80) && (key == temp));//测到P1_6或有键压下结束
if (key == temp) key = 0xff;//无键压下,键码0xff
else {//有组合键压下
temp = 0;//初始键号
while(keytab[temp] && (key != keytab[temp])) temp ++;//查键值表
if (temp >= 28) temp = 0xff;//查无此组合键,出错键码0xff
else key = temp;//得到组合键码0~27,键key1~key28.
}
}
return key;//返回键码0~35或出错码0xff
}
单片机驱动标准PC机键盘的C51程序
/*---------------------------------------------------------------------------------------------------
功能:实现pc机键盘(p/s2接口)与8位单片机连接使用
原理:键盘时钟接在p3.2口,既8051的外部中断int0上,键盘数据接到p1.0上
每次按键,键盘会向单片机发脉冲使单片机发生外部中断,数据有p1.0口一位一位传进来
传回的数据格式为:1位开始位(0),8位数据位(所按按键的通码,用来识别按键),1位校验位(奇校验)
1位结束位(1)
实现:将键盘发回的数据放到一个缓冲区里(数组),当按键结束后发生内部中断来处理所按的按键
缺点:由于51单片机的容量有限所以缓冲区不可以开的太大,这就导致可以记录键盘的按键次数过少,
也就是容错性一般。不过如果正常使用键盘是不会出错的
//#include"reg51.h"
#include "intrins.h"
#include "ku.h" //按键通码与ascii对照表
sbit sda= p1^0; //键盘数据线
unsigned char dat=0,dat1=0,dat2=0; //接收键盘数据变量? 存储通码变量 接受连续通码变量
unsigned char count=0,num=9,temp[5],shu=0; //中数次数 中断控制变量 缓冲区数组 缓冲区指针
unsigned char key=0; //按键最终值
void zhongduan() interrupt 0 //外部中断0 用来接受键盘发来的数据
{
dat>>=1; //接受数据 低->高
if(sda) dat|=0x80;
count++;
if(count==num)
{
if(count==9)
{
dat1=dat; //中断9次后为键盘所按按键的通码(开始位始终为0在第一次中断时右移中忽略)
num=20; //使中断可以继续中断11次
}
if(count==20)
{
dat2=dat; //取回第二个通码
if(dat1==0xe0 || dat2==0xf0) //第一个通码是0xe0则证明所按按键为功能键,第二个通码是0xf0证明按键结束
{
temp[shu]=dat1;temp[shu+1]=dat2; shu+=2; //将所按按键存到缓冲区中
ie=0x82; //关闭外部中断并打开内部中断来处理所按按键
tr0=1;
}
else
{
temp[shu]=dat1;temp[shu+1]=dat2; shu+=2; //如果shift键被按下则记录与它同时按下的那个键
count=0;
}
if((temp[0]==18 || temp[0]==89) && (temp[2]==18 || temp[2]==89) )
tr0=1; //如果缓冲区中有两个间隔的shift键则证明需要的铵键结束
}
}
}
void getkey() interrupt 1 //内部中断0 用来处理缓冲区里的数据
{
unsigned char i=0;
tr0=0;
th0=0;
tl0=0;
count=0; //中断记数则0
if((temp[0]==18 || temp[0]==89) && temp[1]!=0xf0 ) //shift被按下
{
for(i=0;i<21;i++)
{
if(addshift[0]==temp[1]) //搜索shift被按下的表
{
key=addshift[1];
ie=0x83; //打开外部中断
return;
}
}
}
else if(temp[0]==0xe0) //所按下的按键是功能键
{
for(i=0;i<80;i++)
{
if(noshift[0]==temp[1]) //功能键的通码在缓冲区的第二位
{
key=noshift[1];
ie=0x83;
return;
}
}
}
else //普通按键
{
for(i=0;i<80;i++)
{
if(noshift[0]==temp[0]) //普按键的通码在缓冲区的第一位
{
key=noshift[1];
ie=0x83;
return;
}
}
}
for(i=0;i<5;i++)
{
temp=0;
}
}
/*
程序效果:按下按键,蜂鸣器响,数码管有相应的键值
显示,按下E键继电器关,按下C键继电器开。
这与上一程序的功能相同,比上一程序简洁
但理解相对困难些。
运行平台:51hei单片机学习板
*/
#include<reg52.h> //头文件
#include<intrins.h>
#define uchar unsigned char //宏定义
#define uint unsigned int
sbit jdq=P3^5; //位声明,驱动继电器管脚
sbit fmq=P3^4; //位声明,驱动蜂鸣器管脚
code uchar table[]={0x3f,0x06,0x5b,//数码管显示的数值
0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71};
code uchar key_tab[17]={ //此数组为键盘编码
0xed,0x7e,0x7d,0x7b, // 0,1,2,3,
0xbe,0xbd,0xbb,0xde, // 4,5,6,7,
0xdd,0xdb,0x77,0xb7, // 8,9,a, b,
0xee,0xeb,0xd7,0xe7,0xff}; // c,d,e,f,
uchar l_key=0x00; //定义变量,存放键值
uchar l_keyold=0xff; //作为按键放开否的凭证
void readkey(); //扫描键盘,获取键值
void display(uchar *lp,uchar lc); //显示子函数
void delay(); //延时子函数
void main() //主函数
{
EA=1; //打开总中断
EX0=1; //打开外部中断
P0=0xf0; //键值高4位为高电平,低4位为低电平
while(1)
{
display(&l_key,1); //调用显示子函数
if(l_key==14) //是否按下E键,是则关闭继电器
jdq=1;
if(l_key==12) //是否按下C键,是则打开继电器
jdq=0;
}
}
void key_scan() interrupt 0//外部中断0,0的优先级最高
{
EX0=0; //在读键盘时,关闭外部中断,防止干扰带来的多次中断
TMOD&=0xf1; //设置定时器为工作方式1
TH0=0x2e; //设置初值,为12毫秒,十进制值为11776
TL0=0x00;
ET0=1; //开启定时器中断0
TR0=1; //启动定时器计数
}
void time0() interrupt 1 //定时器0的中断函数
{
TR0=0; //关闭定时器0
readkey(); //定时12ms后产生中断,调用此函数,读取键值
}
void readkey() //扫描键盘子函数
{
uchar i,j,key; //定义局部变量
j=0xfe; //设定初值
key=0xff;
for(i=0;i<4;i++) // 逐列扫描键盘
{
P0=j;
if((P0&0xf0)!=0xf0) //有按键按下,高4位不可能全为1
{
key=P0; //读取P0口的值,推出循环,否则循环下次
break;
}
j=_crol_(j,1); //此函数的功能是:左移循环
}
if(key==0xff) //如果读取不到P0口的值,如干扰,则返回
{
l_keyold=0xff;
P0=0xf0; // 恢复P0口的值,等待按键按下
fmq=1;
EX0=1; //在返回前,打开外部中断
return;
}
fmq=0; //有按键按下,打开蜂鸣器
if(l_keyold==key) // 检查按键放开否,如果相等表明没有放开
{
TH0=0x2e; //设置初值
TL0=0x00;
TR0=1; //继续启动定时器,检查按键放开否
return;
}
TH0=0x2e;
TL0=0;
TR0=1; //启动定时器
l_keyold=key; //获取键值,作为放开否的凭证
for(i=0;i<17;i++) //查表获得相应的16进制值存放到l_key中
{
if(key==key_tab)
{
l_key=i;
break;
}
}
//程序运行到此,就表明有键值存放到l_key中,主程序
//就可以检测键盘值并作相应的处理
}
void display(uchar *lp,uchar lc) //显示子函数
{
uchar i; //定义局部变量
P1=0xf8; //点亮第一个数码管
P2=0; //P2口为输出值
for(i=0;i<lc;i++) //循环显示
{
P2=table[lp]; //查表获得相应的要显示的数字的数码段
delay(); //延时
P2=0; //清零,准备显示下一个数值
}
}
void delay() //延时子函数
{
_nop_();_nop_();_nop_();_nop_();_nop_();
}
用户307028 2011-3-10 19:14
用户403143 2011-2-15 17:16