基于AT89C51单片机的电子计算器的课程设计_单片机计算器课程设计
基于AT89C51单片机的电子计算器的课程设计由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“单片机计算器课程设计”。
C51单片机电子计算器课程设计
一.课程设计背景
当今时代,是一个新技术层出不穷的时代。在电子领域,尤其是自动化智能控制领域,传统的分立元件或数字逻辑电路构成的控制系统正以前所未见的速度被单片机智能控制系统所取代。单片机具有体积小、功能强、成本低、应用面广等优点,可以说,智能控制与自动控制的核心就是单片机。目前,一个学习与应用单片机的高潮正在工厂、学校及企事业单位大规模地兴起。过去习惯于传统电子领域的工程师、技术员正面临着全新的挑战,如不能在较短时间内学会单片机,势必会被时代所遗弃,只有勇敢地面对现实,挑战自我,加强学习,争取在较短的时间内将单片机技术融会贯通,才能跟上时代的步伐。
它所给人带来的方便也是不可否定的,它在一块芯片内集成了计算机的各种功能部件,构成一种单片式的微型计算机。20世纪80年代以来,国际上单片机的发展迅速,其产品之多令人目不暇接,单片机应用不断深入,新技术层出不穷。20世纪末,电子技术获得了飞速的发展,在其推动下,现代电子产品几乎渗透了社会的各个领域,有力地推动了社会生产力的发展和社会信息化程度的提高,同时也使现代电子产品性能进一步提高,产品更新换代的节奏也越来越快。本设计是由单片机实现的模拟计算器,它不仅能实现数据的加减乘除运算,而且还能使数据及其计算结果在数码管上显示出来,能够实现0-256的数字四则运算。本设计是用单片机AT89C51来控制,采用共阳极数码显示,软件部分是由C语言来编写的。设计任务
二、元器件清单及简介
89c51型芯片 一片
排阻 两个
晶振12MHZ 一个
电容22uf 两个
面包板 三个
导线 若干
三、设计原理及分析
根据功能和指标要求,本系统选用MCS 51 单片机为主控机。通过扩展必要的外围接口电路,实现对计算器的设计。具体设计考虑如下: ①由于要设计的是简单的计算器,可以进行四则运算,对数字的大小范围要求不高,故我们采用可以进行四位数字的运算,选用8 个LED 数码管显示数据和结果。
②另外键盘包括数字键(0~9)、符号键(+、-、×、÷)、清除键和等号键,故只需要16 个按键即可。系统模块图:
2.1 输入模块:
键盘扫描计算器输入数字和其他功能按键要用到很多按键,如果采用独立按键的方式,在这种情况下,编程会很简单,但是会占用大量的I/O 口资源,因此在很多情况下都不采用这种方式。为此,我们引入了矩阵键盘的应用,采用四条I/O 线作为行线,四条I/O 线作为列线组成键盘。在行线和列线的每个交叉点上设置一个按键。这样键盘上按键的个数就为4×4个。这种行列式键盘结构能有效地提高单片机系统中I/O 口的利用率。矩阵键盘的工作原理:计算器的键盘布局如图所示:一般有16 个键组成,在单片机中正好可以用一个P 口实现16 个按键功能,这种形式在单片机系统中也最常用。
以上键盘从上到下依次编号为1,2,3,4,D,C,B,A 1
由图 3 矩阵键盘内部电路图可以知道,当无按键闭合时,P10~P13 与P14~P17 之间开路。当有键闭合时,与闭合键相连的两条I/O 口线之间短路。判断有无按键按下的方法是:第一步,置列线P14~P17 为输入状态,从行线P10~P13 输出低电平,读入列线数据,若某一列线为低电平,则该列线上有键闭合。第二步,行线轮流输出低电平,从列线P14~P17 读入数据,若有某一列为低电平,则对应行线上有键按下。综合一二两步的结果,可确定按键编号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。2.2 运算模块:(单片机控制)AT89C51 单片机是在一块芯片中集成了CPU、RAM、ROM、定时器/计数器和多功能I/O等一台计算机所需要的基本功能部件。如果按功能划分,它由如下功能部件组成,即微处理器(CPU)、数据存储器(RAM)、程序存储器(ROM/EPROM)、并行I/O 口、串行口、定时器/计数器、中断系统及特殊功能寄存器(SFR)。[3][5]单片机是靠程序运行的,并且可以修改。通过不同的程序实现不同的功能,尤其是特殊的独特的一些功能,通过使用单片机编写的程序可以实现高智能,高效率,以及高可靠性!因此我们采用单片机作为计算器的主要功能部件,可以进行很快地实现运算功能。2.3 显示模块:
LED 显示发光二极管LED 是单片机应用系统中的一宗简单而常用的输出设备,其在系统中的主要作用是显示单片机的输出数据、状态等。因而作为典型的外围器件,LED 显示单元是反映系统输出和操作输入的有效器件。LED 具备数字接口可以方便的和大年纪系统连接;它的优点是价格低,寿命长,对电压电流的要求低及容易实现多路等,因而在单片机应用系统中获得了广泛的应用。[2][4]通常的数码显示器是由7 段条形的LED 组成(如图4 所示),点亮适当的字段,就可显示出不同的数字。我们采用8 段数码管,其中位于显示器右下角的LED 作小数点用。LED 显示器有两种不同的形式:共阴极和共阳极。本次设计采用共阴极接法(如图5所示)。
3、软件设计
在程序设计方法上,模块化程序设计是单片机应用中最常用的程序设计方法。设计的中心思想是把一个复杂应用程序按整体功能划分成若干相对独立的程序模块,各模块可以单独设计、编程和调试,然后组合起来。这种方法便于设计和调试,容易实现多个程序共存,但各个模块之间的连接有一定的难度。根据需要我们可以采用自上而下的程序设计方法,此方法先从主程序开始设计,然后再编制各从属程序和子程序,层层细化逐步求精,最终完成一个复杂程序的设计。这种方法比较符合人们的日常思维,缺点是一级的程序错误会对整个程序产生影响。功能流程图如下:
4、硬件原理以下为简易计算器的总体电路图
加运算:
减运算:
乘运算:
除运算:
清零: 四.总结
通过此次单片机实训设计,我们学到了很多东西,在器件的了解和器件选择上有个明确的认识,并在程序的设计,及理论在实践反面的运用能力有巨大的提高。
这次单片机课程设计由我们六位同学经过一周努力设计得到。软件的编程要我们不断的调试,最终我们终于完成了单片机实训课程设计,很高兴它能按着设计思想与要求运动起来。
当然,这其中也有很多的问题。第一、不够细心,由于对课本理论的不熟悉导致的编程错误,对于器件的实际情况的不了解,理论与实践的差距导致我们在设计实际电路时出现了很多错误,使得实验不能一次通过。第二、是在学习态度上,这次课程设计是对我们的学习态度的一次体验。对于这次单片机综合课程实习,我们的第一大心得体会就是作为一名工程技术人员,要求具备的首要素质绝对应该是严谨,这次的课程设计我们所遇到的问题多半是由于我们不够严谨。第三、在做人上,我们认识到,无论做什么事情,只要你足够坚强,有足够的毅力和决心,有足够的挑战困难的勇气,就没什么办不到的。还有就是团队的合作精神。
在这次难得的课程设计过程中我们锻炼了自己的思考能力和动手能力,加强了我们思考问题的完整性和实际生活联系的可行性。在方案设计选择和芯片的选择上,培养了我们综合应用单片机的能力,对单片机的各个管脚的功能也有了进一步的认识。还锻炼我们个人的查阅技术资料的能力,动手能力,发现问题、解决问题的能力。
五.参考文献
《单片机原理及应用》 张毅刚 高等教育出版社
《MCS—51单片机应用设计》 张毅刚 哈尔滨工业大学出版社 《MCS—51系列单片机实用接口技术》 李华 北京航空航天大学出版社 《单片机应用技术选集》 何立民 北京航空航天大学出版社 《单片机原理及其接口技术》 胡汉才 《数码管显示驱动和键盘扫描控制器CH451及其应用》
清华大学出版社 施隆照 /*********************************************** **实现说明:
1:变量flag_fuhao为键入+、-、*、/运算符标志
(即当前一个键值为+、-、*、/运算符时,flag_fuhao为1,其他键值则flag_fuhao置零,其用在显示时)
2:变量flag_shu数输入情况,flag_shu为0时,输入的符号无效(flag_shu为2时,变量fuhao更新为新键值)
*********************************************/ #include #define uchar unsigned char #define uint unsigned int #define duan P0//数码管显示段选定义 #define wei P2//数码管显示位选定义 #define key P3//键盘接口定义
sbit OFF = P1^0;//关机键定义
float shu1,shu2;//进行运算的两个变量数 uchar num;////键盘扫描返回值
char flag1,flag_shu,flag_fuhao,fuhao,newkey,update;
//flag1开机标志newkey新按键标志,fuhao运算符,update表示等于号 //之后紧接着输入的是数的话则清零shu1
char key_shu;//按键值 char ge=0xdf;//char code Wela[]={0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//六位数码管的位选
unsigned char code Duan[]={0x3f,0x06,0x5b, //
0 1 2 0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};// 3 4 5 6 7 8 9 无显示
共阴极数码管
// 函数声明
uchar keyscan();//键盘扫描函数
void display(float);//数码管显示函数
void delay(uint i)//延时函数 {
while(i--);}
/***********************************************
主函数
************************************************/ void main(){
flag1=0;//标志关机
while(1){
if(keyscan()==15)//开机检测
{
flag1=1;//标志开机
shu1=shu2=fuhao=flag_shu=newkey=0;//初始化变量
while(flag1)//判断是否已开机
{
if(!flag_fuhao)
display(shu1);//如果输入的不是
else
display(shu2);
key_shu=keyscan();
if(newkey==1)//有新键值
{
if(key_shu==15)//按下ON/C键,清零
{
flag_fuhao=update=0;
shu1=shu2=fuhao=flag_shu=newkey=0;
}
else if(key_shu==14&&flag_shu==1&&fuhao)//按下“=”
{
switch(fuhao)
{
case 10:shu1=shu2+shu1;break;
case 11:shu1=shu2-shu1;break;
case 12:shu1=shu2*shu1;break;
case 13:shu1=shu2/shu1;break;
}
flag_fuhao=0;
fuhao=0;
update=1;
} 10
else if((key_shu>=0)&&(key_shu
{
if(update)
shu1=0;
if(shu1
{
shu1=key_shu+shu1*10;
flag_shu=1;
}
update=0;
flag_fuhao=0;
}
else if((key_shu>=10)&&(key_shu
{
flag_fuhao=1;//表示按下了运算符号键
update=0;
if(flag_shu==1)//表示之前有数字键按下
{
if(fuhao==0)//表示计算时只有一次按下运算符,如1*8=8,第二个数字后面是=,而不是其他运算符
{
shu2=shu1;
shu1=0;
fuhao=key_shu;//将按下的运算符号的键的值赋值给fuhao,记录前一个运算符,以便按=后实现相应的计算。
flag_shu=2;
}
else
//表示计算时按了多次运算符,如1*8*9=72,第二个数字后面并没有=,而是*
{
switch(fuhao)
{
case 10:shu2=shu2+shu1;break;
case 11:shu2=shu2-shu1;break;
case 12:shu2=shu2*shu1;break;
case 13:shu2=shu2/shu1;break;
}
shu1=0;
fuhao=key_shu;//将按下的运算符号的键的值赋值给fuhao,记录前一个运算符,以便按=后实现相应的计算。
}
}
else if(flag_shu==2)
fuhao=key_shu;//将按下的运算符号的键的值赋值给fuhao,记录前一个运算符,以便按=后实现相应的计算。
}
newkey=0;
}
}
} } }
/***********************************************
数码管显示函数
************************************************/ void display(float dis_shu){
long zhengshu=(long)dis_shu;char dis_flag,dis_aa,dis_zero=0;uchar dis_data[6]={0,0,0,0,0,0},xiaoshu[6]={0,0,0,0,0,0};
ge=0xdf;//11 01 111 if(zhengshu>99999)dis_flag=6;else if(zhengshu>9999)dis_flag=5;else if(zhengshu>999)dis_flag=4;else if(zhengshu>99)dis_flag=3;else if(zhengshu>9)dis_flag=2;else dis_flag=1;
dis_shu=dis_shu-zhengshu;
for(dis_aa=0;(dis_aa
xiaoshu[dis_aa]=(long)dis_shu;}
for(dis_aa=0;(dis_aa
if(dis_zero||xiaoshu[5-dis_flag-dis_aa])12
{
duan=Duan[xiaoshu[5-dis_flag-dis_aa]];
wei=ge;
ge>>=1;
delay(100);
wei=0xff;
dis_zero=1;
} }
// for(dis_aa=0;dis_aa
dis_data[dis_aa]=zhengshu%10;
zhengshu=zhengshu/10;} //数码管段选
for(dis_aa=0;dis_aa
if(0xdf!=ge&&dis_aa==0)
{
duan=Duan[dis_data[dis_aa]]|0x80;
wei=ge;
ge>>=1;
delay(100);
wei=0xff;
}
else
{
duan=Duan[dis_data[dis_aa]];
wei=ge;
ge>>=1;
delay(100);
wei=0xff;
}
} //数码管位选 }
/***********************************************
键盘扫描函数
************************************************/ uchar keyscan()// 函数返回按键的值
{
//将第一行线置低电平,其余行线全部为高电平,即扫描第一行
key=0xfe;
if(key!=0xfe){
delay(500);//延时消抖操作
if(key!=0xfe)
{
switch(key)
{
case 0xee:num=7;break;//7
case 0xde:num=8;break;//8
case 0xbe:num=9;break;//9
case 0x7e:num=13;break;//除号 ”/”
}
newkey=1;
delay(500);
while(key!=0xfe)
{
if(flag1)//如果已开机
if(!shu1&&num>=0&&num
display(num);
else
display(shu1);
}
return num;
} }
//将第二行线置低电平,其余行线全部为高电平,即扫描第二行
key=0xfd;if(key!=0xfd){
delay(500);//延时消抖操作
if(key!=0xfd)
{
switch(key)
{
case 0xed:num=4;break;//4
case 0xdd:num=5;break;//5
case 0xbd:num=6;break;//6
case 0x7d:num=12;break;//*
}
newkey=1;
delay(500);
while(key!=0xfd)
{
if(flag1)
if(!shu1&&num>=0&&num
display(num);
else
display(shu1);
}
return num;
} }
//将第三行线置低电平,其余行线全部为高电平,即扫描第三行
key=0xfb;if(key!=0xfb){
delay(500);
if(key!=0xfb)
{
switch(key)
{
case 0xeb:num=1;break;//1
case 0xdb:num=2;break;//2
case 0xbb:num=3;break;//3 15
case 0x7b:num=11;break;//-
}
newkey=1;
delay(500);
while(key!=0xfb)
{
if(flag1)
if(!shu1&&num>=0&&num
display(num);
else
display(shu1);
}
return num;
} }
//将第四行线置低电平,其余行线全部为高电平,即扫描第四行
key=0xf7;if(key!=0xf7){
delay(500);
if(key!=0xf7)
{
switch(key)
{
case 0xe7:num=15;break;// ON/C:开关机按键
case 0xd7:num=0;break;// 0
case 0xb7:num=14;break;// =
case 0x77:num=10;break;// +
}
newkey=1;
delay(500);
while(key!=0xf7)
{
if(flag1)
if(!shu1&&num>=0&&num
display(num);
else
display(shu1);
} 16
return num;
} }
//判断关机按键是否被按下 if(!OFF){
delay(500);//延时消抖操作
if(!OFF)
{
flag1=0;//
}
} return 100;
}
若关机按键被按下,则将关机标志位flag1置1 17