51单片机:利用定时器中断写一个简易加法计算器,按键消抖算法很好。

时间:2021-10-30 19:45:36

此加法计算器很简单,且有很多不完善的地方,但逻辑性很强

此函数主要分为两部分:

1、        主函数部分:主函数的作用主要是识别哪一个按键被按下,并且根据被按下的按键,执行相应的状态!比如按下等号键就把两次加数累加起来显示出来

 

首先主函数调用KeyDriver函数进行判断是否有按键被按下,并检测哪一个按键被按下,

然后接着调用KeyAction函数,判断哪一个按键被按下,并执行该按键所对应的功能

然后再把KeyAction函数中所得到的数值,发送到ShowNumber函数中,将得到的数值进行分离,并存储到数码管缓冲区LedBuff中去!

2、        中断函数部分:

中断函数主要进行数码管的刷新和按键状态的检验!

 

主要是按键状态函数难理解:其实这里包含一个按键消抖的算法。就是一毫秒检验一列四个按键的状态,连续四毫秒就把所有按键全都检验一遍他们的状态,并把按键状态存到状态缓冲区keybuff中去,然后连续四个四毫秒就把每一个按键都检验四遍。

如果连续检验四遍发现某个按键一直保持为0状态,说明它一直被按下,则可以确定这个按键一直被按下。如果连续检验四遍发现某个按键一直保持为1状态,说明它一直弹起,则可以确定这个按键一直弹起。

如果连续四次发现某个按键状态又有0又有1,说明按键处于抖动状态!(关于按键抖动自行百度。。。)如果这时候判断按键的状态,即这段代码:

        for(i=0;i<4;i++)

        {

                if((keybuff[col][i]&0x0f)==0x00)//假如连续四毫秒内发现某个按键一直保持0状态,说明此按键被按下,给此按键当前状态赋值为0

                  {                                              

                           keysta[col][i]=0;//keysta这个数组是为了保存按键的当前状态

                  }

                  elseif((keybuff[col][i]&0x0f)==0x0f)//假如连续四毫秒内发现按键一直保持1状态,说明此按键弹起,给此按键当前状态赋值为1

                  {

                          keysta[col][i]=1;//检测到此按键弹起,此按键当前状态赋值为1

                  }

        }

 

现在贴出代码:

 

#include<reg52.h>

 

sbit datacs=P2^6;

sbit chipcs=P2^7;

 

sbit col1=P2^1;              //列端口定义

sbit col2=P2^0;

sbit col3=P3^5;

sbit col4=P3^4;

 

sbit row1=P1^7;               //行端口定义

sbit row2=P1^6;

sbit row3=P3^6;

sbit row4=P3^7;

 

unsigned char key;

 

unsigned char codetable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,

              0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

 

unsigned char LedBuff[8] =

                                                {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};      //数码管更新缓冲区,单片机直接用这里的数据来更新数码管

 

unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表

    {0x31, 0x32, 0x33, 0x26 },                          //数字键1、数字键2、数字键3、向上键

    {0x34, 0x35, 0x36, 0x25 },               //数字键4、数字键5、数字键6、向左键

    {0x37, 0x38, 0x39, 0x28 },               //数字键7、数字键8、数字键9、向下键

    {0x30, 0x1B, 0x0D, 0x27 }              //数字键0ESC键、 回车键、向右键

         };

unsigned char keysta[4][4]={

 {1,1,1,1},

 {1,1,1,1},

 {1,1,1,1},

 {1,1,1,1}

};

 

 

void KeyDriver();

void KeyAction(unsigned char keycode);

void ShowNumber(unsigned long num);

 

void main()

{

        datacs = 0;

         chipcs= 0;

 

         EA= 1;

         TMOD=0X01;

         TH0=0XFC;

         TL0=0X67;

         ET0=1;

         TR0=1;

         LedBuff[0]=table[0];

         while(1)

         {

                 KeyDriver();//检测是否有按键按下,并检验是哪个按键被按下

         }

}

 

void KeyDriver()

{

        unsigned char i,j;

         staticunsigned char backup[4][4]={         //存储按键上一次的状态

                                                                   {1,1,1,1},

                                                                   {1,1,1,1},

                                                                   {1,1,1,1},

                                                                   {1,1,1,1}

                                     };

         for(i=0;i<4;i++)

         {

                 for(j=0;j<4;j++)

                   {

                            if(backup[i][j]!=keysta[i][j])    //当按键上一次状态和当前状态不相等时,说明按键有动作,不是按下就是弹起来

                             {

                                   if(backup[i][j]!=0)//按键上一次状态为1时,说明当前状态为0,按键刚被按下,用i,j来定位哪个一按键被按下

                                     {

                                             KeyAction(KeyCodeMap[i][j]);  //检测到被按下的按键,把按键所对应的数值传到KeyAction()函数中,执行按键所对应的相应动作

                                     }

                                     backup[i][j]=keysta[i][j];

                             }

                   }

         }

}

 

void KeyAction(unsigned char keycode)//把接收到的按键所对应的数值,进行转换,执行加或等于或清零等动作

{

         staticunsigned long result=0;//存储运算结果

         staticunsigned long accend=0;//存储被按下的加数

        if((keycode>=0x30)&&(keycode<=0x39))//09

         {

                 accend= (accend*10)+(keycode-0x30); //假如加数为125时,这一步做作用就是一次把按下的1,2,5累加起来

                   ShowNumber(accend);//把得到的数存到数码管缓冲区中,再中断中利用缓冲区显示出来

         }

         elseif(keycode==0x26)//加号

         {

                 result= result+accend;

                   accend=0;

                   ShowNumber(result);

 

         }

         elseif(keycode==0x0D)   //等于回车键

         {

                   result= result+accend;

              accend=0;

                   ShowNumber(result);

        

 

         }

         elseif(keycode==0x1B)//清零键,ESC

         {

                 result= 0;

                   accend= 0;

                   ShowNumber(accend);

         }

}

 

//把传过来的数进行分离,并更新数码管缓冲区的数值,即LedBuff[][]数组的数值

void ShowNumber(unsigned long num)

{

        signed char i;

         unsignedchar buf[8];//将传进来的num的值进行分离,把每一位分离出来,并存到数组中,b[0]是最低位

         for(i=0;i<8;i++)

         {

                 buf[i]=num%10;

                   num=num/10;

         }

         for(i=7;i>=1;i--)

         {

                   if(buf[i]==0)

                   {

                            LedBuff[i]=0X00;

                   }

                   else

                            break;

         }

         for(;i>=0;i--)

         {

            LedBuff[i]=table[buf[i]];

         }

}

 

 

/*********************中断函数及其所调用的函数**********************************/

 

 

void LedScan();

void KeyScan();

 

//中断函数,每一毫秒进行一次中断函数,中断函数里刷新数码管,并且检验按键此时的状态

void InterruptTimer() interrupt 1

{

        TH0=0XFC;

         TL0=0X67;

         LedScan();//刷新数码管

         KeyScan();//检验按键状态

}

 

void LedScan()

{

        static unsigned char i=0;

         P0=0XFF;

         switch(i)

         {

                 case0:P0=0x7f;chipcs=1;chipcs=0;P0=LedBuff[0];datacs=1;datacs=0;i++;break;

                   case1:P0=0xbf;chipcs=1;chipcs=0;P0=LedBuff[1];datacs=1;datacs=0;i++;break;

                   case2:P0=0xdf;chipcs=1;chipcs=0;P0=LedBuff[2];datacs=1;datacs=0;i++;break;

                   case3:P0=0xef;chipcs=1;chipcs=0;P0=LedBuff[3];datacs=1;datacs=0;i++;break;

                   case4:P0=0xf7;chipcs=1;chipcs=0;P0=LedBuff[4];datacs=1;datacs=0;i++;break;

                   case5:P0=0xfb;chipcs=1;chipcs=0;P0=LedBuff[5];datacs=1;datacs=0;i++;break;

                   case6:P0=0xfd;chipcs=1;chipcs=0;P0=LedBuff[6];datacs=1;datacs=0;i++;break;

                   case7:P0=0xfe;chipcs=1;chipcs=0;P0=LedBuff[7];datacs=1;datacs=0;i=0;break;

                   default:break;

         }

}

 

void KeyScan()

{

        static unsigned char keybuff[4][4]={//这个二维数组,用来存储连续四毫秒所有按键的状态

                              {0XFF,0XFF,0XFF,0XFF},

                              {0XFF,0XFF,0XFF,0XFF},

                              {0XFF,0XFF,0XFF,0XFF},

                              {0XFF,0XFF,0XFF,0XFF}

         };

         staticunsigned char col=0;       //列数,静态变量,每隔一毫秒扫描一列,四毫秒扫描四列,即四毫秒扫描完矩阵按键的所有列

         unsignedchar i;

 

         //当按键被按下是row被拉为0,看电路图理解这里

         keybuff[col][0]=(keybuff[col][0]<<1)|row1;//col列的第一个按键状态

         keybuff[col][1]=(keybuff[col][1]<<1)|row2;//col列的第二个按键状态

         keybuff[col][2]=(keybuff[col][2]<<1)|row3;//col列的第三个按键状态

         keybuff[col][3]=(keybuff[col][3]<<1)|row4;//col列的第四个按键状态

 

         for(i=0;i<4;i++)

         {

                 if((keybuff[col][i]&0x0f)==0x00)//假如四毫秒内发现按键一直保持0状态,说明此按键被按下,给此按键当前状态赋值为0

                   {                                              

                            keysta[col][i]=0;//keysta这个数组是为了保存按键的当前状态

                   }

                   elseif((keybuff[col][i]&0x0f)==0x0f)//假如四毫秒内发现按键一直保持1状态,说明此按键弹起,给此按键当前状态赋值为1

                   {

                           keysta[col][i]=1;//检测到此按键弹起,此按键当前状态赋值为1

                   }

         }

 

         col++;//切换到下一行

         col=col&0x03;//列数col等于四时自动清零

         switch(col)//切换到下一行

         {

                 case0:col1=0;col2=1;col3=1;col4=1;break;

                   case1:col1=1;col2=0;col3=1;col4=1;break;

                   case2:col1=1;col2=1;col3=0;col4=1;break;

                   case3:col1=1;col2=1;col3=1;col4=0;break;

                   default:break;

 

         }

}