单片机实现电子钟主要有两种方案。一种是利用单片机内部的定时/计数器产生标准秒信号,并根据时间系统的进位原则进行加1调整。另一种方法是为单片机配置一片可独立走时的实时钟(RTC)芯片,单片机通过串行信号线与该芯片进行通信,读取或设置其当前时间。第一种方案结构简单、成本低,精度可满足多数场合的需要,因此,本设计采用第一种方案。 C语言源程序如下: #include <reg51.h> #include <string.h> //*****************************特殊功能寄存器声明******************** sfr P4SW = 0xBB; sfr P4 = 0xC0; //*******************************数据类型定义************************ typedef unsigned char uint8; typedef unsigned int uint16; typedef struct { char Hour; char Minute; char Second; } tsRTClock; //**********************************口线声明************************* #define LCD1602_DATA P0 sbit LCD1602_RS=P4^6; sbit LCD1602_EN=P4^5; sbit LCD1602_BL=P1^5; sbit KX0 =P3^2; sbit KX1 =P3^4; sbit KX2 =P3^5; sbit KX3 =P3^7; sbit BUZZ=P1^0; //******************************全局变量声明************************* tsRTClock RTClock; uint8 Timer50ms=0; //******************************函数原型声明************************* void SystemInit(); void LCD1602BLCtrl(uint8 OnOff); void LCD1602CmdWrite(uint8 cmdByte); void LCD1602DataWrite(uint8 DataByte); void LCD1602Init(); void LCD1602ClrScr(); void LCD1602DispChar(uint8 Row, uint8 Col, char Char); void LCD1602DispStr(uint8 Row, uint8 Col, char *Str); void RTClockInit(); void RTClockAdj(); void RTClockDisp(); uint8 key(); void Delay(uint16 ms); void AA(); void ZD (void); //*******************************中断服务函数************************ void T0ISR(void) interrupt 1 { TH0=(65536-45*1024)>>8; //设置50ms定时初值(对应晶振11.0592MHz) TL0=(65536-45*1024)&0xFF; if(++Timer50ms<20 ) return; Timer50ms=0; //已满1s,Timer50ms清0 RTClockAdj(); //时间调整 } //*********************************主函数**************************** void main() { SystemInit(); //系统初始化 RTClockInit(); //时钟初始化 LCD1602ClrScr(); LCD1602DispStr(0, 0, "What time is it ?"); //首行显示提示信息 while(1) { Delay(100); key(); ZD (); RTClockDisp(); //显示当前时间 } } //******************************系统初始化函数*********************** void SystemInit() { P4SW |= 0x70; //将P44-P46设为I/O口 TMOD=0x01; //T0用作定时器(方式1) TH0=(65536-45*1024)>>8; //设置50ms定时初值(对应晶振11.0592MHz) TL0=(65536-45*1024)&0xFF; TR0=1; //启动T0 ET0=1; //允许T0中断 EA=1; LCD1602BLCtrl(0); //背光点亮 LCD1602Init(); //LCD1602初始化 } //****************************LCD1602背光控制函数******************** void LCD1602BLCtrl(uint8 OnOff) //0:背光点亮,1:背光熄灭 { LCD1602_BL=OnOff; } //*****************************LCD1602写命令函数********************* void LCD1602CmdWrite(uint8 cmdByte) { uint16 i; for(i=100;i;i--); //适当延时(取代忙状态检测) LCD1602_RS=0 ; LCD1602_EN=1; LCD1602_DATA=cmdByte; //发送命令字节 LCD1602_EN=0; } //*****************************LCD1602写数据函数********************* void LCD1602DataWrite(uint8 DataByte) { uint16 i; for(i=100;i;i--); //适当延时(取代忙状态检测) LCD1602_RS=1; LCD1602_EN=1; LCD1602_DATA=DataByte; //发送数据字节 LCD1602_EN=0; } //*****************************LCD1602初始化函数********************* void LCD1602Init() { LCD1602_EN=0; LCD1602CmdWrite(0x38); //8位总线方式,两行显示,5*7点阵 LCD1602CmdWrite(0x0C); //LCD显示开,无光标 LCD1602CmdWrite(0x06); //写入一个数据字节后地址计数器自动加1 } //******************************LCD1602清屏函数********************** void LCD1602ClrScr() { LCD1602CmdWrite(0x01); //发送清屏命令 Delay(8); } //****************************LCD1602字符显示函数******************** void LCD1602DispChar(uint8 Row, uint8 Col, char Char) { if(Row) Col |= 0x40; //显示第二行时DDRAM地址从40H开始 Col |= 0x80; //拼为DDRAM地址设置命令 LCD1602CmdWrite(Col); //发送DDRAM地址 LCD1602DataWrite(Char); //发送ASCII码 } //***************************LCD1602字符串显示函数******************* void LCD1602DispStr(uint8 Row, uint8 Col, char *Str) { if(Row) Col |= 0x40; //显示第二行时DDRAM地址从40H开始 Col |= 0x80; //拼为DDRAM地址设置命令 LCD1602CmdWrite(Col); //发送DDRAM地址 while(*Str ) LCD1602DataWrite(*Str++); //连续发送每个字符的ASCII码 } //******************************电子钟初始化函数********************* void RTClockInit() { RTClock.Hour=23; RTClock.Minute=59; RTClock.Second=50; } //*****************************电子钟时间调整函数******************** void RTClockAdj() { if(++RTClock.Second<60 ) return; RTClock.Second=0; if(++RTClock.Minute<60 ) return; RTClock.Minute=0; if(++RTClock.Hour<24 ) return; RTClock.Hour=0; } //*****************************电子钟显示更新函数******************** void RTClockDisp() { if(RTClock.Hour>=10) LCD1602DispChar(1,4,0x30+RTClock.Hour/10); //显示Hour的十位 else LCD1602DispChar(1,4,' '); //Hour的十位为0则隐去 LCD1602DispChar(1,5,0x30+RTClock.Hour%10); //显示Hour的个位 LCD1602DispChar(1,6,':'); LCD1602DispChar(1,7,0x30+RTClock.Minute/10); //显示Minute的十位 LCD1602DispChar(1,8,0x30+RTClock.Minute%10); //显示Minute的个位 LCD1602DispChar(1,9,':'); LCD1602DispChar(1,10,0x30+RTClock.Second/10); //显示Second的十位 LCD1602DispChar(1,11,0x30+RTClock.Second%10); //显示Second的个位 } uint8 key() { if(KX0==0) { Delay(2); if(KX0==0) { EA=0; TH0=0; TL0=0; RTClock.Hour++; RTClock.Second=0; if(RTClock.Hour>=24) RTClock.Hour=0; while(!KX0); } EA=1; return RTClock.Hour; } if(KX1==0) { Delay(2); if(KX1==0) { EA=0; TH0=0; TL0=0; RTClock.Hour--; RTClock.Second=0; if(RTClock.Hour<0) RTClock.Hour=23; while(!KX1); } EA=1; return RTClock.Hour; } if(KX2==0) { Delay(2); if(KX2==0) { EA=0; TH0=0; TL0=0; RTClock.Minute++; RTClock.Second=0; if(RTClock.Minute>=60) RTClock.Minute=0; while(!KX2); } EA=1; return RTClock.Minute; } if(KX3==0) { Delay(2); if(KX3==0) { EA=0; TH0=0; TL0=0; RTClock.Minute--; RTClock.Second=0; if(RTClock.Minute<0) RTClock.Minute=59; while(!KX3); } EA=1; return RTClock.Hour; } } void AA() { int i=0; for(i=0;i<=RTClock.Hour;i++) { BUZZ=0; Delay(100); BUZZ=1; } } void ZD (void) { if(RTClock.Second==0&&RTClock.Minute==0) { { AA(); } } } //******************************软件延时函数************************* void Delay(uint16 ms) { uint16 i; do{ for(i=700;i;i--); //以1ms为延时单位 } while(--ms); }
|