【优化】最强51单片机计算器(源程序+演示+串口通信)

本次优化解决了笔者前期制作的计算器中的下列问题:

1.将计算结果改成了正序输出;

2.使用独立按键可以删除输错的数字(使用外部中断实现);

3.将MODE模块删除(没做拓展的话留着没用);

4.将计算的函数独立出来,模块化更好;

5.将除法改为了保留小数点后2位(太多没必要);

6.将函数整体进行了优化,删减了冗余代码;

7.目前还开发了双机通信功能,可以把计算结果用从机的LCD显示,然后用从机求几次运算的和。(此文没写,需要的朋友请私信我)

优化前:【C语言】51单片机计算器(LCD1602液晶显示)_51单片机计算器程序代码_Gxtoeilee的博客-CSDN博客

 优化后:

e4ecfe2f45cd454cb128d313b0f6a1ca.jpeg

优化后的源代码:

#include <REGX51.H>
typedef unsigned int uint;   //定于无符号int
typedef unsigned char uchar;  //定义无符号char
/*****************************定义引脚**********************************/
sbit rw=P2^5;            
sbit rs=P2^6;            
sbit e=P2^7;             
/*****************************定义变量**********************************/ 
uint fuhao,flag,k,i,j,num,nn,ss;          
code uchar ERROR[]="     ERROR!";//除数为0时,显示 
code uchar wel[]="    Welcome!";//初始化时显示
int cc[15];
long a,b,c,x;
/*****************************延时函数**********************************/ 
void delay(unsigned int xms)//延时x毫秒 
{
  unsigned int i,j;
  for(i=xms;i>0;i--)
    for(j=112;j>0;j--);
return;
}
/*****************************LCD写命令————位置**********************************/ 
void lcdwrc(uint c)
{
 delay(100);
 rs=0;
 rw=0;
 e=0;
 P0=c;
 e=1;
 delay(100);
 e=0;
}
/*****************************LCD写数据————内容**********************************/ 
void lcdwrd(uint dat) //设置LCD写数据
{
 delay(100);
 rs=1;
 rw=0;
 e=0;
 P0=dat;
 e=1;
 delay(100);
 e=0;
 rs=0;
}
/*****************************初始化LCD**********************************/ 
void lcdinit()   //初始化LCD
{
  delay(150);
  lcdwrc(0x38);//写指令38H
  delay(50);
  lcdwrc(0x38);//显示模式设置
  delay(50);
  lcdwrc(0x38);
  delay(50);
  lcdwrc(0x38);//功能设定指令
  lcdwrc(0x08);
  lcdwrc(0x01);//清屏
  lcdwrc(0x06);//显示光标移动设置
  lcdwrc(0x0c); //显示开关控制指令
  /*lcdwrc(0x0f); //显示光标并闪烁 */
  num=0;//用于判定按键是否为“=”或者“归零”时的变量
  fuhao=0;//用于判定是否为运算符号
  flag=0;//用于判定运算方式时的变量
  a=0;//计算的第一个数
  b=0;//计算的后一个数
  c=0;//计算结果
  x=0;//判定小数点的变量,暂存c值
  k=0;//判定小数点的变量,小数点后非零数个数
  j=0;//显示ERROR时的循环用变量
  nn=0;//写结果时表示数组第几位
  ss=0;//往数组里存结果时表示存几位
}
/*****************************显示结果函数**********************************/
void xsjg()//加减乘
{
  if(c==0)//若结果为0 
    lcdwrd(0x30);//显示0 
  while(c!=0)//若结果不为0
  {
	  for(c;c>0;c=c/10)//按位存入数组,倒序存储,最左边是个位
	  { 
	    cc[++ss]=c%10;//从个位开始存,然后存的位数加1并向右移位
		nn++;//结果位数加1
	  }
  }
  for(nn;nn>0;nn--)//循环显示结果
  {
    lcdwrd(0x30+(cc[nn]));//从数组的右向左显示
  }  
} 
void xsjg2()//减得负
{
  lcdwrd(0x2d);//ASCII中的“-”,显示负号
  xsjg();
}

void xsjg3()//除
{
  for(ss;ss>0;ss--)//循环显示结果
  {
    if(ss==3)
	lcdwrd(0x2e);//.
	else
	lcdwrd(0x30+(cc[ss]));//从数组的右向左显示
  }  
} 
/*****************************运算函数**********************************/          
void yunsuan()
{          
  switch(flag)//判断进行哪种运算 
  {
  /*******加法运算*********/
    case 1://加法运算
    {
      c=a+b;//计算结果       
      xsjg();//显示结果
    }break;
  /*******减法运算*********/
    case 2://减法运算
    {
      if(a>b)//大数减小数
      {
	    c=a-b;
		xsjg();
	  }
      else//小数减大数
      {
	  	c=b-a;//计算两数相减的绝对值
	    xsjg2();//显示结果
	  }               
    }break;	 
  /*******乘法运算*********/
    case 3://乘法运算
    {
      c=a*b; //计算结果  
      xsjg();//显示结果
    }break;
  /*******除法运算*********/
    case 4: //除法运算 
    { 
      if(b==0)//若除数为0 
      { 
        lcdwrc(0x01);//清屏 
        for(j=0;j<11;j++)
        {
          lcdwrd(ERROR[j]);//显示语句:ERROR! 
        }
        delay(1000);//延时 
        lcdinit();//重新初始化LCD1602
      } 
      else
      {  
        c=(long)(((float)a/b)*100);//将结果放大100倍,即:若无法整除,保留2位小数
        x=c;//存计算结果,以便确定小数点位置
        ss++;//从1位开始存,无其他意义,只是方便
        while(c!=0)//一位一位存
        {
          k++;//小数点位置
          cc[ss++]=c%10;//从结果的最低位开始存
          c=c/10;//取前面的结果数据
          if(x>0&&x<=9)//0.01到0.99
          {
            if(k==1)//只有小数点后最后一位  0.0x
            {
              cc[ss++]=0;//0
              cc[ss++]='.';//.
              k=0;//防止重复出现小数点
            } 
          }
          else if(k==2)//小数点后二位都非零  0.xx
          {
            cc[ss++]='.';//存.         
          }  
        }
        if(x<100)//若结果小于1,在个位补0 
          cc[ss++]=0;//个位补0
		  ss--;//位置调整,无其他意义,只是方便输出
		  xsjg3();//显示结果
		  k=0;//将k定回0用于下次计算   
      } 
    } break;
  }
}
/*****************************按键及功能判断函数**********************************/ 
void KeyDown()//按键及功能判断函数
{
  k=0;     
  P1=0x0f;   //0000 11111,行都为低电平0,列都为高电平
  if(P1!=0x0f)//P1不为0x0f,则判定为有按键被按下 
  {
    delay(100);//消抖
    if(P1!=0x0f)//延时后,再次判断 
    {
      P1=0x0f;//0000 11111,行都为低电平0,列都为高电平
      switch(P1)//
      {
      /********************第一列有按键按下*********************/ 
		case 0x07://0000 0111 第一列有按键被按下  
		{
          P1=0xf0;//列都为低电平0,行都为高电平1 
          switch(P1)//判断哪一行的按键被按下 
          {
          case 0x70: num = 1/*S1*/;k=7;break;    //0111 0000第一列第一行 数字7
          case 0xb0: num = 5/*S5*/;k=4;break;    //1011 0000第一列第二行 数字4
          case 0xd0: num = 9/*S9*/;k=1;break;    //1101 0000第一列第三行 数字1
          case 0xe0: num = 13/*S13*/;break;       //1110 0000第一列第四行 归零
          } 	  
		  if(num!=13)//被按下的按键不是归零键 
          {
            if(fuhao==0)//fuhao==0,表示第一个数a,fuhao==1,表示第二个数b 
              a=a*10+k;
            else
              b=b*10+k;
          }
          lcdwrd(0x30+k);//显示按下的数,0x30为ASCII码中数字组的开头,0x3k:显示k,即按下数字k 
          
		  if(num==13)//按下的按键是清零键
          {  
            lcdwrc(0x01); //清屏指令   
            lcdinit();//重新初始化LCD1602 
          }      
        }while(P1!=0xf0);break;//当有按键被按下时,结束此次判断,进行下一次判断 
 	 
	 
      /********************第二列有按键按下*********************/
      case 0x0b: //0000 1011 第二列有按键被按下 
      {
        P1=0xf0;//行都为高电平1,列都为低电平0
        switch(P1)//判断哪一行有按键被按下 
        {
          case 0x70: k=8;break;//第二列第一行 数字8
          case 0xb0: k=5;break;//第二列第二行 数字5
          case 0xd0: k=2;break;//第二列第三行 数字2
          case 0xe0: k=0;break;//第二列第四行 数字0
        }   
		if(fuhao==0)//fuhao==0,表示第一个数a,fuhao==1,表示第二个数b
          a=a*10+k;
        else
          b=b*10+k; 
        lcdwrd(0x30+k);//显示按下的数,0x30为ASCII码中数字组的开头,0x3k:显示k,即按下数字k 
      }	
	  while(P1!=0xf0);break;//当有按键被按下时,结束此次判断,进行下一次判断
 	  
	  
	  /********************第三列有按键按下*********************/
      case 0x0d: //0000 1101 第三列有按键被按下 
      {
        P1=0xf0;//行都为高电平1,列都为低电平0
        switch(P1)//判断哪一行有按键被按下
        {
        case 0x70: num=3;k=9;break;//第三列第一行 数字9 
        case 0xb0: num=7;k=6;break;//第三列第二行 数字6 
        case 0xd0: num=11;k=3;break;//第三列第三行 数字3
        case 0xe0: num=15;break;//第三列第四行 “=”键 
        } 
        if(num!=15)//按下的键不为“=”键 
        { 
          if(fuhao==0)//fuhao==0,表示第一个数a,fuhao==1,表示第二个数b
            a=a*10+k;
          else
            b=b*10+k; 
          lcdwrd(0x30+k);//显示按下的数,0x30为ASCII码中数字组的开头,0x3k:显示k,即按下数字k
        } 
	    /************"="键被按下************/
		if(num==15)//按下的键为=键		
        {
          lcdwrd(0x3d);//显示“=” 
		  lcdwrc(0x40+0x80);
		  yunsuan();//输入完成,开始计算
        }
      }
      while(P1!=0xf0);break;//当有按键被按下时,结束此次判断,进行下一次判断
      
	  
	  /********************第四列有按键按下*********************/
      case 0x0e://0000 1110 第四列有按键被按下
      { 
        fuhao=1;//有符号键被按下 
        P1=0xf0;//1111 0000
        switch(P1)
        {
          case 0x70:/*0111 0000*/flag=4;lcdwrd(0xfd);break;//“/”第四列第一行 
          case 0xb0:/*1011 0000*/flag=3;lcdwrd(0x2a);break;//“*”第四列第二行 
          case 0xd0:/*1101 0000*/flag=2;lcdwrd(0x2d);break;//“-”第四列第三行 
          case 0xe0:/*1110 0000*/flag=1;lcdwrd(0x2b);break;//“+”第四列第四行 
        }  
      }  
      while(P1!=0xf0);break;
      }
    }
  }
}
/*****************************外部中断初始化**********************************/
void Int0Init()
{
  IT0=1;
  EX0=1;
  EA=1;
}
/*****************************删除的中断**********************************/
void Int0() interrupt 0
{
  if(P3_2==0)
  {  
	delay(100);
	if(P3_2==0)//消抖后再次判断
	{
      lcdwrc(0x04);//设置显示方式:显示后指针减一,即前移一位
	  lcdwrd(0x20);//清位
	  lcdwrc(0x06);//设置显示方式:显示后指针减一,即后移一位
	  lcdwrd(0x20);//清位
	  lcdwrc(0x04);//设置显示方式:显示后指针减一,即前移一位
	  lcdwrd(0x20);//清位
	  lcdwrc(0x06);//设置显示方式:显示后指针减一,即前移一位
	  if(fuhao==0)
          a=a/10;
	  if(fuhao==1)
	      b=b/10;
	}
  }
}
/*****************************主函数**********************************/      
void main()
{ 
  uint x;  
  lcdinit();//LCD1602初始化 
  for(x=0;x<12;x++)
    lcdwrd(wel[x]);//显示欢迎语句:Welcome 
  delay(300); 
  lcdwrc(0x01);//LCd1602清屏 
  Int0Init();
  while(1)
  {  
    KeyDown();//按键判断函数 
  }
}

猜你喜欢

转载自blog.csdn.net/m0_57007304/article/details/131100573