MCU-51:51单片机实现简单计算器功能

本文介绍了如何使用单片机、LCD1602显示屏和矩阵键盘实现一个具备多位显示、小数计算和连续计算功能的简易计算器。代码实现了按键扫描、数字移位、加减乘除运算以及错误修正。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


今天不打算学新知识了,把前段时间学的复习下。
用单片机按键实现计算器简单功能,开干!

一、要实现什么功能

多位显示,小数计算,连续计算,符号按错修改

二、怎样实现

LCD1602显示屏,矩阵键盘
为什么不用数码管呢?数码管需要扫描,而且对于加减乘除这些符号不是很好显示
矩阵键盘按键图如下:
在这里插入图片描述

三、代码演示

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "delay.h"
#include "anjian.h"
#include "Buzzer.h"
 
/*
number1为第一次输入的按键
number2为第二次输入的按键
number3为第三次输入的按键
number4为第四次输入的按键
number为计算结果
password1和password3构成number1,password2和password4构成number2
count1,count2,count3,count4为避免输入的数字溢出的计数变量
计算的数字可能是:
整数与整数
整数与小数
小数与整数
小数与小数
按键说明:
键盘上的S1到S9分别是1到9,然后S10是0
S11为小数点,S12在第一次个数输入的时候为清除键,在第二个数输入后为等于键
S13到S16分别为加,减,乘,除。
*/
 
float number,password1,password2;
unsigned long	number1,number2,number3,number4,password3,password4;
unsigned char symbol,count1,count2,count3,count4;
void main(){
while(1){  
	 LCD_Init();
	 LCD_ShowString(1,1,"Calculator x4.0");
   LCD_ShowString(2,1,"Wellcome");
	 password3=0;
   count2=0;
	while(1){
      if(symbol==13||symbol==14||symbol==15||symbol==16)
      {break;}
	 
		  number1=jianpan();		
		  if(number1){		
	  	Buzzer_Time(100);
 
			if(number1==12){
			LCD_ShowString(2,1,"            ");
		  count1=0;
			password1=0;
			LCD_ShowNum(2,1,password1,8); 
			}			
 
			if(number1<=10){
 
				if(count1<8){//最多输入8位,避免溢出
 
					  password1=10*password1;//这里两行是关键,将输入的单个数字依次移位
					  password1=number1%10+password1;			      
 
					  if(count1>=0)
							LCD_ShowNum(2,8,password1,1);
						  LCD_ShowString(2,1,"       ");
						if(count1>=1)
							LCD_ShowNum(2,7,(unsigned long)(password1/10)%10,1);
						  LCD_ShowString(2,1,"      ");
						if(count1>=2)
							LCD_ShowNum(2,6,(unsigned long)(password1/100)%10,1);
						  LCD_ShowString(2,1,"     ");
						if(count1>=3)
							LCD_ShowNum(2,5,(unsigned long)(password1/1000)%10,1);
						  LCD_ShowString(2,1,"    ");
						if(count1>=4)
							LCD_ShowNum(2,4,(unsigned long)(password1/10000)%10,1);
						  LCD_ShowString(2,1,"   ");
						if(count1>=5)
							LCD_ShowNum(2,3,(unsigned long)(password1/100000)%10,1);
						  LCD_ShowString(2,1,"  ");
						if(count1>=6)
							LCD_ShowNum(2,2,(unsigned long)(password1/1000000)%10,1);
						  LCD_ShowString(2,1," ");
						if(count1>=7)
							LCD_ShowNum(2,1,(unsigned long)(password1/10000000)%10,1);
 
				}
				   count1++;					 				 			 				 			  				  
		}
 
	  	if(number1==11)//不用switch,case可以提高容错率,多次改变符号的选择
			break;		  
			if(number1==13){	 
			LCD_ShowString(2,1,"            ");	 
			LCD_ShowChar(2,1,'+');   
			symbol=number1;			
			break;
			}
			if(number1==14){	  		
			LCD_ShowString(2,1,"            ");
			LCD_ShowChar(2,1,'-');
			symbol=number1;				
			break;
			}
			if(number1==15){
			LCD_ShowString(2,1,"            ");
			LCD_ShowChar(2,1,'*');
			symbol=number1;				
			break;
			}	
			if(number1==16){
			LCD_ShowString(2,1,"            ");
			LCD_ShowChar(2,1,'/');
			symbol=number1;				
			break;
			} 			
 
		}
}		
    	while(1){
	 
	    if(symbol==13){ 
			LCD_ShowString(2,1,"        ");
			LCD_ShowChar(2,1,'+');				 
			break;
			}
			if(symbol==14){ 
			LCD_ShowString(2,1,"        ");
			LCD_ShowChar(2,1,'-');				 
			break;
			}
			if(symbol==15){ 
			LCD_ShowString(2,1,"        ");
			LCD_ShowChar(2,1,'*');				 
			break;
			}
			if(symbol==16){ 
			LCD_ShowString(2,1,"        ");
			LCD_ShowChar(2,1,'/');				 
			break;
			}
	 
			number2=jianpan();	
	    if(number1==11){//如果上次输入的是小数点,那么进入此循环,否则不会进入(第一个数为整数时)			
			   LCD_ShowChar(2,9,'.');				
	  		 if(number2){
	  		 Buzzer_Time(100);
				 if(number2<=10){
							 if(count2<3){//最多输入3位,避免溢出					
										password3=10*password3;//这里两行是关键,将输入的单个数字依次移位
										password3=number2%10+password3;										
 
								    if(count2>=0)
										{
										  LCD_ShowNum(2,10,password3,1); 
										}
										if(count2>=1)
										{ 
											LCD_ShowNum(2,10,(password3/10)%10,1);	
										  LCD_ShowNum(2,11,password3,1);
                      										
										}
										if(count2>=2)
										{
										  LCD_ShowNum(2,12,password3,1); 
											LCD_ShowNum(2,11,(password3/10)%10,1); 
											LCD_ShowNum(2,10,(password3/100)%10,1);
										}
																
//								    LCD_ShowNum(2,10,password3,1); 
//										LCD_ShowNum(2,11,(password3/10)%10,1); 
//										LCD_ShowNum(2,12,(password3/100)%10,1);					
                    
							 }
							    	count2++;					 				 			 				 			  				  
						}
 
				if(number2==13){//符号显示
				LCD_ShowString(2,1,"            ");	 
				LCD_ShowChar(2,1,'+');
				symbol=number2;		
				break;
				}
				if(number2==14){	  		
				LCD_ShowString(2,1,"            ");
				LCD_ShowChar(2,1,'-');
        symbol=number2;						
				break;
				}
				if(number2==15){
				LCD_ShowString(2,1,"            ");
				LCD_ShowChar(2,1,'*');
        symbol=number2;						
				break;
				}	
				if(number2==16){
				LCD_ShowString(2,1,"            ");
				LCD_ShowChar(2,1,'/');
				symbol=number2;						
				break;
		    }
						
			}		
	 }
     else
		 break;
}
			
password2=0;//(这里要注意一点是在进行连续运算的时候,第二次输入的数字要清零,不然的话到到后来第三次输入的数字是在第二次输入的基础上输入的)
password4=0;	
count3=0;//(这里要注意一点是在进行连续运算的时候,计数变量要清零,不然的话到到后来可能输入不了数字**)
count4=0;
 
  while(1){
	    number3=jianpan();		
		  if(number3){
			Buzzer_Time(100);
			if(number3<=10){
		  if(count3<8){//最多输入8位,避免溢出
				
						password2=10*password2;//这里两行是关键,将输入的单个数字依次移位
						password2=number3%10+password2;	      
 
     				if(count3>=0)
							LCD_ShowNum(2,8,password2,1);
						  LCD_ShowString(2,1,"       ");
						if(count3>=1)
							LCD_ShowNum(2,7,(unsigned long)(password2/10)%10,1);
						  LCD_ShowString(2,1,"      ");
						if(count3>=2)
							LCD_ShowNum(2,6,(unsigned long)(password2/100)%10,1);
						  LCD_ShowString(2,1,"     ");
						if(count3>=3)
							LCD_ShowNum(2,5,(unsigned long)(password2/1000)%10,1);
						  LCD_ShowString(2,1,"    ");
						if(count3>=4)
							LCD_ShowNum(2,4,(unsigned long)(password2/10000)%10,1);
						  LCD_ShowString(2,1,"   ");
						if(count3>=5)
							LCD_ShowNum(2,3,(unsigned long)(password2/100000)%10,1);
						  LCD_ShowString(2,1,"  ");
						if(count3>=6)
							LCD_ShowNum(2,2,(unsigned long)(password2/1000000)%10,1);
						  LCD_ShowString(2,1," ");
						if(count3>=7)
							LCD_ShowNum(2,1,(unsigned long)(password2/10000000)%10,1);
	
				 }
				 count3++;
	   }		 				 		 		 
 
     if(number3==11){break;}
		 if(number3==12){
			 
			  if(count2==1) password3=password3*100;//利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一
				if(count2==2) password3=password3*10;
			 
	   switch (symbol){//整数(小数)x小数
							case 13:  number=(password1+password3*0.001)+(password2+password4*0.001);  break; 	
							case 14:  number=(password1+password3*0.001)-(password2+password4*0.001);  break;
							case 15:  number=(password1+password3*0.001)*(password2+password4*0.001);  break;
							case 16:  number=(password1+password3*0.001)/(password2+password4*0.001);  break;
						}
      
		 	  LCD_ShowString(2,1,"result:");
				LCD_ShowNum(2,8,number,1); 
				LCD_ShowNum(2,7,(unsigned long)(number/10)%10,1); 
				LCD_ShowNum(2,6,(unsigned long)(number/100)%10,1); 
				LCD_ShowNum(2,5,(unsigned long)(number/1000)%10,1); 
				LCD_ShowNum(2,4,(unsigned long)(number/10000)%10,1); 
				LCD_ShowNum(2,3,(unsigned long)(number/100000)%10,1); 
				LCD_ShowNum(2,2,(unsigned long)(number/1000000)%10,1); 
				LCD_ShowNum(2,1,(unsigned long)(number/10000000)%10,1);
		  
        LCD_ShowChar(2,9,'.');		
        LCD_ShowNum(2,10,(unsigned long)(number*1000)%1000,3);
 
 
		 }
 
				if(number3==13||number3==14||number3==15||number3==16)
					{ 
						if(count2==1) password3=password3*100;//连续运算中利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一		
						if(count2==2) password3=password3*10;
						
					switch (symbol){//整数(小数)x小数
					case 13:  number=(password1+password3*0.001)+(password2+password4*0.001);  break; 	
					case 14:  number=(password1+password3*0.001)-(password2+password4*0.001);  break;
					case 15:  number=(password1+password3*0.001)*(password2+password4*0.001);  break;
					case 16:  number=(password1+password3*0.001)/(password2+password4*0.001);  break;
					}
					password1=number;
					symbol=number3;	         				
					number=0;
					break;//结束小循环,重新进行第一步的输入操作
					}  
			
		}
 
		
  }		
  while(1)
   {
		if(number3==13||number3==14||number3==15||number3==16)
		{break;}
    number4=jianpan();		 
		if(number3==11){	  
			LCD_ShowChar(2,9,'.');				
	  		   if(number4){ 
						Buzzer_Time(100);
				    if(number4<=10){
							 if(count4<3){//最多输入3位,避免溢出					
										password4=10*password4;//这里两行是关键,将输入的单个数字依次移位
										password4=number4%10+password4;										
 
										if(count4>=0)
										{
										  LCD_ShowNum(2,10,password4,1); 
										}
										if(count4>=1)
										{ 
											LCD_ShowNum(2,10,(password4/10)%10,1);	
										  LCD_ShowNum(2,11,password4,1);
                      										
										}
										if(count4>=2)
										{
										  LCD_ShowNum(2,12,password4,1); 
											LCD_ShowNum(2,11,(password4/10)%10,1); 
											LCD_ShowNum(2,10,(password4/100)%10,1);
										}
								     				                  
							 }
								 count4++;					 				 			 				 			  				  
						} 
				  
					 if(number4==12){
						 
						 if(count2==1) password3=password3*100;//利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一
						 if(count2==2) password3=password3*10;
						 if(count4==1) password4=password4*100;
						 if(count4==2) password4=password4*10;
						 
						switch (symbol){//整数(小数)x小数
									case 13:  number=(password1+password3*0.001)+(password2+password4*0.001);  break; 	
									case 14:  number=(password1+password3*0.001)-(password2+password4*0.001);  break;
									case 15:  number=(password1+password3*0.001)*(password2+password4*0.001);  break;
									case 16:  number=(password1+password3*0.001)/(password2+password4*0.001);  break;
									}
 
									LCD_ShowString(2,1,"result:");
							    LCD_ShowNum(2,8,number,1); 
									LCD_ShowNum(2,7,(unsigned long)(number/10)%10,1); 
									LCD_ShowNum(2,6,(unsigned long)(number/100)%10,1); 
									LCD_ShowNum(2,5,(unsigned long)(number/1000)%10,1); 
									LCD_ShowNum(2,4,(unsigned long)(number/10000)%10,1); 
									LCD_ShowNum(2,3,(unsigned long)(number/100000)%10,1); 
									LCD_ShowNum(2,2,(unsigned long)(number/1000000)%10,1); 
									LCD_ShowNum(2,1,(unsigned long)(number/10000000)%10,1);
									
							    LCD_ShowChar(2,9,'.');		
								  LCD_ShowNum(2,10,(unsigned long)(number*1000)%1000,3);		 
 
				          }
								  
								 if(number4==13||number4==14||number4==15||number4==16)
								  {
									 if(count2==1) password3=password3*100;//连续运算中利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一		
									 if(count2==2) password3=password3*10;
									 if(count4==1) password4=password4*100;
									 if(count4==2) password4=password4*10;
             										
									switch (symbol){//整数(小数)x小数
									case 13:  number=(password1+password3*0.001)+(password2+password4*0.001);  break; 	
									case 14:  number=(password1+password3*0.001)-(password2+password4*0.001);  break;
									case 15:  number=(password1+password3*0.001)*(password2+password4*0.001);  break;
									case 16:  number=(password1+password3*0.001)/(password2+password4*0.001);  break;
									}
									password1=number;
									symbol=number4;									
									number=0;
									break;//结束小循环,重新进行第一步的输入操作  
									}			
									 
 
	            }
		
          }
 
       } 
    }
 
}

anjian.c

#include <REGX52.H>
#include "delay.h"
/**
  * @brief  矩阵键盘
  * @param  无
  * @retval 按下按键时进入if()和while()中,松开手的一瞬间,返回值生成,并返回原函数
	*         无按键按下时,返回初始值0(为什么主函数要加if?就是用来检测是否按下按键,只有按下才进入循环)
  */
unsigned int jianpan(){
  
	unsigned int number=0;
	
	while(1){
	
	P1=0xFF;//在每一列前面都应该有,否则会出现反复按一个数时,出现和这个数一行的别的数
	P1_3=0;
	if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=1;}
	if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=5;}
	if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=9;}
	if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=13;}
	
	P1=0xFF;
	P1_2=0;
	if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=2;}
	if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=6;}
	if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=10;}
	if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=14;}
	
	P1=0xFF;
	P1_1=0;
	if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=3;}
	if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=7;}
	if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=11;}
	if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=15;}
	
	P1=0xFF;
	P1_0=0;
	if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=4;}
	if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=8;}
	if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=12;}
	if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=16;}
 
 
	return number;
  }
}

LCD1602.c

#include <REGX52.H>
 
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
 
//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;
 
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}
 
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}
 
/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}
 
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}
 
/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}
 
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}
 
/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}
 

delay.c

void delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
while(xms){
			i = 12;
			j = 169;
			do
			{
				while (--j);
			} while (--i);
     xms--;
		}
}

Buzzer.c

#include <REGX52.H>
#include <INTRINS.H>
 
//蜂鸣器端口:
sbit Buzzer=P2^5;
 
/**
  * @brief  蜂鸣器私有延时函数,延时500us
  * @param  无
  * @retval 无
  */
void Buzzer_Delay500us()		//@12.000MHz
{
	unsigned char i;
 
	_nop_();
	i = 247;
	while (--i);
}
 
/**
  * @brief  蜂鸣器发声
  * @param  ms 发声的时长,范围:0~32767
  * @retval 无
  */
void Buzzer_Time(unsigned int ms)
{
	unsigned int i;
	for(i=0;i<ms*2;i++)//2x500us=1ms
	{
		Buzzer=!Buzzer;
		Buzzer_Delay500us();//发声频率1000Hz,一次循环用时0.5ms,故29行的ms要*2
		}//f=1/T,一个周期为2x500us=1ms(一个波峰和一个波谷),f=1/(1x10^-3)=1000Hz
}

注意:一定要看

千万不要直接复制代码,因为是模块化编写,模块的代码没有往上复制,大家一定要自己动手!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hello xiǎo lěi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值