C51之按键定时器消抖版,和简易简易加法计算器

    独立按键的使用很简单,再按下按键的时候会产生一个低电平信号传给单片机芯片,松开就变成了高电平,而代码也是根据对按键信号的检测来确定,但是按键再按下的时候和松开的时候会有抖动,这回时候你的效果就不正常起来,就比如之前我发代码里有个独立键盘,我做了两个按键一个按一下数码管加一,另外一个按键按一下减一,但是由于按键抖动,按下去的时候它可能不是加一而是加了好几个1,所以采用了延时然后做第二次检测来解决这个问题,代码如下

 #include <reg52.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar  code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};


//毫秒级延时函数定义
void delay(uint ms)
{
    uint x,y;
    for(x = ms; x > 0; x--)
        for(y = 114; y > 0 ; y--);         
} 

void main()//main函数自身会循环
{
    WE = 1;//打开位选锁存器
    P0 = 0XFE; //1111 1110
    WE = 0;//锁存位选数据
    
    while(1)
    {
        if(key_s2 == 0)//判断S2是否被按下
        {
            delay(20);//按键消抖
            if(key_s2 == 0)
            {
                if(num!=9)//如果值不等于9则+1,功能把值限定为小于9
                num++;
                while(!key_s2);//松手检测
            }    
        }
        if(key_s3 == 0)//判断S3是否被按下
        {
            delay(20);//按键消抖
            if(key_s3 == 0)
            {
                if(num > 0)    //如果大于0则执行减一
                    num--;
                while(!key_s3);//松手检测
            }    
        }
        //松手之后刷新显示
        DU = 1;//打开段选锁存器
        P0 = tabel[num];//
        DU = 0;//锁存段选数据
    }    
} 

         这个是可以达到最终目的,但是有个问题,在这延时的20毫秒里,有其他的事情要做但是它还在延时,延时完了再去做就完了,所以使用中断来解决这个问题,每两毫秒进行一次中断,对按键状态进行扫描,如果按键状态保持不变如0000 0000 或 1111 1111,这个时候就是按键稳定状态,这样就避免了上面提到的问题,下面是一个独立按键的代码

#include<reg52.h>
sbit DU = P2^6;
sbit WE = P2^7;
sbit keys2 = P3^0;
unsigned char code shuma[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
unsigned char keysta=1;
bit backup = 1;
unsigned char num = 0;

void main()
{
 EA = 1;
 TMOD = 0x01;//工作模式1
 TH0 = 0xf8;//2ms
 TL0 = 0xcd;
 ET0 = 1;//定时器中断;
 TR0 = 1;//定时器1启动;
 WE = 1;
 P0 = 0xfe;
 WE = 0;
 while(1)
 {
	  if(keysta != backup)
	   {  
	   if(backup==0)
	   	 {
		 if(num<=9)
		  num++;
		 if(num>=10)
		  num = 0;
		 }
		  	backup = keysta;
	   }
            DU = 1;
	        P0 = shuma[num];
        	DU = 0;
 }
}

void time0() interrupt 1
{
  static unsigned char keybuf = 0xff;//扫描缓冲区
  TH0 = 0xf8;//2ms
  TL0 = 0xcd;
  keybuf = (keybuf<<1) | keys2;//缓冲区左移一位并把当前扫描值保存到最后一位
    if(keybuf==0x00)//如果八次扫描值都为0,那么认为按键是按下状态
	  keysta = 0;
	else if(keybuf==0xff)
	  keysta = 1;
	else
	{}
}

如果是矩阵按键呢,这个代码有一点绕,中断每次检测的都是上一次输出选择的那行,如果你的原理图上没有外接+5V和上拉电阻其实没有问题在单片机内部有上拉电阻会让它处于高电平,在中后段扫描的过程中因为每次执行的是一次中断结尾选择的行拉低,所以不用刻意再去给8个引脚赋值而且也没地方赋值,你赋值可能还会错所以你对比一下用延时的矩阵按键检测和用定时器中断的矩阵按键检测差别比较大。

#include<reg52.h>

unsigned char code ledchar[]= {
//0     1     2     3     4    5     6      7     8    9
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
//a       b    c   d    e   f     h     L    n    u    -   熄灭
0x77,0x7c,0x39,0x5e,0x79,0x71,0x76,0x38,0x37,0x3e,0x40,0x00};

sbit WE = P2^7;
sbit DU = P2^6;
unsigned char key_value =0;//按键值
unsigned char num;

//延时
void delay(unsigned int ms)
{
    unsigned int x,y;
    for(x = ms; x > 0; x--)
        for(y = 114; y > 0 ; y--);         
} 

void keyscan()
{
 P3= 0xf0;//列拉高,行拉低 ,列扫描
  if(P3!=0xf0) //判断按键是否被按下
   {
     delay(10);//软件消抖10ms
       if(P3!=0xf0)
       {
            switch(P3)//判断列
         {
           case 0xe0: key_value = 0;break;
           case 0xd0: key_value = 1;break;
           case 0xb0: key_value = 2;break;
           case 0x70: key_value = 3;break;
         }
         P3=0x0f;//行扫描
          switch(P3)//判断列
         {
           case 0x0e: key_value = key_value;break;
           case 0x0d: key_value = key_value+4;break;
           case 0x0b: key_value = key_value+8;break;
           case 0x07: key_value = key_value+12;break;
         }
         while(P3!=0x0f);
       }
   }
   P3 = 0xff;
   if(P3!=0xff)
   {
        delay(10);
      if(P3!=0xff)
      {
           switch(P3)//判断列
         {
           case 0xfe: key_value = 16;break;
           case 0xfd: key_value = 17;break;
           case 0xfb: key_value = 18;break;
           case 0xf7: key_value = 19;break;
         }
         while(P3!=0xff);
      }
   }
}
void main()
{
  WE = 1;
  P0 = 0xfe;
  WE = 0;
     while(1)
     { 
     keyscan();//4*4矩阵键盘扫描 
      DU =1;
      P0 =ledchar[key_value];
      DU = 0;    
     }
}
————————————————
版权声明:本文为CSDN博主「五音不六药」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_63084911/article/details/131232256
#include<reg52.h>
sbit DU = P2^6;
sbit WE = P2^7;
sbit keys2 = P3^0;
sbit keyrow4 = P3^3;
sbit keyrow3 = P3^2;
sbit keyrow2 = P3^1;
sbit keyrow1 = P3^0;
sbit keyarr1 = P3^4;
sbit keyarr2 = P3^5;
sbit keyarr3 = P3^6;
sbit keyarr4 = P3^7;

unsigned char code shuma[]= {
//0     1     2     3     4    5     6      7     8    9
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
//a       b    c   d    e   f     h     L    n    u    -   熄灭
0x77,0x7c,0x39,0x5e,0x79,0x71,0x76,0x38,0x37,0x3e,0x40,0x00};
unsigned char keysta[4][4]={
{1,1,1,1},
{1,1,1,1},
{1,1,1,1},
{1,1,1,1}
};
unsigned char panduan[4][4]={
{1,1,1,1},
{1,1,1,1},
{1,1,1,1},
{1,1,1,1}
};


void main()
{
 
 unsigned char j,k;
 EA = 1;
 TMOD = 0x01;//工作模式1
 TH0 = 0xfc;//1ms
 TL0 = 0x67;
 ET0 = 1;//定时器中断;
 TR0 = 1;//定时器1启动;
 WE = 1;
 P0 = 0xfe;
 WE = 0;
 P0=shuma[0];
 while(1)
 {	
	for(j=0;j<4;j++)
	{
       for(k=0;k<4;k++)
	    {
		  	if(panduan[j][k]!=keysta[j][k])
			 { 
			 if(panduan[j][k]!=0)
			   P0 = shuma[j*4+k];		 
			 }
		  panduan[j][k]	= keysta[j][k];
		}
	}

	       }
 }

void time0() interrupt 1
{
  unsigned char i;
  static unsigned char num=0;//扫描索引
  static unsigned char  keybuf[4][4] = {
  {0xff,0xff,0xff,0xff},
  {0xff,0xff,0xff,0xff},//矩阵按键扫描缓冲区
  {0xff,0xff,0xff,0xff},
  {0xff,0xff,0xff,0xff},
};
  TH0 = 0xfc;//1ms
  TL0 = 0x67;
  keybuf[num][0] = (keybuf[num][0]<<1) | keyarr1;//把列的值存起来,放在最低位
  keybuf[num][1] = (keybuf[num][1]<<1) | keyarr2;
  keybuf[num][2] = (keybuf[num][2]<<1) | keyarr3;
  keybuf[num][3] = (keybuf[num][3]<<1) | keyarr4;
  for(i=0;i<4;i++)
  {
  	if((keybuf[num][i]&0x0f)==0x00)
	keysta[num][i]=0;
	else if((keybuf[num][i]&0x0f)==0x0f)
	keysta[num][i]=1;
  }
  num++;
  num =num&0x03;//加到4就重置
  switch(num)
  {
   case 0 :keyrow1 = 0;keyrow2 = 1;keyrow3 = 1;keyrow4 = 1;break;
   case 1 :keyrow1 = 1;keyrow2 = 0;keyrow3 = 1;keyrow4 = 1;break;
   case 2 :keyrow1 = 1;keyrow2 = 1;keyrow3 = 0;keyrow4 = 1;break;
   case 3 :keyrow1 = 1;keyrow2 = 1;keyrow3 = 1;keyrow4 = 0;break;
   default:break;
  }
}

下面是简易计算机注释写的很清楚

#include<reg52.h>
sbit DU = P2^6;
sbit WE = P2^7;
sbit keys2 = P3^0;
sbit keyrow4 = P3^3;
sbit keyrow3 = P3^2;
sbit keyrow2 = P3^1;
sbit keyrow1 = P3^0;
sbit keyarr1 = P3^4;
sbit keyarr2 = P3^5;
sbit keyarr3 = P3^6;
sbit keyarr4 = P3^7;

unsigned char code shuma[]= {
//0     1     2     3     4    5     6      7     8    9
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
//a    b    c   d    e   f     
0x77,0x7c,0x39,0x5e,0x79,0x71};	//共阴极数码管

unsigned char LedBuff[6] = 
{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} //0,esc, 回车键,向右键
};

unsigned char KeySta[4][4]={//矩阵按键当前状态
{1,1,1,1},
{1,1,1,1},
{1,1,1,1},
{1,1,1,1}
};

void KeyDriver();

void main()
{
 EA = 1; //总中断使能打开
 TMOD = 0x01;//工作模式1
 TH0 = 0xfc;//定时1ms
 TL0 = 0x67;
 ET0 = 1;//定时器0中断打开;
 TR0 = 1;//定时器1启动;
 LedBuff[0] = shuma[0];//上面显示一个0
 while(1)
   {
   	 KeyDriver();//调用驱动函数
   }
}

void ShowNumber(unsigned long num)//将数字显示在数码管上
{
   signed char i;
   unsigned char buf[6];

   for(i=0;i<6;i++)	 //把长整型转换成6位十进制
   {
   	buf[i] = num % 10;
	num = num/10;
   }

   for(i=5;i>=1;i--)//从最高位起,不足六位的前面几个存的是0,但是不让它显示,是0就让它灭,直到第一个非零值停止
   {
   	if(buf[i]==0)
	   LedBuff[i]=0x00;
	else
	   break;
   }

   for(;i>=0;i--) //剩余的转化成数码管显示的值
   {
   	 LedBuff[i]=shuma[buf[i]];
   }
}										 

void KeyAction(unsigned char keycode)//按键动作的函数
{
  static unsigned long result = 0;//用于加法和求和的媒介
  static unsigned long addend = 0;//用于保存加数
   if((keycode>=0x30)&&(keycode<=0x39))//输入0~9的数字
    {
	   addend = (addend*10)+(keycode-0x30);//没输入一个值左移一位
	   ShowNumber(addend);//显示到数码管
	}
   else if (keycode == 0x26)//进行加法运算,按下了代表加号的按键
    {
		result += addend;//按下代表加法的按键后把第一个值存到result之中,1+=2等价于1+2=?
		addend = 0;//存入后重置addend值为了下次输入的加数值
		ShowNumber(result);
	}
    else if (keycode == 0x0d)//按下了代表等于号的按键
    {
		result += addend;//此时就是把第一个存起来的值和第二个值做加法运算
		addend = 0;
		ShowNumber(result);//输出结果
	}
	else if (keycode == 0x1b)//按下代表归零的按键,全部重置为0
    {
		result = 0;
		addend = 0;
		ShowNumber(addend);
	}
}

void KeyDriver()//检测按键状态并执行操作,主函数调用
{
  unsigned char i,j;
  static unsigned 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++)//循环检测4*4矩阵按键
   	 {
	 	for(j=0;j<4;j++)
		 {
		  if(backup[i][j]!=KeySta[i][j])//判断按键是否按下
		    {
			  if(backup[i][j]!=0)//按键按下就执行
			   {
			   	 KeyAction(KeyCodeMap[i][j]);
			   }
			   backup[i][j] = KeySta[i][j];//刷新前一次的备份值
			}
		 }
	 }
}

void KeyScan()	//按键扫描函数,在中断中调用
{
  unsigned char i;
  static unsigned char row = 0;	//矩阵按键扫描行索引
  static unsigned char keybuf[4][4]={//矩阵按键扫描缓冲区
  {0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},
  {0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff}};

  keybuf[row][0] = (keybuf[row][0]<<1) | keyarr1; //将按键状态移入缓冲区
  keybuf[row][1] = (keybuf[row][1]<<1) | keyarr2;
  keybuf[row][2] = (keybuf[row][2]<<1) | keyarr3;
  keybuf[row][3] = (keybuf[row][3]<<1) | keyarr4;

    for(i=0;i<4;i++)//消抖更新按键状态
  {
  	if((keybuf[row][i]&0x0f)==0x00)
	KeySta[row][i]=0;
	else if((keybuf[row][i]&0x0f)==0x0f)
	KeySta[row][i]=1;
  }
  row++;
  row =row&0x03;//加到4就重置
  switch(row)
  {
   case 0 :keyrow1 = 0;keyrow2 = 1;keyrow3 = 1;keyrow4 = 1;break;//根据索引释放当前引脚拉低下次输出引脚
   case 1 :keyrow1 = 1;keyrow2 = 0;keyrow3 = 1;keyrow4 = 1;break;
   case 2 :keyrow1 = 1;keyrow2 = 1;keyrow3 = 0;keyrow4 = 1;break;
   case 3 :keyrow1 = 1;keyrow2 = 1;keyrow3 = 1;keyrow4 = 0;break;
   default:break;
  }
}

void LedScan()//数码管动态刷新
{
    static unsigned char i = 0;
    DU=1;
	P0 = 0x00;
    DU=0;
    switch(i)
        {
           case 0:WE= 1;P0=0x7f;i++;WE=0; DU=1;P0=LedBuff[0]; DU=0;break;     
           case 1:WE= 1;P0=0xbf;i++;WE=0; DU=1;P0=LedBuff[1]; DU=0;break;     
           case 2:WE= 1;P0=0xdf;i++;WE=0; DU=1;P0=LedBuff[2]; DU=0;break;     
           case 3:WE= 1;P0=0xef;i++;WE=0; DU=1;P0=LedBuff[3]; DU=0;break;     
           case 4:WE= 1;P0=0xf7;i++;WE=0; DU=1;P0=LedBuff[4]; DU=0;break;
           case 5:WE= 1;P0=0xfb;i=0;WE=0; DU=1;P0=LedBuff[5]; DU=0;break;                               		                                  
           default: break;
        }            

}

void time_0() interrupt 1	 
{
 TH0 = 0xfc;
 TL0 = 0x67;
 LedScan();
 KeyScan();
}

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值