简易计算器
学院:信息工程学院
班级:自动化三班
姓名:
学号:2009550335
日期:9月28日
目 录
一、设计任务和性能指标………………………………………………3
1.1设计任务………………………………………………………3
1.2性能指标………………………………………………………3
二、设计方案……………………………………………………………3
三、系统硬件设计………………………………………………………4
3.1单片机最小系统………………………………………………4
3.2键盘接口电路…………………………………………………5
3.3数码管显示电路………………………………………………6
3.5按键监视电路…………………………………………………6
四、系统软件设计………………………………………………………7
4.1键盘扫描子程序设计…………………………………………7
4.2移位子程序及结果计算子程序设计…………………………8
4.3显示子程序设计………………………………………………8
4.4主程序设计……………………………………………………21
五、调试及性能分析……………………………………………………21
5.1调试步骤………………………………………………………21
5.2性能分析………………………………………………………21
六、心得体会……………………………………………………………22
参考文献 ………………………………………………………………22
附录1、系统硬件电路图………………………………………………23
附录2、硬件实物图……………………………………………………24
附录3、器件清单 …………………………………………………… 25
一、设计任务和性能指标
1.1设计任务
自制一个单片机最小系统,包括复位电路,采用外部小键盘输入数据,能够实现加法、乘法及一个科学计算,计算结果显示在四位一体的数码管上。
1.2性能指标
1.加法:四位加法,计算结果若超过四位则显示计算错误;
2.减法:四位减法,计算结果若小于零则显示计算错误;
3.乘法:个位数乘法;
4.除法:整数除法;
5.取对数;
6.开平方;
7.指数运算;
8.有清零功能
二.设计方案
按照系统设计的功能的要求,初步确定设计系统由主控模块、监测模块、显示模块、键扫描接口电路共四个模块组成,电路系统构成框图如图1.1所示。主控芯片使用51系列ATC52单片机,采用高性能的静态80C51设计,由先进工艺制造,并带有非易失性Flash程序存储器。它是一种高性能、低功耗的8位COMS微处理芯片,市场应用最多。
监测模块采用二极管和扬声器(实验室用二极管代替)组成电路。
键盘电路采用4*4矩阵键盘电路。
显示模块采用4枚共阳极数码管和74ls273锁存芯片构成等器件构成。
整个单片机的接口电路:
P0用于显示输出;
P1用于键扫描输入;
P2用于数码管位选控制;
P3用于键盘扩展(部分运算符输入);
三.系统硬件设计
3.1单片机最小系统
单片机最小系统就是支持主芯片正常工作的最小电路部分,包括主控芯片、复位电路和晶振电路。
主控芯片选取STCC52RC芯片,因其具有良好的性能及稳定性,价格便宜应用方便。
晶振选取11.0592MHz,晶振旁电容选取20pF。
采用按键复位电路,电阻分别选取100Ω和10K,电容选取10μF。
以下为单片机最小系统硬件电路图。
单片机最小系统硬件电路
3.2键盘接口电路
计算器所需按键有:
数字键:’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’,’0’
功能键:’+’, ’-‘ , ’*’, ’/ ’ , ’ = ’, ’ C( 清零)’
扩展键:“log”,“ln”,“x^2”“小数点”,“开方”
共计25个按键,采用4*4矩阵键盘,键盘的行和列之间都有公共端相连,四行和四列的8个公共端分别接P1.0~P1.7,这样扫描P1口就可以完成对矩阵键盘的扫描,通过对16个按键进行编码,从而得到键盘的口地址,对比P1口德扫描结果和各按键的地址,我们就可以得到是哪个键按下,从而完成键盘的功能。
以下为键盘接口电路的硬件电路图
键盘接口电路
实物图:
扩展键接口电路:
3.3数码管显示电路
采用8位数码管对计算数据和结果的显示(实验时只用到了4位),这里选取共阴数码管,利用74LS244N对数码管进行驱动,为了节省I/O资源,采取动态显示的方法来显示计算数据及结果。
P0口输出显示值,
P2.0~P2.7(实际操作用到P2.0-P2.3)用来作为位选端,控制哪几位数码管进行显示。
以下为数码显示电路的硬件电路图
3.4按键监视电路
按键监视电路就是在按键时,发出声音提醒,以确保输入数字有效。这里就采用5V蜂鸣器作为示音设备(实际操作用发光二极管代替)。用p3.7口输出信号。
以下为报警电路硬件电路图
按键监视电路图
系统整体硬件电路图见附录一
四、系统软件设计
4.1键盘扫描子程序设计
要进行数据的计算就必须先进行数据的输入,也就必须确定按键输入的数值是什么,这就需要对键盘进行扫描,从而确定究竟是哪个键按下。
以下为键盘扫描子程序的程序清单。
uchar Keyscan(void)
{
uchar i,j, temp, Buffer[4] = {0xef, 0xdf, 0xbf, 0x7f};
for(j=0; j<4; j++)
{
P1 = Buffer[j];
/*以下三个_nop_();作用为让 P1 口的状态稳定*/
delay();
temp = 0x01;
for(i=0; i<4; i++)
{
if(!(P1 & temp))
{
return (i+j*4);
}
temp <<= 1;
}
}
}
4.2移位程序及结果计算代码设计
输入数据要存储在一四位数组内,而我们键入的值是数据的高位,后键入的值是低位,这样我们就需要在输入低位数值时将高位数值从数组的低位移向数组的高位,这就是编写移位子程序的目的。
对于结果计算子程序,包含加、减、乘、除四种运算。以加法运算为例,各种运算各有其标志位来代表计算类型,当加法标志位add=1是,就将输入的两个数据按照加法进行计算。
首先将数组内的数按照对应的位关系,将其转化为一个十进制数,这样我们就得到了加速和被加数这样俩个十进制数,从而我们就可以简单的将两个数进行相加,结果就是我们所求的数值。但这个数值不能直接显示到数码管上,我们还要对其进行处理,使其变为对应进位的四个数存入数组内,以便显示。既通过对结果数值分别除以1000、100、10和对10取余,得到我们想要的四个数,送显示子程序显示。其余减、乘、除的计算方法与加法的计算方法一样,这里不再累述。
以下为移位子程序和结果计算子程序的程序清单。
这部分嵌入到了主函数中。
调用输入数据函数:
4.3显示子程序设计
从始至终无论是输入的计算数据,还是计算后的结果值。都存储在同一数组dat[ ]中,这样我们只要在显示时一直调用dat[ ]中的值,就能正确的显示数据。
以下为显示子程序的程序清单。
void diaplay(void) interrupt 1
{
uchar len;
TH0=(65536-2000)/256;
TL0=(65536-2000)%256;
P2=Disbuf[dcounter];
len=dcounter;
P0=xx[len];
dcounter+=1;
if(dcounter==8)
{
dcounter=0;
}
}
延时程序:
void Delay_1ms(uint i)//1ms延时
{
uchar x,j;
for(j=0;j for(x=0;x<=148;x++);
}
void delay()
{
int i,j;
for(i=0; i<=10; i++)
for(j=0; j<=2; j++)
;
}
4.4主程序设计
主程序既把以上各子程序串连成一个整体,使整个程序循环运行。而在以上程序中也已经加入了个程序之间的连接点,首先进入程序后就立即进入显示子程序,而显示子程序内又调用键盘扫描子程序,若有键按下,则会跳转到移位子程序和结果计算子程序进行相应的处理。通过计算或移位后,数组内的值发生改变,显示的值也会同时发生改变。之后再进行键盘扫描,如此反复运行,就构成了程序的整体。
整体程序清单如下:
#include #include #include typedef unsigned char uchar; typedef unsigned int uint; void delay(); uchar Keyscan(void); void result_a(void); void diaplay(void); void Delay_1ms(uint i); uchar vie_a[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar exchg_a[16]={7,8,9,13,4,5,6,12,1,2,3,11,14,0,15,10}; uchar xx[8]; uchar xxu[8]; void Speak_a(void); sbit spp=P3^7;//响铃 sbit cl3=P3^6;//小数点 sbit cl=P3^1;//求ln sbit cl0=P3^2;//开方 sbit cl1=P3^3;//求log sbit cl2=P3^4;//求e的x次幂 uchar flo[10]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xef,0xef}; uchar zz[8]={0x77,0x3f,0x77,0x77,0x79}; uchar Disbuf[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; uchar yy[8]={0x3f,0,0}; float a=0,b=0,c; uchar L=1; uchar cal_a=0,dcounter=0; void main(void) { uchar singlp_a=1,n=1; uchar key=18; uchar all_m=0,ent_a=0,att=1; signed long s,r; TMOD=0x01; TH0=(65536-2000)/256; TL0=(65536-2000)%256; EA=1; ET0=1; TR0=1; spp=0; while(1) { if(singlp_a==1) { singlp_a=0; P3=0x7f; if(P3!=0x7f) { Delay_1ms(2); if(cl==0) { Speak_a(); cal_a=14;//求以㏑x key=15; } if(cl0==0) { Speak_a(); cal_a=15;//开方 key=15; } if(cl1==0) { Speak_a(); cal_a=16;//求以十为底对数 key=15; } if(cl2==0) { Speak_a(); cal_a=17;//求e的x次幂 key=15; } if(cl3==0) { att=0; xx[0]=xx[0]+0x80; } } P1=0x0f; if(P1!=0x0f) { Delay_1ms(2); if(P1!=0x0f) { key=exchg_a[Keyscan()]; Speak_a(); } } } P1=0x0f; P3=0x7f; if(P1==0x0f&&P3==0x7f) { singlp_a=1; } if(key<=9&&key>=0) { if(all_m==0) { if(ent_a==0) { uchar i; ent_a=1; for(i=0;i<=7;i++) { xx[i]=0; } } else {} if(att==1) { uchar i; a=a*10+key; for(i=7;i>0;i--) { xx[i]=xx[i-1]; } xx[0]=vie_a[key]; key=18; } else { uchar i; double m; for(i=7;i>0;i--) { xx[i]=xx[i-1]; } xx[0]=vie_a[key]; m=key; for(i=1;i<=n;i++) { m=m/10; } a=m+a; n++; key=28; } } else { if(att==1) { uchar i; b=b*10+key; for(i=7;i>0;i--) { xx[i]=xx[i-1]; } xx[0]=vie_a[key]; key=18; } else { uchar i; double m; for(i=7;i>0;i--) { xx[i]=xx[i-1]; } xx[0]=vie_a[key]; m=key; for(i=1;i<=n;i++) { m=m/10; } b=m+b; n++; key=28; } } } if(key>=10&&key<=13) { uchar i; cal_a=key; for(i=0;i<=7;i++) { xx[i]=xxu[i]; } all_m=1; key=18; att=1; n=1; } if(key==14) { uchar i; key=18; ent_a=0; for(i=0;i<=7;i++) { xx[i]=yy[i]; } a=0; b=0; c=0; all_m=0; L=1; att=1; n=1; cal_a=0; } if(key==15) { uchar i; all_m=0; key=28; for(i=0;i<=7;i++) { xx[i]=0; } result_a();//调用求计算结果 if(L==1) { if(c==0) { xx[0]=0x3f; } if(c>=0.0001) { r=c; s=10000*(c-r); n=0; xx[n]=vie_a[s%10]; if(s%10!=0) { n++; } if(c>=0.001) { r=c; s=1000*(c-r); xx[n]=vie_a[s%10]; if(n==0&&s%10==0) {} else { n++; } if(c>=0.01) { r=c; s=100*(c-r); xx[n]=vie_a[s%10]; if(n==0&&s%10==0) {} else { n++; } if(c>=0.1) { r=c; s=10*(c-r); xx[n]=vie_a[s%10]; if(n==0&&s%10==0) {} else { n++; } if(c>=1) { s=c; xx[n]=flo[s%10]; n++; if(c>=10) { s=c/10; xx[n]=vie_a[s%10]; n++; if(c>=100) { s=c/100; if(n>=8) { uchar i; for(i=0;i<=7;i++) { xx[i]=xx[i+1]; } xx[n-1]=vie_a[s%10]; } else { xx[n]=vie_a[s%10]; n++; } if(c>=1000) { s=c/1000; if(n>=8) { uchar i; for(i=0;i<=7;i++) { xx[i]=xx[i+1]; } xx[n-1]=vie_a[s%10]; } else { xx[n]=vie_a[s%10]; n++; } if(c>=10000) { s=c/10000; if(n>=8) { uchar i; for(i=0;i<=7;i++) { xx[i]=xx[i+1]; } xx[n-1]=vie_a[s%10]; } else { xx[n]=vie_a[s%10]; n++; } if(c>=100000) { s=c/100000; if(n>=8) { uchar i; for(i=0;i<=7;i++) { xx[i]=xx[i+1]; } xx[n-1]=vie_a[s%10]; } else { xx[n]=vie_a[s%10]; n++; } if(c>=1000000) { s=c/1000000; if(n>=8) { uchar i; for(i=0;i<=7;i++) { xx[i]=xx[i+1]; } xx[n-1]=vie_a[s%10]; } else { xx[n]=vie_a[s%10]; n++; } if(c>=10000000) { s=c/10000000; if(n>=8) { uchar i; for(i=0;i<=7;i++) { xx[i]=xx[i+1]; } xx[n-1]=vie_a[s%10]; } else { xx[n]=vie_a[s%10]; n++; } if(c>=100000000) { for(i=0;i<=7;i++) { xx[i]=zz[i]; } } } } } } } } } } else { xx[n]=0xbf; } } } } } } } } } void result_a(void) { switch(cal_a) { case 0:c=a;break; case 10:c=a+b;break; case 11:if(a { uchar i; for(i=0;i<=7;i++) { xx[i]=zz[i]; } L=0; } else { c=a-b; } break; case 12:c=a*b;break; case 13:c=a/b;break; case 14:c=log(a); a=c;break; case 15:c=sqrt(a); a=c;break; case 16:c=log10(a); a=c;break; case 17:c=exp(a); a=c;break; default:c=0; } } void Delay_1ms(uint i)//1ms延时 { uchar x,j; for(j=0;j for(x=0;x<=148;x++); } void delay() { int i,j; for(i=0; i<=10; i++) for(j=0; j<=2; j++) ; } uchar Keyscan(void) { uchar i,j, temp, Buffer[4] = {0xef, 0xdf, 0xbf, 0x7f}; for(j=0; j<4; j++) { P1 = Buffer[j]; /*以下三个_nop_();作用为让 P1 口的状态稳定*/ delay(); temp = 0x01; for(i=0; i<4; i++) { if(!(P1 & temp)) { return (i+j*4); } temp <<= 1; } } } void diaplay(void) interrupt 1 { uchar len; TH0=(65536-2000)/256; TL0=(65536-2000)%256; P2=Disbuf[dcounter]; len=dcounter; P0=xx[len]; dcounter+=1; if(dcounter==8) { dcounter=0; } } void Speak_a(void) { uchar i; for(i=0;i<=20;i++) { spp=1; delay(); delay(); spp=0; delay(); delay(); } } 五、调试及性能分析 5.1调试步骤 在焊接好器件后,先不要将芯片插在芯片座上,要先验证先板上电源是否好用,有无短路等。接上电源,用万用表测量个芯片座对应电源和地之间的电压值,观察电压值是否正常。一切正常后方可将芯片插入芯片座,以继续测试其他功能。 将芯片插上后,对各个模块进行调试,按键是否工作正常,数码管是否显示正常等。编写相关部分的测试程序对其进行测试。 各部分硬件检测无误后,下载程序进行整体调试,一切正常后,结束调试过程。 在具体调试时首先遇到的问题是程序无法下载进入单片机,通过将电路板接线与原理电路图接线的对比发现,串口芯片与单片机连接的输入,输出接反,重新用铜线连接后,依然无法下载程序。后找到原因是由于下载串口与设计封装不符,用相对应的下载线可以下载。 成功下载程序后,发现数码管显示不正确,查看后发现有先没有连接,可能是制板时漏印,连接后显示正常。 5.2性能分析 对于计算器的性能,主要的衡量指标就在于计算的精度,本次制作的计算器性能情况如下: 加法运算:四位加法运算,和值不超过9999,若超过上限,则显示错误提示ERROR。 减法运算:四位减法运算,若结果为负,对其取绝对值。 乘法运算:积不超过9999的乘法运算,若超出上限,显示错误提示Error。 除法运算:整数除法,既计算结果为整数,若除数为零,则显示错误提示Error。 通过对实际性能的分析,可以得到本次设计满足设计的要求。 六、心得体会 通过本次课程设计我真正的自己完成了对给定要求系统的硬件设计、电路设计、电路板设计、软件设计以及对成品的调试过程。从整个过程中学习到了很多方面的知识,了解到以往学习中自己知识在某方面的不足之处,是对以往学习科目的一种贯穿和承接,从而能更好的认识和学习,也对将来从事工作大有裨益。 本次实验过程中,我切实体验到了,认真对待每一个细小零件的重要性。对于实验室提供的零件要具有检错能力。我做的是计算器,实验中换取了4个键盘,最后才得到正确的结果显示。此外从本次试验中我学会到了,焊接电路布局的重要性,以及在布线时,对线路的长度要有一定的冗余,以提供纠错方便。还有最重要的一点是,要学会使用万用表对电路进行检测,查出问题。 从本次课设中我也看到了自身的很多不足之处,对知识的掌握不够扎实,有一知半解的现象。有时做事不够稳定,过于毛躁,不能平心静气的去分析所遇到的问题和错误。这在以后的工作和生活中是不可取的,通过对自身问题的认识与改正相信再遇到同样问题时会更好的解决。以后的设计实验也会更好的完成。 参考文献 [1] 李群芳,黄建. 单片机微型计算机与接口技术. 北京:电子工业出版社,2001 [2] 徐维祥、刘旭敏. 单片微型机原理及应用. 大连:大连理工大学出版社,1996 [3] 李光飞、楼然苗、胡佳文、谢象佐. 单片机课程设计与实例指导. 北京: 北京航空航天大学出版社,2004 [4] 楼然苗、李光飞. 51系列单片机设计实例. 北京:北京航空航天大学出版社,2003 附录1 系统硬件电路图 附录2 硬件实物图 附录3、器件清单序号 元件名称 参数 元件标号 数量 1 无极性电容 0.1uF C1, C2, C7, C8,C9 5 2 极性电容 10uF C3, C4, C10 3 3 无极性电容 20P/27P/30P/33P(均可) C5, C6 2 4 发光二极管 红、绿各一个 D1, D2 2 5 三极管 9013 Q1, Q2, Q3, Q4 4 6 电阻 100 R2, 2 7 电阻 10K R1, R5, R6, R7, R8, R9, R10, R11, R12 10 8 电阻 1K R4, R13 2 9 排阻 4.7K R3 1 10 小按键 0.6*0.6 S1, S3, S4, S5, S6 5 11 芯片插座 DIP16 1 12 芯片插座 DIP20 1 13 芯片插座 DIP24 1 14 芯片插座 DIP40 1 15 插座 2P/2.54MM 电源线入口 1 16 插座 3P/2.54MM 与串口下载线配套 1 17 串口芯片 MAX232 U1 1 18 单片机 STCC54RD+ U2 1 19 驱动 SN74LS244N U3 1 20 晶振 11.0592M Y1 1 21 四位数码管