基于51单片机的简易计算器

1.简介
本计算器是以MCS-51系列AT89C51单片机为核心构成的简易计算器系统。该系统通过单片机控制,实现对4*4键盘扫描进行实时的按键检测,并由LCD1602显示屏将过程与结果显示出来。
2.硬件原理图
基于51单片机的简易计算器_第1张图片
硬件主要由四部分组成

  • 单片机最小系统
  • 4*4矩阵键盘
  • LCD1602显示屏
  • 系统电源

3.程序设计
(1)矩阵键盘驱动程序

/*------------------------------------------------
         矩阵键盘按键值
  			| 1 | 2 | 3 | + |  
  			| 4 | 5 | 6 | - |  
  			| 7 | 8 | 9 | * |  
  			| 0 | . | = | / | 
  #define KeyPort P1

/*------------------------------------------------
按键扫描函数,返回扫描键值
------------------------------------------------*/
unsigned char KeyScan(void)  //键盘扫描函数,使用行列反转扫描法
{
 unsigned char cord_h,cord_l;//行列值中间变量
 KeyPort=0x0f;            //行线输出全为0
 cord_h=KeyPort&0x0f;     //读入列线值
 if(cord_h!=0x0f)    //先检测有无按键按下
 {
  DelayMs(10);        //去抖
  if((KeyPort&0x0f)!=0x0f)
  {
    cord_h=KeyPort&0x0f;  //读入列线值
    KeyPort=cord_h|0xf0;  //输出当前列线值
    cord_l=KeyPort&0xf0;  //读入行线值

    while((KeyPort&0xf0)!=0xf0);//等待松开并输出

    return(cord_h+cord_l);//键盘最后组合码值
   }
  }return(0xff);     //返回该值
}
/*------------------------------------------------
          按键值处理函数,返回扫键值
           可以根据需要改变返回值
------------------------------------------------*/
unsigned char KeyPro(void)
{
 switch(KeyScan())
 {
  case 0x7e:return '1';break;//0 按下相应的键显示相对应的码值
  case 0x7d:return '2';break;//1
  case 0x7b:return '3';break;//2
  case 0x77:return '+';break;//3

  case 0xbe:return '4';break;//4
  case 0xbd:return '5';break;//5
  case 0xbb:return '6';break;//6
  case 0xb7:return '-';break;//7

  case 0xde:return '7';break;//8
  case 0xdd:return '8';break;//9
  case 0xdb:return '9';break;//a
  case 0xd7:return 'x';break;//b

  case 0xee:return '0';break;//c
  case 0xed:return '.';break;//d
  case 0xeb:return '=';break;//e
  case 0xe7:return '/';break;//f
  default:return 0xff;break;
 }
}

(2)LCD1602驱动程序

#define CHECK_BUSY

sbit RS = P2^4;   //定义端口 
sbit RW = P2^5;
sbit EN = P2^6;

#define RS_CLR RS=0 
#define RS_SET RS=1

#define RW_CLR RW=0 
#define RW_SET RW=1 

#define EN_CLR EN=0
#define EN_SET EN=1

#define DataPort P0

/*------------------------------------------------
              判忙函数
------------------------------------------------*/
 bit LCD_Check_Busy(void) 
 { 
#ifdef CHECK_BUSY
 DataPort= 0xFF; 
 RS_CLR; 
 RW_SET; 
 EN_CLR; 
 _nop_(); 
 EN_SET;
 return (bit)(DataPort & 0x80);
#else
 return 0;
#endif
 }
/*------------------------------------------------
              写入命令函数
------------------------------------------------*/
 void LCD_Write_Com(unsigned char com) 
 {  
 while(LCD_Check_Busy()); //忙则等待
 RS_CLR; 
 RW_CLR; 
 EN_SET; 
 DataPort= com; 
 _nop_(); 
 EN_CLR;
 }
/*------------------------------------------------
              写入数据函数
------------------------------------------------*/
 void LCD_Write_Data(unsigned char Data) 
 { 
 while(LCD_Check_Busy()); //忙则等待
 RS_SET; 
 RW_CLR; 
 EN_SET; 
 DataPort= Data; 
 _nop_();
 EN_CLR;
 }

/*------------------------------------------------
                清屏函数
------------------------------------------------*/
 void LCD_Clear(void) 
 { 
 LCD_Write_Com(0x01); 
 DelayMs(5);
 }
/*------------------------------------------------
              写入字符串函数
------------------------------------------------*/
 void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s) 
 {     
       
 while (*s) 
 	{     
 LCD_Write_Char(x,y,*s);     
 s ++;  x++;   
 	}
 }
/*------------------------------------------------
              写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) 
 {     
 if (y == 0) 
 	{     
 	LCD_Write_Com(0x80 + x);     
 	}    
 else 
 	{     
 	LCD_Write_Com(0xC0 + x);     
 	}        
 LCD_Write_Data( Data);  
 }
/*------------------------------------------------
              初始化函数
------------------------------------------------*/
void LCD_Init(void) 
{
	 LCD_Write_Com(0x38);    /*显示模式设置*/ 
	 DelayMs(5); 
	 LCD_Write_Com(0x38); 
	 DelayMs(5); 
	 LCD_Write_Com(0x38); 
	 DelayMs(5); 
	 LCD_Write_Com(0x38);  
	 LCD_Write_Com(0x08);    /*显示关闭*/ 
	 LCD_Write_Com(0x01);    /*显示清屏*/ 
	 LCD_Write_Com(0x06);    /*显示光标移动设置*/ 
	 DelayMs(5); 
	 LCD_Write_Com(0x0C);    /*显示开及光标设置*/
} 

(3)主函数

/*------------------------------------------------
                    主程序
------------------------------------------------*/
main()
{
 unsigned char num,i,sign;                  
 unsigned char temp[16];        //最大输入16个
 bit firstflag;
 float a=0,b=0;
 unsigned char s;

 
 LCD_Init();         //初始化液晶屏
 DelayMs(10);        //延时用于稳定,可以去掉
 LCD_Clear();
 LCD_Write_String(0,0," LCD calculator");    //写入第一行信息,主循环中不再更改此信息,所以在while之前写入
 LCD_Write_String(0,1," Fun: + - x / ");    //写入第二行信息,提示输入密码
 while (1)         //主循环
 {
   num=KeyPro();  //扫描键盘
   if(num!=0xff)  //如果扫描是按键有效值则进行处理
   { 
      if(i==0)    //输入是第一个字符的时候需要把改行清空,方便观看
	  LCD_Clear();
	 if(('+'==num)|| (i==16) || ('-'==num) || ('x'==num)|| ('/'==num) || ('='==num))//输入数字最大值16,输入符号表示输入结束
	  {
	     i=0;  //计数器复位
	     if(firstflag==0)  //如果是输入的第一个数据,赋值给a,并把标志位置1,到下一个数据输入时可以跳转赋值给b
	     {
	       sscanf(temp,"%f",&a);
           firstflag=1;
		 }
	     else  
	       sscanf(temp,"%f",&b);
	     for(s=0;s<16;s++) //赋值完成后把缓冲区清零,防止下次输入影响结果
		    temp[s]=0;
         LCD_Write_Char(0,1,num); 
	  ///////////////////////
	     if(num!='=')      //判断当前符号位并做相应处理
	       sign=num;      //如果不是等号记下标志位
	     else
	     {  
		    firstflag=0;   //检测到输入=号,判断上次读入的符合
            switch(sign)
	       {
		     case '+':a=a+b;
			 break;
			 case '-':a=a-b;
			 break;
			 case 'x':a=a*b;
			 break;
			 case '/':a=a/b;
			 break;
			 default:break;
		   }
		   sprintf(temp,"%g",a);    //输出浮点型,无用的0不输出
           LCD_Write_String(1,1,temp);//显示到液晶屏
		   sign=0;a=b=0;            //用完后所有数据清零
		   for(s=0;s<16;s++)
		      temp[s]=0;
		 }
	  }
     else	if(i<16)
	 {
	    if((1==i)&& (temp[0]=='0') )//如果第一个字符是0,判读第二个字符
	    {
		  if(num=='.')  //如果是小数点则正常输入,光标位置加1
		  {
		    temp[1]='.';
			LCD_Write_Char(1,0,num);//输出数据
			i++;
          }           //这里没有判断连续按小数点,如0.0.0 
		 else
		 {
		   temp[0]=num; //如果是1-9数字,说明0没有用,则直接替换第一位0
		   LCD_Write_Char(0,0,num);//输出数据
		 }
	   }
	   else
	   {
         temp[i]=num; 
         LCD_Write_Char(i,0,num);//输出数据
	     i++;   //输入数值累加
	   }
	 }
    }	
  }
}

源码+AD电路图 下载:关注公众号,首页回复“计算器”获取资料
在这里插入图片描述

你可能感兴趣的:(单片机应用)