简易计算器课程设计DOC文档格式.docx
《简易计算器课程设计DOC文档格式.docx》由会员分享,可在线阅读,更多相关《简易计算器课程设计DOC文档格式.docx(32页珍藏版)》请在冰点文库上搜索。
5、图⑤注释:
21
七、实训心得与体会22
一、设计的大体思路
我选到的本次课程设计的最终目的是要实现一个简单计算器,要求编写一个程序,每运行一次可执行程序,可以实现数的加减乘除四则运算。
比如,十进制数的加减乘除四则运算。
我们曾经学习过两个具体数字进行加减法运算,但是对于简单计算器用汇编语言实现难点在于寄存器所存的数据较少,很难实现多位数的四则运算,C语言确有着解决汇编语言的优点,所以选用C语言完成该程序的制作,首先运用单片机AT89c51扫描4*4矩阵键盘,从而实现按键的输入功能,键盘的输入是按照每行的电平扫描,并判断按下数字键之后是否有符号键,如果没有则在原数之后添加数字,如果按下符号接收符号后数据,并判断是否有等号键按下,如果按下,则调用运算函数和输出结果。
二、所用元件的详细功能
1、运算模块AT89C51
MCS-51单片机是在一块芯片中集成了CPU、RAM、ROM、定时器/计数器和多功能I/O等一台计算机所需要的基本功能部件。
如果按功能划分,它由如下功能部件组成,即微处理器(CPU)、数据存储器(RAM)、程序存储器(ROM/EPROM)、并行I/O口、串行口、定时器/计数器、中断系统及特殊功能寄存器(SFR)。
单片机是靠程序运行的,并且可以修改。
通过不同的程序实现不同的功能,尤其是特殊的独特的一些功能,通过使用单片机编写的程序可以实现高智能,高效率,以及高可靠性!
因此我们采用单片机作为计算器的主要功能部件,可以进行很快地实现运算功能,如图所示:
单片机(AT89S51)的引脚功能
51系列单片机8031、8051及89c51/89s51均采用40Pin封装的双列直接DIP结构。
上图是它们的引脚配置:
40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用,如图所示:
完整引脚图
引脚介绍
电源引脚:
Vcc 40脚 正电源脚,工作电压为5V,另有AT89LV51工作电压则是2.7-6V,引脚功能一样。
GND 20脚 接地端。
型号同样为AT89C51的芯片,在其后面还有频率编号,有12,16,20,24MHz可选。
大家在购买和选用时要注意了。
如AT89C5124PC就是最高振荡频率为24MHz,40P6封装的普通商用芯片。
复位:
在振荡器运行时,有两个机器周期(24个振荡周期)以上的高电平出现在此引脚时,将使单片机复位,只要这个脚保持高电平,51芯片便循环复位。
复位后P0-P3口均置1引脚表现为高电平,程序计数器和特殊功能寄存器SFR全部清零。
当复位脚由高电平变为低电平时,芯片为ROM的0000H处开始运行程序。
复位操作不会对内部RAM有所影响。
当8051通电,时钟电路开始工作,在RESET引脚上出现24个时钟周期以上的高电平,系统即初始复位。
什么叫复位?
复位是单片机重新执行程序代码的意思。
8051的复位方式可以是自动复位,也可以是手动复位,如图3-6所示。
此外,RESET/Vpd还是一复用脚,Vcc掉电期间,此脚可接上备用电源,以保证单片机内部RAM的数据不丢失。
输入输出(I/O)引脚:
P39-P32为P0.0-P0.7输入输出脚,称为P0口,是一个8位漏极开路型双向I/O口。
内部不带上拉电阻,当外接上拉电阻时,P0口能以吸收电流的方式驱动八个LSTTL负载电路。
通常在使用时外接上拉电阻,用来驱动多个数码管。
在访问外部程序和外部数据存储器时,P0口是分时转换的地址(低8位)/数据总线,不需要外接上拉电阻。
P1-P8为P1.0-P1.7输入输出脚,称为P1口,是一个带内部上拉电阻的8位双向I/0口。
P1口能驱动4个LSTTL负载。
通常在使用时外不需要外接上拉电阻,就可以直接驱动发光二极管。
端口置1时,内部上拉电阻将端口拉到高电平,作输入用。
P21-P28为P2.0-P2.7输入输出脚,称为P2口,是一个带内部上拉电阻的8位双向I/O口,P2口能驱动4个LSTTL负载。
对内部Flash程序存储器编程时,接收高8位地址和控制信息。
在访问外部程序和16位外部数据存储器时,P2口送出高8位地址。
而在访问8位地址的外部数据存储器时其引脚上的内容在此期间不会改变。
P10-P17为P3.0-P3.7输入输出脚,称为P3口,是一个带内部上拉电阻的8位双向I/O口,P2口能驱动4个LSTTL负载,这8个引脚还用于专门的第二功能。
P1-P3端口在做输入使用时,因内部有上接电阻,被外部拉低的引脚会输出一定的电流。
除此之外P3端口还用于一些专门功能,如
P3引脚
兼用功能
P3.0
串行通讯输入(RXD)
P3.1
串行通讯输出(TXD)
P3.2
外部中断0(INT0)
P3.3
外部中断1(INT1)
P3.4
定时器0输入(T0)
P3.5
定时器1输入(T1)
P3.6
外部数据存储器写选通WR
P3.7
外部数据存储器写选通RD
控制或复用引脚:
(1)ALE/PROG30访问外部存储器时,ALE(地址锁存允许)的输出用于锁存地址的低位字节。
即使不访问外部存储器,ALE端仍以不变的频率输出脉冲信号(此频率是振荡器频率的1/6)。
在访问外部数据存储器时,出现一个ALE脉冲。
对Flash存储器编程时,这个引脚用于输入编程脉冲PROG
(2)PSEN29该引是外部程序存储器的选通信号输出端。
当AT89C51由外部程序存储器取指令或常数时,每个机器周期输出2个脉冲即两次有效。
但访问外部数据存储器时,将不会有脉冲输出。
(3)EA/Vpp31外部访问允许端。
当该引脚访问外部程序存储器时,应输入低电平。
要使AT89S51只访问外部程序存储器(地址为0000H-FFFFH),这时该引脚必须保持低电平。
对Flash存储器编程时,用于施加Vpp编程电压。
2、键盘输入
当无按键闭合时,P10~P13与P14~P17之间开路;
当有键闭合时,与闭合键相连的两条I/O口线之间短路。
判断有无按键按下的方法是:
第一步,置列线P14~P17为输入状态,从行线P10~P13输出低电平,读入列线数据,若某一列线为低电平,则该列线上有键闭合。
第二步,行线轮流输出低电平,从列线P14~P17读入数据,若有某一列为低电平,则对应行线上有键按下。
综合一二两步的结果,可确定按键编号。
但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。
这种情况下,编程会很简单,但是会占用大量的I/O口资源,因此在很多情况下都不采用这种方式。
为此,我们引入了矩阵键盘的应用,采用四条I/O线作为行线,四条I/O线作为列线组成键盘。
在行线和列线的每个交叉点上设置一个按键。
这样键盘上按键的个数就为4×
4个。
这种行列式键盘结构能有效地提高单片机系统中I/O口的利用率,如图所示:
键盘介绍:
每个按键都有它的行值和列值,行值和列值的组合就是识别这个按键的编码。
矩阵的行线和列线分别通过两并行接口和CPU通信。
键盘的一端(列线)通过电阻接VCC,而接地是通过程序输出数字“0”实现的。
键盘处理程序的任务是:
确定有无键按下,判断哪一个键按下,键的功能是什么?
还要消除按键在闭合或断开时的抖动。
两个并行口中,一个输出扫描码,使按键逐行动态接地;
另一个并行口输入按键状态,由行扫描值和回馈信号共同形成键编码而识别按键,通过软件查表,查出该键的功能。
3、液晶模块简介
LM016L的结构及功能
LM016L液晶模块采用HD44780控制器,hd44780具有简单而功能较强的指令集,可以实现字符移动,闪烁等功能,LM016L与单片机MCU通讯可采用8位或4位并行传输两种方式,hd44780控制器由两个8位寄存器,指令寄存器(IR)和数据寄存器(DR)忙标志(BF),显示数RAM(DDRAM),字符发生器ROMA(CGOROM)字符发生器RAM(CGRAM),地址计数器RAM(AC)。
IR用于寄存指令码,只能写入不能读出,DR用于寄存数据,数据由内部操作自动写入DDRAM和CGRAM,或者暂存从DDRAM和CGRAM读出的数据,BF为1时,液晶模块处于内部模式,不响应外部操作指令和接受数据,DDTAM用来存储显示的字符,能存储80个字符码,CGROM由8位字符码生成5*7点阵字符160中和5*10点阵字符32种.8位字符编码和字符的对应关系
Proteus仿真:
使用Proteus仿真1602--即LM016L--依照数据手册说明可能遇到困难,可以尝试采用以下方案解决:
1、数据手册中可能介绍1602内部D0~D7已有上拉,可以使用P0口直接驱动。
在Proteus里LM016L内部可能没有,应该人为加上拉电阻。
建议不要使用排阻,使用普通电阻一个一个拉应该可以解决问题;
2、可能碰到不能检测忙信号的问题,尝试使用延时把忙信号拖过去.
引脚说明
1602字符型LCD通常有14条引脚线
电路接口设计,AT889C51的P0口作为数据的输出端,P2.0和P2.1,P2.2作为控制信号的输出端
三、计算器程序流程图
1
系统程序流程图②LCD显示流程图
N
Y
四、总的设计图
简易计算器主要包括:
键盘电路、显示电路。
以下是设计的整个系统的图
五、计算器C语言编程
#include<
reg51.h>
//头文件
#defineuintunsignedint//将unit定义为无符号整数
#defineucharunsignedchar//将uchar定义为无符号变量
sbitlcden=P2^3;
//定义引脚
sbitrs=P2^4;
//定义引脚
sbitrw=P2^0;
sbitbusy=P0^7;
chari,j,temp,num,num_1;
longa,b,c;
//a为第一个数b为第二个数c,得数
floata_c,b_c;
ucharflag,fuhao;
//flag表示是否有符号键按下,fuhao表征按下的是哪个符号
ucharcodetable[]={7,8,9,0,4,5,6,0,1,2,3,0,0,0,0,0};
定义无符号数组
ucharcodetable1[]={
7,8,9,0x2f-0x30,
4,5,6,0x2a-0x30,
1,2,3,0x2d-0x30,
0x01-0x30,0,0x3d-0x30,0x2b-0x30
};
//十六进制码2f(除法)2a(乘法)2d(减)01(清零)3d(等号)2b(加号)
voiddelay(ucharz)//延迟函数
{
uchary;
uchar//定义为无符号变量
for(z;
z>
0;
z--)
for(y=0;
y<
110;
y++);
}
voidcheck()//判断忙或空闲
do{
P0=0xFF;
rs=0;
//指令
rw=1;
//读
lcden=0;
//禁止读写
delay
(1);
//等待,液晶显示器处理数据
lcden=1;
//允许读写
}
while(busy==1);
//判断是否为空闲,1为忙,0为空闲
}
void
write_com(ucharcom)//写指令函数
{
P0=com;
//com指令付给P0口
rw=0;
check();
}
voidwrite_date(uchardate)//写数据函数
{
P0=date;
rs=1;
voidinit()//初始化
num=-1;
//使能信号为高电平
write_com(0x38);
//8位,2行
write_com(0x0c);
//显示开,光标关,不闪烁*/
write_com(0x06);
//增量方式不移位显竟獗暌贫柚?
write_com(0x80);
//检测忙信号
write_com(0x01);
//显示开,光标关,不闪烁
num_1=0;
i=0;
j=0;
a=0;
//第一个参与运算的数
b=0;
//第二个参与运算的数
c=0;
flag=0;
//flag表示是否有符号键按下,
fuhao=0;
//fuhao表征按下的是哪个符号
voidkeyscan()//键盘扫描程序
P3=0xfe;
if(P3!
=0xfe)
delay(20);
//延迟20ms
if(P3!
{
temp=P3&
0xf0;
switch(temp)
{
case0xe0:
num=0;
break;
case0xd0:
num=1;
case0xb0:
num=2;
case0x70:
num=3;
}
}
while(P3!
=0xfe);
/////////////////////////////////////////////////////键盘第一行789/
if(num==0||num==1||num==2)//如果按下的是'
7'
'
8'
或'
9
{
if(j!
=0)
if(flag==0)//没有按过符号键
a=a*10+table[num];
else//如果按过符号键
b=b*10+table[num];
else//如果按下的是'
/'
flag=1;
fuhao=4;
//4表示除号已按
i=table1[num];
write_date(0x30+i);
P3=0xfd;
if(P3!
=0xfd)
delay(5);
{
num=4;
num=5;
num=6;
num=7;
}
=0xfd);
/////////////////////////////////////////////////////////////////键盘第二行456*
if(num==4||num==5||num==6&
&
num!
=7)//如果按下的是'
4'
5'
6'
write_com(0x01);
j=0;
if(flag==0)//没有按过符号键
a=a*10+table[num];
else//如果按过符号键
b=b*10+table[num];
}
else//如果按下的是'
flag=1;
fuhao=3;
//3表示乘号已按
P3=0xfb;
=0xfb)
delay(5);
temp=P3&
switch(temp)
case0xe0:
num=8;
break;
case0xd0:
num=9;
case0xb0:
num=10;
case0x70:
num=11;
=0xfb);
////////////////////////////////////////////////////键盘第三行123-
if(num==8||num==9||num==10)//如果按下的是'
1'
2'
3'
write_com(0x01);
elseif(num==11)//如果按下的是'
-'
fuhao=2;
//2表示减号已按
P3=0xf7;
=0xf7)
temp=P3&
case0xe0:
num=12;
case0xd0:
num=13;
num=14;
num=15;
///////////////////////////////////////////////////////键盘第四行
while(P3!
=0xf7);
switch(num)
case12:
{write_com(0x01);
a=0;
b=0;
flag=0;
fuhao=0;
}//按下的是"
清零"
break;
case13:
{//按下的是"
0"
if(flag==0)//没有按过符号键
a=a*10;
write_date(0x30);
P1=0;
elseif(flag==1)//如果按过符号键
b=b*10;
write_date(0x30);
///////////////////////////////////////////////////////////加法
case14:
{j=1;
if(fuhao==1){write_com(0x80+0x4f);
//按下等于键,光标前进至第二行最后一个显示处
write_com(0x04);
//设置从后住前写数据,每写完一个数据,光标后退一格
c=a+b;
while(c!
write_date(0x30+c%10);
c=c/10;
write_date(0x3d);
//再写"
="
a=0;
////////////////////////////////////////////////////////////////////////减法
elseif(fuhao==2)
{
write_com(0x80+0x4f);
//光标前进至第二行最后一个显示处
write_com(0x04);
if(a-b>
0)
c=a-b;
else
c=b-a;
while(c!
{
write_date(0x30+c%10);
c=c/10;
}
if(a-b<
0)
write_date(0x2d);
write_date(0x3d);
a=0;
//////////////////////////////////////////////////////乘法
elseif(fuhao==3)
write_com(0x04);
c=a*b;
write_date(0x30+c%10);
////////////////////////