当前位置:文档之家› 万年历数字钟及可调时钟系统

万年历数字钟及可调时钟系统

万年历数字钟及可调时钟系统一、引言万年历数字钟是一种用万年历时钟芯片实现年、月、日、时、分、秒计时,并通过单片机处理后送给显示芯片显示的装置,与机械式时钟相比具有更高的准确性和直观性,且具有更长的使用寿命。

本系统还可以扩展为可调的自动开关,对家电对用电设备进行控制,笔者在随后改制成为可调时的自动断电的供电系统.二、原理图设计1.单片机及其外围电路设计复位采用X25045芯片,复位电路如图1所示。

图1 复位电路设计单片机采用贴片封装的AT89S51,晶振为11.0592MHz。

其中P1.5~P1.7为下载程序使用,电路如图2所示。

图2 单片机89S51外围电路设计2.时钟芯片电路设计时钟芯片采用PCF8563,晶振采用32.768K,电容使用15pf。

PCF8563 是PHILIPS 公司推出的一款工业级内含I2C 总线接口功能的具有极低功耗的多功能时钟日历芯片。

内部时钟电路、内部振荡电路、内部低电压检测电路(1.0V)以及两线制I2C 总线通讯方式,不但使外围电路及其简洁,而且也增加了芯片的可靠性。

同时每次读写数据后,内嵌的字地址寄存器会自动产生增量。

电路如图3所示。

图3 时钟芯片电路设计3.显示芯片电路设计显示芯片采用ZLG7289,晶振为12MHz。

ZLG7289A 是广州周立功单片机发展有限公司自行设计的,具有SPI 串行接口功能的可同时驱动8 位共阴式数码管(或64 只独立LED )的智能显示驱动芯片,该芯片同时还可连接多达64 键的键盘矩阵,单片即可完成LED 显示﹑键盘接口的全部功能。

电路如图4所示。

图4 显示芯片电路设计4.双电源电路设计系统采用双电源,平时使用V1=10V的外接电源,停电时使用电池,由V2输入。

电池有6节,其电压为9V。

当电池电压低于6V时,LED亮,说明电池电量不足。

电路如图5所示。

图5 双电源电路设计三、程序设计程序开始时先对系统初始化,并设置好各种中断。

下步操作主要是对时钟芯片进行操作,首先要给时钟芯片设置初值,时钟芯片便自行计数。

此时检测是否有按键按下,按键是为了调整时钟。

有按键按下则执行按键中断程序,没有按键按下则执行下一步的操作,即取时钟芯片中的时钟值,然后送显示。

程序流程图如下。

图6 总体流程图四、源程序#include <reg51._date;void RESWDI(void);void WREN(void);void WRDI(void);void WRSR(void);unsigned char RSDR(void);void WIPCHK(void);void OUTByte(unsigned char Byte);unsigned char INPUTByte(void);unsigned char ReadByte(unsigned char ADD);void WriteByte(unsigned char Byte,ADD);#define _Nop() _nop_()sbit zlg7289_cs =P1^1;sbit zlg7289_clk =P2^6;sbit zlg7289_dio =P2^7;sbit zlg7289_key =P3^2;sbit p07=P0^7;sbit p06=P0^6;sbit CS=P2^4;sbit SCK=P2^2;sbit SO=P2^5;sbit SI=P2^3;sbit p10=P1^0;sbit SDA=P1^2; *模拟I2C数据传送位*sbit SCL=P1^3; *模拟I2C时钟控制位*uchar buf[9]={0x00,0x00,0x30,0x23,0x15,0x1,0x05,0x04,0x05}; uchar bufdata,bb,date;uchar SLA=0xA2,SUBA=0x00;uchar *p;uchar keychange=0;uchar key=0; *键盘值*bit keyint=0; *按键中断标志*bit keyok=1; *数据是否修改好*uchar num=0; *移位键移到哪个LED*void delay(uchar i){while(i--);}******************** TIMER1 interrupt process *************************** timer0 (void) interrupt 1 using 1{TH0=0x3c;TL0=0xb0;RESWDI();}void RESWDI(void) 复位看门狗(喂狗){zlg7289_cs=1;CS =1;CS =0;CS =1;zlg7289_cs=1;}void WREN(void) 写使能复位使用)?{zlg7289_cs=1;SCK=0;CS=0;OUTByte(0x06); 发送06H写使能命令字SCK=0;CS=1;zlg7289_cs=1;}void WRDI(void) 写使能复位(禁止写{{zlg7289_cs=1;SCK=0;CS=0;OUTByte(0x04); 发送04H写禁止命令字SCK=0; CS=1;zlg7289_cs=1;}void WRSR(void) 写状态寄存器{WREN();zlg7289_cs=1;SCK=0;CS=0;OUTByte(0x01); 发送01H写寄存器命令字OUTByte(0x00); 发送寄存器值BL0,BL1为0没写保护,WD0=0 W01=1 WD1=0WD1=0看门狗复位时间1.4SSCK=0;CS=1;zlg7289_cs=1;WIPCHK(); 判断是否写入}unsigned char RSDR(void) 读状态寄存器{unsigned char Temp;zlg7289_cs=1;SCK=0;CS=0;OUTByte(0x05); 发送05H读状态寄存器命令字Temp =INPUTByte(); 读状态寄存器值SCK=0;CS=1;return Temp;;这一个调试时没有执行,Temp的值总是0xFF;??????????? zlg7289_cs=1;}void WIPCHK(void) 检查WIP位,判断是否写入完成{unsigned char Temp,TempCyc;for(TempCyc=0;TempCyc<50;TempCyc++){Temp =RSDR(); 读状态寄存器if (Temp&0x01==0)TempCyc =50;}}单字节指令或数据写入X25045在SI线上输入的数据在SCK的上升沿被锁存。

void OUTByte(unsigned char Byte) 输出一个定节{unsigned char TempCyc;zlg7289_cs=1;for(TempCyc=0;TempCyc<8;TempCyc++){SCK =0;if(Byte&0x80)SI =1;elseSI =0;SCK =1;Byte =Byte<<1; 右移}SI=0; 使SI处于确定的状态zlg7289_cs=1;}单字节数据从X25045读到单片机数据由SCK的下降沿输出到SO线上。

unsigned char INPUTByte(void) 输入一个字节{unsigned char Temp=0, TempCyc;zlg7289_cs=1;for(TempCyc=0;TempCyc<8;TempCyc++){Temp =Temp<<1; 右移SCK =1;SCK=0;if (SO)Temp =Temp|0x01; SO为1,则最低位为1elseTemp&=0xFE;}return Temp;;这一个调试时没有执行,Temp的值总是0zlg7289_cs=1;}unsigned char ReadByte(unsigned char ADD) 读地址中的数据这里不做先导字处理,只能读00-FFH{unsigned char Temp;zlg7289_cs=1;SCK=0;CS=0;SO=1;SI=1;OUTByte(0x3); 发送读指令03H 如要支持000-FFF则要把高位地址左移3位再为03H相或OUTByte(ADD); 发送低位地址Temp =INPUTByte();SCK=0;CS=1;return Temp;这一个调试时没有执行,Temp的zlg7289_cs=1;}void WriteByte(unsigned char Byte,ADD) 向地址写入数据这里同样不做先导字处理,只能写00-FFH{WREN();zlg7289_cs=1;SCK=0;CS=0;SO=1;SI=1;OUTByte(0x2); 发送写指令02H 如要支持000-FFF则要把高位地址左移2位再为02H相或OUTByte(ADD); 发送低位地址OUTByte(Byte); 发送数据SCK=0;CS=1;WIPCHK();zlg7289_cs=1;}******************************************************************** ***************模拟I2C总线传输程序*********************************** ******************************************************************** bit ack; *应答标志位********************************************************************起动总线函数******************************************************************** void Start_I2c(){SDA=1; *发送起始条件的数据信号*_Nop();SCL=1;_Nop(); *起始条件建立时间大于4.7us,延时*_Nop();_Nop();_Nop();_Nop();SDA=0; *发送起始信号*_Nop(); * 起始条件锁定时间大于4μs*_Nop();_Nop();_Nop();_Nop();SCL=0; *钳住I2C总线,准备发送或接收数据*_Nop();_Nop();}*******************************************************************结束总线函数******************************************************************** void Stop_I2c(){SDA=0; *发送结束条件的数据信号*_Nop(); *发送结束条件的时钟信号*SCL=1; *结束条件建立时间大于4μs*_Nop();_Nop();_Nop();_Nop();_Nop();SDA=1; *发送I2C总线结束信号*_Nop();_Nop();_Nop();_Nop();}*******************************************************************字节数据传送函数******************************************************************** void SendByte(uchar c){uchar BitCnt;for(BitCnt=0;BitCnt<8;BitCnt++) *要传送的数据长度为8位*{if((c<<BitCnt)&0x80)SDA=1;else SDA=0;_Nop();SCL=1; *置时钟线为高,通知被控器开始接收数据位* _Nop();_Nop(); *保证时钟高电平周期大于4μs*_Nop();_Nop();_Nop();SCL=0;}_Nop();_Nop();SDA=1; *8位发送完后释放数据线,准备接收应答位* _Nop();_Nop();SCL=1;_Nop();_Nop();_Nop();if(SDA==1)ack=0;else ack=1; *判断是否接收到应答信号*SCL=0;_Nop();_Nop();}*******************************************************************字节数据接收函数******************************************************************** uchar RcvByte(){uchar retc;uchar BitCnt;retc=0;SDA=1; *置数据线为输入方式*for(BitCnt=0;BitCnt<8;BitCnt++){_Nop();SCL=0; *置时钟线为低,准备接收数据位*_Nop();_Nop(); *时钟低电平周期大于4.7s*_Nop();_Nop();_Nop();SCL=1; *置时钟线为高使数据线上数据有效*_Nop();_Nop();retc=retc<<1;if(SDA==1)retc=retc+1; *读数据位,接收的数据位放入retc中 * _Nop();_Nop();}SCL=0;_Nop();_Nop();return(retc);}********************************************************************应答子函数******************************************************************** void Ack_I2c(bit a){if(a==0)SDA=0; *在此发出应答或非应答信号*else SDA=1;_Nop();_Nop();_Nop();SCL=1;_Nop();_Nop(); *时钟低电平周期大于4μs*_Nop();_Nop();_Nop();SCL=0; *清时钟线,钳住I2C总线以便继续接收*_Nop();_Nop();}*******************************************************************向有子地址器件发送多字节数据函数******************************************************************** bit ISendStr(uchar sla,uchar suba,uchar *s){uchar i;Start_I2c(); *启动总线*SendByte(sla); *发送器件地址*if(ack==0)return(0);SendByte(suba); *发送器件子地址*if(ack==0)return(0);for(i=0;i<9;i++){SendByte(*s); *发送数据*if(ack==0)return(0);s++;}Stop_I2c(); *结束总线*return(1);}*******************************************************************向有子地址器件读取多字节数据函数******************************************************************** bit IRcvStr(uchar sla,uchar suba,uchar *s){uchar i;Start_I2c(); *启动总线*SendByte(sla); *发送器件地址*if(ack==0)return(0);SendByte(suba); *发送器件子地址*if(ack==0)return(0);Start_I2c();SendByte(sla+1);if(ack==0)return(0);for(i=0;i<8;i++){*s=RcvByte(); *发送数据*Ack_I2c(0); *发送就答位*s++;}*s=RcvByte();Ack_I2c(1); *发送非应位*Stop_I2c();return(1);}**********模拟I2C程序结束***************************void display(uint dis){uchar j;zlg7289_clk=0;delay(20);zlg7289_cs=0;for(j=0;j<16;j++){if((dis&0x8000)==0x8000) zlg7289_dio=1; else zlg7289_dio=0;delay(20);zlg7289_clk=1;delay(10);zlg7289_clk=0;delay(10);dis=dis<<1;}zlg7289_cs=1;delay(20);}void dis_play(uchar aa){uchar i;for(i=0;i<8;i++){if(_crol_(aa,i)&0x80)zlg7289_dio =1; elsezlg7289_dio =0; zlg7289_clk =1;delay(10); zlg7289_clk =0;}}void displaymonth(){bufdata=buf[5]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xc8);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);bufdata=buf[5]&0x30;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xc9);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);bufdata=buf[7]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xca);delay(10);dis_play(bufdata); zlg7289_cs=1;bufdata=buf[7]&0x10;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcf);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);}void displaytime(){bufdata=buf[3]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xce);delay(10);dis_play(bufdata); zlg7289_cs=1;bufdata=buf[3]&0x70;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcd);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);bufdata=buf[4]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcc);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);bufdata=buf[4]&0x30;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcb);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);}void display_x5045(){date=ReadByte(0x40);close_date=date;bufdata=date&0x0f;zlg7289_cs=0;delay(10);dis_play(0xce);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);bufdata=date&0x70;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcd);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);date=ReadByte(0x42);bufdata=date&0x0f;zlg7289_cs=0;open_date=ReadByte(0x42);delay(10);dis_play(0xcc);delay(10);dis_play(bufdata); zlg7289_cs=1;delay(70);bufdata=date&0x30;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcb);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);}****************按键处理程序********************* key=47: 闪烁移位键(shift)key=39: +key=39: -key=31: 确认键(ok)void key_int() interrupt 0{keyint=1;}void keyexe(){uchar i;uchar temp=0;uchar aa;p06=1;EX0=1;zlg7289_cs =0;delay(10);dis_play(0x15); *写入读键盘数据指令* delay(10);for(i=0;i<8;i++){temp=temp<<1;aa=zlg7289_dio; 按位或if(aa==1) 读数据位,接收的数据位放入retc中 temp=temp+1;zlg7289_clk=1;delay(10); 延时zlg7289_clk=0;}zlg7289_cs=1;key=temp;if(key==47) 闪烁移位键{key=0;keyok=0;EX0=0;if(num==8){num=1;}elsenum++;switch(num){ case 1: {display(0x88fe);}break; case 2: {display(0x88fd);}break; case 3: {display(0x88fb);}break; case 4: {display(0x88f7);}break; case 5: {display(0x88ef);}break; case 6: {display(0x88df);}break; case 7: {display(0x88bf);}break;case 8: {display(0x887f);}break;default: ;}EX0=1;}else if(key==39) +{EX0=0;key=0;RESWDI();if(num!=0){keyok=0;keychange=1;RESWDI();}if(num==7) 在分个位上{if((buf[3]&0x0f)==0x09) buf[3]&=0xf0; else ++buf[3];EX0=1;bufdata=buf[3]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xce);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);RESWDI();}else if(num==6) 在分十位上{if((buf[3]&0xf0)==0x50) buf[3]&=0x0f; else buf[3]+=0x10;EX0=1;bufdata=buf[3]&0x70;bufdata=bufdata>>4;bufdata=bufdata&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcd);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);RESWDI();}else if(num==5) 在小时个位上{if((buf[4]&0xf0)==0x20){if((buf[4]&0x0f)>0x02) buf[4]&=0xf0;else ++buf[4];}else if((buf[4]&0x0f)==0x09) buf[4]&=0xf0; else ++buf[4];EX0=1;bufdata=buf[4]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcc);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);RESWDI();}else if(num==4) 在小时十位上{if((buf[4]&0x0f)>0x03){if((buf[4]&0xf0)>0x00) buf[4]&=0x0f;else buf[4]+=0x10;}else if((buf[4]&0xf0)==0x20) buf[4]&=0x0f; else buf[4]+=0x10;EX0=1;bufdata=buf[4]&0x30;bufdata=bufdata>>4;bufdata&=0x0f;zlg7289_cs=0;RESWDI();delay(10);dis_play(0xcb);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);RESWDI();}else if(num==1) 在日的个位上{if((buf[5]&0xf0)==0x30){if((buf[5]&0x0f)>0x00) buf[5]&=0xf0;else ++buf[5];}else if((buf[5]&0x0f)==0x09) buf[5]&=0xf0; else ++buf[5];EX0=1;bufdata=buf[5]&0x0f;RESWDI();zlg7289_cs=0;delay(10);dis_play(0xc8);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);RESWDI();}else if(num==2) 在日的十位上{if((buf[5]&0x0f)>0x01){if((buf[5]&0xf0)>0x10) buf[5]&=0x0f;else buf[5]+=0x10;}else if((buf[5]&0xf0)==0x30) buf[5]&=0x0f; else buf[5]+=0x10;EX0=1;bufdata=buf[5]&0x30;RESWDI();bufdata=bufdata>>4;bufdata&=0x0f;zlg7289_cs=0;delay(10);dis_play(0xc9);delay(10);dis_play(bufdata);delay(70);RESWDI();}else if(num==4) 在月个位上{if((buf[7]&0xf0)==0x10){if((buf[7]&0x0f)>0x01) buf[7]&=0x0f;else ++buf[7];}else if((buf[7]&0x0f)==0x09) buf[7]&=0xf0; else ++buf[7];EX0=1;bufdata=buf[7]&0x0f;RESWDI();zlg7289_cs=0;delay(10);dis_play(0xca);delay(10);dis_play(bufdata);delay(70);RESWDI();}else if(num==8) 在月十位上{if((buf[7]&0x0f)>0x02){buf[7]&=0x0f;}else if((buf[7]&0xf0)==0x10) buf[7]&=0x0f; else buf[7]+=0x10;EX0=1;bufdata=buf[7]&0x10;bufdata=bufdata>>4;bufdata&=0x0f;RESWDI();zlg7289_cs=0;delay(10);dis_play(0xcf);delay(10);dis_play(bufdata); *显示月十位*delay(70);RESWDI();}else ;}else if(key==15) 存放断电时间,数据存放于5045中,每按键数据减少一 {display_x5045();EX0=1;key=0;if(num!=0){keyok=0;keychange=1;}if(num==7) 在分个位上{date=ReadByte(0x40);if((date&0x0f)==0x00) date|=0x09;EX0=1;bufdata=date&0x0f;zlg7289_cs=0;delay(10);dis_play(0xce);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);WriteByte(date,0x40);}else if(num==6) 在分十位上{date=ReadByte(0x40);if((date&0xf0)==0x00) date|=0x50; else date-=0x10;EX0=1;bufdata=date&0x70;bufdata=bufdata>>4;bufdata&=0x0f;zlg7289_cs=0;dis_play(0xcd);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);WriteByte(date,0x40);}else if(num==5) 在小时个位上{date=ReadByte(0x42);if((date&0xf0)==0x20){if(((date&0x0f)==0x00)||((date&0x0f)>0x03)) date|=0x03;else --date;}else if((date&0x0f)==0x00) date|=0x09;else --date;EX0=1;bufdata=date&0x0f;zlg7289_cs=0;delay(10);dis_play(0xcc);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);WriteByte(date,0x42);}else if(num==4) 在小时十位上{date=ReadByte(0x42);if((date&0x0f)>0x03){if((date&0xf0)==0x00) date|=0x10; else date-=0x10;}else if((date&0xf0)==0x00) date|=0x20; else date-=0x10;EX0=1;bufdata=date&0x30;bufdata=bufdata>>4;bufdata&=0x0f;zlg7289_cs=0;delay(10);dis_play(0xcb);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);WriteByte(date,0x42);}else if(num==1) 在日的个位上{if((buf[5]&0xf0)==0x30){if(((buf[5]&0x0f)==0x00)||((buf[5]&0x0f)>0x01)) buf[5]|=0x01;else --buf[5];}else if((buf[5]&0x0f)==0x00) buf[5]|=0x09;else --buf[5];bufdata=buf[5]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xc8);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);}else if(num==2) 在日的十位上{if((buf[5]&0x0f)>0x01){if((buf[5]&0xf0)==0x00) buf[5]|=0x20; else buf[5]-=0x10;}else if((buf[5]&0xf0)==0x00) buf[5]|=0x30; else buf[5]-=0x10;bufdata=buf[5]&0x30;bufdata=bufdata>>4;bufdata&=0x0f;zlg7289_cs=0;delay(10);dis_play(0xc9);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);}else if(num==3) 在月个位上{if((buf[7]&0xf0)==0x10){if(((buf[7]&0x0f)==0x00)||((buf[7]&0x0f)>0x01)) buf[7]|=0x01;else --buf[7];}else if((buf[7]&0x0f)==0x00) buf[7]|=0x09;else --buf[7];bufdata=buf[7]&0x0f;zlg7289_cs=0;delay(10);dis_play(0xca);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);}else if(num==8) 在月十位上{if((buf[7]&0x0f)>0x02) buf[7]&=0x0f;else if((buf[7]&0xf0)==0x00) buf[7]|=0x10; else buf[7]-=0x10;bufdata=buf[7]&0x10;bufdata=bufdata>>4;bufdata&=0x0f;zlg7289_cs=0;delay(10);dis_play(0xcf);delay(10);dis_play(bufdata);zlg7289_cs=1;delay(70);EX0=1;}else ;}else if(key==31) ok{EX0=0;key=0;keyok=1;num=0;if(keychange==1){keychange=0;p=buf;SUBA=0x00;buf[0]=0x00;buf[1]=0x00;buf[2]=0x00;ISendStr(SLA,SUBA,p);for (i=0;i<250;i++)delay(250);}else ;zlg7289_cs=0;delay(10);dis_play(0x88);delay(10);dis_play(0xff); *清LED闪烁* zlg7289_cs=1;delay(70);}EX0=1;}************************************************************ *******主函数************************************************ void main(){unsigned char i,j,word2;*程序的初始化**************************TMOD=0x09;TH0=0x3c;TL0=0xb0;TR0=1;ET0=1;IP=0x02; 中断优先级敲定IT0=1;EX0=1;IE0=1;EA=1; *打开全部中断*p10=1;p06=1;p07=1;WREN();WRSR();WRDI();WriteByte(0x07,0x40);在0x20中写入0x10,( 为了验证)WriteByte(0x0a,0x42);WRDI();word2=ReadByte(0x22);*初始化结束*********************************清LED显示**************************************** zlg7289_cs=0;delay(10);dis_play(0xA4); 复位zlg7289_cs=1;delay(70);*给时钟芯片附初值******************************** Stop_I2c();。

相关主题