本帖最后由 e来e去 于 2023-4-25 18:58 编辑

这是我制作的LCD12864大数字万年历电子钟设计,用了一个多星期时间,经过实物实测,每月时间相差几分钟,这是时钟走时芯片决定的,温度显示还算正常。
附件里面包含单片机的源码,仿真图,PCB原档。请大家多多指教。

制作出来的实物图如下:
image.png

实物镇楼

image.png

主板图

image.png

PCB

image.png

simu图

单片机源程序如下:

  • #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]复制代码