/*
模块名:学校实时时钟
创建人:XIAORUILAI 日期:2010-12-3
修改人:
功能描述:设计基于51单片机的实时时钟;具备调整日期\时间\星期功能;LCD(LM016L)显示
其它说明:
版 本:V1。0
*/
#include
#include
#include
#define HOME 0 //屏幕左上角
#define YEAR 1 //定义年在显示缓冲区位置指针
#define MONTH 2
#define DAY 3
#define WEAK 4
#define HOUR 5
#define MIN 6
#define SEC 7
#define OFF 0 //关
#define ON 1 //开
#define DISP_BUSY 0x80 //LCD忙命令
#define DISP_FUNC 0x38 //功能设置
#define DISP_ENTRY 0x06 //
#define DISP_CNTL 0x08 //显示控制
#define DISP_ON 0x04 //开显示
#define DISP_CURSOR 0x02 //光标开关
#define DISP_CLEAR 0x01 //清缓冲
#define DISP_HOME 0x02 //光标左上角
#define DISP_POS 0x80 //显示缓冲区地址设置
#define DISP_LINE2 0x40 //第二行控制
#define NUM_BEEL 5 //作息时间表记录个数
unsigned char t=0,sec=58,min=59,hour=9,year=0,year1,mon=2,day=28,weak=1;
unsigned char code *weak1[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
unsigned char days_of_month[12]={31,28,31,30,31,30,31,31,30,31,30,31};
unsigned int code table_of_time[NUM_BEEL]={360,420,480,600,720};
//作息时间表,折合(单位)为分钟
unsigned char code field_pos[7]={0x82,0x85,0x88,0x8b,0xc0,0xc3,0xc6};
//显示缓冲区地址
unsigned char delaytime=0,bell_on=OFF,no;
#define REG0 XBYTE[0X0000] //LCD写命令地址
#define REG1 XBYTE[0X0001] //LCD读命令(状态)地址
#define REG2 XBYTE[0X0002] //LCD写数据地址
#define REG3 XBYTE[0X0003] //LCD读数据地址
unsigned char bdata busyflag; //LCD忙标志
unsigned char dat,datn,disp_updata=1,cur_field,set_mode;
unsigned char word1[16]={"2010-11-25 Thu"}; //显示缓冲区
unsigned char word2[16]={"09:59:58"}; //显示缓冲区
sbit busyflag_7=busyflag^7;
sbit SET=P1^0; //调整方式切换键
sbit SELECT=P1^1; //日期时间调整键
sbit BELL_CNTL=P1^7; //控制响铃
void busy() //测试LCD忙?
{
do
{
busyflag=REG1;
}while(busyflag_7);
}
void wrc(unsigned char wcon) //LCD写命令
{
busy();
REG0=wcon;
}
void wrd(unsigned char wdat) //LCD写数据
{
busy();
REG2=wdat;
}
void rdd() //LCD读数据
{
busy();
dat=REG3;
}
void lcdint() //LCD初始化
{
wrc(0x38);
wrc(0x01);
wrc(0x06);
wrc(0x0c);
}
void wrn(unsigned char word[]) //向LCD连续写N个数据
{
unsigned char i;
for(i=0;i<16;i++)
{
wrd(word[i]);
}
}
void time0() interrupt 1
{
TR0=OFF;
TH0=(65536-10000)/256; //装定时器0初值 20MS中断时间常数装截 晶振频率6MHZ
TL0=(65536-10000)%256;
TR0=ON;
if(++t==50)
{
t=0;
disp_updata=1; //刷新显示标志置位
if(++sec==60) //生成当前万年历(年、月、日、星期和时刻(时、分、秒)
{
sec=0;
if(++min==60)
{
min=0;
if(++hour==24)
{
hour=0;
if(++weak>6)
weak=0;
if(++day>days_of_month[mon-1])
{
day=1;
if(++mon==13)
{
mon=1;
year++;
}
}
}
}
}
}
}
void time1() interrupt 3 //响铃控制定时中断(响铃10秒)
{
TR1=OFF;
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
TR1=ON;
if(++delaytime==50)
{
delaytime=0;
bell_on=OFF;
BELL_CNTL=OFF; //延时到,关闭铃
ET1=OFF;
TR1=OFF;
}
}
void disp_time()
{
word2[6]=sec/10+0x30; //数字量转字符
word2[7]=sec%10+0x30;
word2[0]=hour/10+0x30;
word2[1]=hour%10+0x30;
word2[3]=min/10+0x30;
word2[4]=min%10+0x30;
word1[2]=year/10+0x30;
word1[3]=year%10+0x30;
word1[5]=mon/10+0x30;
word1[6]=mon%10+0x30;
word1[8]=day/10+0x30;
word1[9]=day%10+0x30;
word1[11]=*(weak1[weak]);
word1[12]=*(weak1[weak]+1);
word1[13]=*(weak1[weak]+2);
wrc(0x80); //显示输出
wrn(word1);
wrc(0xc0);
wrn(word2);
}
void bell_cntl()
{
if(!bell_on&&((hour*60+min)==table_of_time[no])
&&(sec<1)&&(no++<5)) //判作息时间表到?是,开启定时器1定时10S
{
ET1=ON;
TR1=ON; //起动定时器1,响铃10S
BELL_CNTL=ON;
bell_on=ON;
}
}
void incr_field() //当前域增1(调整日期\时间\星期
{
if(cur_field==YEAR)
{
if(++year>99)
year=0;
word1[2]=year/10+0x30;
word1[3]=year%10+0x30;
}
if(cur_field==MONTH)
{
if(++mon>12)
mon=1;
word1[5]=mon/10+0x30;
word1[6]=mon%10+0x30;
}
if(cur_field==DAY)
{
if(++day>31)
day=1;
word1[8]=day/10+0x30;
word1[9]=day%10+0x30;
}
if(cur_field==WEAK)
{
if(++weak>6)
weak=0;
word1[11]=*(weak1[weak]);
word1[12]=*(weak1[weak]+1);
word1[13]=*(weak1[weak]+2);
}
if(cur_field==HOUR)
{
if(++hour>23)
hour=0;
word2[0]=hour/10+0x30;
word2[1]=hour%10+0x30;
}
if(cur_field==MIN)
{
if(++min>59)
min=0;
word2[3]=min/10+0x30;
word2[4]=min%10+0x30;
}
if(cur_field==SEC)
{
if(++sec>59)
sec=0;
word2[6]=sec/10+0x30; //数字量转字符
word2[7]=sec%10+0x30;
}
}
void set_cursor(unsigned char mode,unsigned char field)
{ //设置指定域光标显示
unsigned char mask;
mask=DISP_CNTL|DISP_ON;
if(mode)
mask|=DISP_CURSOR;
wrc(mask);
if(field==HOME)
mask=DISP_HOME;
else
mask=DISP_POS|field_pos[field-1];
wrc(mask);
}
unsigned char reset_belltime_pos(void) //计算作息时间表指针
{
unsigned char index;
for(index=0;index { if((hour*60+min)<=table_of_time[index]) return index; } return 0; } void delay() //延时子程序 { ET1=OFF; TH1=(65536-50000)/256; TL1=(65536-50000)%256; TR1=ON; while(!TF1); TR1=OFF; } void main() { BELL_CNTL=OFF; lcdint(); wrc(0x80); wrn(word1); wrc(0xc0); wrn(word2); no=reset_belltime_pos(); TMOD=0x11; //设置定时器/计数器T0、T1为16位定时模式 TH1=(65536-50000)/256; //设置定时器1预置时间常数(100MS) TL1=(65536-50000)%256; TH0=(65536-10000)/256; //设置定时器1预置时间常数(20MS) 晶振频率6MHZ TL0=(65536-10000)%256; EA=ON; //开总中断 ET0=ON; //定时器0中断允许 ET1=OFF; //定时器1中断允许 TR0=ON; //起动定时器0 TR1=OFF; //关闭定时器1,适时起动 year1=year+2000; if((year1%4==0)&&(year1%100!=0)||(year1%100!=0)&&(year1%400==0)) days_of_month[1]=29; else //计算闰年 days_of_month[1]=28; while(1) {P1=P1|0x03; if(!SET) //检测设置键是否按下 delay(); //消除键抖动 if(!SET) //确认键按下 { while(!SET); //等待键释放 if(!set_mode) //测设置模式否 { set_mode=1; //置时间设置模式标志 cur_field=YEAR; set_cursor(ON,YEAR); } else { cur_field++; if(cur_field>SEC) //设置结束 { set_mode=0; //设置正常模式 no=reset_belltime_pos(); set_cursor(OFF,HOME); } else set_cursor(ON,cur_field); //当前显示域光标显示 } } if(set_mode&&!SELECT) //日期设置且按选择健调时 { delay(); if(!SELECT) while(!SELECT); incr_field(); disp_time(); set_cursor(ON,cur_field); } if(!set_mode) //若非设置模式\更新显示 if(disp_updata) { disp_time(); disp_updata=0; bell_cntl(); //测试是否响铃 } } }
已发送