简易计算器设计报告

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1602液晶显示计算器
设计报告
2016年10月
一、摘要
计算器是人们的日常生活最常见的电子产品之一,应用广泛,功能强大。

本次我们小组设计制作了一个简易计算器,能够在-9999999~9999999的范围内进
x=b类型方程。

该计算器以AT89C51行整数、小数的四则运算运算,并求解ax+e
单片机芯片作为核心,采4*4矩阵键盘作为输入,将数据输入单片机内部处理,用LCD1602工业字符液晶显示出运算和结果。

进行显示。

经过检验能够实现预设功能,具有很高的实用价值。

关键词:
单片机;简易计算器;AT89C51;LCD1602。

本设计具有以下功能:
1、能够实现-9999999~9999999范围内整数,小数的基本运算(超出范围后报错)。

2、储存运算结果,掉电不丢失。

3、实现6位及以上有效数字。

4、解ax+e x=b方程。

5、能够连续运算,即上次运算结果作为下次的输入。

6、能够在一次操作中连续运算。

二、方案论证
方案描述:
方案一:以51单片机为核心,4*4矩阵键盘输入,数码管显示输入数字和运算结果。

方案二:以51单片机为核心,4*4矩阵键盘输入,LCD1602显示输入数字和运算结果。

方案比较与选择:
LCD1602作为一个成熟的产品,使用简单,模式固定,便于移植到各种类型的程序,又有微功耗、体积小、显示内容丰富超薄轻巧等优点,而数码管虽然亮度高,但是操作复杂、能耗高、且不能显示符号,本设计对亮度要求不是特别高,故采用方案二。

三、解方程算法
ax+e x =b 为非线性方程,采用牛顿法迭代求解。

原理是利用泰勒公式在X 0处展开,且展开到一阶,即f(X)=f(X 0)+(X- X 0)f’(X 0),求解X=X 1= X 0-f(X 0)/f’(X 0),进而推出Xn+1=Xn-f(Xn)/f’(Xn)。

通过迭代,这个式子必然在f(x)=0的时候收敛,进而得到方程的解。

四、电路与程序
系统组成:
电源系统,复位电路,晶振电路,4*4矩阵键盘,独立按键,STC80C52,LCD1602。

具体设计如下:
1、复位电路
2、键盘电路
S17
3、晶振电路
4、1602显示屏
5、STC89C52电路
6、电源电路
系统软件与流程图:1)、总流程图
使用说明:
1、接入电源,按下电源开关后进入上次关闭时的界面(计算或解方程界面)。

2、计算时,输入数字和符号,按等号键输出结果,超出运算范围则报错,按AC 键退出;
3、解方程时,先输入参数a,b,按等号键确认,后输出ax+e x=b的解。

按任意
键继续解方程。

4、在任意时刻短按mode键进入菜单界面,按1进入计算界面,按2进入解方程界面。

5、长按mode键为记忆功能,即显示上次的计算结果。

测试方案及结果:
1、菜单界面
2、加法运算
3、减法运算
4、乘法运算
5、除法运算
6、连续运算
7、上次运算结果作为下次输入
8、报错
9、解方程
10、记忆功能
参考书目
《新概念51单片机C语言教程》电子工业出版社
附录
源代码:
//键盘接P3,1602接P0,STC89C58RC
//by-FCY 2016-10
#include<reg52.h>//单片机头文件
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define RdCommand0x01 //定义ISP的操作命令
#define PrgCommand0x02
#define EraseCommand0x03
#define Error 1
#define Ok 0
#define WaitTime0x01 //定义CPU的等待时间
sfrISP_DATA=0xe2; //寄存器申明
sfrISP_ADDRH=0xe3;
sfrISP_ADDRL=0xe4;
sfrISP_CMD=0xe5;
sfrISP_TRIG=0xe6;
sfrISP_CONTR=0xe7;
sbitlcden=P1^1;//定义1602能使端和数据命令选择端
sbitlcdrs=P1^0;
/*
sbitlcden=P3^4; //TX开发板调试
sbitlcdrs=P3^5;
sbitdula=P2^6;
sbitwela=P2^7;
*/
sbit mode=P1^2; //定义功能键
sbit led=P1^5; //定义1602背光K
uchar time; //定义中断计时时间
char cnumf[14];
char csign[5]={'.','+','-','*','/'}; //定义整数,小数,符号数组
float num1,num2;//定义运算数字
ucharkey_value,sign_value=11,fun_value=1; //定义键值,符号值,功能值void delay(uint z); //函数声明
ucharkeyscan();
void function_choose();
void write_com(uchar com);
void write_data(uchar date);
void memoryread();
float num_input();
float num_input0();
void num_output(float num);
void display(ints,charstr[16],uchar l);
/*===========延时函数===========*/
void delay(uint z)//延时z毫秒
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
/*===========键盘扫描函数=========*/ ucharkeyscan()
{
ucharnum=100,temp;
TR0=1;//计时开始
while(num==100)//只有有键按下的时候才跳出{
P3=0xfe;
temp=P3;
temp=temp&0xf0;
//扫描mode键
if(mode==0)
{
delay(10);
if(mode==0)
delay(500);//按下500毫秒
if(mode==0)
{
while (!mode);//等待释放
memoryread();//读取记忆
}
else//短按
{
while (!mode);
function_choose();//功能选择
ISP_CONTR=0X20;//系统复位
}
}
//扫描矩阵键盘
while(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee:num=7;
break;
case 0xde:num=8;
break;
case 0xbe:num=9;
break;
case 0x7e:num=11;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed:num=4;
break;
case 0xdd:num=5;
break;
case 0xbd:num=6;
break;
case 0x7d:num=12;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb:num=1;
break;
case 0xdb:num=2;
break;
case 0xbb:num=3;
break;
case 0x7b:num=13;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xf7;
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xe7:num=0;
break;
case 0xd7:num=10;
break;
case 0xb7:num=15;
break;
case 0x77:num=14;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
}
led=0;//亮屏
time=0;//有键按下时清零计时
return num;
}
/*=====================数据储存部分=========================*/ //打开ISP,IAP功能
void ISP_IAP_enable(void)
{
EA = 0; //关中断
ISP_CONTR = ISP_CONTR&0x18; // 0001,1000 ISP_CONTR = ISP_CONTR | WaitTime; //写入硬件延时
ISP_CONTR = ISP_CONTR | 0x80; //ISPEN=1
}
//关闭ISP,IAP功能
void ISP_IAP_disable(void)
{
ISP_CONTR = ISP_CONTR&0x7f; //ISPEN = 0
ISP_TRIG = 0x00;
EA = 1; //开中断
}
//公用的触发代码
void ISPgoon(void)
{
ISP_IAP_enable(); //打开ISP,IAP功能
ISP_TRIG = 0x46; // 触发ISP_IAP命令字节1
ISP_TRIG = 0xb9; // 触发ISP_IAP命令字节2
_nop_();
}
//读取数据
unsigned charbyte_read(unsigned intbyte_addr)
{
ISP_ADDRH = (unsigned char)(byte_addr>> 8);//地址赋值
ISP_ADDRL = (unsigned char)(byte_addr&0x00ff);
ISP_CMD = ISP_CMD &0xf8; //清除低3位
ISP_CMD = ISP_CMD | RdCommand; // 写入读命令
ISPgoon(); //触发执行
ISP_IAP_disable(); //关闭ISP,IAP功能
return (ISP_DATA); // 返回读到的数据
}
//扇区擦除
void SectorErase(unsigned intsector_addr)
{
unsigned intiSectorAddr;
iSectorAddr = (sector_addr&0xfe00); //取扇区地址
ISP_ADDRH = (unsigned char)(iSectorAddr>> 8);
ISP_ADDRL =0x00;
ISP_CMD = ISP_CMD&0xf8; //清空低3位
ISP_CMD = ISP_CMD | EraseCommand; //擦除命令3
ISPgoon(); //触发执行
ISP_IAP_disable(); //关闭ISP,IAP功能
}
//写入数据
void byte_write(unsigned intbyte_addr, unsigned char original_data) {
ISP_ADDRH = (unsigned char)(byte_addr>> 8); //取地址
ISP_ADDRL = (unsigned char)(byte_addr&0x00ff);
ISP_CMD = ISP_CMD&0xf8; //清低3位
ISP_CMD = ISP_CMD | PrgCommand; //写命令2
ISP_DATA = original_data; //写入数据准备
ISPgoon(); //触发执行
ISP_IAP_disable(); //关闭IAP功能
}
//储存浮点数函数
void memory(float num)
{
char i=13;
float a;
a=num;
sprintf(cnumf,"%14.6f",a);
while (cnumf[i]==48||cnumf[i]==46)
{
i=i-1;
if(cnumf[i+1]==46)
break;
}//将浮点数拆分成数组并去除尾数的0(或小数点)
SectorErase(0x2200);//擦除扇区
byte_write(0x2200,i);//储存位数
for(;i>=0;i--)
byte_write(0x2201+i,cnumf[i]);//储存数组
}
//读取浮点数函数
void memoryread()
{
char i;
i=byte_read(0x2200);//读取位数
write_com(0x01);//清屏
display(0x80," memory number ",16);
write_com(0x80+0x4f);//定位指针为第二行第16位(最后一位)write_com(0x0c);//不显示光标
write_com(0x04);//写一个字符指针减一
for(;i>=0;i--)//读取数组
{
cnumf[i]=byte_read(0x2201+i);
write_data(cnumf[i]);
}
write_com(0x06);//还原1602设置:写一个字符指针加一
}
/*===============1602相关函数===========*/
//写指令
void write_com(uchar com)
{
lcdrs=0;
P0=com;
delay(5);
delay(5);
lcden=0;
}
//写数据
void write_data(uchar date)
{
lcdrs=1;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
delay(5);
}
//清除某行函数
void clear(int s)//光标起始位(第一行0x80,第二行0x80+0x40)
{
uchari=0;
write_com(s);//定位指针位置
write_com(0x06);
write_com(0x0c);//不显示光标
while(i<=16)
{
write_data(32);
i++;
}//输入16个空格
write_com(s);//指针归位
write_com(0x0e);//显示光标
}
//显示字符函数
void display(ints,charstr[16],uchar l)//定义显示起点,显示的数组,显示字符的个数
{ int j=0;
write_com(s);
write_com(0x06);
for(j=0;j<l;j++)
write_data(str[j]);
}
/*==============初始化函数===============*/
void init()
{
lcden=0;//写命令模式
num2=0;//数据清零
led=0;//亮屏
write_com(0x38);//设置16*2显示,5*7点阵,8位数字据接口
write_com(0x0e);//光标不闪烁
write_com(0x06);//写字符后指针自动加一
write_com(0x01);//显示清零,指针清零
write_com(0x80);//指针定位为第一行首位
TMOD=0x01;//开定时器T0,并设置为16位定时模式,且启动仅受TR0控制TH0=0x4c;
TL0=0x00;//装入初值65536-46080,初值=2^16-计数个数N,N=(计数时长*晶振频率/12)(50ms)
EA=1;
ET0=1;//打开T0中断允许
}
/*==============数字输入函数==============*/
//数字输入函数
float num_input()
{
float num=0;
char key,i=1;
char sign=1;
key=keyscan();
if(key==12)
{ sign=-1;
write_data('(');
write_data('-');
key=keyscan();
}//解决负数运算
if(key<10)
{num=0;
while(key<10)
{
num=num*10+key;
write_data(key+0x30);
key=keyscan();
}
}
if (key==10)
{ write_data(csign[0]);
for(key=keyscan();key<10;key=keyscan())
{
num=num+key/pow(10,i);
write_data(key+0x30);
i++;
}
}
if(sign<0)
write_data(')');
if(key>=11&&key<=14)//如果按下运算符
write_data(csign[key-10]);//显示运算符
key_value=key;//记录运算符
num=sign*num;
return num;
}
//数字输入函数0
float num_input0()
{
float num=num2;//无操作返回num2
char key,i=1;
key=keyscan();
clear(0x80);//清除第一行
if(key<10)
{num=0;
while(key<10)
{
num=num*10+key;
write_data(key+0x30);
key=keyscan();
}
}
if (key==10)
{ write_data(csign[0]);
for(key=keyscan();key<10;key=keyscan())
{
num=num+key/pow(10,i);
write_data(key+0x30);
i++;
}
}
if(key>=11&&key<=14)
write_data(csign[key-10]);
key_value=key;
return num;
}
/*==============数字输出函数==============*/
void num_output(float num)
{
int j=0,k=13;//定义输出字符个数
memory(num);//储存结果
clear(0x80+0x40);//清除第二行
write_com(0x80+0x4f);//定位指针为第二行第16位(最后一位)
write_com(0x0c);//不显示光标
write_com(0x04);//写一个字符指针减一
sprintf(cnumf,"%14.6f",num);//将要显示的数字转化为数组
if(num>=9999999||num<=-9999999)//超出范围报错
{
clear(0x80);
display(0x80+0x40," Error ",16);
while(1);
}
while (cnumf[k]==48||cnumf[k]==46)//当数组不为0或小数点时
{
k=k-1;//记录实际数组长度(比如12.100000实际长度为4)
if(cnumf[k+1]==46)//检测到小数点跳出
break;
}
for(j=k;j>=0;j--)//显示数字num,不显示尾数的零(如12.00000显示为12)write_data(cnumf[j]);
write_com(0x06);//还原1602设置:写一个字符指针加一
}
/*==================功能选择函数==============*/
void function_choose()
{
uchar j=0;
while(1)
{
switch(j%2)//页面选择
{
case 0:
display(0x80,"1 calculate > +",16);//显示菜单
display(0x80+0x40,"2 advance < -",16);
break;
case 1:
display(0x80,"3 equation > +",16);//显示菜单
display(0x80+0x40,"4 information< -",16);
break;
}
fun_value=keyscan();//获取功能值
switch(fun_value)
{
case 11:j++;
continue;
case 12:j--;
continue;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
default: continue;
}
break;
}
SectorErase(0x2000);//擦除扇区
byte_write(0x2000,fun_value);//储存功能值
}
/*================中断计时================*/ void T0_time()interrupt 1
{
uchar t;
int r;//随机种子
TH0=0x4c;
TL0=0x00;//重装初值以保证中断正常循环
t++;//记录进入该中断的次数
r++;
srand(r);
if(t==6)//每0.30s time加一
{
time++;
t=0;
}
if(time>=200)//1min灭屏
led=1;
}
/*===============计算函数================*/
void calculate()
{
num2=num_input0();//获取num2,记录运算符,并判断是否为连续运算while(key_value<=14&&key_value>=11)//如果没有输入等号
{
sign_value=key_value;//转移运算符的符号值
num1=num_input();//获取num1,记录下一个运算符
switch(sign_value)
{case 11:num2=num2+num1;
break;
case 12:num2=num2-num1;
break;
case 13:num2=num2*num1;
break;
case 14:num2=num2/num1;
break;
}//计算,将结果赋给num2
}
if(key_value==15)//如果符号为等号
{
num_output(num2);//显示运算结果
write_com(0x80+0x40);
write_data('=');//显示等号
}
}
/*===============高级计算================*/
void advanced()
{
uchar k=0;
float x=0,y=0;
float z=0;
while(1)
{
display(0x80,"1x^y3cos5ln>+",16);//显示菜单
display(0x80+0x40,"2sin4tan6ra<-",16);
k=keyscan();//获取功能值
if(k>0&&k<=6)
{ init();
switch(k)
{
case 1:
{
x=num_input();//获取x
write_data('^');
y=num_input();//获取y
z=pow(x,y);
}break;
case 6:
{
display(0x80,"rand[",5);
x=num_input();//获取x
write_data(',');
y=num_input();//获取y
write_data(']');
write_data('=');//显示等号
if(x>y)
{
init();
display(0x80+0x40," Error ",16);
while(1);
}
do
{z=rand()%((int)x-(int)y-1)+(int)x;
num_output(z);
k=keyscan();}
while(k==15);
}continue;
case 2:
{
display(0x80,"sin ",4);
x=num_input();//获取x
z=sin(x);
}break;
case 3:
{
display(0x80,"cos ",4);
x=num_input();//获取x
z=cos(x);
}break;
case 4:
{
display(0x80,"tan ",4);
x=num_input();//获取x
z=tan(x);
}break;
case 5:
{
display(0x80,"ln ",3);
x=num_input();//获取x
z=log(x);
}break;
}
write_data('=');//显示等号
num_output(z);
}
keyscan();//等待任意键
}
}
/*===================解方程函数================*/
void equation()
{ long i=0;
float a=0,b=0;
float x=20;//定义方程中的变量a b x
init();
write_data('a');
write_data('=');
a=num_input();//显示并获取a
write_com(0x80+0x40);
write_data('b');
write_data('=');
b=num_input();//显示并获取b
if(floor(a)+floor(b)==fabs(a)+fabs(b)&&a*b!=0)//如果a,b均为正整数{ if(a==1&&b==1)
x=0;
for(;fabs(a*x+exp(x)-b)>1e-5;i++)//设置精度为10^(-5)
{
x=x-(a*x+exp(x)-b)/(a+exp(x));//牛顿法主要循环x1=x0-f(x)/f'(x) if(i>=1000)//1000次迭代没有达到精度就跳出
break;
}
write_com(0x01);//清屏
num_output(x);//第二行显示方程的解
display(0x80+0x40,"x=",2);//显示"x=''
}
else
{
write_com(0x01);//清屏
display(0x80+0x40," Error ",16);//报错
while(1);//程序停止
}
display(0x80,"any key continue",16);//第一行显示"any key continue"
keyscan();//等待任意键
}
/*===================主函数================*/
void main()
{
init();
while(1)
{
fun_value=byte_read(0x2000);//读取功能值
switch(fun_value)
{
case 1:calculate();
break;
case 2:advanced();
break;
case 3:equation();
break;
case 4:{display(0x80,"made by fcy ",16);
display(0x80+0x40," 2016-10-26 ",16);
keyscan();}
break;
default: {SectorErase(0x2000);//擦除扇区
byte_write(0x2000,1);}//储存功能值
}
}
}。

相关文档
最新文档