|
第一届51测试网有奖征文精彩回放,深圳学林电子版权所有 www.51c51.com (专业51单片机学习网站)转载注明出处! 更多精彩稿件请访问论坛http://www.51c51.com/bbs/index.asp |
|
| 作品1:字符型液晶显示模块
作者联系: 邓杰 |
|
| 作品2:用8X8LED点阵屏循环显示 | 作品7:用虚拟IIC总线实现24C02
的应用
|
| 作品3:虚拟仿真仪器UMPS的应用:牛余朋 |
作品8:语音编程、远程控制试验板
社区乞丐 |
| 作品4:万年历数字钟及可调时钟系统 |
作品9:定时开关控制器的制作 |
|
万年历数字钟及可调时钟系统 一、
引言 万年历数字钟是一种用万年历时钟芯片实现年、月、日、时、分、秒计时,并通过单片机处理后送给显示芯片显示的装置,与机械式时钟相比具有更高的准确性和直观性,且具有更长的使用寿命。本系统还可以扩展为可调的自动开关,对家电对用电设备进行控制,笔者在随后改制成为可调时的自动断电的供电系统. 二、
原理图设计 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.h> #include <intrins.h> #include <math.h> #define uchar unsigned char /*宏定义*/ #define uint unsigned int uchar close_date,open_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.4S SCK=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; else SI = 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,则最低位为1 else Temp&=0xFE; } return Temp;;//这一个调试时没有执行,Temp的值总是0 zlg7289_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; else zlg7289_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; delay(70); 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); &n |