单片机课程设计报告.docx
《单片机课程设计报告.docx》由会员分享,可在线阅读,更多相关《单片机课程设计报告.docx(27页珍藏版)》请在冰豆网上搜索。
单片机课程设计报告
万年历的设计
目录
1、概述3
2、原理图3
3、主要程序设计及体会4
3.1、读出时间数据4
3.2、写入时间数据6
3.3、闪烁实现7
3.4、总结10
4、程序清单10
1、概述
本课程设计就是设计一款万年历,以AT89C51芯片为核心,配合数码管、时钟芯片DS1302、译码器74HC154和按键完成。
万年历采用24小时制方式显示时间,在数码管上显示的时间有年、月、日、时、分、秒、星期。
该设计的核心包括硬件设计和软件编程两个方面(我主要是编写了软件方面),硬件的设计使用的Proteus7进行的仿真。
软件使用的是C语言实现的,主要分为从时钟芯片DS1302读出和写入时间数据、显示时间数据、处理按键三部分,在处理按键部分使用了外部中断0和外部中断1。
2、原理图
在本次实验中我们没有真实的硬件供我们使用,所以只能使用Proteus7进行仿真,我们所做的也就是在这个仿真的硬件电路中去实验。
在实验中使用到了主芯片AT89C51、数码管、时钟芯片DS1302、译码器74HC154和按键等硬件,对于这些硬件设备,我也不熟悉它们,只是对实验中使用到的引脚和及它们的特性去学习了一下,别的没有用到的引脚就没理它们了。
这里的电路图也是老师直接释出的,这里说一下自己的对这个原理图的理解。
实验使用的仿真电路图如下图:
图2.1仿真电路图
(1)时间数据的读取和写入
实验中显示的时间就是从DS1302中读取的,在点击ADD和SUB按键设置时间数值时,也是将设置后的数据写入到DS1302中的,AT89C51的I/O引脚和AT89C51的P1.2相连接,时间数据就是通过这个串口链路从DS1302中读取和写入的。
(2)接受按键
在单片机AT89C51的P3.2、P3.3、P3.6都就了一个按键,分别用于开始/结束调节、加时间、减时间,P3.2、P3.3分别是单片机AT89C51的外部中断0和外部中断1的引脚。
所以处理这两个就是通过两个中断服务程序完成的。
处理P3.6是通过扫描端口去得知是否按下了SUB键。
(3)显示数据
在单片机AT89C51的P0引脚控制数据要显示的数码,P2.0、P2.1、P2.2、P2.4控制(经74HC154译码)将数码在哪一块数码管中显示。
P0和P2联合控制数据的显示。
3、主要程序设计及体会
整个软件编程大体可以分为4个模块,分别是:
时间数据的读出和写入,闪烁的是实现、手动修改时间,其中时间数据的读出和写入是对老师给出的代码的理解。
3.1、读出时间数据
实验中的数据时从时间芯片DS1302中读取,从DS1302中读出单字节的数据的流程如下图:
图3.1DS1302中读出单字节数据流程图
在从DS1302中读出数据时,首先要送入地址,然后再能读出我们指定地址处的数据。
具体实验代码如下:
//功能:
从DS1302中读出指定地址的数据
//读取单字节数据时序
//参数:
addr:
命令的第一字节部分,就是地址部分
//返回值:
读取的一字节数据
//
ucharread_ds1302(ucharaddr)
{
uchartmp;//用于保存读取的数据
DS_RST=0;//拉低RST
DS_SCLK=0;//拉低SCLK
DS_RST=1;//拉高RST
write_byte(addr);//写入地址命令
tmp=read_byte();//读取数据
DS_SCLK=0;//
DS_RST=0;//在SCLK为低电平时,完成数据传送
return(tmp);
}
//功能:
向DS1302中写入指定数据
//参数:
dat:
带写入的地址或数据
//
voidwrite_byte(uchardat)
{
uchari=0;
for(i=0;i<8;i++)
{
DS_IO=(dat&1);//sbitDS_IO=P1^2;,就是IO接口
DS_SCLK=0;
_nop_(),_nop_();
DS_SCLK=1;//SCLK有0-->1,P1^2口数据被写入
_nop_(),_nop_();
dat>>=1;//右移一位
}
}
//读取单字节数据
ucharread_byte(void)
{
uchari=0;
uchardat=0;
uchartmp=0;
for(i=0;i<8;i++)
{
DS_SCLK=1;
_nop_(),_nop_();
DS_SCLK=0;
_nop_(),_nop_();
//sbitDS_IO=P1^2;,SCLK:
1-->0,从IO口读取数据到P1^2口中
tmp=DS_IO;
//将dat右移一位
dat>>=1;
//将tmp左移7位然后和dat作或运算,运算结果保存到dat中
dat|=(tmp<<7);
}
//将读出的16进制数据转化为10进制的
dat=BCD2DEC(dat);
return(dat);
}
3.2、写入时间数据
实验中的在将年的最后两位设为自己的学号以及后面手动修改时间时,都要向DS1302中写入指定的数据,向DS1302中指定地址写入单字节的数据的流程如下图:
图3.2DS1302中写入单字节数据流程图
具体实验代码如下:
//功能:
向DS1302指定地址写入指定数据
//写入单字节数据时序
//参数:
addr:
地址;
//dat:
要写入的数据
//
voidwrite_ds1302(ucharaddr,uchardat)
{
uchartmp=0;
tmp=DEC2BCD(dat);//使用宏DEC2BCD对数据进行转码
DS_RST=0;
DS_SCLK=0;
DS_RST=1;
write_byte(addr);//写入地址
write_byte(tmp);//写入数据
DS_SCLK=0;
DS_RST=0;
}
//功能:
向DS1302中写入指定数据
//参数:
dat:
带写入的地址或数据
//
voidwrite_byte(uchardat)
{
uchari=0;
for(i=0;i<8;i++)
{
DS_IO=(dat&1);//sbitDS_IO=P1^2;,就是IO接口
DS_SCLK=0;
_nop_(),_nop_();
DS_SCLK=1;//SCLK有0-->1,P1^2口数据被写入
_nop_(),_nop_();
dat>>=1;//右移一位
}
}
3.3、闪烁实现
一下为共阳数码管显示码的数码表:
十六进制
十进制
0xC0
0
0xF9
1
0xA4
2
0xB0
3
0x99
4
0x92
5
0x82
6
0xF8
7
0x80
8
0x90
9
0xFF
关闭显示
表3.1共阳极数码管数码表
在上表中的最后一行,就是0xff就是关闭数码管的显示,所以这里的设置显示闪烁的设计方法就是让数码管进行关闭和显示数码进行交替进行,关闭数码管显示“一定次数”,然后在显示显示数码“一定次数”,只要这里的“一定次数”设置的合理就可以看出效果。
我再实验代码中设置是10次,该闪烁设计流程如下图(以日期为例):
图3.3闪烁流程图
上图就是我在设计闪烁使用的方法,上面只是对日期显示的闪烁流程,别的时间单位的闪烁处理方法都是一样的,具体实现代码如下:
//show_time(SYSTEM_TIMEstr_time)需要显示的年月日时分秒和星期
voidshow_time(SYSTEM_TIMEstr_time)
{
uchartmp=0;
//显示年
display_num(0,2);//年的第一位,显示:
2
display_num(1,0);//年的第二位,显示:
0
tmp=str_time.year;//获取年份的最后两位
///modifybyliuliang
//////////////////////////////////////////////////////////////////////////////////////////
//这里省略了年的后两位和月的显示
//////////////////////////////////////////////////////////////////////////////////////////
//获取日期,然后显示
tmp=str_time.date;
if(NUM_SET%7==3)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
if(tmp>9)
{
display_num(6,(tmp/10));
}
display_num(7,(tmp%10));
}
else
{
display_num(6,10);//关闭数码管显示
display_num(7,10);//
}
}
else
{
if(tmp>9)
{
display_num(6,(tmp/10));
}
display_num(7,(tmp%10));
}
//////////////////////////////////////////////////////////////////////////////////////////
//这里省略了时、分、秒、星期的显示
//////////////////////////////////////////////////////////////////////////////////////////
///end
}
3.4、总结
4、程序清单
整个程序共三个源文件,三个头文件,分别是:
main.h、main.c、ds1302.h、ds1302.c、delay.h、delay.c,其中main.h、ds1302.h、delay.h、delay.c都没有改变,ds1302.c中我只有修改了ds1302_init函数,别的没有修改,我写的修改或加入的代码都在main.c文件中,所以这里就只释出main.c文件,其修改后的ds1302.c中的ds1302_init函数。
如下:
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//main.c
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//main.c
#include"main.h"
#include"delay.h"
#include"ds1302.h"
#defineDATAP0//P0口用于输出要显示数据
//P1口用于控制在显示数据的LED,使用P2口控制了一个译码器(74hc154),用于选择LED
#defineSHOWP2
//共阳数码管显示码
ucharcodeDIS_CODE[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
///addbyliuliang
intNUM_TIME=0;//记录当前关闭数码管显示的次数
intNUM_TIME_MAX=10;//控制亮闪速度
intNUM_SET=-1;//按下SET按钮的数量
intAJUST=0;//表示是启用功调节
intIsAdjusted=0;//就是表明是否已经按下了ADD或SUB键,IsAdjusted=0表示还没按下ADD或SUB键
sbitSUB=P3^6;//SUB键(p3.6)
SYSTEM_TIMEALL_TIME;//定义全局SYSTEM_TIME结构体变量
///end
//display_num(uchar_show;uhar_data)数码管的选码和显示段码
voiddisplay_num(uchar_show,uchar_data)
{
SHOW=_show;//选择数码管
DATA=~DIS_CODE[_data];//取反共阴
delay_ms
(1);//延时
}
//show_time(SYSTEM_TIMEstr_time)需要显示的年月日时分秒和星期
voidshow_time(SYSTEM_TIMEstr_time)
{
uchartmp=0;
//显示年
display_num(0,2);//年的第一位,显示:
2
display_num(1,0);//年的第二位,显示:
0
tmp=str_time.year;//获取年份的最后两位
///modifybyliuliang
if(NUM_SET%7==1)//按下SET按钮
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
display_num(2,(tmp/10));//年的第三位,显示:
tmp/10
display_num(3,(tmp%10));//年的第四位,显示:
tmp%10
}
else
{
display_num(2,10);//关闭数码管显示
display_num(3,10);//
}
}
else
{
display_num(2,(tmp/10));//年的第三位,显示:
tmp/10
display_num(3,(tmp%10));//年的第四位,显示:
tmp%10
}
//获取月份,然后显示
tmp=str_time.month;
if(NUM_SET%7==2)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
if(tmp>9)
{
display_num(4,(tmp/10));
}
display_num(5,(tmp%10));
}
else
{
display_num(4,10);////关闭数码管显示
display_num(4,10);//
}
}
else
{
if(tmp>9)
{
display_num(4,(tmp/10));
}
display_num(5,(tmp%10));
}
//获取日期,然后显示
tmp=str_time.date;
if(NUM_SET%7==3)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
if(tmp>9)
{
display_num(6,(tmp/10));
}
display_num(7,(tmp%10));
}
else
{
display_num(6,10);//关闭数码管显示
display_num(7,10);//
}
}
else
{
if(tmp>9)
{
display_num(6,(tmp/10));
}
display_num(7,(tmp%10));
}
//获取小时,然后显示
tmp=str_time.hour;
if(NUM_SET%7==4)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
if(tmp>9)
{
display_num(8,(tmp/10));
}
display_num(9,(tmp%10));
}
else
{
display_num(8,10);//关闭数码管显示
display_num(9,10);//
}
}
else
{
if(tmp>9)
{
display_num(8,(tmp/10));
}
display_num(9,(tmp%10));
}
//获取分钟,然后显示
tmp=str_time.min;
if(NUM_SET%7==5)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
if(tmp>9)
{
display_num(10,(tmp/10));
}
display_num(11,(tmp%10));
}
else
{
display_num(10,10);//关闭数码管显示
display_num(11,10);//
}
}
else
{
if(tmp>9)
{
display_num(10,(tmp/10));
}
display_num(11,(tmp%10));
}
//获取秒,然后显示
tmp=str_time.sec;
if(NUM_SET%7==6)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
if(tmp>9)
{
display_num(12,(tmp/10));
}
display_num(13,(tmp%10));
}
else
{
display_num(12,10);//关闭数码管显示
display_num(13,10);//
}
}
else
{
if(tmp>9)
{
display_num(12,(tmp/10));
}
display_num(13,(tmp%10));
}
//获取星期,然后显示
tmp=str_time.day;
if(NUM_SET%7==0)
{
NUM_TIME++;
if(NUM_TIME==NUM_TIME_MAX)
{
NUM_TIME=0;
display_num(14,(tmp%10));
}
else
{
display_num(14,10);//关闭数码管显示
}
}
else
{
display_num(14,(tmp%10));
}
///end
}
/////////////////////////////////////////
///addbyliuilang
////////////////////////////////////////
//外部中断0服务程序
voidint0(void)interrupt0
{
EA=0;//关闭中断总开关
AJUST=1;//开始调节
if(IsAdjusted==0)//还没调节
{
if(NUM_SET==-1)
{
NUM_SET=1;//控制调节对象
}
else
{
NUM_SET++;//控制调节对象
}
}
else
{
IsAdjusted=0;//
AJUST=0;
NUM_SET=-1;
}
EA=1;//开启中断总开关
}
//
//处理时间
//参数:
type=1表示加,type=0表示减
voidpro_time(inttype)
{
SYSTEM_TIMEcur_time;//定义SYSTEM_TIME结构体变量
uchartmp=0;
cur_time=read_time();//获取当前时间
////////
switch(NUM_SET%7)
{
case1:
//年
{
tmp=cur_time.year;
if(type==1)//加操作
{
tmp=tmp+1;
}
else//减操作
{
if(tmp>0){tmp=tmp-1;}
}
write_ds1302(DS1302_YEAR,tmp);//向DS1302中写入数据
ALL_TIME.year=tmp;//更新全局变量中的相应的信息
break;
}
case2:
//月
{
tmp=cur_time.month;
if(type==1)//加操作
{
if(tmp==12){tmp=1;}
else{tmp=tmp+1;}
}
else//减操作
{
if(tmp>1){tmp=tmp-1;}
}
write_ds1302(DS1302_MONTH,tmp);//向DS1302中写入数据
ALL_TIME.month=tmp;//更新全局变量中的相应的信息
break;
}
case3:
//日
{
tmp=cur_time.date;
if(type==1)//加操作
{
if(tmp==31){tmp=1;}//这里就假设月最大位31天
else{tmp=tmp+1;}
}
else//减操作
{
if(tmp>1){tmp=tmp-1;}
}
write_ds1302(DS1302_DATE,tmp);//向DS1302中写入数据
ALL_TIME.date=