这是我制作的LCD12864大数字万年历电子钟设计,用了一个多星期时间,经过实物实测,每月时间相差几分钟,这是时钟走时芯片决定的,温度显示还算正常。
附件里面包含单片机的源码,仿真图,PCB原档。请大家多多指教。
制作出来的实物图如下:
实物镇楼
主板图
PCB
simu图
单片机源程序如下:
[color=rgb(51, 102, 153) !important]复制代码
- #include<reg51.h>
- #include<intrins.h>
- //#include <stdio.h>
- #include<string.h>
- #define uchar unsigned char
- #define uint unsigned int
- /******LCD12864通讯端口******/
- sbit RS_LCD12864=P2^7; //定义选择显示类型, 定义数据或指令,1=数据,0=指令
- sbit RW_LCD12864=P2^6; //定义读写信号控制端, 定义读或写??1=读,??0=写
- sbit EN_LCD12864=P2^5; //定义数据锁存信号, 定义使能操作, 1=能,??0=否
- #define LCD12864_Databus P0 //定义数据端
- #define sc0 0
- #define sc1 1
- uchar DS1302_flag; //定义DS1302RAM中是否写数的标志位,第一次开机初始化后RAM中写1
- sbit CS1=P2^4; //定义选择芯片右半屏信号
- sbit CS2=P2^3; //定义选择芯片左半屏信号
- /*******DS1302时钟端口******/
- sbit SCLK=P2^0; //DS1302时钟口
- sbit DATA=P2^1; //DS1302串口数据
- sbit RST=P2^2; //DS1302的复位口
- uchar code Write_RTC_ADDR[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
- uchar code READ_RTC_ADDR[7] ={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
- uchar TIME[7]={0, 0x38, 0x15, 0x25, 0x05, 0x03, 0x22};
- //2022年5月25星期3,15:38:00
- /***********DS18B20**********/
- sbit DQ=P3^7; //温度探测口
- uchar fb; //文字反白标示
- uint tempvalue; //温度值,此变量若为uchar则温度不正常
- uchar Temp_Flag; //温度正负标志
- /***************按键端口************/
- sbit set =P1^0; //设置
- sbit key1=P1^1; //加
- sbit key2=P1^2; //减
- sbit key3=P1^3; //蜂鸣开关
- sbit key4=P1^4; //背光开关
- uchar setflag=0; //按键计数,决定调节的日期和时间
- sbit bl=P3^5; //背光控制端
- sbit bz=P3^6; //蜂鸣控制端
- uchar tempa; //调时
- /*********农历部分********/
- #define uint unsigned int
- char sec,min,hour,day,month,year,week; //定义全局变量,秒,分,时,日,月,年和星期
- bit century; //世纪位标志,0为20世纪,1为19世纪
- uchar sec_count=0;//定义定时计数,当sec_count==20时,秒加一
- code uchar table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
- code uchar day_code1[9]={0x0,0x1f,0x3b,0x5a,0x78,0x97,0xb5,0xd4,0xf3};//月份数据表
- code uint day_code2[3]={0x111,0x130,0x14e};
- bit c_moon;
- data uchar year_moon,month_moon,day_moon;
- /*******函数声明********/
- //读取数据表中农历月的大月或小月,如果该月为大返回1,为小返回0
- bit get_moon_day(uchar month_p,uint table_addr);
- void Conversion(bit c,char year,char month,char day);//输入阳历数据,输出阴历数据
- void Conver_week(); //输入BCD阳历数据,输出BCD星期数据
- void delay_ms(uint x);
- void Lcd12864_BusyChk();
- void set_xy(unsigned char x,unsigned char y);
- /*************************************************************************************
- * 函数名称 :Conversion
- * 功能描述 :输入阳历数据,输出阴历数据(只允许1901-2099年)
- * 参数 :参数名称
- ****************************************************************************************/
- void Conversion(bit c,char year,char month,char day)
- {
- //c=0 为21世纪,c=1 为19世纪 输入输出数据均为BCD数据
- uchar temp1,temp2,temp3,month_p;
- uint temp4,table_addr;
- bit flag2,flag_y;
- //定位数据表地址
- if(c==0) { table_addr=(year+0x64-1)*0x3; }
- else { table_addr=(year-1)*0x3; }
- //定位数据表地址完成
- //取当年节日所在的公历月份
- temp1=year_code[table_addr+2]&0x60;
- temp1=_cror_(temp1,5);
- //取当年节日所在的公历月份完成
- //取当年节日所在的公历日
- temp2=year_code[table_addr+2]&0x1f;
- //取当年节日所在的公历日完成
- // 计算当年春年离当年元旦的天数,节日只会在公历1月或2月
- if(temp1==0x1) { temp3=temp2-1; }
- else { temp3=temp2+0x1f-1; }
- // 计算当年春年离当年元旦的天数完成
- //计算公历日离当年元旦的天数,为了减少运算,用了两个表
- //day_code1[9],day_code2[3]
- //如果公历月在九月或前,天数会少于0xff,用表day_code1[9],
- //在九月后,天数大于0xff,用表day_code2[3]
- //如输入公历日为8月10日,则公历日离元旦天数为day_code1[8-1]+10-1
- //如输入公历日为11月10日,则公历日离元旦天数为day_code2[11-10]+10-1
- if (month<10){ temp4=day_code1[month-1]+day-1; }
- else { temp4=day_code2[month-10]+day-1;}
- if ((month>0x2)&&(year%0x4==0)&&(year!=0))
- temp4+=1; //如果公历月大于2月并且该年的2月为闰月,天数加1
- //计算公历日离当年元旦的天数完成
- //判断公历日在节日前还是节日后
- if (temp4>=temp3)
- { //公历日在节日后或就是节日当日使用下面代码进行运算
- temp4-=temp3;
- month=0x1;
- month_p=0x1; //month_p为月份指向,公历日在节日前或就是节日当日month_p指向首月
- flag2=get_moon_day(month_p,table_addr); //检查该农历月为大小还是小月,大月返回1,小月返回0
- flag_y=0;
- if(flag2==0)temp1=0x1d; //小月29天
- else temp1=0x1e; //大小30天
- temp2=year_code[table_addr]&0xf0;
- temp2=_cror_(temp2,4); //从数据表中取该年的闰月月份,如为0则该年无闰月
- while(temp4>=temp1)
- {
- temp4-=temp1;
- month_p+=1;
- if(month==temp2)
- {
- flag_y=~flag_y;
- if(flag_y==0)month+=1;
- }
- else month+=1;flag2=get_moon_day(month_p,table_addr);
- if(flag2==0)temp1=0x1d;
- else temp1=0x1e;
- }
- day=temp4+1;
- }
- else
- { //公历日在节日前使用下面代码进行运算
- temp3-=temp4;
- if (year==0x0){year=0x63;c=1;}
- else year-=1;
- table_addr-=0x3;
- month=0xc;
- temp2=year_code[table_addr]&0xf0;
- temp2=_cror_(temp2,4);
- if (temp2==0)month_p=0xc;
- else month_p=0xd;
- //month_p为月份指向,如果当年有闰月,一年有十三个月,月指向13,无闰月指向12
- flag_y=0;
- flag2=get_moon_day(month_p,table_addr);
- if(flag2==0)
- temp1=0x1d;
- else
- temp1=0x1e;
- while(temp3>temp1)
- {
- temp3-=temp1;
- month_p-=1;
- if(flag_y==0)month-=1;
- if(month==temp2)flag_y=~flag_y;
- flag2=get_moon_day(month_p,table_addr);
- if(flag2==0)temp1=0x1d;
- else temp1=0x1e;
- }
- day=temp1-temp3+1;
- }
- c_moon=c;
- //原程序这里又把这得出的值变回BCD码。但根据我后面的程序,我不需要。
- //后面用来显示的农历信息的程序分别是disp_t3()和disp_t4()
- year_moon=year;
- month_moon=month;
- day_moon=day;
- }
- /******************************************************************************************
- * 函数名称 :Conver_week
- * 功能描述 :输入BCD阳历数据,输出BCD星期数据(只允许1901-2099年)
- * 参数 : 参数名称: 输入/输出? 类型 描述
- * 算法:日期+年份+所过闰年数+月较正数之和除7 的余数就是星期,但如果是在
- 闰年又不到3 月份上述之和要减一天再除7星期数。余数为0时,为星期天。
- ******************************************************************************************/
- void Conver_week()
- {//century=0 为21世纪,c=1 为19世纪 输入输出数据均为BCD数据
- uchar temp1,temp2;
- temp1=year;
- if (century==0){temp1=temp1+100;} //如果为21世纪,年份数加100
- temp2=temp1/4; //所过闰年数只算1900年之后的
- temp2=temp1+temp2;
- temp2=temp2%7; //为节省资源,先进行一次取余,避免数大于255
- temp2=temp2+day+table_week[month-1];
- if (year%0x4==0&&month<3)temp2-=1;
- week=temp2%7;
- }
- /********************************LCD12864部分*****************************/
- /******************************************************************************
- 端口说明:EN_LCD12864:使能端锁存信号。RS_LCD12864:D/I 命令/数据寄存器选择端
- RW_LCD12864:读写信号控制端. define LCD_Data P0
- RS(D/I)=1,表示DB0-DB7显示数据; RS(D/I)=0,表示DB0-DB7显示指令数据
- RW=1,E=1,数据被读到DB7-DB0;RW=0,E=1--0,数据被读到DB7-DB0;数据被
- 写到IR或DR。
- (定义数据端口为 LCD_Data为P0口)
- RST_LCD12864复位端口,不接或接高电平,低电平会导致复位
- 函数名称:void Lcd12864_BusyChk()
- 函数功能:忙检测,若忙则等待,最长等待时间为60MS(此判忙函数经过仿真已OK)
- 入口参数:无
- 出口参数:无
- ***********************************************************************/
- void Lcd12864_BusyChk()
- {
- LCD12864_Databus=0x00;
- EN_LCD12864=0;
- RS_LCD12864=0;
- RW_LCD12864=1;
- EN_LCD12864=1;
- while(LCD12864_Databus&0x80); //判断P0.7口
- _nop_();
- EN_LCD12864=0;
- }
- /**********************************************************************
- * 函数名称: void set_xy(unsigned char x,unsigned char y)
- (仿真时的函数,已OK)
- * 功能描述: 设置显示位置! X,Y分别代表横纵坐标!
- * 输入参数?: unsigned char x,unsigned char y
- *输出参数: 无??
- *参数名称:? ?//列地址赋值,?x取值0----64,页面地址赋值,y取值0----8
- ***********************************************************************/
- /*void set_xy(unsigned char x,unsigned char y)
- {
- if (x>=64)
- {CS1=1;CS2=0;} //选择右半屏
- else
- {CS1=0;CS2=1;} //选择左半屏
- Lcd12864_BusyChk();
- RS_LCD12864=0;
- RW_LCD12864=0;
- LCD12864_Databus=0x40|x; //列地址赋值,x取值0----64
- EN_LCD12864=1;
- EN_LCD12864=0;
- Lcd12864_BusyChk();
- RS_LCD12864=0;
- RW_LCD12864=0;
- LCD12864_Databus=0xb8|y; //页面地址赋值,y取值0----8
- EN_LCD12864=1;
- EN_LCD12864=0;
- LCD12864_Databus=0xff;
- }*/
- /**********************************************************************
- * 函数名称: void set_xy(unsigned char x,unsigned char y)
- (实物时的函数,已OK)
- * 功能描述: 设置显示位置! X,Y分别代表横纵坐标!
- * 输入参数?: unsigned char x,unsigned char y
- *输出参数: 无??
- *参数名称:? ?//列地址赋值,?x取值0----64,页面地址赋值,y取值0----8
- ***********************************************************************/
- /***************************************************************************************
- 函数名称:void LCD12864_DispYanYueRi(fb)
- 函数功能:设置日期的时候,选中的参数可以反白显示,右屏第一行显示程序,
- 由which决定第几个参数黑显,如which==1时,年份黑显.
- 入口参数:无
- 出口参数:无
- ****************************************************************************************/
- void LCD12864_DispYanYueRi(fb) //显示年月日
- {
- if(century) //如果世纪为1
- {
- Lcd12864_DispChar(0,0,1,0); //则第1行第1列显示’1”
- Lcd12864_DispChar(1,0,9,0); //则第1行第2列显示’9”
- }
- else
- {
- Lcd12864_DispChar(0,0,2,0); //则第1行第1列显示’2”
- Lcd12864_DispChar(1,0,0,0); //则第1行第2列显示’0”
- }
- Lcd12864_DispChar(2,0,year/10,fb); //第1行第3列显示年十位数
- Lcd12864_DispChar(3,0,year%10,fb); //第1行第4列显示年个位数
- Lcd12864_DispChar(4,0,11,0); //第1行第5列显示第11个数据”-”
- Lcd12864_DispChar(5,0,month/10,fb); //第1行第6列显示月十位数
- Lcd12864_DispChar(6,0,month%10,fb); //第1行第7列显示月个位数
- Lcd12864_DispChar(7,0,11,0); //第1行第8列显示第11个数据”-”
- Lcd12864_DispChar(8,0,day/10,fb); //第1行第9列显示日十位数
- Lcd12864_DispChar(9,0,day%10,fb); //第1行第10列显示日个位数
- }
- /******************************************************************************
- 函数名称:void LCD12864_DispShiFenMiao(fb) 显示时分秒
- 函数功能:设置时间的时候,选中的参数可以反白显示,右屏第二行显示程序,
- 由which决定第几个参数黑显,如which==1时,年份黑显.
- 入口参数:无
- 出口参数:无
- ***********************************************************************/
- void LCD12864_DispShiFenMiao(fb)
- {
- Lcd12864_DispDaShuZi(0,2,hour/10,fb); //第2行第1列显示小时十位数
- Lcd12864_DispDaShuZi(2,2,hour%10,fb); //第2行第3列显示小时个位数
- if(sec%2==0) //如果秒为奇数则显示冒号,否则显示空
- Lcd12864_DispDaShuZi(4,2,10,0); //第2行第4列第10个大数字为显示冒号,不反白
- else
- Lcd12864_DispDaShuZi(4,2,11,0); //第2行第4列第11个大数字为显示空,不反白
- Lcd12864_DispDaShuZi(6,2,min/10,fb); //第2行第6列显示分钟的十位数
- Lcd12864_DispDaShuZi(8,2,min%10,fb); //第2行第8列显示分钟的个位数
- if(sec%2==0)
- Lcd12864_DispDaShuZi(10,2,10,0); //第2行第10列第10个大数字为显示冒号,不反白
- else
- Lcd12864_DispDaShuZi(10,2,11,0); //第2行第10列第11个大数字为显示空,不反白
- Lcd12864_DispDaShuZi(12,2,sec/10,fb); //第2行第10列显示秒钟的十位数
- Lcd12864_DispDaShuZi(14,2,sec%10,fb); //显示秒钟
- }
- /******************************************************************************
- 函数名称:void LCD12864_DispWeek(fb) 显示 周
- 函数功能:设置时间的时候,选中的参数可以反白显示,右屏第一行显示程序,
- 由which决定第几个参数黑显,如which==1时,年份黑显.
- 入口参数:无
- 出口参数:无
- *******************************************************************************/
- void LCD12864_DispWeek()
- {
- uchar temp;
- Lcd12864_DispWord(12,0,27,0); //第1行第13列第27个汉字为显示“周”字
- Conver_week(); //调用星期阳历数据
- if(week)
- temp=week; //赋week值为1----6,用来显示“一”----“六”
- else
- week=26; //赋week值为0时,赋值26用来显示“日”
- Lcd12864_DispWord(14,0,week,0); //第1行第14列第26个汉字为显示“日”
- //verify_time(); //此函数加上才能得到十二生肖后的农历日期
- ReadTime_DS1302();
- GetTime_DS1302();
- Conversion(century,year,month,day); //得到农历日期
- if(c_moon)
- temp=year_moon%12+12; //如果是19世纪,则可直接得出年的属性
- else
- temp=(year_moon+100)%12+12; //农历年也过了世纪,则农历年加100再求属性
- Lcd12864_DispWord(0,6,temp,1); // 第7行第1列 显示十二生肖属性
- }
- /******************************************************************************
- 函数名称:void LCD12864_DispNongLiRiQi(fb) 显示 农历日期
- 函数功能:第4行农历显示
- 由which决定第几个参数黑显,如which==1时,年份黑显.
- 入口参数:无
- 出口参数:无
- *******************************************************************************/
- void LCD12864_DispNongLiRiQi()
- {
- uchar temp1,temp2;
- // verify_time();
- Conversion(century,year,month,day); //得到农历日期,世纪年月日
- temp1=month_moon/10;
- temp2=month_moon%10;
- //月份高位为0,低位为1,则月份前位显"正",后位显示“月”字
- if(temp1==0&&temp2==1) //如果农历日期,月的十位为0,个位为1,则为正月
- {
- Lcd12864_DispWord(2,6,39,0); //第7行第3列 显示”正”字
- Lcd12864_DispWord(4,6,25,0); //第7行第5列 显示”月”字
- }
- //月份高位为0,低位都大于等于2,则月份高位显月份,后位显月。
- else if (temp1==0&&temp2>=2)
- {
- Lcd12864_DispWord(2,6,temp2,0);//第7行第3列 显示月份
- Lcd12864_DispWord(4,6,25,0); //第7行第5列 显示”月”字
- }
- //月份高位为1,低位为0,则月份高位显“十”,后位显月。
- else if (temp1==1&&temp2==0)
- {
- Lcd12864_DispWord(2,6,0,0); //第7行第3列 显示”十”字
- Lcd12864_DispWord(4,6,25,0); //第7行第5列 显示”月”字
- }
- //月份高位为1,低位为1,则月份高位显“冬”,后位显月。
- else if (temp1==1&&temp2==1)
- {
- Lcd12864_DispWord(2,6,29,0); //第7行第3列 显示”冬”字
- Lcd12864_DispWord(4,6,25,0); //第7行第5列 显示”月”字
- }
- else
- {
- Lcd12864_DispWord(2,6,30,0); //第7行第3列 显示”腊”字
- Lcd12864_DispWord(4,6,25,0); //第7行第5列 显示”月”字
- }
- temp1=day_moon/10;
- temp2=day_moon%10;
- switch (temp1)
- { //日期高位为0,则前位显“初”
- case 0: Lcd12864_DispWord(6,6,10,0); //第7行第7列 显示”初”字
- Lcd12864_DispWord(8,6,temp2,0);//第7行第9列 显示低位字
- break;
- //日期高位为1且低位不为0,则前位显“十”
- case 1: if(temp2)
- {
- Lcd12864_DispWord(6,6,0,0); //第7行第7列 显示”十”字
- Lcd12864_DispWord(8,6,temp2,0); //第7行第9列 显示低位农历月份
- break;
- }
- else //日期高位为1且低位为0,则显“初十”
- {
- Lcd12864_DispWord(6,6,10,0); //第7行第7列 显示”初”字
- Lcd12864_DispWord(8,6,0,0); //第7行第9列 显示”十”字
- break;
- }
- //日期高位为2,则前位显“廿”
- case 2: Lcd12864_DispWord(6,6,11,0); //第7行第7列 显示”"廿"字
- Lcd12864_DispWord(8,6,temp2,0); //第7行第9列 显示低位农历月份
- break;
- //日期高位为3,则前位显“三”,低位显“十”
- case 3: Lcd12864_DispWord(6,6,3,0); //第7行第7列 显示”三”字
- Lcd12864_DispWord(6,6,temp2,0); //第7行第9列 显示低位农历月份
- break;
- }
- }
- /*主程序*/
- void main(void)
- {
- bl=0;
- lcd12864_Init(); //液晶初始化,此函数只起清屏的作用
- century=0; //对世纪赋初值
- Conversion(century,year,month,day);
- DS1302_flag=Read_DS1302(0xc0); //读取首地址中的值,0xc0为DS1302RAM的首地址,
- //偶数为写,奇数读
- if(DS1302_flag==0)
- {
- Init_DS1302(); //时间初始化,起决于当前显示的时间
- Write_DS1302(0xc0,1);
- }
- while(1)
- {
- ReadTime_DS1302(); //读出时钟数据
- GetTime_DS1302(); //将BCD时钟数据转换为可以计算的HEX数据
- GetTemp();
- Lcd12864_DispTemp();
- KeyScan();
- if(setflag==0)
- {
- Lcd12864_DispTime(); //这里调用显示时间程序
- }
- }
- }
[color=rgb(51, 102, 153) !important]复制代码