基于51单片机的简易计算器2.docx
《基于51单片机的简易计算器2.docx》由会员分享,可在线阅读,更多相关《基于51单片机的简易计算器2.docx(21页珍藏版)》请在冰豆网上搜索。
基于51单片机的简易计算器2
基于51单片机的
计算器设计
2015年5月1日
摘要
电子计算器是日常生活中常用的电子计算仪器,他广泛应用于超市、大中型商场、大小企业与学校中。
具有精度高。
体积小、应用范围广泛、易于操作等优点。
本作品以MCS-51系列中的AT89C51单片机为核心,能够实现单步加、减、乘、除运算。
该系统通过检测矩阵键盘扫描,判断是否按键,实现对4*4键盘扫描进行实时的按键检测,并把检测数据存储下来。
经数据转换把数值送入lcd1602液晶屏显示。
整个计算器系统的工作过程为:
首先存储单元初始化,显示初始值和键盘扫描,判断按键位置,查表得出按键值,单片机则对数据进行储存与相应处理转换,之后送入lcd1602显示。
整个系统可分为三个主要功能模块:
功能模块一,实时键盘扫描;功能模块二,数据转换为了数码管显示;功能模块三,lcd1602显示。
能实现6位或6位以内的精确运算,若输出数据超过6位则会以科学计数法显示。
关键词:
AT89C51单片机;计算器;加减乘除;矩阵键盘;液晶屏
1、前言
本设计是基于51系列单片机来进行的数字计算器系统设计,可以完成计算器的键盘输入,进行加、减、乘、除基本四则运算,并在LCD上显示相应的结果;设计电路采用STC89C51单片机为主要控制电路,显示采用1602LCD显示;软件方面使用C语言编程。
最后用PROSE99画PCB,焊接万用板,进行硬件调试。
1)主要的问题及目标:
●键盘输入
●数值显示
●能实现加、减、乘、除四则运算;
●可计算小数,负数;
●当计算器执行过程中有错误时,会在液晶屏上做出相应的提示。
当除数为0时,程序运算出错,液晶屏会显示+INF。
2)针对上述目标,做出以下的设计:
●以STC89C51位主控芯片,P0口连接1602液晶屏,P1口连接4*4矩阵键盘。
●将所有输入数据已浮点型进行运算,故最大输入数据为16位。
●为了更好的显示效果使用采用1602液晶屏作为显示模块。
●由于按键包含数字键“0~9”与“+”“-”“*”“/”“.”“=”这16个按键。
故以4*4矩阵键盘作为输入模块。
●以3节串联的5号电池作为电源。
3)系统设计依据:
●实用性
●可靠性
●美观性
2、
系统方案设计
1.方案一
显示模块采用数码管,数值只能显示一行,且无法显示“+”“*”“/”“=”等符号。
使用效果欠佳。
电源模块采用USB,电压为5.0V,符合单片机的要求。
但使计算器不方便使用,必须通过USB通电,实用性不强。
功能设计中添加复位键,可以使电路恢复到起始状态,确保微机系统中稳定可靠,避免计算器出现“死机”“程序走飞”等现象。
但添加复位键会使计算器显得繁琐。
双精度型(double),占用64位的存储空间。
在操作值很大的数字时,双精度型是最好的选择。
2.方案二
以1602lcd作为显示器,可显示双行数据,还可以显示多种运算符号。
实用性强,便于计算器的升级。
将独立电源盒作为电源,以3节5号电池串联,电压可达4.5V,可以使
单片机正常工作。
并且易于携带,给使用者带来很大便利。
采用上电复位,将复位电路与电源开关结合。
既可以确保微机系统中稳定可靠的运行,又使计算器更为精简。
单精度浮点型(float)专指占用32位存储空间的单精度值。
单精度在一些处理器上比双精度更快而且只占用双精度一半的空间,但是当值很大或很小的时候,它将变得不精确。
当你需要小数部分并且对精度的要求不高时,单精度浮点型的变量是有用的。
结合上述考虑论证,小组采用方案二作为计算器系统的设计方案。
3、理论分析与计算
本作品为了要实现键盘输入,液晶显示屏输出,加、减、乘、除计算,上电复位等功能。
小组做出以下的分析与计算:
将4*4矩阵键盘连接到单片机的P1口上,液晶显示屏连接到P0口上。
并在软件中用矩阵键盘扫描程序对其实时检测,将键盘输入的数据显示到液晶屏上,并通过运算程序计算,最终将计算结果输出到液晶屏上。
将输入与输出数据以单精度浮点型定义,以%g或%f显示输出数据,可显示精确数字或以科学计数法表示。
4、系统电路设计
1.显示模块
液晶显示器(LCD)的主要原理是一电流刺激液晶分子产生点、线、面并配合背部灯管构成画面。
本系统采用的1602液晶为5V电压驱动,带背光,可显示两行,每行16个字符,能显示汉字,内置128个字符的ASCII字符集字库,只有并行接口,无串行接口。
2.输入模块
4*4矩阵键盘将16个按键排成4行4列,第一行将每个按键的一端连在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,共有4行4列8根线,将这八根线接到单片机的8个I/O口上,通过程序扫描键盘就可检
测16个键。
3.控制模块
AT89C51是一种带4K字节FLASH存储器(FPEROM—FlashProgrammableandErasableReadOnlyMemory)的低电压、高性能CMOS8位微处理器,俗称单片机。
AT89C51是一种带2K字节闪存可编程可擦除只读存储器的单片机。
单片机的可擦除只读存储器可以反复擦除1000次。
该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。
由于将多功能8位CPU和闪烁存储器组合在单个芯片中,ATMEL的AT89C51是一种高效微控制器,AT89C51是它的一种精简版本。
AT89C51单片机为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。
4.元器件的选择
1602LCD、4*4矩阵键盘、10千欧排阻、自锁开关、20pF电容、12MHZ晶振、103千欧电位器。
5.特殊器件的简介
自锁开关是指开关自带机械锁定功能,按下去,松手后按钮是不会完全跳起来的,处于锁定状态,需要再按一次,才解锁完全跳起来。
按下后接通,弹起来后断开。
6.各单元模块的连接
如图所示:
液晶接PO口,键盘接P1口。
液晶使能端P2.5,液晶数据命令选择端P2.6,液晶读写选择端P2.7。
电源正负极分别接到VCC和GND。
5、系统软件设计
1.设计原理
以keilV4.0设计。
采用大循环嵌套的设计思想。
程序主要由“液晶显示模块”“矩阵键盘扫描模块”“运算模块”构成。
大循环一直进行,是计算器一直处于工作状态。
不停地调用键盘扫描函数,将键盘输入的数据送给液晶显示与运算模块,经运算模块计算出结果后,只需调用液晶显示的子函数就可将答案显示到液晶屏上。
2.程序结构框图
定义变量,便于后面程序的使用。
位定义:
液晶使能端P2.5,液晶数据命令选择端P2.6,液晶读写选择端P2.7。
初始化:
液晶开显示,清屏。
矩阵键盘扫描程序:
包含软件去逗,在大循环中不断调用键盘扫描程序当检测到有键按下后,如果是有效值就进行处理,否则继续扫描键盘。
3.程序流程框图
否是
否
是
否是
是
否
是
否
6、系统测试
1.测试方法
将计算器断电,把万用表调到蜂鸣器档上,把万用表两表笔放在待测的两个端子上,若短路蜂鸣器就会响。
经测得开关处电路存在短路,经修复后电路焊接正常。
2.计算器功能测试
加法测试:
减法测试:
乘法测试:
除法测试:
错误处理:
3.测试结果分析
经测试,各项功能均已达成。
对于一般的整形运算,计算器能准确无误的计算出来。
由于使用浮点型数据,计算器只能进行6~7位以内的精确运算。
7、结束语
1.心得感悟
经过两个星期的设计与制作,本小组完成了基于51单片机的计算器的设计。
期间我们遇到许多困难和问题都一一解决,最终完全达到了预期的目标,体会到团体合作与成功的喜悦。
我感到只有亲手实践才能更深刻,更全面的学好知识,并且要在设计制作中多加入自己的想法,力求创新而不要模仿前人做过的作品。
在设计的每一小步都要尽自己最大努力做到最好,这样才能做出出色的作品。
2.改进的设想
1)使计算器能完成多步混合运算的功能。
2)添加一个功能键,当按下功能键后改变矩阵键盘的键值。
将第四列改为平方,开根号,求模,求余。
再次按下此键后第四列改回加,减,乘,除。
并设计一个led灯来显示键值是否被改变。
8、附录
1.系统设计图
2.设计程序
#include
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
#definecheck_busy
sbitrs=P2^7;
sbitrw=P2^6;
sbiten=P2^5;
voiddelay(intz)
{
intx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
/***********判忙函数***********/
bitLCD_check_busy()//当LCD“忙”时,LCD的DATA.7位输出为高电平信号,当LCD“不忙”时,LCD的DATA.7位输出为低电平信号
{
P0=0xFF;//为便于检测
rs=0;//rs=0,rw=1,en=1忙
rw=1;//voidbusy(void){P1=0xff;RS=0;RW=1;E=1;while((P1&0x80)==0x80);E=0;}
en=0;
_nop_();
en=1;
return(bit)(P0&0x80);
//else
/*lcd1602判忙函数
bitLCD_Check_Busy(void)
{
bitresult;//修改了判忙函数
DataPort=0xFF;
RS=0;
RW=1;
EN=1;
_nop_();
result=(bit)(DataPort&0x80);
EN=0;
returnresult;*/
return0;
}
/***********写入命令函数***********/
voidwrite_com(ucharcom)
{
while(LCD_check_busy());//忙则等待
rs=0;
rw=0;
en=1;
P0=com;
_nop_();
en=0;
}
/**********写入数据函数**********/
voidwrite_dat(uchardat)
{
while(LCD_check_busy());//忙则等待
rs=1;
rw=0;
en=1;
P0=dat;
_nop_();
en=0;
}
/*******写入字符函数***********/
voidLCD_write_char(ucharx,uchary,uchardat)
{
if(y==0)
{
write_com(0x80+x);
}
else
{
write_com(0xC0+x);
}
write_dat(dat);
}
/******写入字符串函数***********///?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
voidwrite_string(ucharx,uchary,uchar*s)
{
while(*s)
{
LCD_write_char(x,y,*s);
s++;
x++;
}
}
/*****初始化函数******/
voidLCD_init()
{
write_com(0x38);/*显示模式设置*/
delay(5);
write_com(0x06);/*显示光标移动设置*/
delay(5);
write_com(0x0C);/*显示开及光标设置*/
write_com(0x01);/*显示清屏*/
}
/*按键扫描函数,返回扫描键值*/
ucharkeyscan()//键盘扫描函数,使用行列反转扫描法
{
unsignedcharcord_h,cord_l;//行列值中间变量
P1=0x0f;//行线输出全为0
cord_h=P1&0x0f;//读入列线值
if(cord_h!
=0x0f)//先检测有无按键按下
{
delay(10);//去抖
if((P1&0x0f)!
=0x0f)
{
cord_h=P1&0x0f;//读入列线值
P1=cord_h|0xf0;//输出当前列线值
cord_l=P1&0xf0;//读入行线值
while((P1&0xf0)!
=0xf0);//等待松开并输出
return(cord_h+cord_l);//键盘最后组合码值
}
}
return(0xff);//返回该值
}
unsignedcharkeypro()
{
switch(keyscan())
{
case0x7e:
return'+';break;
case0x7d:
return'-';break;
case0x7b:
return'x';break;
case0x77:
return'/';break;
case0xbe:
return'3';break;
case0xbd:
return'6';break;
case0xbb:
return'9';break;
case0xb7:
return'=';break;
case0xde:
return'2';break;
case0xdd:
return'5';break;
case0xdb:
return'8';break;
case0xd7:
return'0';break;
case0xee:
return'1';break;
case0xed:
return'4';break;
case0xeb:
return'7';break;
case0xe7:
return'.';break;
default:
return0xff;break;
}
}
/***************主函数***********/
voidmain()
{
unsignedcharnum,i,sign;
unsignedchartemp[16];//最大输入16个
bitfirstflag;
floata=0,b=0;
unsignedchars;
LCD_init();//初始化液晶屏
delay(10);//延时用于稳定,可以去掉
write_com(0x01);//清屏
while
(1)//主循环
{
num=keypro();//扫描键盘
if(num!
=0xff)//如果扫描是按键有效值则进行处理
{
if(i==0)//输入是第一个字符的时候需要把该行清空,方便观看
write_com(0x01);
if(('+'==num)||(i==16)||('-'==num)||('x'==num)||('/'==num)||('='==num))//输入数字最大值16,输入符号表示输入结束
{
i=0;//计数器复位
if(firstflag==0)//如果是输入的第一个数据,赋值给a,并把标志位置1,到下一个数据输入时可以跳转赋值给b
{
sscanf(temp,"%f",&a);
firstflag=1;
}
else
sscanf(temp,"%f",&b);
for(s=0;s<16;s++)//赋值完成后把缓冲区清零,防止下次输入影响结果
temp[s]=0;
LCD_write_char(0,1,num);
if(num!
='=')//判断当前符号位并做相应处理
sign=num;//如果不是等号记下标志位
else
{
firstflag=0;//检测到输入=号,判断上次读入的符合
switch(sign)
{
case'+':
a=a+b;
break;
case'-':
a=a-b;
break;
case'x':
a=a*b;
break;
case'/':
a=a/b;
break;
default:
break;
}
sprintf(temp,"%g",a);//输出浮点型,无用的0不输出
write_string(1,1,temp);//显示到液晶屏
sign=0;a=b=0;//用完后所有数据清零
for(s=0;s<16;s++)
temp[s]=0;
}
}
elseif(i<16)//?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
{
if((0==i)&&(temp[0]=='0'))//如果第一个字符是0,判读第二个字符
{
if(num=='0')//如果是小数点则正常输入,光标位置加1
{
write_com(0x01);
}
else
{
temp[0]=num;
i++;//如果是1-9数字,说明0没有用,则直接替换第一位0
LCD_write_char(0,0,num);//输出数据
}
}
else
{
temp[i]=num;
LCD_write_char(i,0,num);//输出数据
i++;
}
}
}
}
}