简易计算器--带小数点--单片机课程设计

计算器

带小数点计算器

1、本设计是实现一个有加、减、乘、除、计算功能的多功能计算器。它的硬件主要由四部分组成,一个STC89C52单片机芯片,一块LCD1602液晶显示器,一个4X4的键盘。采用STC89C52单片机为主要控制电路,然后使用液晶显示管显示数据,利用44矩阵键盘加上一些功能键完成电路的输入操作部分。电路比较简单,但是很实用方便。
2、本设计拟实现的性能指标如下:
(1) 采用LCD1602液晶屏显示
(2) 4
4键盘来输入数据。
(3) 能够进行小数运算。
(4) 可以计算不超过13位数的加减乘除。

LCD显示模块
LCD1602是一种工业字符型液晶,能够同时显示16x02 即32个字符。(16列2行)。在日常生活中,我们对液晶显示器并不陌生。液晶显示模块已作为很多电子产品的通过器件,如在计算器、万用表、电子表及很多家用电子产品中都可以看到,显示的主要是数字、专用符号和图形。在单片机的人机交流界面中,一般的输出方式有以下几种:发光管、LED数码管、液晶显示器。发光管和LED数码管比较常用,软硬件都比较简单。
在单片机系统中应用晶液显示器作为输出器件有以下几个优点:
由于液晶显示器每一个点在收到信号后就一直保持那种色彩和亮度,恒定发光,而不像阴极射线管显示器(CRT)那样需要不断刷新新亮点。因此,液晶显示器画质高且不会闪烁。
液晶显示器都是数字式的,和单片机系统的接口更加简单可靠,操作更加方便。
液晶显示器通过显示屏上的电极控制液晶分子状态来达到显示的目的,在重量上比相同显示面积的传统显示器要轻得多。
相对而言,液晶显示器的功耗主要消耗在其内部的电极和驱动IC上,因而耗电量比其它显示器要少得多。
(1)引脚说明:
第1脚:VSS为地电源。
第2脚:VDD接5V正电源。
第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。
第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平
R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。
第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
第7~14脚:D0~D7为8位双向数据线。
第15脚:背光源正极。
第16脚:背光源负极。

硬件电路设计

显示电路设计
当系统需要显示少量数据时,采用LCD液晶显示屏进行显示是一种经济实用的方法。P0口作为液晶显示的数据端口,P3.5-P3.7口作为其控制端口,控制LCD液晶显示屏显示输出数据。电路如图:
LCD液晶显示屏显示
LCD液晶显示屏显示
键盘电路的设计
键盘可分为两类:编码键盘和非编码键盘。编码键盘是较多按键(20个以上)和专用驱动芯片的组合,当按下某个按键时,它能够处理按键抖动、连击等问题,直接输出按键的编码,无需系统软件干预。通用计算机使用的标准键盘就是编码键盘。在智能仪器中,使用并行接口芯片8279或串行接口芯片HD7279均可以组成编码键盘,同时还可以兼顾数码管的显示驱动,其相关的接口电路和接口软件均可在芯片资料中得到。当系统功能比较复杂,按键数量很多时,采用编码键盘可以简化软件设计。非编码键盘成本低廉。
从成本角度出发,本设计选用的是非编码键盘。
键盘电路
键盘电路接线图
源文件下载
链接: 源文件下载.

仿真图

计算器

代码

#include "reg52.h"
#include <stdlib.h>        //包含atof(),字符串->实数
#include <stdio.h>         //包含sprintf(),实数->字符串
#include <string.h>        //包含strcpy(),字符串复制
#include <math.h>		   //数学函数头文件
#define uchar unsigned char
#define uint unsigned int
//sbit choose=P3^4;          //时钟和计算器选择键
/*************lcd****************/
sbit lcdrs=P2^7;           //LCD控制脚
sbit lcdrw=P2^6;
sbit lcden=P2^5;
bit idata form;            //lcd显示方式切换
uchar idata state;         //lcd状态字存储
/************计算器*************/
//sbit reset=P3^0;           //计算器复位
//sbit back=P3^1;            //撤销上次键入键
//sbit save=P3^2;            //存储键,存储有上次的结果和另外某次计算值
bit idata eqsign;          //按等号标志
bit idata press;           //键按标志
bit idata savesign;        //可存储结果标志
bit idata sc;			   //复用标志
uchar idata process[30],proc; //记忆计算过程,以便错误恢复和撤销输入
uchar idata continu=0;        //连续计算运算符号存储
uchar idata pai[2][2];        //π 前有数据标志
uchar idata ferror;        //计算检错标志
uchar idata ywei;		   //屏幕移位
uchar idata count=0;       //输入计数
uchar idata count_num=0;   //组号计数
uchar idata result[15];    //计算结果立存,save
//uchar idata saveresult[15];//存储结果数组, sc+save
uchar idata jieguo[15];    //结果字符串
uchar idata bdate[2][14];  //待计算字符串二维组
uchar idata on_symbol;     //运算符号
uchar idata fsym[2];       //函数前符号
uchar idata ssym[2];       //存储组前符号
uchar idata bfun[2];       //计算值调用函数选择
uchar idata futojiao[2];   //幅度 to 角度变换
double idata date[2];      //计算值双精度变量组
double idata resultdate;   //双精度结果值
/***************时钟****************/
sbit stime=P3^3;           //时间设置键,同时用于计算器模式的复用键
uchar idata ms50=0;        //时间调节变量
uchar idata s=0;           //秒
uchar idata m=0;           //分
uchar idata h=0;           //小时

/******************函数声明**********************/

/***************延时******************/
void delay(uchar z);	
/*************lcd写命令***************/
void write_com(uchar com);
/*************lcd写数据***************/
void write_date(uchar date);
/*************lcd读状态***************/
void read_date(void);
/*************lcd写字符串*************/
void write_str(uchar *str);
/************液晶初始化***************/
void init(void );
/**************键盘扫描***************/
uchar keyscan(void);
/*************计算器复位**************/
void fuwei();          
/************error处理****************/
void callerror(void);
/***************撤销键入**************/
void huifu(void);
/**********函数组前符号处理***********/
uchar funqian(void);
/************运算符预处理*************/
void  cullars(uchar);
/***********输出存储数据预处理********/
char  memory(void);
/***********按键功能主处理************/
void process_date(uchar press_date);
/***********按键功能子处理************/
void calculator(uchar press_date);
/************************************************/
/*************LCD程序****************/
/**************延时******************/
void delay(uchar z)			   //0.2 ms
{
      uchar  x,y;
      for(x=z;x>0;x--)
      for(y=100;y>0;y--);
}

/**************写命令*****************/
void write_com(uchar com)
{
      lcdrs=0;
      lcdrw=0;
      lcden=0;
      P0=com;
      delay(5);
      lcden=1;
      delay(5);
      lcden=0;
}
/*************写数据******************/
void write_date(uchar date)
{
      lcdrs=1;
      lcdrw=0;
      lcden=0;
      P0=date;
      delay(5);
      lcden=1;
      delay(5);
      lcden=0;
}
/*************读AC 地址*****************/
void read_date(void)
{
      lcdrs=0;
      lcdrw=1;
	  delay(5);
	  P0=0xff;
      lcden=1;
      delay(5);
      state=P0;
      delay(5);
      lcden=0;delay(5);
	  state=state&127;
}
/*************写字符串******************/
void write_str(uchar *str)
{     
      uchar idata i;
      for(i=0;str[i]!='\0';i++)
         write_date(str[i]);
}
/***********液晶初始化***************/
void init(void )
{
      write_com(0x38);       //模式:8位数据,两行,5*7字体
      write_com(0x0c);       //开显示,无光标
      write_com(0x06);       //向左增量移动
      write_com(0x01);       //清屏
}


/**************键盘扫描*************/
uchar keyscan(void)          //按行扫描,有键按下则返回键符号,否则返回null
{     uchar idata key0,key1,keyment=0;
      P1=0Xef;					   //行1
      key0=P1;
      key0=key0&0x0f;
      key1=key0;
      if(key0!=0x0f)
      {
           delay(5);               //键抖动处理
           P1=0Xef;
           key0=P1;
           key0=key0&0x0f;
           if(key0==key1)
           {
                switch(key0)
                {
                  case 0x0e:keyment='7';break;
                  case 0x0d:keyment='8';break;
                  case 0x0b:keyment='9';break;
                  case 0x07:keyment=0xfd;break;  //0xfd除号在1602液晶中的代码
                }
                while(key0!=0x0f)  //键释放处理
                {
                  key0=P1;
                  key0=key0&0x0f;
                }
                press=1;		   //键按标志置1
                return(keyment);
           }
      }
      P1=0Xdf;					  //行2
      key0=P1;
      key0=key0&0x0f;
      key1=key0;
      if(key0!=0x0f)
      {
           delay(5);
           P1=0Xdf;
           key0=P1;
           key0=key0&0x0f;
           if(key0==key1)
           {
               switch(key0)
               {
                  case 0x0e:keyment='4';break;
                  case 0x0d:keyment='5';break;
                  case 0x0b:keyment='6';break;
                  case 0x07:keyment='*';break;
               }
               while(key0!=0x0f)
               {
                  key0=P1;
                  key0=key0&0x0f;
               }
               press=1;
               return(keyment);
           }
      }
      P1=0Xbf;						 //行3
      key0=P1;
      key0=key0&0x0f;
      key1=key0;
      if(key0!=0x0f)
      {
           delay(5);
           P1=0Xbf;
           key0=P1;
           key0=key0&0x0f;
              if(key0==key1)
           {
               switch(key0)
               {
                   case 0x0e:keyment='1';break;
                   case 0x0d:keyment='2';break;
                   case 0x0b:keyment='3';break;
                   case 0x07:keyment='-';break;
               }
               while(key0!=0x0f)
               {
                   key0=P1;
                   key0=key0&0x0f;
               }
               press=1;
               return(keyment) ;
           }
      }
      P1=0X7f;					  //行4
      key0=P1;
      key0=key0&0x0f;
      key1=key0;
      if(key0!=0x0f)
      {
           delay(5);
           P1=0X7f;
           key0=P1;
           key0=key0&0x0f;
           if(key0==key1)
           {
               switch(key0)
               {
                 case 0x0e:keyment='.';break;
                 case 0x0d:keyment='0';break;
                 case 0x0b:keyment='=';break;
                 case 0x07:keyment='+';break;
               }
               while(key0!=0x0f)
               {
                 key0=P1;
                 key0=key0&0x0f;
               }
               press=1;
               return(keyment);
           }
     }
/*     else  if(save==0)		   //储存数据键
     {	delay(5);
	    if(save==0)   
	    { if(sc==0)	   //输出前次计算结果键
	       {	
		      keyment='m';
              press=1;
              while(save==0);
              return(keyment);
		   }
		   else			   //更新存储的数据或输出存储的数据
		   {
              keyment='n';
              press=1;
			  sc=0;
              while(save==0);
              return(keyment);		      
		   }
		}  
     } */
	 else return(0);	   //无键按下返回null
}

/**************计算器复位****************/
void fuwei()
{   uchar idata j,i;
    write_com(0x0f);	    //lcd设置
    write_com(0x01);
    write_com(0x81);
    resultdate=0;
    for(i=0;i<2;i++)
	{ date[i]=0;bfun[i]=0;pai[0][i]=0;pai[1][i]=0;fsym[i]=0;futojiao[i]=0; //各标志置0
	  for(j=0;j<14;j++)		//字符数组初始化
      {	
        bdate[i][j]='\0';
      }
	}
    count=0;				//各标志置0
    count_num=0;
	proc=0;
    ferror=0;
    eqsign=0;
	sc=0;
	savesign=0;
	ywei=0;
	press=0;
	on_symbol=0; 
}
/**************error处理******************/
void callerror(void)
{   uchar idata i;
    write_com(0x01);
    write_com(0x81);
    write_str("     error!     ");
    for(i=0;i<200;i++)delay(25); //持续显示 1s
}

/*****************运算符预处理*****************/
void  cullars(uchar csym)
{	read_date();		   //看第二组是否有输入
	ferror++;              //检错标志+1
	continu=csym;		   //存储运算符号
	if(ferror==2&&state<0x43){callerror();huifu();return;}// 第二组无输入且ferror=2,错误
	if(ferror==2&&state>0x42)	//第二组有输入且ferror=2,则进行连续计算
	{  
	   press=1;					//模拟按键‘=’和‘M+’;将本次的计算结果作为下一次计算的第一组
	   process_date('=');
	   press=1;
	   process_date('m');
	   ferror=1;				//恢复现场,避免变化
	   if(continu=='^'||continu=='%'){process[proc]='h';
	        proc++;process[proc]=continu;proc++;}
	   else{process[proc]=continu;proc++;}	  //恢复连续计算过程
	}
	if(continu=='%'){write_str(" f");}else write_date(' ');
	write_date(continu);//将用与连续计算的运算符号取出
	ywei=0;				   
	savesign=0;
	count_num=1;     //组标志加1
	count=0;		 //输入计数清零
	on_symbol=continu;  //运算符号调用
	write_com(0xc2);
	return;
}
/*************输出存储数据预处理************/
char  memory(void)
{	if((bdate[count_num][0]=='+'||bdate[count_num][0]=='-')&&count==1)//符号处理			
    {
	 	ssym[count_num]=bdate[count_num][0];
	 	bdate[count_num][0]='\0';
		bdate[count_num][1]='\0';
		count=0;
	}
    if(bdate[count_num][0]!='\0'){callerror();huifu();return(1);}	//错误输入方式处理
	return(0);
}
/**************按键功能主处理****************/
void process_date(uchar press_date)
{    
     if(form==0){write_com(0x0f);form=1;}   //显示方式切换 光标闪烁	
     if(eqsign==1&&press==1)fuwei();		//复位再次计算,记忆过程清零
	 press=0;
	 if(press_date=='c'){huifu();press_date=0;}
	 if(press_date==0)return;         	//无按键
	 process[proc]=press_date;
	 proc++;
	 calculator(press_date);
	 return;
}
/***************按键功能子处理***************/
void calculator(uchar press_date)
{	 
     uchar idata j=0;
	 /*************************************/
	 if((press_date<='9'&&press_date>='0')||(press_date=='.')) //数字键处理
     {	savesign=0;			  //不可更新存储数据
        if(sc==0)			  //是否为复用sc为0,是数字
        { 
           if(count<14-ywei)  //未调用函数时最多输入14位数,包括小数点
           {	
			       write_date(press_date);
                   bdate[count_num][count]=press_date; //储存键值
                   count++;
           }
           else {callerror();huifu();}  //超出14位出错,恢复

        }
      }
       /*************************************************/
      else if(press_date=='m')       //上次计算结果输出
      {	 
           if(memory()==1)return;						  //输出存储数据预处理
           for(j=0;result[j]!='\0';j++)write_date(result[j]);	        //输出存储值
           strcpy(bdate[count_num],result);
		   savesign=1;		  //此时可以更新saveresult中存储的数据
		   count=14;          //count=14是让本组数据已确定
	  }
      /****************'+','-'处理*****************/
       else if((press_date=='+')||(press_date=='-'))
       {    
		      if(count==0)      // 表示输入数据的符号,此时默认数值为0
              {    				   
                write_date(press_date);
                bdate[count_num][0]=press_date; 
				bdate[count_num][1]='0';
				read_date();			//读状态
	            write_date('0');
	            write_com(state+0x81);//光标还回原来位置
                count=1;
              }
              else			   //为运算符号
              {   
			    cullars(press_date);
              }			
       }
       /****************'*','/'处理*****************/
       else if(press_date==0xfd) //0xfd除号在1602液晶中的代码
       {	
	       if(sc==1)	   //幅度 to 角度
		   {  if(count!=0)
		      {futojiao[count_num]=1;sc=0;write_date(0xDF);count=14;}
			  else {callerror();huifu();}
		   }
     	   else cullars(press_date);
       }
	   else if(press_date=='*') //0xfd除号在1602液晶中的代码
       {		       
     	    cullars(press_date);
       }
       /*******************'='号处理*****************/
       else if(press_date=='=')
       { 
            write_com(0x01);           //清屏
            write_com(0x80+0x40+4);
            write_date(press_date);    //显示等号
		    for(j=0;j<2;j++)		   //两运算组的函数处理
			{
			   	date[j]=atof(bdate[j]);  //俩组数据,字符串->实数
				if(fsym[j]=='-')date[j]=-date[j];	 //函数前的符号处理 
			}
			if(ferror==0)							 //仅仅函数计算
			   {  resultdate=date[0];}
            else									 //运算符号计算
			{
			   switch(on_symbol)
               {
                  case '+':resultdate=date[0]+date[1];break;
                  case '-':resultdate=date[0]-date[1];break;
                  case '*':resultdate=date[0]*date[1];break;
                  case 0xfd:resultdate=date[0]/date[1];break;       //0xfd除号在1602液晶中的代码
				  case '^':resultdate=pow(date[0],date[1]);break;	//pow()函数	x^y
				  case '%':resultdate=fmod(date[0],date[1]);break;  //fmod()函数 求余数(双精度)
               }
			}			
            sprintf(jieguo,"%g",resultdate);//结果实数->字符串,采用格式字符g,系统选择%f或%e输出格式,六位有效数字           
	        if((jieguo[0]>'9'||jieguo[0]<'0')&&(jieguo[1]>'9'||jieguo[1]<'0')&&jieguo[1]!='\0'&&jieguo[1]!='.')
			 {   callerror();fuwei();return;  }	 //错误处理,当组1或组2不合法时结果是几个英文字母提示	
			else strcpy(result,jieguo);   //没错误,保存结果,结果立存 M+键			                                        	   			 
            for(j=0;jieguo[j]!='\0';j++)               //显示结果
                 write_date(jieguo[j]);
			for(;j<15;j++)write_date(' ');	 //让光标出屏幕
			eqsign=1;						 //按等号键标志置1
	   }
	   return;
}											 
/*********************主程序**********************/
void main()
{   
      fuwei();		//计算器初始化
      init();       //显示初始化	  
      TH0=0x3c;     //定时器0初始化
      TL0=0xb7;
      TMOD=0x1;     //定时器0,方式1
      TR0=1;        //开始计时
      ET0=1;
      EA=1;         //允许溢出中断设置
	  form=1;		//显示方式初始化
	  proc=0;		//计算工程存储初始化
	  	      write_com(0x81); 
      while(1)      
      {
	   process_date(keyscan()); //计算器
      }
}
/*********************end**********************/
/**********************************************/

源文件下载
链接: 源文件下载.

  • 13
    点赞
  • 138
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个小小白 - 呀吼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值