基于51单片机的可掉电走表万年历设计

文章目录

  • 前言
    • 使用到的模块
      • 本项目声明的变量
        • 程序用到的初始化函数
      • LCD12864液晶代码
      • 独立按钮代码
      • DS18B20温度模块
      • DS1302时钟模块
      • main函数(包括中断服务函数)
  • 致谢
  • 参考文献

前言

之前跟随郭天祥老师学习51单片机的时候就很想实现掉电走表这个功能,奈何那时候跟随视频学的还是非常浅显,到了这个这期刚好老师需要做一个万年历所以接触到了DS1302时钟模块以至于用了差不多10天时间每天捉摸一点点模块拼凑,终于完成这个功能,虽还有瑕疵,恳请各位指出我的不足和漏洞

下面的是效果演示,采用锂电池供电

使用到的模块

  1. LCD12864液晶
  2. 独立按钮
  3. DS18B20温度模块
  4. DS1302时钟模块
  5. LM7805降压模块和18650锂电池

本项目声明的变量

本文以模块化的方式阐述如何一步步丰富功能,担心下面有些模块里面的变量名读者看不懂现在先将其全部写出来,读者可以看了下面的再回来看上面的变量声明

#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint  unsigned int
/*12864端口定义*/
#define LCD_data  P0//数据口
sbit LCD_RS  =  P3^5;            //寄存器选择输入
sbit LCD_RW  =  P3^6;            //液晶读/写控制
sbit LCD_EN  =  P3^4;            //液晶使能控制
sbit LCD_PSB =  P3^7;            //串/并方式控制
sbit beep=P2^3;                  //蜂鸣器
sbit LED=P1^0;                  //灯泡
sbit s1=P3^2;                   //设置按钮
sbit s2=P2^1;                   //+1按钮
sbit s3=P2^0;                   //-1按钮
//ds1302端口
sbit TSCLK = P1^1;//串行时钟输入端
sbit TIO   = P1^2;//双向输入线(数据线)
sbit TRST  = P1^3;//使能端
//温度的
sbit ds=P2^2;                   //温度传感器信号线
uint temp;
float f_temp;
void init_T1(void);//温度用的定时器
uchar code dis1[] = "20  --  --  周 ";
uchar  dis3[] = "室内温度:";
uchar code dis4[] = "开发作者:Ramsey   ";
//测试 开始
uchar code tab1[]={"时  分  秒"};
uchar num=0,i,h=23,m=00,s=00,hl,hr,ml,mr,sl,sr,s1num,n=1;
uint run,year=2021,month=11,day=13,y1,y2,y3,y4,mol,mor,dl,dr;
uchar testnum,Flag=0;
uchar week=0;
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
uchar IRDIS[2];
uchar IRCOM[4];
//变量定义
uchar count,s1num=0;
uchar testcount;
char miao,shi,fen;
void lcd_pos(uchar X,uchar Y);  //确定显示位置
void delay0(uchar x);  //x*0.14MS
void beepon();
void display();
void  dataconv();
void lcd_wcmd(uchar cmd);
void lcd_wdat(uchar dat);
//温度函数声明
void dsreset(void);
bit tmpreadbit(void);       //read a bit
bit tempreadbit(void);
uchar tempread(void);
void tempwritebyte(uchar dat);
void tempchange(void);
uint get_temp();
void tempdisplay();

程序用到的初始化函数

/*********************************************************/
/*                                                       */
/*              定时器尝试使用初始化                       */
/*         这里用了2个定时器,一个走表一个刷新温度                                             *//*********************************************************/
void init_T1(void)
{TMOD=0x11;TH0=(65536-50000)/256;TL0=(65536-50000)%256;TH1=(65536-50000)/256;TL1=(65536-50000)%256;EA=1;//开总中断ET0=1;//开定时器0中断ET1=1;TR0=1;//启动定时器0TR1=1;//启动定时器1//外部中断EX0=1;IT0=1;
}

LCD12864液晶代码

/*12864端口定义*/
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
#define LCD_data  P0//数据口
sbit LCD_RS  =  P3^5;            //寄存器选择输入
sbit LCD_RW  =  P3^6;            //液晶读/写控制
sbit LCD_EN  =  P3^4;            //液晶使能控制
sbit LCD_PSB =  P3^7;            //串/并方式控制
uchar IRDIS[2];
uchar IRCOM[4];
uchar code dis1[] = "20  --  --  周 ";//预留的空位是留来显示的
uchar  dis3[] = "室内温度:";
uchar code dis4[] = "开发作者:Ramsey   ";
uchar code tab1[]={"时  分  秒"};
/*******************************************************************/
/*                                                                 */
/*检查LCD忙状态                                                    */
/*lcd_busy为1时,忙,等待。lcd-busy为0时,闲,可写指令与数据。      */
/*                                                                 */
/*******************************************************************/
bit lcd_busy(){                          bit result;LCD_RS = 0;LCD_RW = 1;LCD_EN = 1;delayNOP();result = (bit)(P0&0x80);LCD_EN = 0;return(result); }/*******************************************************************/
/*                                                                 */
/*写指令数据到LCD                                                  */
/*RS=L,RW=L,E=高脉冲,D0-D7=指令码。                             */
/*                                                                 */
/*******************************************************************/
void lcd_wcmd(uchar cmd)
{                          while(lcd_busy());LCD_RS = 0;LCD_RW = 0;LCD_EN = 0;_nop_();_nop_(); P0 = cmd;delayNOP();LCD_EN = 1;delayNOP();LCD_EN = 0;
}
/*******************************************************************/
/*                                                                 */
/*写显示数据到LCD                                                  */
/*RS=H,RW=L,E=高脉冲,D0-D7=数据。                               */
/*                                                                 */
/*******************************************************************/
void lcd_wdat(uchar dat)
{                          while(lcd_busy());LCD_RS = 1;LCD_RW = 0;LCD_EN = 0;P0 = dat;delayNOP();LCD_EN = 1;delayNOP();LCD_EN = 0;
}
/*******************************************************************/
/*                                                                 */
/*  LCD初始化设定                                                  */
/*                                                                 */
/*******************************************************************/
void lcd_init()
{ LCD_PSB = 1;         //并口方式lcd_wcmd(0x34);      //扩充指令操作delay(5);lcd_wcmd(0x30);      //基本指令操作delay(5);lcd_wcmd(0x0C);      //显示开,关光标delay(5);lcd_wcmd(0x01);      //清除LCD的显示内容delay(5);//lcd_wcmd(0x06);      //清除LCD的显示内容delay(5);
}
/*********************************************************/
/*                                                       */
/* 设定显示位置                                          */
/*                                                       */
/*********************************************************/
void lcd_pos(uchar X,uchar Y)
{                          uchar  pos;if (X==0){X=0x80;}else if (X==1){X=0x90;}else if (X==2){X=0x88;}else if (X==3){X=0x98;}pos = X+Y ;  lcd_wcmd(pos);     //显示地址
}
/*******************************************************************/
/*                                                                 */
/*  LCD显示函数                                                  */
/*                                                                 */
/*******************************************************************/
void display(){uchar i;delay(10);lcd_pos(0,0);             //设置显示位置为第一行的第1个字符i = 0;while(dis1[i] != '\0'){                         //显示字符lcd_wdat(dis1[i]);i++;}//暂时把第二行的隔开来\//第二行的汉字
lcd_wcmd(0x92);
for(i=0;i<10;i++)
{
lcd_wdat(tab1[i]);
delay(10);
}
/*    lcd_pos(1,0);             //设置显示位置为第二行的第1个字符i = 0;while(dis2[i] != '\0'){lcd_wdat(dis2[i]);      //显示字符i++;}
*/
//把第三行业隐藏起来
/*lcd_pos(2,0);             //设置显示位置为第三行的第1个字符i = 0;while(dis3[i] != '\0'){lcd_wdat(dis3[i]);      //显示字符i++;}
*/lcd_pos(3,0);             //设置显示位置为第四行的第1个字符i = 0;while(dis4[i] != '\0'){lcd_wdat(dis4[i]);      //显示字符i++;}
}

上述是LCD12864的基础显示内容下面需要根据具体时间写数据到对应的位置,具体位置代码详情看LCD12864说明文档,下面不在赘述

/*******************************************************************/
/*                                                                 */
/*          计算时间变化,每次改变就要引用一次这个方法                                    */
/*                                                                 */
/*******************************************************************/
void change()        //时间变化
{
//时分秒的
hl=shi/10;
hr=shi%10;
ml=fen/10;
mr=fen%10;
sl=miao/10;
sr=miao%10;
//年月日的
y1=year/1000;
y2=year/100%10;
y3=year/10%10;
y4=year%10;
mol=month/10;
mor=month%10;
dl=day/10;
dr=day%10;
}
/*******************************************************************/
/*                                                                 */
/*          时间显示函数                                     */
/*                                                                 */
/*******************************************************************/
void display1()          //时间显示函数
{
change();
//测试开始
//年
lcd_wcmd(0x80);           //数字显示位置
lcd_wdat(y1+48);
delay1(10);
lcd_wdat(y2+48);
delay1(10);
lcd_wdat(y3+48);
delay1(10);
lcd_wdat(y4+48);
//月
lcd_wcmd(0x83);
lcd_wdat(mol+48);
delay1(10);
lcd_wdat(mor+48);
delay1(10);
//日
lcd_wcmd(0x85);
lcd_wdat(dl+48);
delay1(10);
lcd_wdat(dr+48);
delay1(10);//测试结束//时
delay1(10);
lcd_wcmd(0x91);
lcd_wdat(hl+48);
delay1(10);
lcd_wdat(hr+48);
//分
delay1(10);
lcd_wcmd(0x93);
lcd_wdat(ml+48);
delay1(10);
lcd_wdat(mr+48);
//秒
delay1(10);
lcd_wcmd(0x95);
lcd_wdat(sl+48);
delay1(10);
lcd_wdat(sr+48);
delay1(10);
//搞完复位
lcd_wcmd(0x97);
lcd_wdat(0x20);
lcd_wdat(0x20);
delay1(10);
}

上述代码就已经将年月日和时分秒的数据填入,现在需要根据年月日来计算今天是星期几
算法如下:
基姆拉尔森计算公式
W= (d+2m+3(m+1)/5+y+y/4-y/100+y/400) mod 7

在公式中d表示日期中的日数,m表示月份数,y表示年数。

注意:在公式中有个与其他公式不同的地方:
把一月和二月看成是上一年的十三月和十四月,例:如果是
2004-1-10则换算成:2003-13-10来代入公式计算。
以公元元年为参考,公元元年1月1日为星期一
基姆拉尔森计算公式引用地址

/*******************************************************************/
/*                                                                 */
/*          基姆拉尔森计算星期几                                    */
/*                                                                 */
/*******************************************************************/
int CaculateWeek(int y, int m, int d) {int tempweek;if (m == 1 || m == 2) {m += 12;--y;}tempweek = (d + 2 * m + 3 * (m + 1) / 5 + y + (y >> 2) - y / 100 + y / 400) % 7 + 1;//加一为了后面方便计算return tempweek;}
/*******************************************************************/
/*                                                                 */
/*          写入星期几到屏幕里面                                     */
/*                                                                 */
/*******************************************************************/
void displayweek()
{week=CaculateWeek(year,month,day);switch(week){case 1:lcd_wcmd(0x87);lcd_wdat(0XD2);lcd_wdat(0XBB);delay1(10);break;case 2:lcd_wcmd(0x87);lcd_wdat(0XB6);lcd_wdat(0XFE);delay1(10);break;case 3:lcd_wcmd(0x87);lcd_wdat(0XC8);lcd_wdat(0XFD);delay1(10);break;  case 4:lcd_wcmd(0x87);lcd_wdat(0XCB);lcd_wdat(0XC4);delay1(10);break;case 5:lcd_wcmd(0x87);lcd_wdat(0XCE);lcd_wdat(0XE5);delay1(10);break;    case 6:lcd_wcmd(0x87);lcd_wdat(0XC1);lcd_wdat(0XF9);delay1(10);break;    case 7:lcd_wcmd(0x87);lcd_wdat(0XC8);lcd_wdat(0XD5);delay1(10);break;            }
}

独立按钮代码

独立按钮的需要实现的功能是对时间进行调整,我用了3个按钮分别是1个设置按钮和2个用来加减时间的按钮,设置按钮将其优先级设置最高,只有设置按钮按下的时候,加减按钮才会有效,因此作者将设置按钮的代码放在外部中断,具体请看下面的代码

sbit s1=P3^2;                   //设置按钮
sbit s2=P2^1;                   //+1按钮
sbit s3=P2^0;                   //-1按钮
/*******************************************************************/
/*                                                                 */
/*         键盘检测函数                                                       */
/*                                                                 */
/*******************************************************************/
void keyScan(){LCD_RW=0;if(testnum!=0){if(s2==0)  {delay1(5);if(s2==0){   beepon();//testnum++;while(!s2);if(testnum==1){miao++;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x80,dat_to_BCD(miao));//秒write_DS1302(0x8e,0x80);//开写保护//if(miao==60)miao=0;change();//秒delay1(10);lcd_wcmd(0x95);lcd_wdat(sl+48);delay1(10);lcd_wdat(sr+48);lcd_wcmd(0x95);        }if(testnum==2){fen++;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x82,dat_to_BCD(fen));//分write_DS1302(0x8e,0x80);//开写保护delay1(100);//if(fen==60)fen=0;change();//分delay1(10);lcd_wcmd(0x93);lcd_wdat(ml+48);delay1(10);lcd_wdat(mr+48);lcd_wcmd(0x93);}if(testnum==3){shi++;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x84,dat_to_BCD(shi));//时write_DS1302(0x8e,0x80);//开写保护//if(shi==24)shi=0;change();//时delay1(10);lcd_wcmd(0x91);lcd_wdat(hl+48);delay1(10);lcd_wdat(hr+48);delay1(10);lcd_wcmd(0x91);}//年月日的加减if(testnum==4){day++;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x86,dat_to_BCD(day));//时write_DS1302(0x8e,0x80);//开写保护//if(day==31)day=1;change();//日delay1(10);lcd_wcmd(0x85);lcd_wdat(dl+48);delay1(10);lcd_wdat(dr+48);delay1(10);lcd_wcmd(0x85);//写星期displayweek();lcd_wcmd(0x85);}if(testnum==5){month++;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x88,dat_to_BCD(month));//时write_DS1302(0x8e,0x80);//开写保护//if(month==13)month=1;change();//月delay1(10);lcd_wcmd(0x83);lcd_wdat(mol+48);delay1(10);lcd_wdat(mor+48);delay1(10);lcd_wcmd(0x83);//写星期displayweek();lcd_wcmd(0x83);}if(testnum==6){year++;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x8c,dat_to_BCD(year%100));//年write_DS1302(0x8e,0x80);//开写保护//if(year==2040)year=2021;change();//年delay1(10);lcd_wcmd(0x81);            //数字显示位置lcd_wdat(y3+48);delay1(10);lcd_wdat(y4+48);lcd_wcmd(0x81);//写星期displayweek();lcd_wcmd(0x81);}//测试//displayweek();}}//后面减法的代码if(s3==0){delay1(5);if(s3==0){beepon();while(!s3);if(testnum==1){miao--;write_DS1302(0x8e,0);//清除写保护write_DS1302(0x80,dat_to_BCD(miao));//秒write_DS1302(0x8e,0x80);//开写保护if(miao==-1)miao=59;change();//秒delay1(10);lcd_wcmd(0x95);lcd_wdat(sl+48);delay1(10);lcd_wdat(sr+48);lcd_wcmd(0x95);}if(testnum==2){fen--;write_DS1302(0x8e,0);//清除写保护write_DS1302(0x82,dat_to_BCD(fen));//分write_DS1302(0x8e,0x80);//开写保护if(fen==-1)fen=59;change();//分delay1(10);lcd_wcmd(0x93);lcd_wdat(ml+48);delay1(10);lcd_wdat(mr+48);lcd_wcmd(0x93);}if(testnum==3){shi--;write_DS1302(0x8e,0);//清除写保护write_DS1302(0x84,dat_to_BCD(shi));//时write_DS1302(0x8e,0x80);//开写保护if(shi==-1)shi=23;change();//时delay1(10);lcd_wcmd(0x91);lcd_wdat(hl+48);delay1(10);lcd_wdat(hr+48);delay1(10);lcd_wcmd(0x91);}//测试开始if(testnum==4){day--;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x86,dat_to_BCD(day));//时write_DS1302(0x8e,0x80);//开写保护if(day==-1)day=30;change();//日delay1(10);lcd_wcmd(0x85);lcd_wdat(dl+48);delay1(10);lcd_wdat(dr+48);delay1(10);lcd_wcmd(0x85);//写星期displayweek();lcd_wcmd(0x85);}if(testnum==5){month--;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x88,dat_to_BCD(month));//时write_DS1302(0x8e,0x80);//开写保护if(month==-1)month=12;change();//月delay1(10);lcd_wcmd(0x83);lcd_wdat(mol+48);delay1(10);lcd_wdat(mor+48);delay1(10);lcd_wcmd(0x83);//写星期displayweek();lcd_wcmd(0x83);}if(testnum==6){year--;//write_DS1302(0x8e,0);//清除写保护write_DS1302(0x8c,dat_to_BCD(year%100));//年write_DS1302(0x8e,0x80);//开写保护if(year==2009)year=2040;change();//年delay1(10);lcd_wcmd(0x81);          //数字显示位置lcd_wdat(y3+48);delay1(10);lcd_wdat(y4+48);lcd_wcmd(0x81);//写星期displayweek();lcd_wcmd(0x81);}//测试结束}}}}/*********************************************************/
/*                                                       */
/*          外部中断函数                                   */
/*                                                       */
/*********************************************************/
void timer1() interrupt 0
{LCD_RW=0;if(s1==0)  {delay1(5);if(s1==0){   beepon();testnum++;while(!s1);if(testnum==1){Flag=1;display1();TR0=0;TR1=0;lcd_wcmd(0x95);lcd_wcmd(0x0f);}}if(testnum==2){display1();lcd_wcmd(0x93);}if(testnum==3){display1();lcd_wcmd(0x91);}/*if(testnum==4){Flag=0;display1();testnum=0;lcd_wcmd(0x0c);TR0=1;TR1=1;delay1(10);}*///继续测试if(testnum==4){display1();lcd_wcmd(0x85);}if(testnum==5){display1();lcd_wcmd(0x83);}if(testnum==6){display1();lcd_wcmd(0x81);}if(testnum==7){//退出的时候写时间函数//结束Flag=0;display1();testnum=0;lcd_wcmd(0x0c);TR0=1;TR1=1;delay1(10);}//继续结束}

DS18B20温度模块

DS18B20是常用的数字温度传感器,其输出的是数字信号,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。 [1] DS18B20数字温度传感器接线方便,封装成后可应用于多种场合,如管道式,螺纹式,磁铁吸附式,不锈钢封装式,型号多种多样,有LTM8877,LTM8874等等。
DS18B20温度模块资料来源于百度百科
本次项目需求: 需要在一定的频率内将实时温度的数值呈现到显示屏上,所以我将温度的变化封装在第二个定时器 上具体代码如下

/*******************************************************************/
/*                                                                 */
/* 18B20复位,初始化函数函数                                       */
/*                                                                 */
/*******************************************************************/
void dsreset(void)
{uint i;ds=0;i=103;while(i>0)i--;//提供480-950us延时ds=1;i=4; while(i>0)i--;//提供60-240us的延时;单片机检测到低电平复位成功
}
/*******************************************************************/
/*                                                                 */
/* 18B20读1位数据函数                                              */
/*                                                                 */
/*******************************************************************/
bit tempreadbit(void)
{uint i;bit dat;ds=0;i++;          //i++ 起延时作用ds=1;i++;i++;dat=ds;           //ds为低电平则读0反之则为1i=8;while(i>0)i--;   //提供延时return (dat);
}
/*******************************************************************/
/*                                                                 */
/* 18B20读1字节数据函数                                              */
/*                                                                 */
/*******************************************************************/
uchar tempread(void)
{uchar i,j,dat;dat=0;for(i=1;i<=8;i++){j=tempreadbit();dat=(j<<7)|(dat>>1);   //读出的数据最低位在最前面,这样刚好一个字节在DAT里}return(dat);
}
/*******************************************************************/
/*                                                                 */
/* 18B20写1字节数据函数                                              */
/*                                                                 */
/*******************************************************************/
void tempwritebyte(uchar dat)
{uint i;uchar j;bit testb;for(j=1;j<=8;j++){testb=dat&0x01;dat=dat>>1;if(testb)     //写 1{ds=0;i++;i++;ds=1;i=8;while(i>0)i--;}else{ds=0;       //写 0i=8;while(i>0)i--;ds=1;i++;i++;}}
}
/*******************************************************************/
/*                                                                 */
/* 18B20开始获取温度并转换                                         */
/*                                                                 */
/*******************************************************************/
void tempchange(void) {dsreset();delay(1);tempwritebyte(0xcc);  // 写跳过读ROM指令tempwritebyte(0x44);  // 写温度转换指令
}
/*******************************************************************/
/*                                                                 */
/* 18B20读取寄存器中存储的温度数据                                 */
/*                                                                 */
/*******************************************************************/
uint get_temp()
{uchar a,b;dsreset();delay(1);tempwritebyte(0xcc);tempwritebyte(0xbe);  //读取RAMa=tempread();         //读低8位b=tempread();         //读高8位temp=b;temp<<=8;            //两个字节组合为1个字temp=temp|a;f_temp=temp*0.0625;      //温度在寄存器中为12位 分辨率位0.0625°temp=f_temp*10+0.5;    //乘以10表示小数点后面只取1位,加0.5是四舍五入f_temp=f_temp+0.05; return temp;         //temp是整型
}
/*******************************************************************/
/*                                                                 */
/*              18B20温度显示                                      */
/*                                                                 */
/*******************************************************************/
void tempdisplay() //这里非常重要,位置是已经选好的,各位读者可仔细分析
{uint mm;tempchange();
mm=get_temp();
dis3[9]=mm%1000/100+'0';
dis3[10]=mm%100/10+'0';
dis3[11]='.';
dis3[12]=mm%10+'0';
lcd_wcmd(0x88);
for(i=0;i<14;i++){lcd_wdat(dis3[i]);delay(10);}
//搞完复位
//lcd_wcmd(0x01);
//delay1(10);}

DS1302时钟模块

DS1302时钟芯片是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。DS1302芯片包含一个用于存储实时时钟/日历的 31 字节的静态 RAM,可通过简单的串行接口与微处理器通讯,将当前的是时钟存于RAM。DS1302芯片对于少于 31 天的月份月末会自动调整,并会自动对闰年进行校正。由于有一个 AM/PM 指示器,时钟可以工作在 12 小时制或者 24小时制。
————————————————
版权声明:本文为CSDN博主「_会飞_的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文出处链接
DS1302时钟模块需求分析: 需要将模块内的时间显示在显示屏上,同时在用按钮调整时间的时候每调整一次加或减都要将数据写入到模块里面(上述按键检测有对应的代码),作者在设计的时候一开始犯得错误是调整完才进行写入导致不是想要的结果

/*******************************************************************/
/*                                                                 */
/*                  ds1302写一个字节                               */
/*                                                                 */
/*******************************************************************/
void write_DS1302(uchar cmd,uchar dat)//写一个字节
{uchar i ;TRST = 0;//拉低使能端TSCLK = 0;//拉低数据总线TRST = 1;//使能端产生上升沿for(i = 0; i < 8; i ++)//写命令{TSCLK = 0;TIO = cmd & 0x01;//最低位先传TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走cmd >>= 1;//右移1位} for(i = 0; i < 8;i ++)//写数据{TSCLK = 0;TIO = dat & 0x01;//最低位先传TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走dat >>= 1;//右移1位}TRST = 0;//拉低使能端,关闭读写功能
}
/*******************************************************************/
/*                                                                 */
/*                  ds1302读取数据                                */
/*                                                                 */
/*******************************************************************/
uchar read_DS1302(uchar cmd)//读取数据
{uchar i,dat;TRST = 0;//拉低使能端TSCLK = 0;//拉低数据总线TRST = 1;//使能端产生上升沿for(i = 0; i < 8; i ++)//传命令{TSCLK = 0;TIO = cmd & 0x01;//最低位先传TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走cmd >>= 1;//右移1位}for(i = 0;i < 8; i ++)//读数据{TSCLK = 0;//拉低数据总线dat >>= 1;if(TIO){dat |= 0x80;}TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走}TRST = 0;//拉低使能端,关闭读写功能return dat;
}
/*******************************************************************/
/*                                                                 */
/*                  数据转BCD码                                    */
/*                                                                 */
/*******************************************************************/
uchar dat_to_BCD(uchar dat)//数据转BCD码
{uchar dat1,dat2;dat1 = dat / 10;dat2 = dat % 10;dat2 = dat2 + dat1 * 16;return dat2;
}
/*******************************************************************/
/*                                                                 */
/*                  BCD码转数据                                    */
/*                                                                 */
/*******************************************************************/
uchar BDD_to_dat(uchar dat)//BCD码转数据
{uchar dat1,dat2;dat1 = dat /16;dat2 = dat % 16;dat2 = dat2 + dat1 * 10;return dat2;
}/*******************************************************************/
/*                                                                 */
/*          讲数据从ds1302读取                                     */
/*                                                                 */
/*******************************************************************/
void GetDate_ds1302()
{shi = BDD_to_dat(read_DS1302(0x85));//读秒寄存器(进行BCD码转换)fen = BDD_to_dat(read_DS1302(0x83));//读分寄存器miao = BDD_to_dat(read_DS1302(0x81));//读时寄存器day = BDD_to_dat(read_DS1302(0x87));//读日寄存器month = BDD_to_dat(read_DS1302(0x89));//读月寄存器//年比较特殊year=BDD_to_dat(read_DS1302(0x8d))+2000;//读年寄存器write_DS1302(0x8e,0x80); //开写保护
}

main函数(包括中断服务函数)

主函数只负责初始化在while大循环中只检测按键再根据按键调整时间
注: 上面在模块分析的时候已经给出对应的中断函数,此处给出只是为了实验的完整性和作者本人编程的风格(中断函数写在主函数的下面,功能函数放在主函数的上面)

/*********************************************************/
/*                                                       */
/* 主程序                                               */
/*                                                       */
/*********************************************************/main(){// day1118    start
//      write_DS1302(0x8e,0);//清除写保护
//  write_DS1302(0x86,dat_to_BCD(18));//日
//  write_DS1302(0x88,dat_to_BCD(11));//月
//  write_DS1302(0x8c,dat_to_BCD(21));//年
//  write_DS1302(0x8e,0x80);//开写保护// day1118 enddelay(10);                 //延时init_T1();         // 初始化定时器lcd_init();                //初始化LCDdisplay();//除了第二行的都显示display1();//试试写摄氏度符号lcd_wcmd(0x8F);lcd_wdat(0xA1);lcd_wdat(0xE6);//写星期displayweek();delay1(10);while(1){   keyScan();delay(10);GetDate_ds1302();//测试//  display1();//    delay(10);// tempdisplay();// delay(10);//测试// tempdisplay();
//     delay1(10);
//test//display1();
//   delay(100);
//   display();
//   keyscan();// delay(100);//测试//lcd_pos(1,4);
//      lcd_wcmd(0x0f); //分钟
//      delay1(1000);
//      lcd_pos(1,5);
//      delay1(1000);}
}
/*********************************************************/
/*                                                       */
/* 定时器0                                          */
/*                                                       */
/*********************************************************/
void timer0() interrupt 1
{TH0=(65536-50000)/256;TL0=(65536-50000)%256;count++;
if(count==14){//测试display1();LED=!LED;//tempdisplay();//测试count=0;miao++;//  beepon();if(miao==60){miao=0;fen++;if(fen==60){fen=0;shi++;if(shi==24){shi=0;}//write_sfm(4,shi);// dis2[4]=table0[shi/10];//小时的十位// dis2[5]=table0[shi%10];//小时的个位}//write_sfm(7,fen);//   dis2[7]=table0[fen/10];//分钟的十位// dis2[8]=table0[fen%10];//分钟的个位}//write_sfm(10,miao);//dis2[10]=table0[miao/10];//秒钟的十位//dis2[11]=table0[miao%10];//秒钟的个位}
}
/*********************************************************/
/*                                                       */
/*                定时器1刷新温度                                               */
/*                                                       */
/*********************************************************/
void T1_ISR(void) interrupt 3
{TH1=(65536-50000)/256;         //给TH1和TL1重新赋值TL1=(65536-50000)%256;testcount++;if(testcount==21){testcount=0;tempdisplay();}
}
/*********************************************************/
/*                                                       */
/*          外部中断函数                                   */
/*                                                       */
/*********************************************************/
void timer1() interrupt 0
{LCD_RW=0;if(s1==0)  {delay1(5);if(s1==0){   beepon();testnum++;while(!s1);if(testnum==1){Flag=1;display1();TR0=0;TR1=0;lcd_wcmd(0x95);lcd_wcmd(0x0f);}}if(testnum==2){display1();lcd_wcmd(0x93);}if(testnum==3){display1();lcd_wcmd(0x91);}/*if(testnum==4){Flag=0;display1();testnum=0;lcd_wcmd(0x0c);TR0=1;TR1=1;delay1(10);}*///继续测试if(testnum==4){display1();lcd_wcmd(0x85);}if(testnum==5){display1();lcd_wcmd(0x83);}if(testnum==6){display1();lcd_wcmd(0x81);}if(testnum==7){//退出的时候写时间函数//结束Flag=0;display1();testnum=0;lcd_wcmd(0x0c);TR0=1;TR1=1;delay1(10);}//继续结束}

致谢

感谢CSDN的许多文章对我的帮助

参考文献

[1] 郭天祥.新概念51单片机C语言教程[M]. 北京:电子工业出版社, 2018. 120-140.
[2] 魏二有.单片机应用系统设计与实现教程[M]. 北京:清华大学出版社, 2019. 100-120.

基于51单片机的可掉电走表万年历设计(LCD12864+DS1302+DS18B20+LM7805+18650电池外部供电)相关推荐

  1. 基于51单片机的自动浇花系统设计/基于51单片机的智能抽奖系统控制设计/基于51单片机的数字时钟与日历显示控制设计 毕业设计

    1147基于51单片机的自动浇花系统设计 设计思路:通湿度传感器实时监测湿度,通过LCD显示出实时的湿度采集值,可以通过按键设定目标界限,当达到这个界限值时,浇花系统的电机装置运行. 电路包含:LCD ...

  2. 基于51单片机的多层电梯(1-16层)运行系统仿真设计_层数可改

    基于51单片机的多层电梯(1-16层)运行系统仿真设计_层数可改 仿真图proteus 8.9 程序编译器:keil 4/5 编程语言:C语言 设计编号:S0027 视频演示 基于51单片机的多层电梯 ...

  3. 基于51单片机宠物自动投料喂食器控制系统仿真设计( proteus仿真+程序+讲解视频)

    基于51单片机宠物自动投料喂食器控制系统仿真设计( proteus仿真+程序+讲解视频) 仿真图proteus 7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S00 ...

  4. 【036】基于51单片机的电子时钟与秒表Proteus仿真设计

    一.压缩包资料内容 (1).基于51单片机的电子时钟与秒表proteus仿真设计一份: (2).基于51单片机的电子时钟与秒表proteus仿真设计keli源代码一份: (3).基于51单片机的电子时 ...

  5. 【041】基于51单片机的自动音乐播放器Proteus仿真设计

    一.压缩包资料内容 (1).基于51单片机的自动音乐播放器系统proteus仿真设计一份: (2).基于51单片机的自动音乐播放器系统proteus仿真设计keli源代码一份: (3).基于51单片机 ...

  6. 基于51单片机射频RFID卡考勤上课上班统计设计

    具体实现功能 系统由STC89C52单片机电路+RFID模块电路+1602液晶显示电路+电源电路组成. 具体功能: (1)系统有3张卡,分别代表依次是张三.李四.王二: (2)任何一个人刷卡成功能,1 ...

  7. 基于51单片机红外热释电人体感应蓝牙防盗报警器系统设计

    毕设帮助.开题指导.技术解答(有偿)见文末. 目录 摘要 一.硬件方案 二.设计功能 三.实物图 四.原理图 五.PCB图 六.Proteus仿真 七.程序源码 八.资料包括 摘要 随着现在社会的发展 ...

  8. 基于51单片机的4位竞赛抢答器的设计

    设计编号:Q001 资料下载 功能要求: 以单片机为核心,设计一个4位竞赛抢答器:同时供4名选手或4个代表队比赛,分别用4个按钮S0-S4表示. 1.设置一个系统清除和抢答控制开关S,开关由主持人控制 ...

  9. 【020】基于51单片机智能路灯系统Proteus仿真与实物设计

    一.资料内容 (1).基于51单片机的智能路灯系统proteus仿真设计一份: (2).基于51单片机的智能路灯系统proteus仿真设计keli源代码一份: (3).基于51单片机的智能路灯系统pr ...

  10. 【008】【毕业设计】基于51单片机的烟雾报警系统proteus仿真与实物设计

    ​一.压缩包资料内容(私信获取) (1).基于51单片机的烟雾报警系统proteus仿真设计一份: (2).基于51单片机的烟雾报警系统proteus仿真设计keli源代码一份: (3).基于51单片 ...

最新文章

  1. STL中的map、unordered_map、hash_map
  2. 【Android 面试基础知识点整理】
  3. WINDOWS与LINUX下的DNS轮询配置
  4. JAVA中int、String的类型转换
  5. Java中性能优化的35种方法汇总
  6. 10天学安卓-第二天
  7. 计算机画画作品 人物 游泳,夏天主题的儿童画-游泳的儿童绘画作品
  8. 第十章:在Spark集群上掌握比较重要的图操作之Computing Degree
  9. SAP License:制造行业环境分析
  10. MySql Connector-Java下载
  11. MyBatis-Plus-Generator配置
  12. 用于主题检测的临时日志(861e8353-61d5-43a9-b1b4-e055dac9cf39 - 3bfe001a-32de-4114-a6b4-4005b770f6d7)...
  13. yii2添加自定义字段
  14. 编译警告级别之重要性
  15. 屏幕录像专家使用手册
  16. 【转】VS2010测试功能之旅:编码的UI测试(2)-操作动作的录制原理(上)
  17. HEIF图像编码标准-python读取HEIF图像
  18. ZZ教你卸载 Office 正版增值计划通知 (KB949810)-CHS
  19. Topas——基于Geant4的放射治疗蒙特卡罗算法模拟工具
  20. 数学建模二:TOPSIS法(优劣解距离法) 附代码详解

热门文章

  1. 【视频格式】webm用什么播放
  2. scrt 上传和下载文件
  3. python将注释写入xml_向xml文档添加注释
  4. 极域电子教室超级管理员密码
  5. 离线网页 HTML+CSS+DIV
  6. 深度学习导论 - 读李宏毅《1天搞懂深度学习》
  7. HTMLTestRunner文件
  8. thinkphp自定义汉字转拼音类
  9. 截止到2022年9月底可用的与大屏可视化相关的网站和网页
  10. WIN7无法卸载掉中文繁体注音输入法