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."
解決辦法: 亂摸下數碼管附近的針腳就好了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM