河南机电高等专科学校综合实训报告系部:专业:班级:学生姓名:学号:2013年 6月 28日实训任务书1.时间:2013年6月15日~2013年6月28日2. 实训单位:河南机电高等专科学校3. 实训目的:深入学习电子产品设计制作的全过程4. 实训任务:①加深学习电路图绘制软件的相关常识及其特点;②以51单片机为核心设计出一个LED万年历;③万年历具有年份、月、日、时、分、秒、周、温度、整点报时等功能;④自己独立完成设计电路图,生成PCB,转印、腐蚀电路板,焊接等硬件制作工作;⑤会使用keil软件编写程序,并完成烧写过程;⑥学会调试程序,敢于发现问题,解决问题;⑦参考相关的的书籍、资料,认真完成实训报告。
综合实训报告前言:万年历是我们经常见到和使用的计时工具,使用方便,功能多。
但自己做一个让人满意的万年历,就不一定有那么轻而易举了,本次实训的任务就是自己亲手做一个万年历。
利用本学期学过的单片机知识,设计出具有显示年、月、日、时、分、秒、周、温度功能,同时有整点报时的功能的万年历。
一、实训器件1.温度传感器DS18B20采用数字式温度传感器DS18B20,此类传感器为数字式传感器而且仅需要一条数据线进行数据传输,易于与单片机连接,可以避免A/D模数转换模块,降低硬件成本,简化系统电路。
另外,数字式温度传感器还具有测量精度高、测量范围广等优点。
因此,本设计DS18B20温度传感器作为温度采集模块。
2.时钟芯片DS1302采用DS1302时钟芯片实现时钟,它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。
采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。
DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。
具有主电源/后背电源双电源引脚,同时提供了对后背电源进行涓细电流充电的能力。
主要特点是采用串行数据传输,可为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。
采用普通32.768kHz晶振。
因此,本设计中采用DS1302提供时钟。
3.74LS15474LS154 为 4 线-16 线译码器,其主要电特性的典型值如下:当选通端(G1、G2)均为低电平时,可将地址端(ABCD)的二进制编码在一个对应的输出端,以低电平译出。
若将G1和G2中的一个作为数据输入端,由ABCD对输出寻址,54/74154 还可作4线-16线数据分配器。
引出端符号: A、B、C、D 译码地址输入端(低电平有效)G1、G2 选通端(低电平有效)0-15 输出端(低电平有效)可用此种芯片作为数码管的选址芯片。
4.74HC57374HC573是八进制3态非反转透明锁存器,是高性能硅门CMOS器件。
当锁存使能端为高时,这些器件的锁存对于数据是透明的(也就是说输出同步)。
当锁存使能变低时,符合建立时间和保持时间的数据会被锁存。
输出能直接接到CMOS,NMOS和 TTL接口上操作电压范围:2.0V~6.0V×低输入电流: 1.0uA 。
本实训采用此芯片作为数码管的驱动芯片。
5.数码管本实训使用共阴极数码管作为显示器件,采用 LED 数码管动态扫描,LED 数码管价格适中,对于显示数字也最合适。
综上各模块的选择方案与论证,确定最后的主要硬件资源如下:采用AT89S51作为主控制系统;DS1302提供时钟;DS18B20作为数字式温度传感器;74LS154作为选址芯片;74HC573作为数码管的驱动芯片;共阴极数码管显示数字;S8550驱动扬声器。
二、实训原理为了使万年历在包装美化时简单化及布线的方便,我将万年历分为两部分,一部分是主体部分含有年份、月、日、时、分、秒,另一部分是小模块包含周、温度。
电路图如下:主电路图:主电路PCB:周,温度电路图:周,温度PCB:三、程序编写使用Keil软件编写程序。
程序包括主函数、时间显示函数、温度显示函数、时间调整函数、DS18B20模块、DS1302模块和周计算函数等。
程序的编写使我熟练的掌握了Keil软件的应用。
具体程序见附录。
四、程序的烧写与调试程序烧写需要用专用的烧写软件将Keil软件声生成的.hex文件写进单片机里。
经过多次的程序的改写与调试,最终制作出比较满意的成品。
五、成品展示:心得体会:经过此次实训我学到了很多,刚开始以为做万年历很难,着手开始做这个产品没有一点头绪,经过上网查资料,产生了最初的制作框架,然后就开始找芯片,查芯片资料,设计电路图,又请同学帮忙指点,修改原理图。
接着买元器件,做封装,生成PCB,制作电路板,焊接元器件,最后完成了整个硬件部分的制作。
完成了硬件制作后,我又编了测试软件测试硬件是否有问题,发现数码管不亮,经过问题排查及资料查询,发现单片机P0口缺少了上拉电阻,接上后,解决了问题。
编程也是比较困难的,由于掌握的知识不够多,借鉴了别人的程序,并加入自己的想法,经过调试,程序编写成功。
最终完成了产品的制作。
在实习的过程中我得到了别人的帮助,同时也主动帮助别人,得到了很多经验,为以后学习提供了方便。
实习就是学习的过程,本次实习是最成功的一次,也是学到最多的一次。
参考文献:《单片机C51程序设计教程与实验》北京航空航天大学出版社《单片机原理及应用(C51语言)》清华大学出版社《数字电子技术》黄河水利出版社《Protel DXP 2004电路设计与仿真教程》北京航空航天大学出版社附录:程序万年历.c#include<reg51.h>#include<intrins.h>#define uchar unsigned char#define uint unsigned intuchar code digit[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x39,0x00}; uchar mode,TH,TL,TN,seconds,minutes,hours,years,months,days,WEEK,n;uchar count=0;sbit SET=P3^4;sbit ADD=P3^5; //增加sbit RED=P3^6; //减小sbit CANL=P3^7;sbit le=P2^6;sbit oe=P2^7;sbit en=P1^4;sbit beep=P2^4;uchar GetWeekFromDay(uchar years,uchar months,uchar days);void WriteSet1302(uchar cmd,uchar date);uchar ReadSet1302(uchar cmd);void ReadyreadDS18B20(void);uchar ReadDS18B20(void);void IntDS1302(void);void delay1ms( uint x){uchar i,j;for(i=x;i>0;i--)for(j=124;j>0;j--);}void delaynus(uchar n) //延时若干微秒{while(n--);}void display_Year(year) //显示年{uchar i,j;i=year/10;j=year%10;P0=digit[2];P1=8;delay1ms(2);P0=digit[0];P1=9;delay1ms(2);P0=digit[i];P1=10;delay1ms(2);P0=digit[j];P1=11;delay1ms(1);}void display_Month(month) //显示月{uchar i,j;i=month/10;j=month%10;P0=digit[i];P1=6;delay1ms(2);P0=digit[j];P1=7;delay1ms(1);}void display_Day(day) //显示日{uchar i,j;i=day/10;j=day%10;P0=digit[i];P1=4;delay1ms(2);P0=digit[j];P1=5;delay1ms(1);}void display_Hour(hour) //显示时{uchar i,j;i=hour/10;j=hour%10;P0=digit[i];P1=2;delay1ms(2);P0=digit[j]+n*0x80;P1=3;delay1ms(1);}void display_Minute(minute) //显示分{uchar i,j;i=minute/10;j=minute%10;P0=digit[i];P1=0;delay1ms(2);P0=digit[j];P1=1;delay1ms(1);}void display_Week(void) //显示周{WEEK=GetWeekFromDay(years,months,days); P1=12;switch(WEEK){case 1:P0=digit[1];delay1ms(2);break;case 2:P0=digit[2] ;delay1ms(2);break;case 3:P0=digit[3];delay1ms(2);break;case 4:P0=digit[4];delay1ms(2);break;case 5:P0=digit[5];delay1ms(2);break;case 6:P0=digit[6];delay1ms(2);break;case 7:P0=digit[8];delay1ms(2);break;}}void display_Time(void) //显示实时时间{uchar value;value=ReadSet1302(0x83);minutes=(((value&0x70)>>4)*10+(value&0x0f));display_Minute(minutes);value=ReadSet1302(0x85);hours=(((value&0x70)>>4)*10+(value&0x0f));display_Hour(hours);value=ReadSet1302(0x87);days=(((value&0x70)>>4)*10+(value&0x0f));display_Day(days);value=ReadSet1302(0x89);months=(((value&0x70)>>4)*10+(value&0x0f));display_Month(months);value=ReadSet1302(0x8D);years=(((value&0xf0)>>4)*10+(value&0x0f));display_Year(years);display_Week();}/*****************时间调整部分*********************/void hourset(void) //调时{uchar timevalue,hour;delay1ms(500); //防止多次触发WriteSet1302(0x8e,0x00);//将写保护去掉,确保能正常将调整后的数值写入DS1302timevalue=ReadSet1302(0x85); //读取此时的数值hour=(((timevalue&0x70)>>4)*10+(timevalue&0x0f));while(1){if(ADD==0){delay1ms(20);if(ADD==0){hour++;delay1ms(100);while(ADD==0);}}if(RED==0){delay1ms(20);if(RED==0){hour--;delay1ms(100);if(hour==0) hour=23;while(RED==0);}}if(hour>=24) hour=0;timevalue=(((hour)/10)<<4|(hour%10));WriteSet1302(0x84,timevalue);delay1ms(2);display_Hour(hour);delay1ms(10);if(CANL==0){mode=0;break;}if(SET==0) break;}WriteSet1302(0x8e,0x80);}void minuteset(void) //调分{uchar timevalue,minute;delay1ms(500);WriteSet1302(0x8e,0x00);timevalue=ReadSet1302(0x83);minute=(((timevalue&0x70)>>4)*10+(timevalue&0x0f));while(1){if(ADD==0){delay1ms(20);if(ADD==0){minute++;if(minute>=60) timevalue=0;while(ADD==0);}}if(RED==0){delay1ms(10);if(RED==0){minute--;if(minute==0) minute=59;delay1ms(300);while(RED==0);}}if(minute>=60) minute=0;timevalue=((minute/10)<<4|(minute%10));WriteSet1302(0x82,timevalue);delay1ms(2);display_Minute(minute);delay1ms(10);if(CANL==0){mode=0;break;}if(SET==0) break;}WriteSet1302(0x8e,0x80);}void yearset(void) //调年{uchar datevalue,year;delay1ms(500);WriteSet1302(0x8e,0x00);datevalue=ReadSet1302(0x8d);year=(((datevalue&0x70)>>4)*10+(datevalue&0x0f));while(1){if(ADD==0){delay1ms(20);if(ADD==0){year++;while(ADD==0);}}if(RED==0){delay1ms(20);if(RED==0){year--;delay1ms(100);while(RED==0);}}datevalue=((year/10)<<4|(year%10));WriteSet1302(0x8c,datevalue);delay1ms(5);display_Year(year);delay1ms(5);if(CANL==0){mode=0;break;}if(SET==0) break;}WriteSet1302(0x8e,0x80);}void monthset(void) //调月{uchar datevalue,month;delay1ms(500);WriteSet1302(0x8e,0x00);datevalue=ReadSet1302(0x89);month=(((datevalue&0x70)>>4)*10+(datevalue&0x0f));while(1){if(ADD==0){delay1ms(20);if(ADD==0){month++;if(month>12) month=1;while(ADD==0);}}if(RED==0){delay1ms(20);if(RED==0){month--;delay1ms(100);if(month==0) month=12;while(RED==0);}}datevalue=((month/10)<<4|(month%10));WriteSet1302(0x88,datevalue);delay1ms(5);display_Month(month);delay1ms(5);if(CANL==0){mode=0;break;}if(SET==0) break;}WriteSet1302(0x8e,0x80);}void dayset(void) //调日{uchar datevalue,day;delay1ms(500);WriteSet1302(0x8e,0x00);datevalue=ReadSet1302(0x87);day=(((datevalue&0x70)>>4)*10+(datevalue&0x0f));while(1){if(ADD==0){delay1ms(10);if(ADD==0){day++;if(day>31) day=1;while(ADD==0);}}if(RED==0){delay1ms(10);if(RED==0){if(day==0) day=31;day--;delay1ms(300);while(RED==0);}}datevalue=((day/10)<<4|(day%10));WriteSet1302(0x86,datevalue);delay1ms(5);display_Day(day);delay1ms(5);if(CANL==0){mode=0;break;}if(SET==0) break;}WriteSet1302(0x8e,0x80);}void TimeSet(void) //时间调整函数{if(SET==0){delay1ms(20);if(SET==0){ while (!SET) ;mode++;delay1ms(20);switch(mode){case 1:{yearset();delay1ms(20);} break;case 2:{monthset();delay1ms(20);} break;case 3:{dayset();delay1ms(20 );} break;case 4:{hourset(); delay1ms(20);} break;case 5:{minuteset();delay1ms(20);} break;}if(mode==5) mode=0;}}}/***************显示温度模块********************/uchar flag;/*********************显示温度整数部分*************************/ void Display_Integer(unsigned char x){uchar s,g;s=x%100/10;g=x%10;P0=digit[s];P1=13;delay1ms(3);P0=digit[g]+0x80;P1=14;delay1ms(3) ;P0=digit[10];P1=15;delay1ms(1);}void display_Temperature(void){ReadyreadDS18B20();TL=ReadDS18B20();TH=ReadDS18B20();if((TH&0XF8)!=0X00){flag=1;TL=~TL;TH=~TH;TL+=1;if(TL>255) TH+=1;TN=TH*16+TL/16;}TN=TH*16+TL/16;Display_Integer(TN);}/**************** 设置模块******************/void Set(void){if(SET==0){delay1ms(5);if(SET==0){delay1ms(50);while(1){TimeSet();if(CANL==0){break;}}}}}void InitTimer0(void){TMOD = 0x01;TH0 = 0x3C;TL0 = 0x0B0;EA = 1;ET0 = 1;TR0 = 1;}/***************主函数**********************/void main(){int i;P0=0;InitTimer0();IntDS1302(); //初始化DS1302delay1ms(5);le=1;oe=0;en=0;delay1ms(200);beep=1;while(1){for (i=1000;i>0;i--){Set( );display_Time( );Display_Integer(TN);if(hours==7||hours==8||hours==9||hours==10||hours==11||hours==12||hours==14||hours==15||hours==16||hours==17||hours==18||hours==19||hours==20||hours==21||hours==22){if(minutes==0x00){uchar value,w,k;value=ReadSet1302(0x81);seconds=(((value&0x70)>>4)*10+(value&0x0f));if(seconds>0&&seconds<2){for(k=5;k>=0;k--){for(w=100;w>0;w--){beep=0;delay1ms(3);beep=1;delay1ms(3);}delay1ms(1000);}}else beep=1;}}}display_Temperature();}}void t0() interrupt 1{TH0 = 0x3C;TL0 = 0x0B0;count++;if(count==10){n=0;}if(count==20){ count=0;n=1;}}/*******DS1302模块*************/sbit SCLK=P2^0; //DS1302时钟输入sbit DATE=P2^1; //DS1302数据输入sbit REST=P2^2; //DS1302复位端口void Write1302(uchar date)//向1302写数据{uchar i;SCLK=0;delaynus(2);for(i=0;i<8;i++){DATE=date&0x01;SCLK=1;delaynus(2);SCLK=0;delaynus(2);date>>=1;}}void WriteSet1302(uchar cmd,uchar date) //根据相应的命令输入相应的数据{REST=0;SCLK=0;REST=1;Write1302(cmd);delaynus(5);Write1302(date);SCLK=1;REST=0;}uchar Read1302(void) //读取1302数据{uchar i,date;delaynus(2);for(i=0;i<8;i++){date>>=1;if(DATE==1)date|=0x80;SCLK=1;delaynus(2);SCLK=0;delaynus(2);}return date;}uchar ReadSet1302(uchar cmd)//根据命令读取1302相应的值{uchar date;REST=0;SCLK=0;REST=1;Write1302(cmd);delaynus(2);date=Read1302();SCLK=1;REST=0;return date;}void IntDS1302(void) //DS1302初始化{uchar flag;flag= ReadSet1302(0x81);if(flag&0x80) //判断时钟芯片是否关闭{WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令WriteSet1302(0x80,((0/10)<<4|(0%10))); //根据写秒寄存器命令字,写入秒的初始值WriteSet1302(0x82,((0/10)<<4|(0%10))); //根据写分寄存器命令字,写入分的初始值WriteSet1302(0x84,((0/10)<<4|(0%10))); //根据写小时寄存器命令字,写入小时的初始值WriteSet1302(0x86,((0/10)<<4|(0%10))); //根据写日寄存器命令字,写入日的初始值WriteSet1302(0x88,((0/10)<<4|(0%10))); //根据写月寄存器命令字,写入月的初始值WriteSet1302(0x8c,((10/10)<<4|(10%10))); //根据写年寄存器命令字,写入年的初始值WriteSet1302(0x90,0xa5); //打开充电功能选择2K电阻充电方式WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入保护指令}}/**********************操作DS18B20模块***************************/sbit DQ=P2^3;uchar time;void delayms(uchar x){ uchar i,j;for(i=x;i>0;i--)for(j=124;j>0;j--);}/**********************DS18B20初始化****************************/bit IntDS18B20(void){bit temp;DQ=1;for(time=0;time<2;time++);DQ=0;for(time=0;time<200;time++);DQ=1;for(time=0;time<10;time++);temp=DQ;for(time=0;time<200;time++);return temp;}/**************************读DS18B20**********************/uchar ReadDS18B20(void){uchar dat,i;for(i=0;i<8;i++){DQ=1;_nop_();DQ=0;_nop_();DQ=1;for(time=0;time<2;time++);dat>>=1;if(DQ==1)dat=dat|0x80;elsedat=dat|0x00;for(time=0;time<10;time++);}return dat;}/*********************向DS18B20写数据**************************/ void WriteDS18B20(uchar date){uchar i;for(i=0;i<8;i++){DQ=1;_nop_();DQ=0;DQ=date&0x01;for(time=0;time<10;time++);DQ=1;for(time=0;time<1;time++);date>>=1;}for(time=0;time<4;time++);}/*******************为读取温度做好准备************************/void ReadyreadDS18B20(void){IntDS18B20();WriteDS18B20(0XCC);WriteDS18B20(0X44);IntDS18B20();WriteDS18B20(0XCC);WriteDS18B20(0XBE);}// 计算2000~2099年任一天星期几// year : 00-99// month: 01-12// day : 01-31uchar GetWeekFromDay(uchar years,uchar months,uchar days){if( months == 1 || months == 2 ){months+=12;if( years> 0 )years--;elseyears ;}return (1+(( days + 2*months + 3*(months+1)/5 + years + years/4 ) %7)); // 返回星期几//(星期一用1表示,而星期天用7表示)}。