资源描述
32
大连民族学院2012级电子信息工程专业单片机系统课程设计报告
信息与通信工程学院
简易计算器
学 院(系): 信息与通信工程
专 业: 电子信息工程
学 生 姓 名: 丁璞 杨一雄
学 号: 2012131604
指 导 教 师: 刘忠富
完 成 日 期:
大连民族学院
32
目 录
一、设计任务和性能指标 2
1.1设计任务 2
1.2性能指标 2
二.设计方案 2
三.系统硬件设计 3
3.1单片机最小系统 3
3.2键盘接口电路 3
3.3数码管显示电路 4
3.4错误报警电路 5
四、系统软件设计 6
4.1键盘扫描子程序设计 6
4.2移位子程序及结果计算子程序设计 10
4.3显示子程序设计 12
4.4主程序设计 13
五、调试及性能分析 13
5.1调试步骤 13
5.2性能分析 14
六、心得体会 14
参考文献 14
附录1 系统硬件电路图 15
附录2 程序清单 16
一、设计任务和性能指标
1.1设计任务
印刷电路板(要求布局合理,线路清晰),绘出程序流程图,并给出程 利用单片机及外围接口电路(键盘接口和显示接口电路)设计制作一个计算器,用LED显示计算数值及结果。
要求用Protel 画出系统的电路原理图(要求以最少组件,实现系统设计所要求的功能),序清单(要求思路清晰,尽量简洁,主程序和子程序分开,使程序有较强的可读性)。
1.2性能指标
1. 加法:四位加法,计算结果若超过四位则显示计算错误
2. 减法:四位减法,计算结果若小于零则显示计算错误
3. 乘法:个位数乘法
4. 除法:整数除法
5. 有清零功能,计算错误报警
二.方案总体设计
按照系统设计的功能的要求,初步确定设计系统由主控模块、显示模块、错误报警模块、键扫描接口电路共四个模块组成,电路系统构成框图如图1.1所示。主控芯片使用51系列AT89C52单片机,采用高性能的静态80C51设计,由先进工艺制造,并带有非易失性Flash程序存储器。它是一种高性能、低功耗的8位COMS微处理芯片,市场应用最多。
键盘电路采用4*4矩阵键盘电路。
显示模块采用4枚共阳极数码管和74ls273锁存芯片构成等器件构成。
错误报警电路采用5V蜂鸣器。
系统选用以STC89C51单片机为主控机。通过扩展必要的外围接口电路,实现对计算器的设计。
显示电路
4×4矩阵键盘
单
片
机
复位电路
晶振
图2.1基本结构
三.系统硬件设计
3.1单片机最小系统
单片机最小系统就是支持主芯片正常工作的最小电路部分,包括主控芯片、复位电路和晶振电路。
主控芯片选取STC89C52RC芯片,因其具有良好的性能及稳定性,价格便宜应用方便。
晶振选取11.0592MHz,晶振旁电容选取30pF。
采用按键复位电路,电阻分别选取100Ω和10K,电容选取10μF。
以下为单片机最小系统硬件电路图。
图3.1 单片机最小系统
3.2键盘接口电路
计算器所需按键有:
数字键:’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’,’0’
功能键:’+’, ’-‘ , ’*’, ’/ ’ , ’ = ’, ’ C( 清零)’
共计16个按键,采用4*4矩阵键盘,键盘的行和列之间都有公共端相连,四行和四列的8个公共端分别接P1.0~P1.7,这样扫描P1口就可以完成对矩阵键盘的扫描,通过对16个按键进行编码,从而得到键盘的口地址,对比P1口德扫描结果和各按键的地址,我们就可以得到是哪个键按下,从而完成键盘的功能。
以下为键盘接口电路的硬件电路图
图3.2 矩阵键盘内部电路
3.3 LCD显示模块:
本设计采用LCD液晶显示器来显示输出数据.通过D0-D7引脚向LCD写指令字或写数据以使LCD实现不同的功能或显示相应数据.
在与单片机最小系统连接时,采用并行连接方式,此方式的特点是传输速度快。并行接口的输入由p0.0-p0.7进行控制,传输信息。
LCD显示使用了两片极化材料,在它们之间是液体水晶溶液。电流通过该液体时会使水晶重新排列,以使光线无法透过它们。因此,每个水晶就像百叶窗,既能允许光线穿过又能挡住光线。
LCD也就显示具有直角显示、低耗电量、体积小、还是零辐射等优点。
图3.3 LCD显示电路
3.4错误报警电路
错误报警电路就是在计算结果出现错误时或输入数据出现错误时,发出声音警报,提示使用者错误出现。
这里就采用5V蜂鸣器作为报警设备,利用PNP三极管对蜂鸣器进行驱动,有P2.2对其进行控制,这样在出现错误的同时用P2.2输出低,就可以使蜂鸣器工作,完成报警任务。
在编写程序时,报警电路实现的功能是:当计算正常时使p2.2口输出高电平时,蜂鸣器不会响应。当计算出现错误(例如0作为被除数时)此时给p2.2输出低电平,使蜂鸣器响应,达到报警功能。
以下为报警电路硬件电路图
图3.4 报警电路
四.系统软件设计
首先初始化参数;然后扫描键盘看是否有键输入,若有,读取键码;判断键码是数字键、清零键还是功能键(“+”“-”“*”“/”“=”),是数值键则送数码管显示并保存数值,是清零键则做清零处理,是功能键则又判断是“=”还是运算键,若是“=”则计算最后结果并送数码管显示,若是其它功能键则不显示。(其中里面包含了延时去抖动程序)。
开始
初始化
清零键和功能键
输入
是否是数字键?
是
否
否
否
是
数码管不显示
清零键
数字键
数码管显示结果
计算
是否是运算键?
数码管显示数字
图4.1程序流程图
4.1键盘扫描子程序设计
要进行数据的计算就必须先进行数据的输入,也就必须确定按键输入的数值是什么,这就需要对键盘进行扫描,从而确定究竟是哪个键按下。
对于键盘的扫描,既可以用行扫描也可以用列扫描,这里采用行扫描的方法来完成对键盘的扫描。
行扫描就是逐行扫描键盘,看是哪一行有键按下,再通过返回的键码来确定究竟是哪个按键按下。如对第一行扫描就令P1.0为低,P1口其余为高,这样若第一行有键按下,则P1口的值就会由0xfe变为其他值,再由这个值来确定是哪个键按下。
以下为键盘扫描子程序的程序清单。
void keyscan() //矩阵键盘
{
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee: key=1; break;
case 0xde: key=2; break;
case 0xbe: key=3; break;
case 0x7e: key=15; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key>=1&&key<=3)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
if(key==15)
{
L1602_string(1,i++,"/");
e='/';
A=f(ss,b);
b=0;
}
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed: key=4; break;
case 0xdd: key=5; break;
case 0xbd: key=6; break;
case 0x7d: key=14; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key>=4&&key<=6)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
if(key==14)
{
L1602_string(1,i++,"*");
e='*';
A=f(ss,b);
b=0;
}
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb: key=7; break;
case 0xdb: key=8; break;
case 0xbb: key=9; break;
case 0x7b: key=13; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key>=7&&key<=9)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
if(key==13)
{
L1602_string(1,i++,"-");
e='-';
A=f(ss,b);
b=0;
}
}
}
P3=0XF7;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xe7: key=10; break;
case 0xd7: key=0; break;
case 0xb7: key=11; break;
case 0x77: key=12; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key==12)
{
L1602_string(1,i++,"+");
e='+';
A=f(ss,b);
b=0;
}
if(key==11)
{
L1602_string(1,i++,"=");
e='=';
A=f(ss,b);
b=0;
}
if(key==11) //=
{
H=f(ss,b);
b=0;
switch(e)
{
case '+': result=A+H; break;
case '-': result=A-H; break;
case '*': result=A*H; break;
case '/': result=A/H; break;
default: result=H;
}
L1602_string(1,i++,"=");
show(result);
}
if(key==10) //清零
{
wcmd(0x01);
wcmd(0x80);
b=0;
i=1;
k=1;
}
if(key==0)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
}
}
}
4.2移位子程序及结果计算子程序设计
输入数据要存储在一四位数组内,而我们键入的值是数据的高位,后键入的值是低位,这样我们就需要在输入低位数值时将高位数值从数组的低位移向数组的高位,这就是编写移位子程序的目的。
对于结果计算子程序,包含加、减、乘、除四种运算。以加法运算为例,各种运算各有其标志位来代表计算类型,当加法标志位add=1是,就将输入的两个数据按照加法进行计算。
首先将数组内的数按照对应的位关系,将其转化为一个十进制数,这样我们就得到了加速和被加数这样俩个十进制数,从而我们就可以简单的将两个数进行相加,结果就是我们所求的数值。但这个数值不能直接显示到数码管上,我们还要对其进行处理,使其变为对应进位的四个数存入数组内,以便显示。既通过对结果数值分别除以1000、100、10和对10取余,得到我们想要的四个数,送显示子程序显示。其余减、乘、除的计算方法与加法的计算方法一样,这里不再累述。
以下为移位子程序和结果计算子程序的程序清单。
uint exp(uint m) //位数函数
{
uint n=1;
while(m)
{
n=n*10;
--m;
}
return n;
}
uint f(uint g[],uint d) //结果函数
{
uint m=0,i,j;
for (i=0;i<d;++i)
{
j=d-i-1;
m=m+g[i]*(exp(j));
} return m;
}
uint length(uint z) //判断得数长度函数
{
uint n=1;
while(z>10)
{
z=z/10;
n++;
}
return n;
}
void show(uint h) //显示得数函数
{
uint n,i,dat;
n=length(h);
for(i=n;i>0;--i)
{ dat=h/(exp(i-1));
L1602_char(2,k++,dat + 48);
h=h%(exp(i-1));
}
}
4.3显示子程序设计
从始至终无论是输入的计算数据,还是计算后的结果值。都存储在同一数组dat[ ]中,这样我们只要在显示时一直调用dat[ ]中的值,就能正确的显示数据。
以下为显示子程序的程序清单。
void show(uint h) //显示得数函数
{
uint n,i,dat;
n=length(h);
for(i=n;i>0;--i)
{ dat=h/(exp(i-1));
L1602_char(2,k++,dat + 48);
h=h%(exp(i-1));
}
}
4.4主程序设计
主程序既把以上各子程序串连成一个整体,使整个程序循环运行。而在以上程序中也已经加入了个程序之间的连接点,首先进入程序后就立即进入显示子程序,而显示子程序内又调用键盘扫描子程序,若有键按下,则会跳转到移位子程序和结果计算子程序进行相应的处理。通过计算或移位后,数组内的值发生改变,显示的值也会同时发生改变。之后再进行键盘扫描,如此反复运行,就构成了程序的整体。
以下为主程序清单。
void main() //主函数
{
L1602_init();
while(1)
{
keyscan();
}
}
整体程序清单见附录二。
五、调试及性能分析
5.1调试步骤
在焊接好器件后,先不要将芯片插在芯片座上,要先验证先板上电源是否好用,有无短路等。接上USB电源,用万用表测量个芯片座对应电源和地之间的电压值,观察电压值是否正常。一切正常后方可将芯片插入芯片座,以继续测试其他功能。
将芯片插上后,对各个模块进行调试,按键是否工作正常,数码管是否显示正常等。编写相关部分的测试程序对其进行测试。
各部分硬件检测无误后,下载程序进行整体调试,一切正常后,结束调试过程。
在具体调试时首先遇到的问题是程序无法下载进入单片机,通过将电路板接线与原理电路图接线的对比发现,串口芯片与单片机连接的输入,输出接反,重新用铜线连接后,依然无法下载程序。后找到原因是由于下载串口与设计封装不符,用相对应的下载线可以下载。
成功下载程序后,发现LCD显示不正确,查看后发现有先没有连接,可能是制板时漏印,连接后显示正常。
5.2性能分析
对于计算器的性能,主要的衡量指标就在于计算的精度,本次制作的计算器性能情况如下:
加法运算:四位加法运算,和值不超过9999,若超过上限,则显示错误提示E,蜂鸣器报警提示。
减法运算:四位减法运算,若结果为负,对其取绝对值。
乘法运算:积不超过9999的乘法运算,若超出上限,显示错误提示E,蜂鸣器报警提示。
除法运算:整数除法,既计算结果为整数,若除数为零,则显示错误提示E,蜂鸣器报警提示。
通过对实际性能的分析,可以得到本次设计满足设计的要求。
六、心得体会
通过本次课程设计我真正的自己完成了对给定要求系统的硬件设计、电路设计、电路板设计、软件设计以及对成品的调试过程。从整个过程中学习到了很多方面的知识,了解到以往学习中自己知识在某方面的不足之处,是对以往学习科目的一种贯穿和承接,从而能更好的认识和学习,也对将来从事工作大有裨益。
在试验过程中,充分的让理论和实践进行了结合。实践是检验真理的唯一标准,确实只有在实践中,才能让理论知识得以实现,这也是工科所期望的成果,在这次试验里,让我们更具象的认识理论,对学习有莫大的帮助。
从本次课设中我也看到了自身的很多不足之处,对知识的掌握不够扎实,有一知半解的现象。有时做事不够稳定,过于毛躁,不能平心静气的去分析所遇到的问题和错误。这在以后的工作和生活中是不可取的,通过对自身问题的认识与改正相信再遇到同样问题时会更好的解决。以后的设计实验也会更好的完成。
参考文献
[1] 徐维祥、刘旭敏. 单片微型机原理及应用. 大连:大连理工大学出版社,1996
[2] 李光飞、楼然苗、胡佳文、谢象佐. 单片机课程设计与实例指导. 北京: 北京航空航天大学出版社,2004
[3] 余永权. 89系列FLASH单片机原理及应用. 北京:电子工业出版社,2002
[4] 刘岩川、董玉华、刘忠富、韩志敏.单片机原理及系统设计.北京:电子工业出版社,2014
附录1 系统硬件电路图
附录2 实物图
附录3 程序清单
#include<reg51.h>
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
#define ulong unsigned long
uchar code table[10] = {0x03, 0x9f, 0x25, 0x0d, 0x99, 0x49, 0x41, 0x1f, 0x01, 0x09};
uchar i=1,j=0;
uchar key,temp;
uchar e;
uint ss[8];
uint b=0,A,H,result,k=1;
sbit E=P2^3; //1602使能引脚
sbit RW=P3^6; //1602读写引脚
sbit RS=P2^4; //1602数据/命令选择引脚
void Delay_1ms(uint i) //1ms延时
{
uint x,j;
for (j=0;j<i;j++);
for (x=0;x<=148;x++);
}
void delay()
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
bit Busy(void)
{
bit busy_flag = 0;
RS = 0;
RW = 1;
E = 1;
delay();
busy_flag = (bit)(P0 & 0x80);
E = 0;
return busy_flag;
}
void wcmd(uchar del)
{
while(Busy());
RS = 0;
RW = 0;
E = 0;
delay();
P0 = del;
delay();
E = 1;
delay();
E = 0;
}
void wdata(uchar del)
{
while(Busy());
RS = 1;
RW = 0;
E = 0;
delay();
P0 = del;
delay();
E = 1;
delay();
E = 0;
}
void L1602_init(void)
{
wcmd(0x38);
Delay_1ms(5);
wcmd(0x38);
Delay_1ms(5);
wcmd(0x38);
Delay_1ms(5);
wcmd(0x38);
wcmd(0x08);
wcmd(0x0c);
wcmd(0x04);
wcmd(0x01);
}
void L1602_char(uchar hang,uchar lie,char sign)
{
uchar a;
if(hang == 1) a = 0x80;
if(hang == 2) a = 0xc0;
a = a + lie - 1;
wcmd(a);
wdata(sign);
}
void L1602_string(uchar hang,uchar lie,uchar *p)
{
uchar a,b=0;
if(hang == 1) a = 0x80;
if(hang == 2) a = 0xc0;
a = a + lie - 1;
while(1)
{
wcmd(a++);
b++;
if((*p == '\0')||(b==16)) break;
wdata(*p);
p++;
}
}
uint exp(uint m) //位数函数
{
uint n=1;
while(m)
{
n=n*10;
--m;
}
return n;
}
uint f(uint g[],uint d) //结果函数
{
uint m=0,i,j;
for (i=0;i<d;++i)
{
j=d-i-1;
m=m+g[i]*(exp(j));
} return m;
}
uint length(uint z) //判断得数长度函数
{
uint n=1;
while(z>10)
{
z=z/10;
n++;
}
return n;
}
void show(uint h) //显示得数函数
{
uint n,i,dat;
n=length(h);
for(i=n;i>0;--i)
{ dat=h/(exp(i-1));
L1602_char(2,k++,dat + 48);
h=h%(exp(i-1));
}
}
void keyscan() //矩阵键盘
{
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee: key=1; break;
case 0xde: key=2; break;
case 0xbe: key=3; break;
case 0x7e: key=15; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key>=1&&key<=3)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
if(key==15)
{
L1602_string(1,i++,"/");
e='/';
A=f(ss,b);
b=0;
}
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed: key=4; break;
case 0xdd: key=5; break;
case 0xbd: key=6; break;
case 0x7d: key=14; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key>=4&&key<=6)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
if(key==14)
{
L1602_string(1,i++,"*");
e='*';
A=f(ss,b);
b=0;
}
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb: key=7; break;
case 0xdb: key=8; break;
case 0xbb: key=9; break;
case 0x7b: key=13; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key>=7&&key<=9)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
if(key==13)
{
L1602_string(1,i++,"-");
e='-';
A=f(ss,b);
b=0;
}
}
}
P3=0XF7;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
Delay_1ms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xe7: key=10; break;
case 0xd7: key=0; break;
case 0xb7: key=11; break;
case 0x77: key=12; break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
if(key==12)
{
L1602_string(1,i++,"+");
e='+';
A=f(ss,b);
b=0;
}
if(key==11)
{
L1602_string(1,i++,"=");
e='=';
A=f(ss,b);
b=0;
}
if(key==11) //=
{
H=f(ss,b);
b=0;
switch(e)
{
case '+': result=A+H; break;
case '-': result=A-H; break;
case '*': result=A*H; break;
case '/': result=A/H; break;
default: result=H;
}
L1602_string(1,i++,"=");
show(result);
}
if(key==10) //清零
{
wcmd(0x01);
wcmd(0x80);
b=0;
i=1;
k=1;
}
if(key==0)
{
L1602_char(1,i,key + 48);
i++;
ss[b]=key;
b++;
Delay_1ms(300);
}
}
}
}
void main() //主函数
{
L1602_init();
while(1)
{
keyscan();
}
}
展开阅读全文