视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
基于单片机的万年历设计
2025-09-23 18:37:36 责编:小OO
文档
实现功能:

(1)显示年月日,时分秒,星期与温度

(2)年月日,时分秒均可调,具备自动调节闰年的功能

(3)具有闹钟功能,且开关控制闹钟的开启与关闭

(4)具有整点报时功能

一、概述、设计思路

该设计方案是以ATC51单片机为核心,采用LCD1602液晶屏幕显示系统,辅以时钟模块,温度采集模块、日期提醒、键盘时间调整预设置等模块,所构建的数字时钟系统,能动态显示实时时钟的时、分、秒,数据显示(误差在30每天),对闹铃方式与温度调节模块进行了重点设计 实现SB0、SB1、SB2、SB3四个键实现时钟正常显示,调时,及闹钟时间设置。本系统设计大部分功能有软件来实现,使电路简单明了,系统稳定性也得大大提高。

二、系统组成与工作原理

1、工作原理:

   本设计采用STCC51单片机作为本次课程设计的控制模块。单片机可把由DS18B20、DS1302、AT24C02中的数据利用软件来进行处理,从而把数据传输到显示模块,实现温度、日历和闹铃的显示。以LCD液晶显示器为显示模块,把单片机传来的的数据显示出来,并且显示多样化,在显示电路中,主要靠按键来实现各种显示要求的选择与切换。

2、总是设计框架图:

3、系统硬件概述:

该电路是由单片机STCC52为核心,具有在线编程功能,低功耗,时钟电路采用芯片1302,它是一款高性能、低功耗、自带RAM的实时时钟芯片,具有掉电自动保存功能。可自动对秒、分、时、日、周、月、年进行计数,具有闰年补偿功能,而且精度高位的RAM做为数据暂存区,工作电压2.5V~5.5V范围内,2.5V时耗电小于300nA。显示模块采用LCD1602显示屏,它内置192种字符,可显示32个符号或数字,清晰可见,而且功率消耗小寿命长抗干扰能力强。其工作电压为5v。温度采集模块采用温度传感器18b20。

三、单元电路的设计与分析

    整个电子时钟系统电路可分为六大部分:处理单元(CPU)、复位电路部分、显示部分、键盘输入部分、温度采集部分。

1、MCS-51单片机

S51各引脚功能介绍:

VCC:

S51 电源正端输入,接+5V。

VSS:

电源地端。

XTAL1:

单芯片系统时钟的反相放大器输入端。

XTAL2:

系统时钟的反相放大器输出端,一般在设计上只要在 XTAL1 和 XTAL2 上接上一只石英振荡晶体系统就可以动作了,此外可以在两引脚与地之间加入一 20PF 的小电容,可以使系统更稳定,避免噪声干扰而死机。

RESET:

S51的重置引脚,高电平动作,当要对晶片重置时,只要对此引脚电平提升至高电平并保持两个机器周期以上的时间,ATS51便能完成系统重置的各项动作,使得内部特殊功能寄存器之内容均被设成已知状态,并且至地址0000H处开始读入程序代码而执行程序。

EA/Vpp:

"EA"为英文"External Access"的缩写,表示存取外部程序代码之意,低电平动作,也就是说当此引脚接低电平后,系统会取用外部的程序代码(存于外部EPROM中)来执行程序。因此在8031及8032中,EA引脚必须接低电平,因为其内部无程序存储器空间。如果是使用 8751 内部程序空间时,此引脚要接成高电平。此外,在将程序代码烧录至8751内部EPROM时,可以利用此引脚来输入21V的烧录高压(Vpp)。

ALE/PROG:

端口3的管脚设置:

P3.0:RXD,串行通信输入。

P3.1:TXD,串行通信输出。

P3.2:INT0,外部中断0输入。

P3.3:INT1,外部中断1输入。

P3.4:T0,计时计数器0输入。

P3.5:T1,计时计数器1输入。

P3.6:WR:外部数据存储器的写入信号。

P3.7:RD,外部数据存储器的读取信号。

2、复位电路

电路图如下:

上电自动复位是通过外部复位电路的电容充电来实现的。只要Vcc的上升时间不超过1ms,就可以实现自动上电复位。

     

3、时钟电路

时钟是单片机的心脏,单片机各功能部件的运行都是以时钟频率为基准,有条不紊的一拍一拍地工作。因此,时钟频率直接影响单片机的速度,时钟电路的质量也直接影响单片机系统的稳定性。常用的时钟电路有两种方式:一种是内部时钟方式,另一种为外部时钟方式。本文用的是内部时钟方式。

电路图如下:

3.2 时钟电路模块设计

DS1302内含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。图中VCC1为后备电源,VCC2为主电源。DS1302由VCC1或VCC2两者中的较大者供电。所以在主电源关闭的情况下,也能保持时钟的连续运行。X1和X2是振荡源,外接32.768KHz晶振用来为芯片提供计时脉冲。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。DS1302

的硬件接线图如图所示:

                   

3.3 温度传感采集模块设计

如图所示。采用数字式温度传感器DS18B20,它是数字式温度传感器,具有测量精度高,电路连接简单特点。

                   

3.4 按键设计

电路由三个按键开关组成,分别与单片机的I/O相连,控制调整电路的加、减以及确定。

按键的开关状态通过一定的电路转换为高、低电平状态。按键闭合过程在相应的I/O端口形成一个负脉冲。闭合和释放过程都要经过一定的过程才能达到稳定,这一过程是处于高、低电平之间的一种不稳定状态,称为抖动。抖动持续时间的常长短与开关的机械特性有关,一般在5-10ms之间。为了避免CPU多次处理按键的一次闭合,应采用措施消除抖动。本文采用的是式按键,直接用I/O口线构成单个按键电路,每个按键占用一条I/O口线,每个按键的工作状态不会产生互相影响。

P1.0口表示功能移位键,按键选择要调整的时十位、时个位、分十位或分个位。 

    P1.1口表示数字“+“键,按一下则对应的数字加1。

    P1.2口表示数字“-”键,按一下则对应的数字减1。

    P1.3口表示时间表的切换,程序默认为日常时间表,当按下该开关,使输入为低电平时,表示当前执行的是考试时间表,并有绿发光二极管显示。再按键,使键抬起,输入维高电平时,表示当前执行的是日常作息时间表,用红发光二级管显示。

4、显示电路

采用LCD显示,LCD显示具有丰富多样性,灵活性,电路简单、易于控制而且功耗小,对于信息量多的系统,是比较适合的,LCD液晶显示模块采用LCD1602型号,具有很低的功耗,正常工作室电流仅2.0mA/5.0V。通过编程实现总动关闭屏幕能够更有效地降低功耗。LCD1602分两行显示,每行可现实多达16个字符,其内部的字符发生器已经存储了160个不同的点阵字符图形,通过内部指令可实现对其显示多样的控制。

        

四、程序设计

#include

#include"DS18B20_3.H"

#define uint unsigned int

#define uchar unsigned char

uchar a,b,miao,shi,fen,ri,yue,nian,week,flag,key1n,temp,miao1,shi1=12,fen1=1,miao1=0,clock=0 ;

//flag用于读取头文件中的温度值,和显示温度值

#define yh 0x80 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80)

#define er 0x80+0x40 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)

//液晶屏的与C51之间的引脚连接定义(显示数据线接C51的P0口)

sbit rs=P2^0;

sbit en=P2^2;

sbit rw=P2^1; //如果硬件上rw接地,就不用写这句和后面的rw=0了

sbit led=P2^6; //LCD背光开关

//DS1302时钟芯片与C51之间的引脚连接定义

sbit IO=P1^1;

sbit SCLK=P1^0;

sbit RST=P1^2;

sbit CLO=P1^4;

sbit ACC0=ACC^0;

sbit ACC7=ACC^7;

/************************************************************

ACC累加器=A

ACC.0=E0H 

ACC.0就是ACC的第0位。Acc可以位寻址。

累加器ACC是一个8位的存储单元,是用来放数据的。但是,这个存储单元有其特殊的地位,

是单片机中一个非常关键的单元,很多运算都要通过ACC来进行。以后在学习指令时,

常用A来表示累加器。但有一些地方例外,比如在PUSH指令中,就必须用ACC这样的名字。

一般的说法,A代表了累加器中的内容、而ACC代表的是累加器的地址。 

***************************************************************/

//校时按键与C51的引脚连接定义

sbit key1=P1^5;  //设置键

sbit key2=P1^6;  //加键

sbit key3=P1^7;  //减键

sbit buzzer=P1^3;//蜂鸣器,通过三极管9012驱动,端口低电平响

/**************************************************************/

uchar code tab1[]={"20  -  -   "}; //年显示的固定字符

uchar code tab2[]={"  :  :  "};//时间显示的固定字符

uchar code tab3[]={"   Congratulation!The New World is coming! "};//开机动画

//延时函数,后面经常调用

void delay(uint xms)//延时函数,有参函数

{uint x,y;

for(x=xms;x>0;x--)

for(y=110;y>0;y--);}

void delay1ms(int i)//1毫秒延时

{   int j,k;

   while(i--)

for(j=76;j>1;j--);

for(k=29;k>1;k--);     }

/********液晶写入指令函数与写入数据函数,以后可调用**************/

/*在这个程序中,液晶写入有关函数会在DS1302的函数中调用,所以液晶程序要放在前面*/

write_1602com(uchar com)//****液晶写入指令函数****

{    rs=0;//数据/指令选择置为指令

    rw=0; //读写选择置为写

    P0=com;//送入数据

    delay(1);

    en=1;//拉高使能端,为制造有效的下降沿做准备

    delay(1);

    en=0;//en由高变低,产生下降沿,液晶执行命令}

write_1602dat(uchar dat)//***液晶写入数据函数****

{    rs=1;//数据/指令选择置为数据

    rw=0; //读写选择置为写

    P0=dat;//送入数据

    delay(1);

    en=1; //en置高电平,为制造下降沿做准备

    delay(1);

    en=0; //en由高变低,产生下降沿,液晶执行命令}

lcd_init()//***液晶初始化函数****

{    uchar j;

    write_1602com(0x0f|0x08);  

for(a=0;a<42;a++)

       write_1602dat(tab3[a]);

      j=42;

    while(j--)

    {    write_1602com(0x1a);//循环左移

        delay(700);    } 

    write_1602com(0x01);

    delay(10);

    write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据

    write_1602com(0x0c);//开显示不显示光标

    write_1602com(0x06);//整屏不移动,光标自动右移

    write_1602com(0x01);//清显示

    /***开机动画显示hello welcome dianzizhong****/

    write_1602com(yh+1);//日历显示固定符号从第一行第1个位置之后开始显示

for(a=0;a<14;a++)

    {write_1602dat(tab1[a]);//向液晶屏写日历显示的固定符号部分

    //delay(3);

    }write_1602com(er+1);//时间显示固定符号写入位置,从第1个位置后开始显示

for(a=0;a<8;a++){write_1602dat(tab2[a]);//写显示时间固定符号,两个冒号

    //delay(3);

    }

    write_1602com(er+0);

    write_1602dat(0x20);

}

/***************DS1302有关子函数********************/

void write_byte(uchar dat)//写一个字节

{    ACC=dat;

    RST=1;

for(a=8;a>0;a--)

    {    IO=ACC0;

        SCLK=0;

        SCLK=1;

     ACC=ACC>>1;

    }

}

uchar read_byte()//读一个字节

{RST=1;

for(a=8;a>0;a--)

    {

        ACC7=IO;

        SCLK=1;

        SCLK=0;

     ACC=ACC>>1;    }

    return (ACC);

}

//----------------------------------------

void write_1302(uchar add,uchar dat)//向1302芯片写函数,指定写入地址,数据

{   RST=0;

    SCLK=0;

    RST=1;

    write_byte(add);

    write_byte(dat);

    SCLK=1;

    RST=0;

}

uchar read_1302(uchar add)//从1302读数据函数,指定读取数据来源地址

{    uchar temp;

    RST=0;

    SCLK=0;

    RST=1;

    write_byte(add);

    temp=read_byte();

    SCLK=1;

    RST=0;

    return(temp);

}uchar BCD_Decimal(uchar bcd)//BCD码转十进制函数,输入BCD,返回十进制

{ uchar Decimal;

Decimal=bcd>>4;

 return(Decimal=Decimal*10+(bcd&=0x0F));}

//--------------------------------------

void ds1302_init() //1302芯片初始化子函数(2012-12-22,00:00:00,week6)

{RST=0;

SCLK=0;

write_1302(0x8e,0x00); //允许写,禁止写保护 

write_1302(0x80,0x00); //向DS1302内写秒寄存器80H写入初始秒数据00

write_1302(0x82,0x00);//向DS1302内写分寄存器82H写入初始分数据00

write_1302(0x84,0x00);//向DS1302内写小时寄存器84H写入初始小时数据00

write_1302(0x8a,0x06);//向DS1302内写周寄存器8aH写入初始周数据6

write_1302(0x86,0x22);//向DS1302内写日期寄存器86H写入初始日期数据22

write_1302(0x88,0x12);//向DS1302内写月份寄存器88H写入初始月份数据12

write_1302(0x8c,0x12);//向DS1302内写年份寄存器8cH写入初始年份数据12

write_1302(0x8e,0x80); //打开写保护

}//------------------------------------

//温度显示子函数

void write_temp(uchar add,uchar dat)//向LCD写温度数据,并指定显示位置

{uchar gw,sw,bw;

if(dat>=0&&dat<=128)

    {gw=dat%10;//取得个位数字

    sw=dat%100/10;//取得十位数字

    bw=-5    ;//取得百位数字

    }

    else

    {

    dat=256-dat;

    gw=dat%10;//取得个位数字

    sw=dat%100/10;//取得十位数字

    bw=-3;         //0x30-3表示为负号

    }

    write_1602com(er+add);//er是头文件规定的值0x80+0x40

    write_1602dat(0x30+bw);//数字+30得到该数字的LCD1602显示码

    write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码

    write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码

    write_1602dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码

      write_1602dat(0x43);    //显示"C"符号,0x43是液晶屏字符库里大写C的地址码            

}//------------------------------------

//时分秒显示子函数

void write_sfm(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、现示数据,两个参数

{   uchar gw,sw;

    gw=dat%10;//取得个位数字

    sw=dat/10;//取得十位数字

    write_1602com(er+add);//er是头文件规定的值0x80+0x40

    write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码

    write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码

}

//-------------------------------------

//年月日显示子函数

void write_nyr(uchar add,uchar dat)//向LCD写年月日,有显示位置加数、显示数据,两个参数

{    uchar gw,sw;

    gw=dat%10;//取得个位数字

    sw=dat/10;//取得十位数字

    write_1602com(yh+add);//设定显示位置为第一个位置+add

    write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码

    write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码

    }

//-------------------------------------------

void write_week(uchar week)//写星期函数

{    write_1602com(yh+0x0c);//星期字符的显示位置

    switch(week)

    {        case 1:write_1602dat('M');//星期数为1时,显示

               write_1602dat('O');

               write_1602dat('N');

               break;

             case 2:write_1602dat('T');//星期数据为2时显示

               write_1602dat('U');

               write_1602dat('E');

               break;

            case 3:write_1602dat('W');//星期数据为3时显示

               write_1602dat('E');

               write_1602dat('D');

               break;

            case 4:write_1602dat('T');//星期数据为4是显示

               write_1602dat('H');

               write_1602dat('U');

               break;

            case 5:write_1602dat('F');//星期数据为5时显示

               write_1602dat('R');

               write_1602dat('I');

               break;

            case 6:write_1602dat('S');//星期数据为6时显示

               write_1602dat('T');

               write_1602dat('A');

               break;

        

        case 7:write_1602dat('S');//星期数据为7时显示

               write_1602dat('U');

               write_1602dat('N');

               break;}}

//****************键盘扫描有关函数**********************

void keyscan()

{    if(key1==0)//---------------key1为功能键(设置键)--------------------

    {    delay(9);//延时,用于消抖动

    if(key1==0)//延时后再次确认按键按下

    {    buzzer=1;//蜂鸣器短响一次

    delay(20);

    buzzer=0;

    while(!key1);

    key1n++;

    if(key1n==12)

        key1n=1;//设置按键共有秒、分、时、星期、日、月、年、返回,8个功能循环

    switch(key1n)

    {    case 1: TR0=0;//关闭定时器

            //TR1=0;

            write_1602com(er+0x08);//设置按键按动一次,秒位置显示光标

               write_1602com(0x0f);//设置光标为闪烁

               temp=(miao)/10*16+(miao)%10;//秒数据写入DS1302

               write_1302(0x8e,0x00);

               write_1302(0x80,0x80|temp);//miao

              write_1302(0x8e,0x80);

               break;

    case 2:  write_1602com(er+5);//按2次fen位置显示光标

        //write_1602com(0x0f);

            break;

    case 3: write_1602com(er+2);//按动3次,shi

             //write_1602com(0x0f);

            break;

    case 4: write_1602com(yh+0x0e);//按动4次,week

             //write_1602com(0x0f);

            break;

    case 5: write_1602com(yh+0x0a);//按动5次,ri

             //write_1602com(0x0f);

            break;

    case 6: write_1602com(yh+0x07);//按动6次,yue

             //write_1602com(0x0f);

            break;

    case 7: write_1602com(yh+0x04);//按动7次,nian

             //write_1602com(0x0f);

            break;

    case 8: write_1602com(er+0);

            write_1602dat(0x53);

            write_1602com(er+0);

            break;

    case 9: write_1602com(er+0);

            write_1602dat(0x4d);

            write_1602com(er+0);

            break;

    case 10:write_1602com(er+0);

            write_1602dat(0x48);

            write_1602com(er+0);

            break;

    case 11:write_1602com(er+0);

            write_1602dat(0x20);

            write_1602com(0x0c);//按动到第8次,设置光标不闪烁

            TR0=1;//打开定时器

         temp=(miao)/10*16+(miao)%10;

           write_1302(0x8e,0x00);

           write_1302(0x80,0x00|temp);//miao数据写入DS1302

           write_1302(0x8e,0x80);

            break;                                                                

    }}}

//------------------------------加键key2----------------------------        

    if(key1n!=0)//当key1按下以下。再按以下键才有效(按键次数不等于零)

    {if(key2==0)  //上调键

{    delay(10);

    if(key2==0)

    {   buzzer=1;//蜂鸣器短响一次

    delay(20);

    buzzer=0;

    while(!key2);

    switch(key1n)

    {    case 1:miao++;//设置键按动1次,调秒

            if(miao==60)

                miao=0;//秒超过59,再加1,就归零

            write_sfm(0x07,miao);//令LCD在正确位置显示"加"设定好的秒数

            temp=(miao)/10*16+(miao)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00); //允许写,禁止写保护 

               write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码

               write_1302(0x8e,0x80); //打开写保护

            write_1602com(er+0x08);//因为设置液晶的模式是写入数据后,光标自动右移,所以要指定返回

    //write_1602com(0x0b);

            break;

    case 2:fen++;

            if(fen==60)

                fen=0;

            write_sfm(0x04,fen);//令LCD在正确位置显示"加"设定好的分数据

            temp=(fen)/10*16+(fen)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护 

               write_1302(0x82,temp);//向DS1302内写分寄存器82H写入调整后的分数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            break;

    case 3:shi++;

            if(shi==24)

                shi=0;

            write_sfm(1,shi);//令LCD在正确的位置显示"加"设定好的小时数据

            temp=(shi)/10*16+(shi)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护 

               write_1302(0x84,temp);//向DS1302内写小时寄存器84H写入调整后的小时数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

    case 4:week++;

            if(week==8)

                week=1;

            write_1602com(yh+0x0C);//指定'加'后的周数据显示位置

                write_week(week);//指定周数据显示内容

            temp=(week)/10*16+(week)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护 

               write_1302(0x8a,temp);//向DS1302内写周寄存器8aH写入调整后的周数据BCD码

               write_1302(0x8e,0x80);//打开写保护

               write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

    case 5:ri++;

                switch(yue)

            {   case 1:case 3:case 5:case 7:case 8:case 10:case 12:

             if(ri>31) ri=1;

                break;

                case 2: 

                if(nian%4==0||nian%400==0)

           { if(ri>29) ri=1; } else

                  { if(ri>28) ri=1;}break;

               case 4:case 6:case 9:case 11:

              ri++;

if(ri>30) ri=1;

 break;

 }         

            write_nyr(9,ri);//令LCD在正确的位置显示"加"设定好的日期数据

            temp=(ri)/10*16+(ri)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护

               write_1302(0x86,temp);//向DS1302内写日期寄存器86H写入调整后的日期数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

    case 6:yue++;

        switch(ri)

            {   case 31:

                if(yue==2|yue==4|yue==6|yue==9|yue==11)    {yue++;}

                else 

             {if(yue>=13) yue=1;}

                

                break;

               case 30:

                if(yue==2){yue++;}

                else  

             {if(yue>=13) yue=1;}

                case 29:

                if(nian%4==0||nian%400==0)

             {if(yue>=13) {yue=1;}}

                else{if(yue==2){yue++;}

             else if(yue>=13) {yue=1;}}

                break;

              }

         if(yue>=13) yue=1;        

            write_nyr(6,yue);//令LCD在正确的位置显示"加"设定好的月份数据

            temp=(yue)/10*16+(yue)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护

               write_1302(0x88,temp);//向DS1302内写月份寄存器88H写入调整后的月份数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

    case 7:nian++;

         if(nian==100)

            nian=0;

            write_nyr(3,nian);//令LCD在正确的位置显示"加"设定好的年份数据

            temp=(nian)/10*16+(nian)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护

               write_1302(0x8c,temp);//向DS1302内写年份寄存器8cH写入调整后的年份数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

    case 8:    write_1602com(er+8); //设置闹钟的秒定时

            miao1++;

            if(miao1==60)

               miao1=0;

            write_sfm(0x07,miao1);//令LCD在正确位置显示"加"设定好秒的数据

            write_1602com(er+8);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            break;

    case 9:    write_1602com(er+5); //设置闹钟的分钟定时

            fen1++;

            if(fen1==60)

            fen1=0;

            write_sfm(0x04,fen1);//令LCD在正确位置显示"加"设定好的分数据

            write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            break;

    case 10:    write_1602com(er+2); //设置闹钟的小时定时

            shi1++;

            if(shi1==24)

            shi1=0;

            write_sfm(0x01,shi1);//令LCD在正确的位置显示"加"设定好的小时数据

            write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

    }

    }

    }

    //------------------减键key3,各句功能参照'加键'注释---------------

    if(key3==0)

    {

    delay(10);//调延时,消抖动

    if(key3==0)

    {

    buzzer=1;//蜂鸣器短响一次

    delay(20);

    buzzer=0;

    while(!key3);

    switch(key1n)

    {

        case 1:miao--;

                if(miao==-1)

                    miao=59;//秒数据减到-1时自动变成59

            write_sfm(0x07,miao);//在LCD的正确位置显示改变后新的秒数

            temp=(miao)/10*16+(miao)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00); //允许写,禁止写保护 

               write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码

               write_1302(0x8e,0x80); //打开写保护

            write_1602com(er+0x08);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            //write_1602com(0x0b);

            break;

        case 2:fen--;

                if(fen==-1)

                    fen=59;

            write_sfm(4,fen);

            temp=(fen)/10*16+(fen)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护 

               write_1302(0x82,temp);//向DS1302内写分寄存器82H写入调整后的分数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            break;

        case 3:shi--;

               if(shi==-1)

                    shi=23;

            write_sfm(1,shi);

            temp=(shi)/10*16+(shi)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护 

               write_1302(0x84,temp);//向DS1302内写小时寄存器84H写入调整后的小时数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

        case 4:week--;

                if(week==0)

                    week=7;

            write_1602com(yh+0x0C);//指定'加'后的周数据显示位置

                write_week(week);//指定周数据显示内容

               temp=(week)/10*16+(week)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护 

               write_1302(0x8a,temp);//向DS1302内写周寄存器8aH写入调整后的周数据BCD码

               write_1302(0x8e,0x80);//打开写保护

               write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

        case 5:ri--;

                switch(yue)

            { 

                  case 1:case 3:case 5:case 7:case 8:case 10:case 12:

                   

                 if(ri==0) ri=31;

                 break;

                  case 2: if(nian%4==0||nian%400==0)

                     { if(ri==0) ri=29;     }

                   else { if(ri==0) ri=28;     }break;

                 case 4:case 6:case 9:case 11:

                  if(ri==0) ri=30;

                     break;}

            write_nyr(9,ri);

            temp=(ri)/10*16+(ri)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护

               write_1302(0x86,temp);//向DS1302内写日期寄存器86H写入调整后的日期数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

        case 6:yue--;

                switch(ri)

            {  case 31:

                if(yue==2|yue==4|yue==6|yue==9|yue==11)    {yue--;}

                else 

                 {if(yue==0) yue=12;}

                break;

               case 30:

                if(yue==2){yue--;}

                else  

                {if(yue==0) yue=12;}

                case 29:

                if(nian%4==0||nian%400==0)

                {if(yue==0) {yue=12;}}

                else{if(yue==2){yue--;}

                      else if(yue==0) {yue=12;}}

                break;

              }

              if(yue==0) yue=12;

            write_nyr(6,yue);

            temp=(yue)/10*16+(yue)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护

               write_1302(0x88,temp);//向DS1302内写月份寄存器88H写入调整后的月份数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;    

        case 7:nian--;

         if(nian==-1)

            nian=99;

            write_nyr(3,nian);

         temp=(nian)/10*16+(nian)%10;//十进制转换成DS1302要求的DCB码

               write_1302(0x8e,0x00);//允许写,禁止写保护

               write_1302(0x8c,temp);//向DS1302内写年份寄存器8cH写入调整后的年份数据BCD码

               write_1302(0x8e,0x80);//打开写保护

            write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;

        case 8:    write_1602com(er+8); //设置闹钟的秒定时

            miao1--;

            if(miao1==-1)

               miao1=59;

            write_sfm(0x07,miao1);//令LCD在正确位置显示"加"设定好秒的数据

            write_1602com(er+8);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            break;

        case 9:    write_1602com(er+5); //设置闹钟的分钟定时

            fen1--;

            if(fen1==-1)

            fen1=59;

            write_sfm(0x04,fen1);//令LCD在正确位置显示"加"设定好的分数据

            write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置

            break;

        case 10:    write_1602com(er+2); //设置闹钟的小时定时

            shi1--;

            if(shi1==-1)

            shi1=23;

            write_sfm(0x01,shi1);//令LCD在正确的位置显示"加"设定好的小时数据

            write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

            break;        

    }

    }

    }

    }

}

//定时器0初始化程序

void init() //定时器、计数器设置函数

{

    TMOD=0x11; //指定定时/计数器的工作方式为1

    TH0=0;  //定时器T0的高四位=0

    TL0=0;  //定时器T0的低四位=0

    EA=1;  //系统允许有开放的中断

    ET0=1; //允许T0中断

    TR0=1; //开启中断,启动定时器

}

//*******************主函数**************************

//***************************************************

void main()

{    buzzer=0;

    lcd_init();      //调用液晶屏初始化子函数

    ds1302_init();   //调用DS1302时钟的初始化子函数

    init();          //调用定时计数器的设置子函数

    led=0;           //打开LCD的背光电源

    buzzer=1;//蜂鸣器长响一次

    delay(80);

    buzzer=0;

    while(1)  //无限循环下面的语句:

    {    

    keyscan();      //调用键盘扫描子函数 }

}

/*************通过定时中断实现定是独处并显示数据******************/

void timer0() interrupt 1  //取得并显示日历和时间

{  //Init_DS18B20();//温度传感器DS18b2初始化子函数,在头文件中

   flag=ReadTemperature();//将18b2头文件运行返回的函数结果送到变量FLAG中,用于显示

  //读取秒时分周日月年七个数据(DS1302的读寄存器与写寄存器不一样):

 miao = BCD_Decimal(read_1302(0x81));

    fen = BCD_Decimal(read_1302(0x83));

    shi  = BCD_Decimal(read_1302(0x85));

    ri  = BCD_Decimal(read_1302(0x87));

    yue = BCD_Decimal(read_1302(0x));

    nian=BCD_Decimal(read_1302(0x8d));

    week=BCD_Decimal(read_1302(0x8b));

//显示温度、秒、时、分数据:

      write_temp(11,flag);//显示温度,从第二行第12个字符后开始显示

   write_sfm(7,miao);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数)

    write_sfm(4,fen);//分,从第二行第5个字符后开始显示

    write_sfm(1,shi);//小时,从第二行第2个字符后开始显示

//显示日、月、年数据:

    write_nyr(9,ri);//日期,从第二行第9个字符后开始显示

   write_nyr(6,yue);//月份,从第二行第6个字符后开始显示

    write_nyr(3,nian);//年,从第二行第3个字符后开始显示

    write_week(week);

/***********整点报时程序************/

    if(fen==0&&miao==0)

if(shi<22&&shi>6 )

          {    buzzer=1;//蜂鸣器短响一次

        delay(20);

        buzzer=0;}

/**************闹钟程序: 将暂停键按下停止蜂鸣********************/

     if(CLO==0)        /*按下p1.3停止蜂鸣*/

     { clock=0; return;}    

     if(shi1==shi&&fen1==fen&&miao1==miao)

        {    clock=1; }    

    if(clock==1)

       {buzzer=1;//蜂鸣器短响一次

        delay(20);

        buzzer=0;  }}

5、测试结果分析与结论

Protel总电路图:

设计电路的PROTEU仿真图:

整体PCB版图如下:

六、调试所需的仪器设备

基本测试仪器:万用电表、秒表、电源、温度表

七、设计的难点和可能出现的问题

1、设计难点:

   (1):温度设计模块的设计,

   (2):LCD显示模块的设计

   (3):闹铃设计模块的实现设计

   (4):单片机个功能模块的综合设计

   (5):电路总体设计,元器件选择及焊接,调试

2、可能出现的问题:

   (1):温度显示,与实际偏差大

   (2):LCD显示模块的功能显示不正常

   (3):闹铃设计与其他时间功能设计冲突

   (4):软件程序设计功能不完善,系统无法正常运转

八、预期达到的性能指标

1、预期达到的性能指标:

能动态显示实时时钟的时、分、秒,数据显示(误差在30每天),对闹铃方式与温度调节模块进行了重点设计 实现SB0、SB1、SB2、SB3四个键实现时钟正常显示,调时,及闹钟时间设置。

2、多功能数字时钟使用说明:

A:(调整切换键)进行时间、闹铃以及生日的设置,调整内容切换。

B: (调整加减/生日的设定)在时间显示界面中按A键后,进入时间调整。按B键对应闪烁内容加1;在时间显示界面中长按改建进入圣体信息设定,按A键选择调整内容,按B键加1,C键减1,D减退出设定,当设定生日到时,当日七点整第一次响铃提醒,以后每隔一小时整点提醒,直至当日晚上22点后最后一次提醒。

C:(调整减/日期提醒)在时间显示界面中按A键后,进入时间调整。按C键对应闪烁内同加1;在时间显示界面中常按A键后,进入日期提醒查看。

D (退出键/闹铃设定)在各种设定模式(时间、生日、闹铃)下,按该键退出设定;若在时间显示界面中短按键后,则进入闹铃、生日信息查看;长按则进行闹铃设定。

E:(屏幕背光键):按下该键不放则屏幕背光。调整左上方的蓝色变阻器课调整LCD显示灰度;调整右上方的蓝色编著器可调整LCD背光的亮度。(注:系统再不进行日和操作时,10秒后自动关闭屏幕,按任意键返回)

F:(复位键)按下该键系统复位,系统从头开始执行程序,如果与长按可按下改嫁进行系统复位,复位不会造成时间、生日、和闹铃等细细的丢失。下载本文

显示全文
专题