简易计算器设计
目录
1前言 .................................................................. 1
1.1设计背景 ............................................................ 1
1.2系统设计目的和意义 .................................................. 1
2 总体方案 .............................................................. 2
2.1方案论证 ............................................................ 2
2.3 最终方案 ............................................................ 3
3单元模块设计 .......................................................... 4
3.1各单元模块功能介绍及电路设计 ........................................ 4
3.2系统元器件选择 ...................................................... 7
4软件设计 .............................................................. 8
4.1系统程序流程 . ...................................................... 8
5系统调试 ............................................................. 10
5.1 硬件调试 ........................................................... 10
5.2 软件调试 ........................................................... 10
5.3 软硬件调试 ......................................................... 10
7 总结 ................................................................. 12
8 参考文献 ............................................................. 13
附录 . .................................................................. 14
1前言
1.1设计背景
计算器是一种在日常生活中应用广泛的电子产品,无论是在超市商店,还是在办公室,或是家庭都有着它的身影。计算器随着供应量的增多、用户使用方便度日益更新,从又大又重到又小又轻,从复杂的模拟电路到一块几厘米的单片机,计算器实现的功能越来越多样化,从简单的加减乘除运算到乘方、开方运算,指数、对数、三角函数、反三角函数的计算不断的变化着。现今,市面上已经出现了使用太阳能电池的计算器,使用ASIC 设计的计算器。如何使计算器变得越来越轻便化、智能化已经成为电子领域研究的重要课题之一。
1.2系统设计目的和意义
本次实验的任务就是要以51系列单片机为核心实现一个4*4矩阵式键盘计算器,满足计算器的基本要求, 可以基本的运算(加减乘除),数据归零功能。采用AT89C51单片机为核心,主要由矩阵式键盘电路、译码电路、显示电路等组成,软件选用汇编语言编程,通过软件编程可实现简单无符号加减乘除, 。单片机将检测到的按键信号转换成数字量,显示于LED 显示器上。该系统灵活性强,易于操作,可靠性高,将会有更广阔的开发前景。设计仿真和调试要用到Protues 、Keil 等软件。
通过这次设计, 进一步掌握单片机理论知识,知道AT89C51单片机的原理、编程和各种功能的应用, 了解简易计算器的工作原理,初步掌握计算器的硬软件设计、编写、调试和仿真,充分提高动手能力和排除故障的能力。同时通过此次设计加深我们对单片机的认识和兴趣,提高我们的动手能力。
2 总体方案
2.1方案论证
本设计可以采用两种方案,一种是以FPGA 为核心处理芯片,配备相应的外设;另外一种是以AT89S52处理器,配备相应的外设。
2.1.1
方案一:采用FPGA 控制
图2.1 基于FPGA 计算器框图
FPGA 是一种高密度的可编程逻辑器件, 自从Xilinx 公司1985年推出第一片FPGA 以来,FPGA 的集成密度和性能提高很快, 其集成密度最高达500万门/片以上, 系统性能可达200MHz 。由于FPGA 器件集成度高, 方便易用, 开发和上市周期短, 在数字设计和电子生产中得到迅速普及和应用, 并一度在高密度的可编程逻辑器件领域中独占鳌头。
2.1.2方案二:采用AT89C51
图2.2 基于A T89C51的计算器原理框图
单片机是单片微型机的简称,故又称为微控制器MCU (Micro Control Unit )。通常由单块集成电路芯片组成,内部包含有计算机的基本功能部件:中央处理器CPU ,存储器和I/O接口电路等。因此,单片机只要和适当的软件及外部设备相结合,便可成为一个单片机控制系统。
2.2 方案比较选择
两个方案都能实现本次设计的功能要求。但是方案一基于 SRAM编程的FPGA, 其编程信息需存放在外部存储器上 ,需外部存储器芯片 ,且使用方法复杂 ,保密性差,而其对于一个简单的计算器而言,实用FPGA 有点大材小用,成本太高。而方案二运用单片机, 单片机广泛用于智能产品,智能仪表,测控技术,智能接口等,具有操作简单,实用方便,价格便宜等优点而其中AT89C51,是单片机中典型的代表,应用于各种控制领域。因此, 通过以上两种方案论证和比较,从设计的实用性,方便性和成本出发,选择了以AT89C52单片机作为中央处理单元进行计算器的设计,这样设计能够实现本次设计的基本要求。
2.3 最终方案
经过仔细分析和论证,决定了系统各模块的最终方案如下:
(1)控制模块:采用AT89C51单片机;
(2)液晶显示模块:LCD1602液晶显示器;
方案原理图如图2.2所示, 通过AT89C51芯片PO 口与键盘相接,键盘中的键就是一个行列开关,该开关位于行列的交点处,通过按下某个键,该交点的行线和列线联通,相应的行列电平发生变化,从而可以确定按下的功能键。读取P0的值就可以确定按键,再由AT89C51芯片读取按键的值通过P1口和P2口显示在LCD12864上。
3单元模块设计
3.1各单元模块功能介绍及电路设计
3.1.1主控制模块
该模块以AT89C51为控制核心, AT89C51单片机是在一块芯片中集成了CPU 、RAM 、ROM 、定时器/计数器和多功能I/O等一台计算机所需要的基本功能部件。如果按功能划分,它由如下功能部件组成,即微处理器(CPU )、数据存储器(RAM )、程序存储器(ROM/EPROM)、并行I/O 口、串行口、定时器/计数器、中断系统及特殊功能寄存器(SFR )。单片机是靠程序运行的,并且可以修改。通过不同的程序实现不同的功能,尤其是特殊的独特的一些功能,通过使用单片机编写的程序可以实现高智能,高效率,以及高可靠性!因此我们采用单片机作为计算器的主要功能部件,可以实现简单计算器运算功能。
AT89C51是一种带4K 字节FLASH 存储器(FPEROM —Flash Programmable and Erasable Read Only Memory)的低电压、高性能CMOS 8位微处理器,俗称单片机。AT89C2051是一种带2K 字节闪存可编程可擦除只读存储器的单片机。单片机的可擦除只读存储器可以反复擦除1000次。该器件采用ATMEL 高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。由于将多功能8位CPU 和闪速存储器组合在单个芯片中,ATMEL 的AT89C51是一种高效微控制器,AT89C2051是它的一种精简版本。AT89C51单片机为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。外形及引脚排列如图所示:
图3.1 AT89C51外形及引脚图
3.1.2时钟电路
时钟电路由AT89C51得18、19引脚的时钟端(XTAL1及XTAL 2)以及12MHZ 晶振、33pF 的电容C1、C2组成,采用片内振荡方式。其时间周期为1/12us,机器周期为1s 。单片机的外部晶振电路如图3所示。AT89C51中有一个用于构成内部振荡器的高增益反相放大器,引脚XTAL1和XTAL2分别是该放大器的输入端和输出端。这个放大器与作为反馈元件的片外石英晶体或陶瓷谐振器一起构成振荡器。振荡器特性: XTAL1和XTAL2分别为反向放大器的输入和输出。该反向放大器可以配置为片内振荡器。石晶振荡和陶瓷振荡均可采用。如采用外部时钟源驱动器件,XTAL2应不接。有余输入至内部时钟信号要通过一个二分频触发器,因此对外部时钟信号的脉宽无任何要求,但必须保证脉冲的高低电平。
3.2 时钟电路图
3.2.3 复位电路
用上位电路,由100电阻及1uF 电容接至AT89C51的RST 复位端电阻给电容充电,电容的电压缓慢上升直到vcc ,没到vcc 时芯片复位脚近似低电平,于是芯片复位,接近vcc 时芯片复位脚近高电平,于是芯片停止复位,复位完成。按键后电容器被短路放电、RST 直接和VCC 相连,就是高电平,此时进入“复位状态”。松手后电源开始对电容器充电,此时,充电电流在电阻上,形成高电平送到RST ,仍然是“复位状态”,稍后,充电结束,电流降为0,电阻上的电压也将为0,RST 降为低电平,开始正常工作。
3.3 复位电路图
3.3.4键盘电路
键盘电路的设计原理:首先行列式键盘中的键实际上就是一个机械开关,该开关位于行线和列线的交点处,通过按键加以连接。当按下某个键时,该交点的行线和列线接通,相应行线或列线上的的电平发生变化,从而可以确定被按下的功能键其次运用线翻转法判断有无键按下: 键盘的高4位用于列控制,低4位用于行控制,并将全部行线Y0~Y3置低电平,然后再检查列线电平的状态。只要其中有一列电平为低,则表示右键按下,并且被按下的键位于低电平和4根行线交叉的某一个按键中。
图3.4 键盘电路图
3.2系统元器件选择
主要用到的硬件:单片机STC89C52 、液晶显示屏LCD1602 、4*4按键键盘 硬件分配:
1、P3口:作为输入口,与键盘连接,实现数据的输入;
2、P0、P2口:作为输出口(P2口为高位,P0口为低位),控制LCD 液晶显示屏显示数据的结果;
3、液晶显示屏LCD1602显示输出。
4软件设计
在程序设计方法上,模块化程序设计是单片机应用中最常用的程序设计方法。设计的中心思想是把一个复杂应用程序按整体功能划分成若干相对独立的程序模块,各模块可以单独设计、编程和调试,然后组合起来。这种方法便于设计和调试,容易实现多个程序共存,但各个模块之间的连接有一定的难度。
4.1系统程序流程
根据需要我们可以采用自上而下的程序设计方法,此方法先从主程序开始设计,然后再编制各从属程序和子程序,层层细化逐步求精,最终完成一个复杂程序的设计。这种方法比较符合人们的日常思维,缺点是一级的程序错误会对整个程序产生影响。程序流程图如下:
图4.1 程序流程图
如上图所示:初始化后,程序开始进入主程序,1、进行按键判断,如果有键按下,继续往下执行,否则重新扫描;2、如果有键按下,读取按键键码,判断按键是数字键、清零键还是功能键3、如果是数字键则送到缓冲区,如果是清零键则状态清零,如果是功能键则再次进行判断是什么功能键4、判断功能键之后,等待数值输入,再根据上次输入计算结果; 5、把处理的数据,放到LED 数码管中动态显示。实现简单的四则运算。
5系统调试
单片机应用系统的调试包括硬件和软件两部分,但是他们并不能完全分开。一般方法是排除明显的硬件故障,在进行综合调试,排除可能的软(硬)件故障。
5.1 硬件调试
硬件调试分为静态调试和动态调试,对于硬件调试而言,只要认真焊接,硬件一般不会出现什么问题的。 静态调试一般采用的工具是万用表,它是用户系统未工作时的一种硬件检测。 动态调试是在用户系统工作的情况下发现和排查错误的一种硬件检测。调试步骤为:首先把电路分为若干模块,调试过程中与该模块无关的元件可以不加考虑,这样可把故障限定在一定的范围内,故障清除后,把各个模块合在一起进行联调,即可完成整个硬件调试工作。
5.2 软件调试
软件调试是通过对程序的汇编、连接、执行来发现程序中存在的语法错误与逻辑错误并加以排除纠正的过程。
调试过程:
1、代码录入完成进行调试。
2、在 Keil uVision3中检测查找错误。 3、检测过程中总是有一处错误无法解决。
4、最后把原程序分开逐个调试,检查每段程序的错误,修正每个代码错误。 5、这种分开调试方法的效率还是很不错的,经过几次修改就完成了程序的调试,运行结果没有错误,电路显示也完全正确。
5.3 软硬件调试
软硬件联调是指把调试无误的软件程序烧制进单片机芯片内部,通上电源后,检查硬件工作是否有预期的效果,如果没有则需要检测软件是否在实现功能上有欠缺。若有错误,通过改写软件来调试,直至达到预期效果。
6结论
基于单片机的设计至今为止已经进入了令人鼓舞的阶段,在进行了两周时间的摸索与设计,我们不仅仅对于单片机软件与硬件的常用设计与功能有所认识,如AT89C51单片机包含中央处理器、程序存储器、数据存储器、定时/计数 器、并行接口、串行接口和中断系统等几大单元及数据总线、地址总线和控制总线等三大总线,。键盘控制程序需完成的任务有:检查是否有按键按下, 有键按下时, 如无硬件去抖动电路时,应用软件延时方法消除按键抖动;当有多个按键按下时, 只响应一个按键,不管持续多长时间,仅执行一次按键功能程序。还使我对于一项设计研究的制作过程所需要的详细步骤和具体实现方法有了进一步的掌握。
由于我们的初步尝试,当中的缺点是无可非议地存在着。当我们焊接好后进行实物仿真出现了一系列问题,如显示屏显示不明显,不能正常清零等。经过一系列调试终于能正确运行。通过调试,本次设计能实现的功能有:简单的加减乘除运算,以及计算器的清零,基本上达到本次设计的要求。但是, 需要改进的地方还有很多:运算方式不多,显示不够具体等。虽然遇到的困难很多但是我们学到了很多经验,希望在下次课程设计过程中能吸取这次设计经验有更大的进步。
7 总结
课程设计是培养学生综合运用所学知识,发现、提出、分析和解决实际问题,锻炼实践能力的重要环节,是对学生实际工作能力的具体训练和考察过程。随着科学技术发展的日新日异,单片机已经成为当今计算机应用中空前活跃的领域,在生活中可以说得是无处不在。因此作为自动化专业的学生来说掌握单片机的开发技术是十分重要的。我的题目是简易计算器硬软件的设计,对于我们这些工科学生来说,这是一次考验。怎么才能找到课堂所学与实际应用的最佳结合点?怎样让自己的业余更接近专业?怎样让自己的计划更具有序性,而不会忙无一用?这都是我们所要考虑和努力的。这次课程设计我学到很多很多的东西,学会了怎么样去制定计划,怎么样去实现这个计划,并掌握了在执行过程中怎么样去克服心理上的不良情绪。不仅巩固了以前所学过的知识,而且学到了很多在书本上所没有 学到过的知识,掌握了一种系统的研究方法,可以进行一些简单的编程。通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能 真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。 同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固,对单片机汇编语言掌握得不够好。这次课程设计通过自己的努力,同学的帮助,网络资料的收集,最终顺利完成了。
8 参考文献
[1]范立南, 谢子殿等. 单片机原理及应用教程[M].北京大学出版社
[2]马忠梅,籍顺心,张凯等. 单片机的C 语言应用程序设计[M].北京航空航天大学出版社,2006.
[3]张毅刚,彭喜元, 董继成. 《单片机原理及应用》 高等教育出版社,2003. [4]谭浩强《C 语言程序设计(第二版)》
[5]肖洪兵. 跟我学用单片机. 北京:北京航空航天大学出版社,2002
[6]何立民. 单片机高级教程(第1版). 北京:北京航空航天大学出版社,2001 [7]杨居义. 单片机课程设计指导. 北京:清华大学出版社,2009
附录
附录1:相关设计图
设计总原理图
减法运算 除法运算
附录2:
元器件清单表
附录3:相关设计软件
软件编程 编译用keil 软件, 原理图绘制软件仿真用Proteus 软件
附录4:相关源代码
#include #include
#define uchar unsigned char #define uint unsigned int uchar temp3[8],temp4[8];
uchar zero[8]={0,0,0,0,0,0,0,0}; uchar code table[]={ 0xc0,0xf9,0xa4,0xb0, 0x99,0x92,0x82,0xf8, 0x80,0x90,0x88,0x83, 0xc6,0xa1,0x86,0x8e};
uchar num,temp,num1,cnt,sub,add,mul,div,ent,shift,cal; void delay(uint z) { uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--); }
uchar keyscan() //键盘扫描程序 { P1=0xfe; temp=P1;
temp=temp&0xf0; while(temp!=0xf0) {delay(5); temp=P1;
temp=temp&0xf0; while(temp!=0xf0) { temp=P1;
switch(temp) //将键盘第一行分别定义为7,8,9和'/'号 { case 0xee:num=7; break;
case 0xde:num=8; break;
case 0xbe:num=9; break;
case 0x7e:num=10;div=1,cal='/'; break; }
while(temp!=0xf0) { temp=P1;
temp=temp&0xf0; } } } P1=0xfd; temp=P1; temp=temp&0xf0; while(temp!=0xf0) { delay(5); temp=P1;
temp=temp&0xf0; while(temp!=0xf0) { temp=P1;
switch(temp) //将键盘第二行分别定义为4,5,6和'*'号 { case 0xed:num=4; break;
case 0xdd:num=5; break;
case 0xbd:num=6; break;
case 0x7d:num=11;mul=1,cal='*'; break; }
while(temp!=0xf0) { temp=P1;
temp=temp&0xf0; }
} } P1=0xfb; temp=P1;
temp=temp&0xf0; while(temp!=0xf0) { delay(5); temp=P1;
temp=temp&0xf0; while(temp!=0xf0) { temp=P1;
switch(temp) //将键盘第三行分别定义为1,2,3和'-' 号 { case 0xeb:num=1; break;
case 0xdb:num=2; break;
case 0xbb:num=3; break;
case 0x7b:num=12;sub=1,cal='-'; break; }
while(temp!=0xf0) { temp=P1;
temp=temp&0xf0; } } } P1=0xf7; temp=P1;
temp=temp&0xf0; while(temp!=0xf0) { delay(5); temp=P1;
temp=temp&0xf0; while(temp!=0xf0) { temp=P1;
switch(temp) //将键盘第四行分别定为13,0,14和'/'号
{ case 0xe7:num=13; shift=1;
break;
case 0xd7:num=0; break;
case 0xb7:num=14;ent=1;
break;
case 0x77:num=15;add=1,cal='+'; break; }
while(temp!=0xf0) { temp=P1;
temp=temp&0xf0; } } } return num; }
void move(uchar *a) //定义左移函数move() { uchar k;
for(k=7;k>0;k--) a[k]=a[k-1]; }
void mov(uchar *a,uchar *b) //定义交换函数mov() { uchar k;
for(k=0;k
uchar count() //将输入的第一个数存入数组temp3 { num=16;
num=keyscan(); if(num
{ move(temp3); temp3[0]=num; cnt++; }
return cnt; }
void display() //动态显示程序 { mov(temp3,zero); while(1)
{ uchar k,n=0x80; cnt=count(); k=0;
for(k=0;k
n=n>>1; }
if((num>9&&num
{cnt=0;add=0;sub=0;mul=0;div=0,ent=0;num=16; break;} } }
void dynadisp(uchar tep[],uint z) { while(1) { int i;
int n=0x80;
for(i=0;i
P0=table[tep[i]]; delay(3); n=n>>1; } num=keyscan();
if(num>9&&num
{cnt=0;add=0;sub=0;mul=0;div=0,ent=0;num=16; break;} } }
//计算,参数即为计算符号 long compute(uchar c) { long a1,b1; long result;
a1=temp4[7]*10000000+temp4[6]*1000000+temp4[5]*100000
+temp4[4]*10000+temp4[3]*1000+temp4[2]*100+temp4[1]*10+temp4[0]; b1=temp3[7]*10000000+temp3[6]*1000000+temp3[5]*100000+
temp3[4]*10000+temp3[3]*1000+temp3[2]*100+temp3[1]*10+temp3[0]; switch(c) {
case '+':
result=a1+b1; break; case '-':
result=a1-b1; break; case '*':
result=a1*b1; break; case '/':
result=a1/b1;
break;
}
return result;
}
long compute2(uchar c)
{
long a1,b1,c1;
long result;
a1=temp4[7]*10000000+temp4[6]*1000000+temp4[5]*100000
+temp4[4]*10000+temp4[3]*1000+temp4[2]*100+temp4[1]*10+temp4[0]; b1=temp3[7]*10000000+temp3[6]*1000000+temp3[5]*100000+
temp3[4]*10000+temp3[3]*1000+temp3[2]*100+temp3[1]*10+temp3[0]; switch(c)
{ case '+':
result=sqrt(b1);
break;
case '-':
result=log10(b1);
break;
case '*':
result=b1*b1*b1;
break;
case '/':
for(c1=b1;c1>1;c1--)
{ b1=b1*(c1-1);
}
result=b1;
break;
}
return result;
}
void main()
{ uchar k, jieguo[8]={0,0,0,0,0,0,0,0};
long C;
P0=0xc0;
P2=0x80;
display();
if( shift==1)
{ C=compute2(cal);
goto next;
}
P0=0xc0;
第 20 页
P2=0x00;
lp: mov(temp4,temp3);
display();
C=compute(cal);
next:
jieguo[7]=C/10000000;
jieguo[6]=C%10000000/1000000;
jieguo[5]=C%1000000/100000;
jieguo[4]=C%100000/10000;
jieguo[3]=C%10000/1000;
jieguo[2]=C%1000/100;
jieguo[1]=C%100/10;
jieguo[0]=C%10;
for(k=7;k>0;k--)
{ if(jieguo[k])break;
}
dynadisp(jieguo,k+1);
mov(temp3,jieguo);
goto lp;
} 第 21 页