原创 《KEY(按键)应用》大全

2011-2-15 11:11 3551 5 7 分类: MCU/ 嵌入式

 

设计中你是否遇到过这样的问题:你的产品上要求有几十个按键,处理器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_();

}

 

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户307028 2011-3-10 19:14

是啊 楼主能否整理一份给我们这些后学小辈参考参考呢?

用户403143 2011-2-15 17:16

博主能否整理个pdf文档或keil编译器的工程给大家下载呢?
相关推荐阅读
用户309858 2011-03-17 09:36
医疗电子技术大会即将举行,有兴趣可以去官网免费报名
以下是官网的介绍: 作为专注医疗电子的高端技术活动,CMET2011仅面向家用医疗电子、便携医疗电子、云端医疗系统、医疗成像设备、以及其他医疗仪器设备企业的设计人员、管理人员、采购人员,和相关机构与媒...
用户309858 2011-03-11 16:18
苏泊尔电磁炉电路图
...
用户309858 2011-03-07 11:16
MTK配置文件参数说明
MTK配置文件参数说明 很详细,支持下attachment download...
用户309858 2011-02-22 14:42
GSM手机校准终测原理
13.1 校准终测的基本原理 13.1.1校准 、终测的目的  现在生产的相同型号手机虽然使用都是相同器件,但这相同器件还是有的一定的偏差,由此组合的手机就必然存在着差异,但这差异是在一定的范围,超出...
用户309858 2011-02-17 13:42
WCDMA协议和信令
【阅读语言】:中文【页数】:132【作者】: 【文件格式】:word【资料原名】:WCDMA协议流程【摘要目录】:WCDMA协议和信令 1第1章  UTRAN接口协议和功能 51.1  概述 51.2...
用户309858 2011-02-16 10:03
静电防护(ESD)设计
ESD(Electrostatic Discharge)是静电放电的简称。 非导电体由于摩擦,加热或与其它带静电体接触而产生静电荷, 当静电荷累积到一定的电场梯度时(Gradient of Fiel...
EE直播间
更多
我要评论
2
5
关闭 站长推荐上一条 /3 下一条