使用单片机实现简易计算器

单片机实现简易计算器

  • 涉及元器件
  • 使用软件
  • 整体介绍
  • 具体实现步骤
  • 一种数据结构——栈
  • 核心思路分析
  • 面临问题
  • 代码实现
  • LCD1602模块
  • 蜂鸣器模块Buzzer
  • Delay延时函数模块
  • 矩阵键盘模块Matrix
  • 主函数模块
  • 需要导入的头文件
  • 定义宏,替换加减乘除的序号
  • 定义的变量
  • 函数声明
  • 主函数
  • 主函数模块整体代码
  • 结果展示
  • 涉及元器件

  • STC89C52RC单片机
  • 矩阵键盘
  • LCD1602液晶显示屏
  • 使用软件

  • Keil uVision5
  • stc-isp-v6.93
  • 整体介绍

    实现功能:使用矩阵键盘输入操作数和运算符,可以在LCD1602液晶显示屏上实时显示,并且按下矩阵键盘的S16,相当于等于号,就可以得出计算结果。

    具体操作方法:

    1. 矩阵键盘S1-S9分别对应数字1-9,S10代表数字0,S11-S14对应运算符加减乘除,S15的作用是清屏,S16的作用是显示计算结果,相当于等号。
    2. 使用者只需要输入一串表达式,LCD1602会实时显示在第一行上,但是由于LCD1602只有16*2的尺寸,所以输入的表达式不能过长,输入完表达式之后按下S16就会显示计算结果在第二行上。
    3. 输入的操作数控制在三位整数以内,运算结果不能超过unsigned int类型的表示范围,不支持负数运算,不支持括号运算。

    具体实现步骤

    一种数据结构——栈

    栈是一种先入后出的数据结构,如下图所示:

    可以看到,不管是入栈还是出栈,操作的都是栈顶,这样就很轻松的完成了先入后出的操作,因为先入的元素沉在了最底下,变成了栈底元素。本次的程序需要用到此数据结构

    核心思路分析

    如何让输入的中缀表达式能够进行运算,我们此处需要定义两个栈,一个是操作数栈,一个是运算符栈,同时要定义操作符的优先级

    1. 从左到右扫描输入的表达式,当遇到操作数时直接入栈
    2. 遇到运算符时,查看运算符栈是否为空
    3. 如果运算符栈不为空,比较运算符栈顶的运算符和当前扫描到的运算符的优先级,如果运算符栈顶的运算符优先级低,那么当前运算符直接入栈
    4. 如果运算符栈顶的运算符优先级高,运算符栈顶元素出栈,同时在操作数栈中取两个操作数,让这两个操作数和出栈的运算符进行运算,得到的结果再入操作数栈,最后将扫描到的运算符入运算符栈
      此处注意,先出栈的操作数的右操作数,后出栈的操作数是左操作数
    5. 当表达式扫描完毕,也就是按下矩阵键盘上的S16,就顺序的从操作数栈和运算符栈中取出相应的操作数和运算符,进行最后的运算

    面临问题

    1. 如何按下矩阵按键实时显示在LCD1602屏幕上面?
    2. 由于是边输入边进行计算,而不是现成的完整表达式,应该选择在什么时机进栈?
    3. 如何判断输入一个操作数是否完成?此处可输入三位的操作数,可以输入002也可以输入200,如何保证读取到的结果是正确的?
    4. 最后按下S16计算结果的时候,运算符栈中真的只有一个元素吗?
    5. 要定义什么栈的基本操作

    代码实现

    LCD1602模块

    LCD1602主要有以下几个函数,可以在屏幕上显示字符、字符串、无符号数、有符号数、十六进制数、二进制数
    使用方法:导入头文件,先调用LCD_Init();初始化函数进行初始化,然后就可以任意调用其中的函数进行显示你想显示的内容

    LCD1602.h头文件定义如下:

    #ifndef __LCD1602_H__
    #define __LCD1602_H__
    
    //用户调用函数:
    void LCD_Init();
    void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
    void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
    void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
    void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    #endif
    
    

    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');
    	}
    }
    
    /**
      * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:-32768~32767
      * @param  Length 要显示数字的长度,范围:1~5
      * @retval 无
      */
    void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
    {
    	unsigned char i;
    	unsigned int Number1;
    	LCD_SetCursor(Line,Column);
    	if(Number>=0)
    	{
    		LCD_WriteData('+');
    		Number1=Number;
    	}
    	else
    	{
    		LCD_WriteData('-');
    		Number1=-Number;
    	}
    	for(i=Length;i>0;i--)
    	{
    		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
    	}
    }
    
    /**
      * @brief  在LCD1602指定位置开始以十六进制显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:0~0xFFFF
      * @param  Length 要显示数字的长度,范围:1~4
      * @retval 无
      */
    void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
    {
    	unsigned char i,SingleNumber;
    	LCD_SetCursor(Line,Column);
    	for(i=Length;i>0;i--)
    	{
    		SingleNumber=Number/LCD_Pow(16,i-1)%16;
    		if(SingleNumber<10)
    		{
    			LCD_WriteData(SingleNumber+'0');
    		}
    		else
    		{
    			LCD_WriteData(SingleNumber-10+'A');
    		}
    	}
    }
    
    /**
      * @brief  在LCD1602指定位置开始以二进制显示所给数字
      * @param  Line 起始行位置,范围:1~2
      * @param  Column 起始列位置,范围:1~16
      * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
      * @param  Length 要显示数字的长度,范围:1~16
      * @retval 无
      */
    void LCD_ShowBinNum(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(2,i-1)%2+'0');
    	}
    }
    
    

    蜂鸣器模块Buzzer

    蜂鸣器模块主要是为了发出按键提示音

    Buzzer.h

    #ifndef __BUZZER_H__
    #define __BUZZER_H__
    
    void Buzzer_Time(unsigned int xms);
    
    #endif
    

    Buzzer.c

    #include <REGX52.H>
    #include <INTRINS.H>
    #include "Delay.h"
    
    //蜂鸣器端口
    sbit Buzzer=P2^5;
    
    /**
     * @brief 蜂鸣器私有延迟500us函数
     * @param 无
     * @retval 无
     */ 
    void Buzzer_Delay500us(void)	//@11.0592MHz
    {
    	unsigned char data i;
    
    	_nop_();
    	i = 227;
    	while (--i);
    }
    
    /**
     * @brief 蜂鸣器持续发声时间
     * @param 时间 单位ms
     * @retval 无
     */ 
    void Buzzer_Time(unsigned int ms)
    {
    	unsigned int i;
    	for(i=0;i<ms*2;i++) 
    	{
    		Buzzer=!Buzzer;
    		Buzzer_Delay500us();
    	}
    }
    
    

    Delay延时函数模块

    其主要作用是延迟一定时间

    Delay.h

    #ifndef __DELAY_H__
    #define __DELAY_H__
    
    void Delay(unsigned int xms);
    
    #endif
    

    Delay.c

    void Delay(unsigned int xms)	//@12.000MHz
    {
    	unsigned char data i, j;
    	
    	while(xms--)
    	{
    		i = 2;
    		j = 239;
    		do
    		{
    			while (--j);
    		} while (--i);
    	}
    }
    
    

    矩阵键盘模块Matrix

    主要是为了扫描矩阵键盘,读取矩阵键盘的按键码

    Matrix.h

    #ifndef __MATRIX_H__
    #define __MATRIX_H__
    
    unsigned char MatrixKey();
    
    #endif
    

    Matrix.c

    #include <REGX52.H>
    #include "Delay.h"
    
    /**
     * @brief 矩阵键盘读取按键键码
     * @param 无
     * @retval KeyNumber 按下按键的键码值
    					 如果按键按下不放,程序会停留在子函数,松手的一瞬间返回按键键码 没有按键按下时返回0
     */
    unsigned char MatrixKey()
    {
    	unsigned char KeyNumber = 0;
    	
    	P1=0xFF;   //全部置高
    	//第一列
    	P1_3=0;   //P1_3置0 检测第1列
    	if(P1_7 == 0) {Delay(20);while(P1_7 == 0);Delay(20);KeyNumber=1;}
    	if(P1_6 == 0) {Delay(20);while(P1_6 == 0);Delay(20);KeyNumber=5;}
    	if(P1_5 == 0) {Delay(20);while(P1_5 == 0);Delay(20);KeyNumber=9;}
    	if(P1_4 == 0) {Delay(20);while(P1_4 == 0);Delay(20);KeyNumber=13;}
    	
    	P1=0xFF;   //全部置高
    	//第二列
    	P1_2=0;   //P1_2置0 检测第2列
    	if(P1_7 == 0) {Delay(20);while(P1_7 == 0);Delay(20);KeyNumber=2;}
    	if(P1_6 == 0) {Delay(20);while(P1_6 == 0);Delay(20);KeyNumber=6;}
    	if(P1_5 == 0) {Delay(20);while(P1_5 == 0);Delay(20);KeyNumber=10;}
    	if(P1_4 == 0) {Delay(20);while(P1_4 == 0);Delay(20);KeyNumber=14;}
    	
    	P1=0xFF;   //全部置高
    	//第三列
    	P1_1=0;   //P1_1置0 检测第3列
    	if(P1_7 == 0) {Delay(20);while(P1_7 == 0);Delay(20);KeyNumber=3;}
    	if(P1_6 == 0) {Delay(20);while(P1_6 == 0);Delay(20);KeyNumber=7;}
    	if(P1_5 == 0) {Delay(20);while(P1_5 == 0);Delay(20);KeyNumber=11;}
    	if(P1_4 == 0) {Delay(20);while(P1_4 == 0);Delay(20);KeyNumber=15;}
    	
    	P1=0xFF;   //全部置高
    	//第四列
    	P1_0=0;   //P1_0置0 检测第4列
    	if(P1_7 == 0) {Delay(20);while(P1_7 == 0);Delay(20);KeyNumber=4;}
    	if(P1_6 == 0) {Delay(20);while(P1_6 == 0);Delay(20);KeyNumber=8;}
    	if(P1_5 == 0) {Delay(20);while(P1_5 == 0);Delay(20);KeyNumber=12;}
    	if(P1_4 == 0) {Delay(20);while(P1_4 == 0);Delay(20);KeyNumber=16;}
    	
    	return KeyNumber;
    }
    

    主函数模块

    需要导入的头文件
    #include <REGX52.H>
    #include "Delay.h"
    #include "LCD1602.h"
    #include "Matrix.h"
    #include "Buzzer.h"
    
    定义宏,替换加减乘除的序号
    #define ADD 1
    #define SUB 2
    #define MUL 3
    #define DIV 4
    
    定义的变量
  • 定义两个全局栈,分别是NumArray[]和Operation[],表示操作数栈和运算符栈。
  • 定义变量NumLen和OperationLen,表示两个栈的元素个数。
  • 定义变量NumLenShow和OperationLenShow,表示当前在LCD1602上显示的操作数个数和运算符个数,方便定位LCD1602光标进行显示
  • 定义变量KeyNum,用于暂存每次按下的键码值
  • 定义变量Temp,用于存放和显示每个操作数
  • 定义变量TempOperation,用于存放和显示输入的运算符
  • 定义变量flag,表明一次运算是否完成,也就是是否按了等号(S16)
  • 定义数组OperationPriority,用于存放各个运算符的优先级,OperationPriority[0]不使用
  • bit flag;   //表明一次运算完成,也就是按了等号
    unsigned char KeyNum;   //每次按下的键码值
    unsigned int NumArray[10]
    unsigned char Operation[5], OperationLen, NumLen, OperationLenShow, NumLenShow;
    unsigned char OperationPriority[5] = {0,1,1,2,2};
    
    函数声明

    操作数栈的操作:入栈、出栈、获取栈顶元素

  • void NumArrayPush(unsigned int Num);
  • unsigned int NumArrayPop();
  • unsigned int NumArrayGetTop();
  • 运算符栈的操作:入栈、出栈、获取栈顶元素

  • void OperationPush(unsigned char OperationNum);
  • unsigned char OperationPop();
  • unsigned char OperationGetTop();
  • 清空操作:清空栈和清空LCD屏幕

  • void ClearStack();
  • void ClearLCD();
  • 运算操作:输入两个无符号整数和对应的运算符进行运算

  • unsigned int Operate(unsigned int Num1, unsigned int Num2, unsigned char Operation);
  • //函数声明
    void OperationPush(unsigned char OperationNum);
    unsigned char OperationPop();
    unsigned char OperationGetTop();
    void NumArrayPush(unsigned int Num);
    unsigned int NumArrayPop();
    unsigned int NumArrayGetTop();
    unsigned int Operate(unsigned int Num1, unsigned int Num2, unsigned char Operation);
    void ClearStack();
    void ClearLCD();
    
    主函数
    1. 定义a, b, res用于存放一次运算的两个操作数和一个运算结果,之后进行LCD初始化
    2. 在循环体中进行按键扫描,扫描按键按下,如果有按键按下,蜂鸣器发出提示音。获取按下按键键码值,检测标志位,如果上一次已经计算完成过一次,需要先进行清屏,标志位改为0。
    3. 再判断按键属于什么范围,如果是操作数范围S1-S10(S1-S9表示数字1-9,S10表示0,使用%10的方式就可以快速完成映射),用Temp暂存一个操作数,如果Temp不超过三位数即合法,否则清屏显示Overflow。每次输入一个数字,Temp*10+KeyNum,并且实时在屏幕上显示Temp,显示起始位置使用1+NumLenShow*3+OperationLenShow来计算
    4. 如果按下按键属于运算符,意味着之前的操作数已经完整,将NumLenShow++,并将Temp存入操作数栈中,Temp清0。并将矩阵按键键码与运算符的序号通过TempOperation=KeyNum-10;进行映射,存放到TempOperation中,并显示在LCD1602上,OperationLenShow++;。判断当前运算符栈是否为空,根据刚刚的思路进行
    5. 如果按下的是S15(清空键),将Temp=0,清空两个栈和LCD1602屏幕
    6. 如果按下的是S16(等于键),将操作数和运算符栈中剩下的元素进行运算,最终在LCD第二行显示运算结果
    void main()
    {
    	unsigned int a, b, res;
    	LCD_Init();
    	while(1)
    	{
    		KeyNum = MatrixKey();   //按键数字
    		if(KeyNum) 
    		{
    			Buzzer_Time(100);   //提示音
    			
    			if(flag)
    			{
    				//如果计算完成一次,先进行清屏
    				flag = 0;
    				ClearStack();
    				ClearLCD();
    			}
    			
    			if(KeyNum>=1&&KeyNum<=10)
    			{
    				//如果输入是数字
    				if(Temp*10+KeyNum>999)
    				{
    					ClearStack();
    					ClearLCD();
    					LCD_ShowString(1,1,"Overflow");
    					//现在清空Temp
    					Temp=0;
    				}
    				else
    				{
    					KeyNum%=10;
    					Temp*=10;
    					Temp+=KeyNum;
    					LCD_ShowNum(1,1+NumLenShow*3+OperationLenShow,Temp,3);
    				}
    			}
    			else if(KeyNum>=11&&KeyNum<=14)
    			{
    				if(Temp>999)
    				{
    					ClearStack();
    					ClearLCD();
    					LCD_ShowString(1,1,"Overflow");
    					//现在清空Temp
    					Temp=0;
    				}
    				else
    				{
    					//现在是运算符,操作数入栈
    					NumLenShow++;
    					NumArrayPush(Temp);
    					//现在清空Temp
    					Temp=0;
    					//加减乘除 1,2,3,4
    					TempOperation=KeyNum-10;
    					switch(TempOperation)
    					{
    						case ADD:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'+');
    							break;
    						case SUB:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'-');
    							break;
    						case MUL:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'*');
    							break;
    						case DIV:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'/');
    							break;
    					}
    					//显示出一个运算符,运算符数++
    					OperationLenShow++;
    					if(OperationLen==0)
    					{
    						//如果当前运算符栈中为空,直接入栈
    						OperationPush(TempOperation);
    					}
    					else if(OperationLen!=0&&OperationPriority[TempOperation]<=OperationPriority[OperationGetTop()]) 
    					{
    						//如果运算符栈非空并且当前运算符优先级没有之前的高,取出一个运算符和两个运算数进行运算
    						//运算后的数放入数栈,当前运算符放入运算符栈
    						b=NumArrayPop();
    						a=NumArrayPop();
    						res = Operate(a,b,OperationPop());
    						NumArrayPush(res);
    						OperationPush(TempOperation);
    					}
    					else if(OperationLen!=0&&OperationPriority[TempOperation]>OperationPriority[OperationGetTop()]) 
    					{
    						//栈非空并且当前运算符优先级更高,直接把运算符放入栈中
    						OperationPush(TempOperation);
    					}
    				}
    			}
    			else if(KeyNum==15)
    			{
    				//清空键
    				Temp=0;
    				ClearLCD();
    				ClearStack();
    			}
    			else
    			{
    				//确认键
    				//LCD_ShowNum(2,10,Temp,3);
    				NumArrayPush(Temp);
    				Temp=0;
    				//这里先出来的是右操作数,搞半天..
    				b=NumArrayPop();
    				a=NumArrayPop();
    				res = Operate(a,b,OperationPop());
    				NumArrayPush(res);
    				LCD_ShowChar(2,2,'=');
    				LCD_ShowNum(2,3,NumArrayGetTop(),6);
    				flag=1;
    				while(OperationLen!=0)
    				{
    					b=NumArrayPop();
    					a=NumArrayPop();
    					res = Operate(a,b,OperationPop());
    					NumArrayPush(res);
    					LCD_ShowChar(2,2,'=');
    					LCD_ShowNum(2,3,NumArrayGetTop(),6);
    				}
    			}
    		}
    	}
    }
    			
    
    主函数模块整体代码
    #include <REGX52.H>
    #include "Delay.h"
    #include "LCD1602.h"
    #include "Matrix.h"
    #include "Buzzer.h"
    
    #define ADD 1
    #define SUB 2
    #define MUL 3
    #define DIV 4
    
    bit flag;   //表明一次运算完成,也就是按了等号
    unsigned char KeyNum;   //每次按下的键码值
    unsigned int NumArray[10], Temp, TempOperation;   
    unsigned char Operation[5], OperationLen, NumLen, OperationLenShow, NumLenShow;
    unsigned char OperationPriority[5] = {0,1,1,2,2};
    
    //函数声明
    void OperationPush(unsigned char OperationNum);
    unsigned char OperationPop();
    unsigned char OperationGetTop();
    void NumArrayPush(unsigned int Num);
    unsigned int NumArrayPop();
    unsigned int NumArrayGetTop();
    unsigned int Operate(unsigned int Num1, unsigned int Num2, unsigned char Operation);
    void ClearStack();
    void ClearLCD();
    
    /**
     * 小小计算器,使用矩阵键盘,并且能发出提示音
     * S1-S10分别表示 1-9,0
     * S11-S14分别表示+ - * /
     * S15表示清屏
     * S16表示等于 显示运算结果
     * 但是目前有许多限制,只能三位整数运算,最大不能超过unsigned int类型的表示范围,不能有负数,结果也不行
     * 屏幕不够大,无法实现滑动效果
     */
    void main()
    {
    	unsigned int a, b, res;
    	LCD_Init();
    	while(1)
    	{
    		KeyNum = MatrixKey();   //按键数字
    		if(KeyNum) 
    		{
    			Buzzer_Time(100);   //提示音
    			
    			if(flag)
    			{
    				//如果计算完成一次,先进行清屏
    				flag = 0;
    				ClearStack();
    				ClearLCD();
    			}
    			
    			if(KeyNum>=1&&KeyNum<=10)
    			{
    				//如果输入是数字
    				if(Temp*10+KeyNum>999)
    				{
    					ClearStack();
    					ClearLCD();
    					LCD_ShowString(1,1,"Overflow");
    					//现在清空Temp
    					Temp=0;
    				}
    				else
    				{
    					KeyNum%=10;
    					Temp*=10;
    					Temp+=KeyNum;
    					LCD_ShowNum(1,1+NumLenShow*3+OperationLenShow,Temp,3);
    				}
    			}
    			else if(KeyNum>=11&&KeyNum<=14)
    			{
    				if(Temp>999)
    				{
    					ClearStack();
    					ClearLCD();
    					LCD_ShowString(1,1,"Overflow");
    					//现在清空Temp
    					Temp=0;
    				}
    				else
    				{
    					//现在是运算符,操作数入栈
    					NumLenShow++;
    					NumArrayPush(Temp);
    					//现在清空Temp
    					Temp=0;
    					//加减乘除 1,2,3,4
    					TempOperation=KeyNum-10;
    					switch(TempOperation)
    					{
    						case ADD:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'+');
    							break;
    						case SUB:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'-');
    							break;
    						case MUL:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'*');
    							break;
    						case DIV:
    							LCD_ShowChar(1,1+NumLenShow*3+OperationLenShow,'/');
    							break;
    					}
    					//显示出一个运算符,运算符数++
    					OperationLenShow++;
    					if(OperationLen==0)
    					{
    						//如果当前运算符栈中为空,直接入栈
    						OperationPush(TempOperation);
    					}
    					else if(OperationLen!=0&&OperationPriority[TempOperation]<=OperationPriority[OperationGetTop()]) 
    					{
    						//如果运算符栈非空并且当前运算符优先级没有之前的高,取出一个运算符和两个运算数进行运算
    						//运算后的数放入数栈,当前运算符放入运算符栈
    						b=NumArrayPop();
    						a=NumArrayPop();
    						res = Operate(a,b,OperationPop());
    						NumArrayPush(res);
    						OperationPush(TempOperation);
    					}
    					else if(OperationLen!=0&&OperationPriority[TempOperation]>OperationPriority[OperationGetTop()]) 
    					{
    						//栈非空并且当前运算符优先级更高,直接把运算符放入栈中
    						OperationPush(TempOperation);
    					}
    				}
    			}
    			else if(KeyNum==15)
    			{
    				//清空键
    				Temp=0;
    				ClearLCD();
    				ClearStack();
    			}
    			else
    			{
    				//确认键
    				//LCD_ShowNum(2,10,Temp,3);
    				NumArrayPush(Temp);
    				Temp=0;
    				//这里先出来的是右操作数,搞半天..
    				b=NumArrayPop();
    				a=NumArrayPop();
    				res = Operate(a,b,OperationPop());
    				NumArrayPush(res);
    				LCD_ShowChar(2,2,'=');
    				LCD_ShowNum(2,3,NumArrayGetTop(),6);
    				flag=1;
    				while(OperationLen!=0)
    				{
    					b=NumArrayPop();
    					a=NumArrayPop();
    					res = Operate(a,b,OperationPop());
    					NumArrayPush(res);
    					LCD_ShowChar(2,2,'=');
    					LCD_ShowNum(2,3,NumArrayGetTop(),6);
    				}
    			}
    		}
    	}
    }
    
    //进行运算
    unsigned int Operate(unsigned int Num1, unsigned int Num2, unsigned char Operation)
    {
    	unsigned int res;
    	switch(Operation)
    	{
    		case ADD:
    			res = Num1 + Num2;
    			break;
    		case SUB:
    			res = Num1 - Num2;
    			break;
    		case MUL:
    			res = Num1 * Num2;
    			break;
    		case DIV:
    			res = Num1 / Num2;
    			break;
    	}
    	
    	return res;
    }
    
    //操作数进栈
    void NumArrayPush(unsigned int Num)
    {
    	NumArray[NumLen]=Num;
    	NumLen++;
    }
    
    //操作数出栈
    unsigned int NumArrayPop()
    {
    	NumLen--;
    	return NumArray[NumLen];
    }
    
    //得到操作数栈顶元素
    unsigned int NumArrayGetTop()
    {
    	return NumArray[NumLen-1];
    }
    
    //运算符进栈
    void OperationPush(unsigned char OperationNum)
    {
    	Operation[OperationLen]=OperationNum;
    	OperationLen++;
    }
    
    //运算符出栈
    unsigned char OperationPop()
    {
    	OperationLen--;
    	return Operation[OperationLen];
    }
    
    //得到运算符栈顶元素
    unsigned char OperationGetTop()
    {
    	return Operation[OperationLen-1];
    }
    
    //清空两个栈
    void ClearStack()
    {
    	NumLen=0;
    	OperationLen=0;
    }
    
    //清空屏幕
    void ClearLCD()
    {
    	NumLenShow=0;
    	OperationLenShow=0;
    	LCD_ShowString(1,1,"                ");
    	LCD_ShowString(2,1,"                ");
    }
    

    结果展示


    作者:JackIes_

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用单片机实现简易计算器

    发表评论