zoukankan      html  css  js  c++  java
  • 51单片机实现多模式计算器

    介绍

    单片机型号: 普中89C51
    能够最大输出4位数结果,保留两位小数。
    实现计算器一些功能。适用于C51单片机。

    模式1: 加减陈除
    模式2: 三角函数
    模式3: 阶乘,开方,e的x次方,log运算

    若有错误和不规范之处,还恳请各位看官多多指教。

    经验吸取

    保留两位小数的时候由于c语言float精度、无符号整数和浮点数强制类型转换等问题,总是有误差。
    我最后选择将结果加上0.005再进行强制类型转换(向下取整)从而模拟了四舍五入,问题得以解决。

    long型很重要,单片机里float 和long型是4个字节,而int是2个字节
    若处理的数据大于255,不想溢出的话就不要用int

    调试的时候,可以使用在怀疑有错误的地方加上led显示,或者数码管显示的代码。根据单片机上的显示来获取错误反馈。

    代码

    /*
    Author: WuYE
    */
    
    
    #include "reg52.h"
    #include "math.h"
    #include "string.h"
    
    typedef unsigned char u8;
    typedef unsigned int u16;
    
    #define GPIO_KEY P1                     //定义数码管为P1引脚
    #define GPIO_DIG P0                     //定义矩阵键盘引脚
    #define PI 3.14159
    sbit LSA = P2 ^ 2;                      //定义数码管引脚
    sbit LSB = P2 ^ 3;                      //
    sbit LSC = P2 ^ 4;                      //
    
    sbit k1 = P3 ^ 1;                       //定义独立按键引脚
    sbit k2 = P3 ^ 0;                       //
    sbit k3 = P3 ^ 2;                       //
    sbit k4 = P3 ^ 3;                       //
    
    
    u8 code numbertube[20] = {              //定义数码管显示的数值
    	0x3f, 0x06, 0x5b, 0x4f, 0x66,       //显示0~9数字   
    	0x6d, 0x7d, 0x07, 0x7f, 0x6f,       //
    	0Xbf, 0x86, 0xdb, 0xcf, 0xe6,       //显示0~9带小数点数字
    	0xed, 0xfd, 0x87, 0xff, 0xbf        //
                             };
    u8 opkenkey = 0;                        //按下数码管不再显示0000
    u8 model = 0;                           //计算器模式为初始模式
    u8 AllowOutPutResult = 0;               //默认不允许输出结果,因为不满足计算条件
    u8 numberseat = 0;                      //保存当前输入的数字的位数
    u8 number[4];                           //保存输入的数字的数组
    u8 StopCalc = 0;                        //计算结束后就关闭计算器计算功能,但是按键检测功能仍然活跃。
    
    u8 dispalynumber[6] = 0;                //让结果在数码管显示的数组
    u8 keyvalue = 0;                        //给矩阵键盘赋按键值用的变量
    
    u8 s1 = 0;                              //保存运算符信息
    float leftresult, rightresult = 0;      //定义模式一下运算符左两边数字,和模式一、模式二、三下运算符右边数字
    float finalresult = 0;                  //计算的最终结果
    int NextOperateAllow = 0;               //允许执行下一步操作的钥匙
    
    void Timer0Init()                       //定时器初始化设置函数
    {
    	TMOD |= 0x01;                       //
    	TH0 = 0xFC;                         //
    	TL0 = 0x18;                         //
    	ET0 = 1;                            //
    	EA = 1;                             //
    	TR0 = 1;                            //
    }
    
    void _Init()                            
    //声明一些函数
    {
        void delay(u16 i);                  //声明延迟函数
        void ModelChoose();                 //声明选择模式函数
        void Display();                     //声明数码管显示功能函数
    
        void ScanInput();                   //声明矩阵按键扫描函数
        float DecConverse();                //声明数值转换函数
    	void JudgeInput();                  //声明处理输入数字函数
        void DisplayOutPutResult();         //声明将结果转换为数码管可以显示的数字函数
        void OutputResult();                //声明计算结果函数
        void Clean();                       //声明清零函数
        float JieCheng(float i);            //声明阶乘函数
    }
    
    void Calc()                             
    //计算器函数
    {
        keyvalue = 0;                       //对矩阵键盘扫描结果保存的变量初始化,防止重复执行JudgeInput里的函数
        ScanInput();                        //调用检测矩阵键盘函数并赋值
        JudgeInput();                       //判断刚刚输入的内容
        if(AllowOutPutResult == 1 && StopCalc == 0)          //
        {
            OutputResult();                 //计算输入得到结果
            DisplayOutPutResult();          //将结果显示到数码管上
        }
    
    }
    
    void main()                             
    //主函数
    {
        Timer0Init();                           //
        _Init();                                //
        while(1)                                //
        {
            while(1)                            //一直处于初始状态,请选择计算器模式
            {
                ModelChoose();                  //选择模式
                if(model != 0)                  //
                    break;                      //
            }
            while(1)                            //一直处于检测按键状态
            {
                if(k1 == 0)                     //
                {
                    delay(1000);                //
                    if(k1 == 0)                 //
                    {  
                        model = 0;              //
                        Clean();                //
                        break;                  //
                    }
                }
                Calc();                         //计算器开启
            }
        }
    
    }
    
    void Time0() interrupt 1                //定时器中断
    {
    	static u8 i = 0;                    //
    	TH0 = 0xFC;                         //
    	TL0 = 0x18;                         //
    	i++;                                //
    	if (i == 15)                        //				
    	{
    		i = 0;                          //
    		Display();				        //数码管显示功能
    	}
    }
    
    
    
    
    void delay(u16 i)                       
    //延迟函数
    {
    	while (i--);                        //
    }
    
    void ModelChoose()                      
    //选择计算器模式
    {
        if(k1 == 0)                         //如果独立按键1被按下     
        {
            delay(1000);                    //消抖
            if(k1 == 0)                     //
            {   
                opkenkey = 1;               //
                LSA = LSB = LSC = 0;        //
                GPIO_DIG = numbertube[0];        //显示0
                delay(10000000);                    //
                GPIO_DIG = 0x00;                    //
            }
        } 
        if(opkenkey == 1 && k2 == 0)
        {
            delay(1000);                    //
            if(k2 == 0)                     //
            {
                model = 1;                  //打开模式1
                LSA = LSB = LSC = 0;        //
                GPIO_DIG = numbertube[model];    //
                delay(10000000);                    //
                GPIO_DIG = 0x00;                    //
            }
        }      
        if(opkenkey == 1 && k3 == 0)
        {
            delay(1000);                    //
            if(k3 == 0)                     //
            {
                model = 2;                  //打开模式2
                LSA = LSB = LSC = 0;        //
                GPIO_DIG = numbertube[model];       //
                delay(10000000);                    //
                GPIO_DIG = 0x00;                    //
            }
        } 
        if(opkenkey == 1 && k4 == 0)
        {
            delay(1000);                    //
            if(k4 == 0)                     //
            {
                model = 3;                  //打开模式3
                LSA = LSB = LSC = 0;        //
                GPIO_DIG = numbertube[model];       //
                delay(10000000);                    //
                GPIO_DIG = 0x00;                    //
            }
        }    
    }
    
    void Display()                          
    {
        u8 i;
        if(opkenkey == 0)                   
        //计算器未打开,显示0000
        {
    		for (i = 0; i < 4; i++)         //
    		{
    			switch (i)                  //打开不同的数码管引脚
    			{  
    			case (0): LSA = 0; LSB = 0; LSC = 0; break;         //
    			case (1): LSA = 1; LSB = 0; LSC = 0; break;         //
    			case (2): LSA = 0; LSB = 1; LSC = 0; break;         //
    			case (3): LSA = 1; LSB = 1; LSC = 0; break;         //
    			}
    			GPIO_DIG = numbertube[0];                           //显示数字“0”
    			delay(100);                                         //
    			GPIO_DIG = 0x00;                                    //
    		}
            i = 0;
        }
    
        if(opkenkey == 1 && AllowOutPutResult == 0)                   
        //计算器打开,但不允许显示结果,实时显示已输入的数字
        {
    		switch (numberseat)	                                      //根据当前数字位数来显示数字						  		
    		{
    		case 4: LSA = 0; LSB = 0; LSC = 1; GPIO_DIG = numbertube[number[3]]; delay(100); GPIO_DIG = 0x00;			
    		case 3: LSA = 1; LSB = 0; LSC = 1; GPIO_DIG = numbertube[number[2]]; delay(100); GPIO_DIG = 0x00;			
    		case 2: LSA = 0; LSB = 1; LSC = 1; GPIO_DIG = numbertube[number[1]]; delay(100); GPIO_DIG = 0x00;			
    		case 1: LSA = 1; LSB = 1; LSC = 1; GPIO_DIG = numbertube[number[0]]; delay(100); GPIO_DIG = 0x00; break;	
    		}
        }
        
        if(opkenkey == 1 && AllowOutPutResult == 1)
        //计算器打开,允许显示结果
        {
    		switch (numberseat)                                       //根据结果数字位数来显示数字
    		{
    		case 6: LSA = 1; LSB = 0; LSC = 1; GPIO_DIG = numbertube[dispalynumber[5]]; delay(10); GPIO_DIG = 0x00;
    		case 5: LSA = 0; LSB = 0; LSC = 1; GPIO_DIG = numbertube[dispalynumber[4]]; delay(10); GPIO_DIG = 0x00;
    		case 4: LSA = 1; LSB = 1; LSC = 0; GPIO_DIG = numbertube[dispalynumber[3]]; delay(10); GPIO_DIG = 0x00;
    		case 3: LSA = 0; LSB = 1; LSC = 0; GPIO_DIG = numbertube[dispalynumber[2]]; delay(10); GPIO_DIG = 0x00;
    		case 2: LSA = 1; LSB = 0; LSC = 0; GPIO_DIG = numbertube[dispalynumber[1]]; delay(10); GPIO_DIG = 0x00;
    		case 1: LSA = 0; LSB = 0; LSC = 0; GPIO_DIG = numbertube[dispalynumber[0]]; delay(10); GPIO_DIG = 0x00;
    		}	
        }
    }
    
    void ScanInput()                        
    //矩阵键盘扫描,扫描结果赋值给全局变量keyvalue
    {
    	u8 a = 0;													
    	GPIO_KEY = 0x0f;
    	if (GPIO_KEY != 0x0f)
    	{
    		delay(10000);                                               //
    		if (GPIO_KEY != 0x0f)
    		{
    			GPIO_KEY = 0x0f;                                        //
    			switch (GPIO_KEY)
    			{
    			case (0x07): keyvalue = 7; break;                       //
    			case (0x0b): keyvalue = 8; break;                       //
    			case (0x0d): keyvalue = 9; break;                       //
    			case (0x0e): keyvalue = 16; break;                      //
    			}
    			GPIO_KEY = 0xf0;
    			switch (GPIO_KEY)
    			{
    			case (0x70): keyvalue = keyvalue; break;                            //
    			case (0xb0): keyvalue = keyvalue - 3; break;                        //
    			case (0xd0): keyvalue = keyvalue - 6; break;                        //
    			case (0xe0): keyvalue = keyvalue + 90; break;                       //
    			}
    			while ((a < 50) && (GPIO_KEY != 0xf0)) 	                //避免键盘一直按着,占用内存
    			{
    				delay(3000);                                        //
    				a++;                                                //
    			}
    		}
    	}
    }
    
    void JudgeInput()
    //对刚刚的keyvalue进行处理
    {
        if(keyvalue == 97)                     //按下了清零符号
        {
            Clean();                           //
        }
        if(model == 1)                         
        //模式1条件下执行
        {
            if(NextOperateAllow == 0 || NextOperateAllow == 1)
            {
                if(keyvalue < 10 && keyvalue > 0 || keyvalue == 98)              //输入了0~9的数字
                {
                    if(numberseat < 5)
                    {
                        numberseat++;                                            //
                        number[numberseat - 1] = keyvalue;                       //保存当前数字
                        if(keyvalue == 98)
                            number[numberseat - 1] = 0;
                    }
                    NextOperateAllow = 1;                                        //下一步用户必须输入运算符号,否则不处理
                }
            }
    
            if(keyvalue == 16 || keyvalue == 13 || keyvalue == 10 || keyvalue == 106)
            {
                if(NextOperateAllow == 1)                                        
                {
                leftresult = DecConverse();                                      //计算刚刚输入的数字成为十进制可运算结果,结果保存到运算符左边
                s1 = keyvalue;                                                   //
                }
                NextOperateAllow = 2;                                            //
            }
    
            if(s1 != 0 && NextOperateAllow > 1)                                  //下一步输入必须是输入数字
            {
                if(keyvalue < 10 && keyvalue > 0 || keyvalue == 98)
                {
                    if(numberseat < 5)
                    {
                        numberseat++;                                            //
                        number[numberseat - 1] = keyvalue;                       //保存当前数字
                        if(keyvalue == 98)
                            number[numberseat - 1] = 0;                    
                    }
                    NextOperateAllow = 3;                                        //下一步输入是等于号
                }
            }
    
            if(NextOperateAllow == 3)
            {
                if (keyvalue == 99)
                {
                    NextOperateAllow = 0;                                       //防止多次按下等于号
                    rightresult = DecConverse();                                //计算刚刚输入的数字成为十进制可运算结果,结果保存到运算符右边
                    AllowOutPutResult = 1;                                      //允许计算数据
                }
            }
        }
        else                 
        //其他模式执行
        {
            if(NextOperateAllow == 0 || NextOperateAllow == 1)
            {
                if(keyvalue == 16 || keyvalue == 13 || keyvalue == 10 || keyvalue == 106)
                {
                    NextOperateAllow = 1;                                  //下一步是输入数字
                    s1 = keyvalue;                                         //保存运算符到s1
                }    
            }
    
            if(keyvalue < 10 && keyvalue > 0 || keyvalue == 98) 
            {
                if(NextOperateAllow > 0)
                {
                    if(numberseat < 5)
                    {
                        numberseat++;                                       //
                        number[numberseat - 1] = keyvalue;                  //保存当前数字
                        if(keyvalue == 98)
                            number[numberseat - 1] = 0;                    
                    }
                    NextOperateAllow = 2;                                   //下一步是输入等于号                   
                }
                  
            }
    
            if(NextOperateAllow == 2)
            {
                if (keyvalue == 99)
                {
                    NextOperateAllow = 0;                               //防止多次按下等于号
                    rightresult = DecConverse();                        //
                    AllowOutPutResult = 1;                              //允许计算数据
                }           
            }
        }
    }
    
    float DecConverse()                                                
    //将矩阵键盘输入的数字变成真正可以进行运算的数字,并且每调用一次会置零一些数据。以防上一次输入影响数码管显示
    {
    	float result;                                                   //
    	switch (numberseat)									
    	//判断输入了几位,就执行相应的十进制转换
    	{
    	case 1: result = (float)number[0]; break;                       //
    	case 2: result = (float)number[0] * 10 + number[1]; break;      //
    	case 3: result = (float)number[0] * 100 + number[1] * 10 + number[2]; break;                //
    	case 4: result = (float)number[0] * 1000 + number[1] * 100 + number[2] * 10 + number[3]; break;                 //
    	}
    	memset(number, 0, sizeof(number));							    //置零number。
        numberseat = 0;                                                 //置零数字位数
    	return result;                                                  //返回float型结果
    }
    
    void OutputResult()
    //处理结果
    {
        float e = 2.7181;
    
    	if (model == 1)
    	{
    		switch (s1)
    		//判断输入的运算操作符
    		{
    		case(16): finalresult = leftresult / rightresult; break;
    		case(13): finalresult = leftresult * rightresult; break;
    		case(10): finalresult = leftresult - rightresult; break;
    		case(106): finalresult = leftresult + rightresult; break;
    		}
    	}
    
    	if (model == 2)
    	{
    		rightresult = (rightresult * PI) / 180;
    		switch (s1)
    		{
    		case(16): finalresult = sin(rightresult); break;
    		case(13): finalresult = cos(rightresult); break;
    		case(10): finalresult = tan(rightresult); break;
    		case(106): Clean(); break;
    		}
    	}
    	if (model == 3)
    	{
    		switch (s1)
    		{
    		case(16): finalresult = log(rightresult); break;
    		case(13): finalresult = sqrt(rightresult); break;
    		case(10): finalresult = pow(e, (u8)rightresult); break;
    		case(106): finalresult = JieCheng(rightresult); break;
    		}
    	}
    }
    
    void DisplayOutPutResult()
    //将结果变成一个个独立的数字,并且显示结果
    {
        long zenshu = finalresult;                              //如果不定义为long,则到256会数据溢出
        u8 xiaoshu = (finalresult - zenshu + 0.005) * 100;
        if (finalresult > 1 || finalresult == 1)
    	{
    		dispalynumber[1] = xiaoshu / 10;			        //小数第一位
    		dispalynumber[0] = xiaoshu % 10;			    	//小数第二位
    
    		if (zenshu < 10)	
    		{
    			numberseat = 3;						            //结果加上小数点后二位,一共有三位数
    			dispalynumber[2] = zenshu + 10;			        //显示整数,加10是为了显示小数点
    		}
    		if (zenshu < 100 && zenshu > 10)          //
    		{
    			numberseat = 4;
    			dispalynumber[3] = zenshu / 10;                 //
    			dispalynumber[2] = zenshu % 10 + 10;            //
    		}
    		if (zenshu < 1000 && zenshu > 100)
    		{
    			numberseat = 5;                                 //
    			dispalynumber[4] = zenshu / 100;
    			dispalynumber[3] = zenshu % 100 / 10;           //
    			dispalynumber[2] = zenshu % 100 % 10 + 10;      //
    		}
    		if (zenshu < 10000 && zenshu > 1000)
    		{
    			numberseat = 6;                                 //
    			dispalynumber[5] = zenshu / 1000;               //
    			dispalynumber[4] = zenshu % 1000 / 100;         //
    			dispalynumber[3] = zenshu % 1000 % 100 / 10;    //
    			dispalynumber[2] = zenshu % 1000 % 100 % 10 + 10;    //
    		}
    	}
    
        if(finalresult < 1)  
        {
            numberseat = 3;                                         //显示位数为3位
            dispalynumber[2] = 10;                                  //显示带小数点的数字“0”
            if(finalresult > 0)
            {
                dispalynumber[1] = xiaoshu / 10;			        //小数第一位
                dispalynumber[0] = xiaoshu % 10;			    	//小数第二位   
            }
            else
            {          
                dispalynumber[1] = 0;			
                dispalynumber[0] = 0;
            }
            
         
        }
    
        StopCalc = 1;
    }
    
    void Clean()
    //清零函数
    {
        s1 = 0;                                                 //
        AllowOutPutResult = 0;                                  //
        numberseat = 0;                                         //
        NextOperateAllow = 0;                                   //
        StopCalc = 0;                                           //
        memset(number, 0, sizeof(number));                      //
        memset(dispalynumber, 0, sizeof(dispalynumber));        //
    }
    
    float JieCheng(float i)
    //阶乘运算函数
    {
    	u8 j;                                                   //
        u8 z = (u8)i + 1;
    	float Jresult = 1;                                       //
    	for (j = 1; j < z; j++)                             
    	{
    		Jresult *= j;                                       //
    	}
    
    	return Jresult;                                         //返回长整结果
    }
    

    运行结果

    编译无错误

    模式1下除法

    测试 : (47 / 23)

    手机计算器得到的答案:2.0434

    模式2下tan函数

    测试 : tan(48 * PI) / 180

    手机计算器得到答案:1.1106

    模式3下测试

    测试阶乘 : 7!

    手机计算器得到答案: 5048

    测试平方根: sqrt(1024)

    手机计算器得到答案: 32

    测试e的x次方: pow(7)

    手机计算器得到答案: 1096.1197

    目前发现的问题

    这块51单片机的引脚总是接触不良,偶尔数码管显示会显示错数字。
    比如把 "6." 显示成 "8."
    解决办法: 乱摸下数码管附近的针脚就好了

  • 相关阅读:
    自定义函数
    取小数的有效值函数
    数据恢复bak
    脚本启动windows服务
    创建表
    PostgreSQL和SQL SERVER的数据库差异
    vs2019莫名自动退出调试状态
    postgresql 设置调试
    Google Web字体,让你的网页更迷人
    翻译:观察者模式—使用JavaScript实现
  • 原文地址:https://www.cnblogs.com/beidaxmf/p/14170907.html
Copyright © 2011-2022 走看看