一、课题任务及要求
要求:1、掌握数码管移位动态扫描显示的编程方法
2、掌握矩阵扫描的编程方法
3、掌握数据在内部运算的编程方法
任务:1、实现最大6位正整数加、减、乘、除
2、具备清零、等于功能
3、16个按键功能依次为: 数字0、数字1、数字2、数字3、数字4、数字5、数字6、数字7、数字8、数字9、清零、等于、加、减、乘、除
二、硬 件 设 计
1. 原理图
2. 原理分析
该设计通过89C51芯片控制6个一位数码管显示,并实时检测按键按下情况来实现计算器功能,16个按键有10个为数字按键 其他6个分别为加、减、乘、除、复位、等于
电路接上电源后 数码管显示个位显示数字0 ,芯片对按键进行实时扫描,通过矩阵键盘进行计算,特别注意的是,当结果为负数时,数码管显现EORR。
(一)、硬件部分
1、数码管为一位共阴数码管,共6个;故在段码输出口外加NPN型三极管作驱动
2、51芯片P1口接键盘端(矩阵按键)、P3口接段码、P2口的P2_0—P2_5接位码
3、按键为四乘四矩阵,共16个键
(二)、软件部分
1、数码管移位显示的实现是通过标志位wei2的数值变化控制dispiay函数显示位来实现的
2、程序的重要算法是通过类型为unsigned int 的数temp和数组str[]实现整体加减乘除、各位显示的 只要弄懂这一算法,此程序就可轻易掌握
3. PCB图
略。
4. 元件清单及造价预算
按键 20个 单价 0.2 元 总计 4元
万用板 2块 单价 4 元 总计 8元
一位共阴数码管 6个 单价 2 元 总计 2元
三极管 7个 单价 0.2 元 总计 1.4元
1K电阻 20个 单价 0.01 元 总计 2毛
排插及排线 5对 单价 1元 总计 5元
电容 3个 单价 0.2元 总计 6毛
晶振 1个 单价 1元 总计 1元
8051芯片 1 个 单价 6元 总计 6元
费用总计 26.2元
5.实物照片
三、程 序 设 计
头程序
str[6]=str[5]=str[4]=str[3]=str[2]=str[1]=10;
str1[6]=str1[5]=str1[4]=str1[3]=str1[2]=str[1];
#include<reg51.h> //51单片机基本定义头文件
#include<intrins.h> //循环位移头文件
#define uchar unsigned char //宏定义
#define uint unsigned int
sbit P1_4=P1^4; //IO端口定义 (矩阵扫描后4位端口)
sbit P1_5=P1^5;
sbit P1_6=P1^6;
sbit P1_7=P1^7;
uchar i,num,s; //全局变量定义
uchar wei,wei2,ss,ss1,ss2,str1[]={0,0,0,0,0,0,0};
long temp,temp1,str[]={10,10,10,10,10,10,10};
uchar code dutable[]={ //段位编码
0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90,0x88,0x83,
0xc6,0xa1,0x86,0x8e};
uchar code wetable[]={
0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
void init(); //函数声明
void panduan();
void display();
void delay(uint z);
void shaomiao();
void main() //主函数
{
init(); //调用变量初始化函数
while(1) //大循环
{
shaomiao(); //调用矩阵扫描加处理函数
display(); //调用显示函数
}
}
void init() //变量初始化函数
{
ss2=0;
wei2=1;
temp1=0;
ss=0;
ss1=0;
temp=0;
wei=0;
num=0;
}
void delay(uint z)//延时函数(单位ms)
{
uchar i;
uint j;
for(j=z;j>0;j--)
for(i=114;i>0;i--);
}
void shaomiao() //扫描加处理函数
{
for(i=0,s=0xfe;i<4;i++) //低四位端口依次赋值1
{
P1=s; //对P1口赋值
panduan(); //调用判断处理函数
s=_crol_(s,1); //s循环位左移
s=s|0xf0; //进行位或运算
} (使高4位复原)
}
void panduan()
{
uchar n;
if(P1_4==0||P1_5==0||P1_6==0||P1_7==0)
{
delay(10);
P1=s|0xf0;
if(P1_4==0||P1_5==0||P1_6==0||P1_7==0)
{
if(P1_4==0)
{ n=1;num=i*4+n-1;}
else if(P1_5==0)
{ n=2;num=i*4+n-1;}
else if(P1_6==0)
{ n=3;num=i*4+n-1;}
else if(P1_7==0)
{ n=4;num=i*4+n-1;}
if(num<10&&wei!=7)
{
wei++;
if(ss1==1)
{
temp=0;
str[6]=str[5]=str[4]=str[3]=str[2]=str[1]=10; str1[6]=str1[5]=str1[4]=str1[3]=str1[2]=str1[1]=0;
ss1=0;
}
str[wei]=num;
if(str[1]!=10) temp=str[1];
if(str[2]!=10) temp=str[1]*10+str[2];
if(str[3]!=10) temp=str[1]*100+str[2]*10+str[3];
if(str[4]!=10) temp=str[1]*1000+str[2]*100+str[3]*10+str[4];
if(str[5]!=10) temp=str[1]*10000+str[2]*1000+str[3]*100+str[4]*10+str[5];
if(str[6]!=10) temp=str[1]*100000+str[2]*10000+str[3]*1000+str[4]*100+str[5]*10+str[6];
}
if(num>=10)
{
wei=0;
if(num==10)
{
temp=0;
1[1]=0;
wei=0;
temp1=0;
ss=0;
wei2=1;
}
if(ss1==0&&num==11||(ss1==0&&ss2!=0&&num>11&&num<16))
{
if(num==11)
ss2=0;
switch(ss)
{
case 0: break;
case 1: temp=temp+temp1;break;
case 2: temp=temp1-temp;break;
case 3: temp=temp*temp1;break;
case 4: temp=temp1/temp;break;
}
if(temp>999999)
temp=0;
ss1=1;
}
if(num==12)
{
temp1=temp;
ss=1;ss1=1;ss2=1;
}
if(num==13)
{
temp1=temp;
ss=2;ss1=1;ss2=1;
}
if(num==14)
{
temp1=temp;
ss=3;ss1=1;ss2=1;
}
if(num==15)
{
temp1=temp;
ss=4;ss1=1;ss2=1;
}
}
}
str1[6]=temp/100000;
str1[5]=temp%100000/10000;
str1[4]=temp%10000/1000;
str1[3]=temp%1000/100;
str1[2]=temp%100/10;
str1[1]=temp%10/1;
if(str1[1]!=0)
wei2=1;
if(str1[2]!=0)
wei2=2;
if(str1[3]!=0)
wei2=3;
if(str1[4]!=0)
wei2=4;
if(str1[5]!=0)
wei2=5;
if(str1[6]!=0)
wei2=6;
}
while(P1_4==0||P1_5==0||P1_6==0||P1_7==0);
}
void display()
{
char i1;
for(i1=1;i1<wei2+1;i1++)
{
if(num==0&&ss==4||temp<0)
{
P2=wetable[0];
P3=dutable[14];
}
else
{
P2=wetable[i1-1];
P3=dutable[str1[i1]];
delay(10);
P3=0xff;
}
}
}
四、调 试 结 果
1、硬件调试
a. 把电路板焊好后,先通过检查电路板表面检查是否有漏焊、错焊、接触不良等
b. 编写检测程序逐个点亮数码管,检查数码显示部分是否有硬件问题
c.编写检测程序检测矩阵扫描是否有硬件问题
2、程序调试
重点就是这个部分,很多程序就是在调试过程中慢慢完善,先前所完成的程序部分只能算一个基本框架,当然,这一切是建立在硬件没用问题的基础上
程序在进行调试时,可以像硬件一样使用分模块调试,这样可以最迅捷的找出问题所在,不受其他模块的影响
我们在硬件调试时,一开始时51芯片不工作,经过反复检查后发现原来51芯片的31脚(/EA/VPP)必须接入高电平才能使单片机在读取程序时优先访问内部程序存储器,否则只访问外部程序存储器,而我们的程序是在内部的,故芯片不工作,我们随即在31脚外接VCC和限流电阻,在解决了这个后,硬件OK了。
软件调试时问题就比较多了,很多都是小问题,大多是因为在编写程序时一些地方不够注意或笔误引起的,在这里就不过多解释,少部分是一些难以解决的大问题,一是在输入数字的储存上,开始打算只使用一个数组str[]解决,但在编程时发现在数字转换为一个一个的编码中发现直接引用会引起数据混乱,最后在添入另一数组str1[]后问题得到解决;二是输入数据在内部进行加减乘除时,加法乘法没有错误,但减法除法因为一次输出和二次输入数据的位置关系导致第二次运作时出现错误,最后我们调整了储存数据的变量,这个问题顺利的解决了,最后一个问题困扰我们最久,甚至导致调试过程一度中止,由于我们是6位数的运算,在数字显示过程中,出现了跳位现象,比如你输入123,当输如12时,数码管正常显示12,但将3输入后,数码管显示变为了312,由于一直找不出原因,这个问题一直得不到解决,最后我尝试改变display( )函数和大循环中函数的位置循序后才能正常显示,但这从理论上说不通,最后得出可能是受到延时函数delay( )的延时影响,导致数码管动态显示位码出错。在将这个问题解决后,我们的程序就基本调试完成了,但这并不是最终结果,我将做好的计算器给其他同学进行操作,在他们找茬似的操作中,我们又发现了几个隐藏的很深的BUG并加以解决,虽然不知道还有什么BUG没发现,但至少在BUG发现之前,我们的程序是没问题了。
五 、设 计 总 结
从这次课题设计任务中,我们学习到了很多,经过我们四个的共同努力,老师的精心指导,还有和在网上找到的一些资料,主要让我们掌握了数码管移位动态扫描显示的编程方法,掌握了矩阵扫描的编程方法,掌握了数据在内部运算的编程方法,这也是编程必须掌握的知识。当然调试的时候你也是一个重点,也让我们收获良多,这其中就分硬件和软件两部分,硬件只要我们注意一些焊接问题,对板子输入简单的程序进行检测。这个只要自己细心一点都可以按照原理图做好,其中最重要,最麻烦,也是学到和掌握的东西最多的地方就是软件调试了,这其中包括程序的编写检验,这样你必须对程序的每部分都很熟悉,掌握每句程序的再整个整体的作用。这样你出现问题是你才能根据问题改变程序内容而达到目的。我们出问题最多的也是这个地方,这证明了我们对程序的掌握还有欠缺,不能熟悉的掌握和运用程序,所以导致我们不能很快很好的完成课题任务。当然也还有很多细节方面和人工的失误问题,我们几个第一次的配合还是不能很好的进行。程序编写上也出现了很多细节的错误,并不能把程序依次性就编写好,这也浪费了我们很多的时间,这主要也是我们没有把这方面的知识学扎实。所以我们要把一次课题设计好,不但要学习好课堂的知识,而且要注意各方面的细节。这次课题设计使我们各方面都得到了一定的锻炼。
第二篇:基于51单片机的计算器
单片机原理及应用实验基于51单片机的计算器
设计性实验
2008112020338王加元电子信息科学与技术物理与电子科学学院20xx年6月20日
实验基于51单片机的计算器
一、实验目的
1、
2、学会用程序来检测4×5矩阵键盘,并且在仿真软件中实现。学会处理按键数据,以及数据显示,数据的小数点问题。
二、实验环境
Keil软件和protus软件
三、实验内容
计算器中存在很多数据,数据的输入需要很多按键,那么这就要涉及到按键的检测问题,同时产生的数据要送到单片机中进行处理,处理完的数据要送到数码管上显示出来。
实验仿真图如下:
图1实验仿真图
(由于我的开发板上面P2^3脚接的蜂鸣器,仿真图中就没有用到
P2^3)
实验代码如下:
#include<reg52.h>
#include<stdlib.h>
#include<math.h>
#defineucharunsignedchar
#defineuintunsignedint
ucharcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};ucharcodeled[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};ucharxx[8];
sbitdula=P2^6;
sbitwela=P2^7;
voiddisplaypro(doubleh)
{
ucharpoint=8,m;
bitsymbol;
chari;
doubleproh;
symbol=0;
if(h<0)
{
symbol=1;
h=-h;
}
if(h>=0&&h<10){point=1;proh=h*10000000;}
if(h>=10&&h<100){point=2;proh=h*1000000;}
if(h>=100&&h<1000){point=3;proh=h*100000;}
if(h>=1000&&h<10000){point=4;proh=h*10000;}
if(h>=10000&&h<100000){point=5;proh=h*1000;}
if(h>=100000&&h<1000000){point=6;proh=h*100;}
if(h>=1000000&&h<10000000){point=7;proh=h*10;}
if(h>=10000000&&h<100000000){point=8;proh=h;}
if(h<100000000)
{
for(i=7;i>=0;i--)
{
m=proh/pow(10,i);
xx[7-i]=table[m];
proh=proh-(m*pow(10,i));
if(proh<0)
proh=0;
}
if(h>=1)//由于keil中单精度和双精度是一样的,只能表示六位数,如不把后面两位清零,将会出现乱码。{
xx[6]=0x3f;
xx[7]=0x3f;
}
elsexx[7]=0x3f;
xx[point-1]+=128;//显示小数点
}
else
{
for(i=6;i>=0;i--)
xx[i]=0x00;
xx[7]=0x79;
}
while(xx[7]==0x3f)//去除0.0000000显示的问题,即把0.0000000显示成0.
{
for(i=7;i>0;i--)
xx[i]=xx[i-1];
xx[0]=0x00;
}
if(symbol==1)//若为负数时,将数组中的数据后移
{
for(i=6;i>=0;i--)
{
if(xx[i]==0x00)
{
xx[i]=0x40;
break;
}
}
}
}
voiddelay(unsignedcharx)
{
unsignedchari,j;
for(i=0;i<x;i++)
for(j=0;j<x;j++);
}
unsignedcharkeyscan()//key为按键返回值
{
uchartemp,row=0,col=0,key;
uintadd;
P2&=0xe8;
P1|=0x1f;//超过八位数时,计算器报错显示E
temp=P1;
temp|=0xe0;
if(temp!=0xff)
{
delay(15);
temp=P1;
temp|=0xe0;
if(temp!=0xff)
{
row=P1;
P2|=0x17;
P1&=0xe0;
col=P2;
col|=0xe8;
add=row*256+col;
switch(add)
{
case0xfefe:key=0;break;case0xfdfe:key=1;break;case0xfbfe:key=2;break;case0xf7fe:key=3;break;case0xeffe:key=10;break;case0xfefd:key=4;break;case0xfdfd:key=5;break;case0xfbfd:key=6;break;case0xf7fd:key=7;break;case0xeffd:key=11;break;case0xfefb:key=8;break;case0xfdfb:key=9;break;case0xfbfb:key=15;break;case0xf7fb:key=14;break;case0xeffb:key=12;break;case0xfeef:key=16;break;case0xfdef:key=17;break;case0xfbef:key=18;break;case0xf7ef:key=19;break;case0xefef:key=13;break;default:key=20;break;}
returnkey;
}
elsereturn20;
}
elsereturn20;
}
voidmain()
{
uchark,i,hand,h2;
bitxsd=0;
chardatae;
doubledatatemp,h1=0,h3=0,h4=0;displaypro(0);
while(1)
{
k=keyscan();
if(k==20)
hand=0;
for(i=0;i<8;i++)
{
wela=1;
P0=led[i];
wela=0;
P0=0xff;
dula=1;
P0=xx[i];
dula=0;
delay(20);
P0=0xff;
}
switch(k)
{
case20:break;
case0:
case1:
case2:
case3:
case4:
case5:
case6:
case7:
case8:
case9:if(hand==0)
{
h4=0;
if(h2==0)
{
if(xsd==0)
{
if(h1>=0)h1=h1*10+k;elseh1=h1*10-k;}
if(xsd==1)
{
e--;
temp=pow(10,e);
if(h1>=0)h1=h1+k*temp;elseh1=h1-k*temp;}
displaypro(h1);
}
else
{
if(xsd==0)
{
if(h3>=0)h3=h3*10+k;elseh3=h3*10-k;}
if(xsd==1)
{
e--;
temp=pow(10,e);
if(h3>=0)h3=h3+k*temp;elseh3=h3-k*temp;}
displaypro(h3);
}
}
hand++;
break;
case10:if(hand==0)//加号
{
if(h4!=0)
{
h1=h4;
h4=0;
}
switch(h2)
{
case0:h2=1;break;
case1:h1=h1+h3;h2=1;h3=0;displaypro(h1);break;case2:h1=h1-h3;h2=1;h3=0;displaypro(h1);break;case3:h1=h1*h3;h2=1;h3=0;displaypro(h1);break;case4:h1=h1/h3;h2=1;h3=0;displaypro(h1);break;}
xsd=0;e=0;
}
hand++;
break;
case11:if(hand==0)//减号
{
if(h4!=0)
{
h1=h4;
h4=0;
}
switch(h2)
{
case0:h2=2;break;
case1:h1=h1+h3;h2=2;h3=0;displaypro(h1);break;case2:h1=h1-h3;h2=2;h3=0;displaypro(h1);break;case3:h1=h1*h3;h2=2;h3=0;displaypro(h1);break;case4:h1=h1/h3;h2=2;h3=0;displaypro(h1);break;}
xsd=0;e=0;
}
hand++;
break;
case12:if(hand==0)//乘号
{
if(h4!=0)
{
h1=h4;
h4=0;
}
switch(h2)
{
case0:h2=3;break;
case1:h1=h1+h3;h2=3;h3=0;displaypro(h1);break;case2:h1=h1-h3;h2=3;h3=0;displaypro(h1);break;case3:h1=h1*h3;h2=3;h3=0;displaypro(h1);break;case4:h1=h1/h3;h2=3;h3=0;displaypro(h1);break;
}
xsd=0;e=0;
}
hand++;
break;
case13:if(hand==0)//除号
{
if(h4!=0)
{
h1=h4;
h4=0;
}
switch(h2)
{
case0:h2=4;break;
case1:h1=h1+h3;h2=4;h3=0;displaypro(h1);break;case2:h1=h1-h3;h2=4;h3=0;displaypro(h1);break;case3:h1=h1*h3;h2=4;h3=0;displaypro(h1);break;case4:h1=h1/h3;h2=4;h3=0;displaypro(h1);break;}
xsd=0;e=0;
}
hand++;
break;
case14:if(hand==0)//负号或正号
{
if(h2==0)
{
h1=-h1;
displaypro(h1);
}
else
{
h3=-h3;
displaypro(h3);
}
}
hand++;
break;
case15:if(hand==0)//开方
{
if(h4!=0)
{
h1=h4;
h4=0;
}
if(h2==0)
{
h1=sqrt(h1);
displaypro(h1);
}
else
if(h3==0)
{
h1=sqrt(h1);
displaypro(h1);
h2=0;
}
else
{
h3=sqrt(h3);
displaypro(h3);
}
xsd=0;e=0;
}
hand++;
break;
case16:if(hand==0)//小数点
{
xsd=1;
}
hand++;
break;
case17:if(hand==0)//1/x
{
if(h4!=0)
{
h1=h4;
h4=0;
}
if(h2==0)
{
h1=1/h1;
displaypro(h1);
}
else
if(h3==0)
{
h1=1/h1;
displaypro(h1);
h2=0;
}
else
{
h3=1/h3;
displaypro(h3);
}
xsd=0;e=0;
}
hand++;
break;
case18:if(hand==0)//全部清除
{
h1=0;
h2=0;
h3=0;
xsd=0;
e=0;
displaypro(0);
}
hand++;
break;
case19:if(hand==0)//等于号
{
switch(h2)
{
case0:break;
case1:h4=h1+h3;h1=0;h2=0;h3=0;displaypro(h4);break;
case2:h4=h1-h3;h1=0;h2=0;h3=0;displaypro(h4);break;
case3:h4=h1*h3;h1=0;h2=0;h3=0;displaypro(h4);break;
case4:h4=h1/h3;h1=0;h2=0;h3=0;displaypro(h4);break;
}
xsd=0;e=0;
}
}
}
}
四、实验结果
实验结果达到了预期的效果,但是由于单片机中只有单精度数据,我用的是双精度,出来的还是单精度,以至于计算数据出现了六
位数据以上时,后面的数据就会默认零,如果不默认为零,出现的就是乱码。
下面是实验仿真截取的图片,以12
的开方为例:
五、经验总结
在做这个计算器的过程中,遇到了很多棘手的问题,其中最大的问题有如何把数据储存到数组中以便送到数码管显示,如何把小数点显示所需要显示的位置上。在完善她的时候,出现了按键检测到有效果,但是数码管却没有反应,当时找了很长时间,最后却发现是因为调用按键检测函数的时候,把她写在while(1)的外面了。通过这个实验让我明白了,遇到问题时,要静下心来,一个个去解决她。要知道,如果你做一件事,总是一帆风顺,那么只能说明这个事机器也能做的了!