1、 1602液晶显示计算器 设计报告 10月 一、摘要 计算器是人们寻常生活最常用电子产品之一,应用广泛,功能强大。本次咱们小组设计制作了一种简易计算器,可以在-9999999~9999999范畴内进行整数、小数四则运算运算,并求解ax+ex=b类型方程。该计算器以AT89C51单片机芯片作为核心,采4*4矩阵键盘作为输入,将数据输入单片机内部解决,用LCD1602工业字符液晶显示出运算和成果。进行显示。通过检查可以实现预设功能,具备很高实用价值。 核心词: 单片机;简易计算器;AT
2、89C51;LCD1602。 本设计具备如下功能: 1、可以实现-9999999~9999999范畴内整数,小数基本运算(超过范畴后报错)。 2、储存运算成果,掉电不丢失。 3、实现6位及以上有效数字。 4、解ax+ex=b方程。 5、可以持续运算,即上次运算成果作为下次输入。 6、可以在一次操作中持续运算。 二、方案论证 方案描述: 方案一:以51单片机为核心,4*4矩阵键盘输入,数码管显示输入数字和运算成果。 方案二:以51单片机为核心,4*4矩阵键盘输入,LCD1602显示输入数字和运算成果。 方案比较与选取: LCD1602作为一种成熟产品,使用简朴,模式固定
3、便于移植到各种类型程序,又有微功耗、体积小、显示内容丰富超薄轻巧等长处,而数码管虽然亮度高,但是操作复杂、能耗高、且不能显示符号,本设计对亮度规定不是特别高,故采用方案二。 三、解方程算法 ax+ex=b为非线性方程,采用牛顿法迭代求解。原理是运用泰勒公式在X0处展开,且展开到一阶,即f(X)=f(X0)+(X- X0)f’(X0),求解X=X1= X0-f(X0)/f’(X0),进而推出Xn+1=Xn-f(Xn)/f’(Xn)。通过迭代,这个式子必然在f(x)=0时候收敛,进而得到方程解。 四、电路与程序 系统构成: 电源系统,复位电路,晶振电路,4*4矩阵键盘,独立按键,STC
4、80C52, LCD1602。 详细设计如下: S17 1K 1、复位电路 2、键盘电路 3、晶振电路 4、1602显示屏 1602 5、STC89C52电路 6、电源电路 系统软件与流程图: 1)、总流程图 开始 初始化 读取Function value 存入eeprom 扫描4*4矩阵键盘生成Function value 计算 扫描mode键 显示选取界面(短按),显示上次计算成果 (长按) 解方程 否 是 与否按下 Fuction value 2)、计算某些流程图 0~9为数字 运算符 等号 10~
5、15为符号 0~9 10~14 生成num2 扫描4*4矩阵键盘 1602显示 显示num2 扫描4*4矩阵键盘 记录符号 生成num1 1602显示 计算生成num2 0~15? keyvalue 0~15? 计算生成num2 3)、解方程某些流程图 初值x0=0 0~9为数字 0~9为数字 是 否 15为确认 15为确认 生成参数a 生成参数b 扫描4*4矩阵键盘 扫描4*4矩阵键盘 1602显示 1602显示 显示成果Xn Xn+1=Xn-f(Xn)/f'(Xn) 0~15? 0~15? f(Xn)<0
6、0005? 使用阐明: 1、接入电源,按下电源开关后进入上次关闭时界面(计算或解方程界面)。 2、计算时,输入数字和符号,按等号键输出成果,超过运算范畴则报错,按AC键退出; 3、解方程时,先输入参数a,b,按等号键确认,后输出ax+ex=b解。按任意键继续解方程。 4、在任意时刻短按mode键进入菜单界面,按1进入计算界面,按2进入解方程界面。 5、长按mode键为记忆功能,即显示上次计算成果。 测试方案及成果: 1、 菜单界面 2、 加法运算 3、 减法运算 4、乘法运算 5、除法运算 6、持续运算 7、上次运算成果作为下次输入 8、报
7、错
9、解方程
10、记忆功能
参照书目
《新概念51单片机C语言教程》电子工业出版社
附录
源代码:
//键盘接P3,1602接P0,STC89C58RC
//by-FCY -10
#include
8、mmand 0x01 //定义ISP操作命令 #define PrgCommand 0x02 #define EraseCommand 0x03 #define Error 1 #define Ok 0 #define WaitTime 0x01 //定义CPU等待时间 sfr ISP_DATA=0xe2; //寄存器声明 sfr ISP_ADDRH=0xe3; sfr ISP_ADDRL=0xe4; sfr ISP_CMD=0xe5; sfr ISP_TRIG=0xe6; sfr ISP_CONTR=0xe7; sbit l
9、cden=P1^1; //定义1602能使端和数据命令选取端 sbit lcdrs=P1^0; /* sbit lcden=P3^4; //TX开发板调试 sbit lcdrs=P3^5; sbit dula=P2^6; sbit wela=P2^7; */ sbit mode=P1^2; //定义功能键 sbit led=P1^5; //定义1602背光K uchar time; //定义中断计时时间 char cnumf[14]; char csign[5]={'.','+','-','*','/'}; //定义整数,小数,符号数组 f
10、loat num1,num2;//定义运算数字 uchar key_value,sign_value=11,fun_value=1; //定义键值,符号值,功能值 void delay(uint z); //函数声明 uchar keyscan(); void function_choose(); void write_com(uchar com); void write_data(uchar date); void memoryread(); float num_input(); float num_input0(); void num_out
11、put(float num); void display(int s,char str[16],uchar l); /*===========延时函数===========*/ void delay(uint z)//延时z毫秒 { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } /*===========键盘扫描函数=========*/ uchar keyscan() { uchar num=100,temp; TR0=1;//计时开始 while(num==100)//只有有键
12、按下时候才跳出 { 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();//功能选取
13、 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;
14、 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;
15、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)
16、 { 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;
17、 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; wh
18、ile(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:n
19、um=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
20、 //关中断 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 =
21、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 char byte_read(unsigned int byte_addr) { ISP_ADDRH = (unsigned char)(byt
22、e_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 SectorEra
23、se(unsigned int sector_addr) { unsigned int iSectorAddr; 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(); /
24、/触发执行 ISP_IAP_disable(); //关闭ISP,IAP功能 } //写入数据 void byte_write(unsigned int byte_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
25、 = 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-
26、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," m
27、emory 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有关函数===========*/ /
28、/写指令 void write_com(uchar com) { lcdrs=0; P0=com; delay(5); lcden=1; 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) {
29、 uchar i=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(int s,char str[16],uchar l)//定义显示起点,显示数组,显示字符个数 { int j=0; write_com
30、s);
write_com(0x06);
for(j=0;j 31、1);//显示清零,指针清零
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 32、 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 33、]);
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
f 34、loat 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;k 35、ey=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); 36、//储存成果
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) 37、
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设立:写一种字符指针加一
}
/*=============== 38、功能选取函数==============*/
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) 39、//显示菜单
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;
40、 case 4:
break;
default: continue;
}
break;
}
SectorErase(0x);//擦除扇区
byte_write(0x,fun_value);//储存功能值
}
/*================中断计时================*/
void T0_time()interrupt 1
{
uchar t;
int r;//随机种子
TH0=0x4c;
TL0=0x00;//重装初值以保证中断正常循环
t++;//记录进入该中断次数
r++; 41、
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;//转移运 42、算符符号值
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)// 43、如果符号为等号
{
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^y 3cos 5ln >+",16);//显示菜单
display(0x80+0x40, 44、"2sin 4tan 6ra <-",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);
45、 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- 46、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 47、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('=');//显示等号
nu 48、m_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');
49、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次迭代没有达到精度就跳出
br 50、eak;
}
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






