51单片机实现多模式计算器
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
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."
解决办法: 乱摸下数码管附近的针脚就好了。