介紹
單片機型號: 普中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."
解決辦法: 亂摸下數碼管附近的針腳就好了