很久前做过的一个电子万年历,拿来和大家分享一下,做成实物在家里放着跑了2年多了,极其稳定,日误差小于1秒。
唯一的问题:不用DS1302虽然是省去了不少焊接的功夫,但是程序掉电又得重设时间,所以建议接交流电+蓄电池浮充,这样就不会掉电了,完美解决问题,可以跑一个月都不用调时间。
源代码:
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit k2=P3^2; //K1 K2 K3 K4按键接口,建议从左到右接K1-K4
sbit k1=P3^5;
sbit k3=P3^3;
sbit k4=P3^4;
sbit DQ=P3^6;
sbit beep=P1^6;
sbit Data=P2^0; //有源蜂鸣器在P16口
sbit lcden=P1^4; //LCD1602接口定义
sbit lcdrs=P1^0;
sbit lcdrw=P2^6;
sbit bg=P2^7; //背光变量接入LCD1602的K极,通过bg的高低电平就可控制自动熄灭与点亮
u8 k,e,d,o=1,p=1,temp,nxflag=0,mbflag=0,tianmax;
float temperature; //温度变量,18B20接收时为浮点数
u8 miao1,miao2,fen1,fen2,shi1,shi2,tian1,tian2,wk,tem1,tem2,tem3,wendufu;
u8 day=1,mon=1,day1,day2,yue1,yue2,mbw,n=0,m=0,beiguang,RH,RL,TH,TL,tempH,tempL;
u8 se1,se2,mon1,mon2,hou1,hou2,min1,min2,MB2,nian1,nian2,nian3,nian4;
u16 y,MB1;
u16 temper,buchang,tempwarn=0,tempwarn1=500; //tempwarn为温度下限,1为温度上限
u8 code set[]={"NaoZhong Status"}; //上电后傻瓜调节模块
u8 code tempwarng[]={"Wendu Waring!"};
u8 code temmin[]={"Min"};
u8 code temmax[]={"Max"};
u8 code naozhongON[]={" ON"};
u8 code naozhongOFF[]={"OFF"};
u8 code setsucces[]={"Set Success!"};
u8 code thanks[]={"Welcome to use !"};
u8 code thanksu[]={"thanks for using"};
u8 code make[]={"Designed by LY"};
u8 code MB[]={"Stopwatch"};
u8 code week1[]={"[MON]"};
u8 code week2[]={"[TUE]"};
u8 code week3[]={"[WED]"};
u8 code week4[]={"[THU]"};
u8 code week5[]={"[FRI]"};
u8 code week6[]={"[SAT]"};
u8 code week7[]={"[SUN]"};
void delay(unsigned int z) //定义延时函数
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=1;y>0;y--);
}
void DHT11_delay_us(u8 n)
{
while(--n);
}
void DHT11_delay_ms(u16 z) //定义DHT11延迟函数
{
u16 i,j;
for(i=z;i>0;i--)
for(j=110;j>0;j--);
}
void DHT11_start()
{
Data=1;
DHT11_delay_us(2);
Data=0;
DHT11_delay_ms(20); //延时18ms以上
Data=1;
DHT11_delay_us(30);
}
u8 DHT11_rec_byte() //接收一个字节
{
u8 i,dat=0;
for(i=0;i<8;i++) //从高到低依次接收8位数据
{
while(!Data); //等待50us低电平过去
DHT11_delay_us(8); //延时60us,如果还为高则数据为1,否则为0
dat<<=1; //移位使正确接收8位数据,数据为0时直接移位
if(Data==1) //数据为1时,使dat加1来接收数据1
dat+=1;
while(Data); //等待数据线拉低
}
return dat;
}
void DHT11_receive() //接收40位的数据
{
u8 R_H,R_L,T_H,T_L,revise;
DHT11_start();
if(Data==0)
{
while(Data==0); //等待拉高
DHT11_delay_us(40); //拉高后延时80us
R_H=DHT11_rec_byte(); //接收湿度高八位
R_L=DHT11_rec_byte(); //接收湿度低八位
T_H=DHT11_rec_byte(); //接收温度高八位
T_L=DHT11_rec_byte(); //接收温度低八位
revise=DHT11_rec_byte(); //接收校正位
DHT11_delay_us(25); //结束
if((R_H+R_L+T_H+T_L)==revise) //校正
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;
}
}
}
void beepon() //PWM(如果你是无源蜂鸣器,这里写PWM函数)
{ //当前为有源蜂鸣器,有源蜂鸣器无需更改
beep=0;
delay(1200);
beep=1;
}
void write_nian(); //预定义
void write_yue();
void write_tian();
void write_shi();
void write_fen();
void write_miao();
void write_week();
void zhuanhuan();
void write_temp();
void write_nz();
void nzON();
void write_setsuccess();
void beiguangpd();
void write_com(u8 com) //LCD1602初始化程序,了解1602时序图后新手的话可以直接照搬使用
{
lcdrs=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_data(u8 date)
{
lcdrs=1;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init()
{
lcdrw=0;
lcden=0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80+0x10);
}
void Timer0Init() //定时器0工作方式1 中断初始化,923位12M晶振在KEIL仿真后的计算结果
{
TMOD=0X01;
TH0=(65535-901)/256;
TL0=(65535-901)%256;
ET0=1; //打开定时器0中断允许位
EA=1; //打开总中断允许
TR0=1; //打开 定时器0,开始计时
}
Init_DS18B20(void) //DS18B20初始化与时序图程序,仿真时温度问题改进这个初始化程序就解决了
{
DQ=1;
delay(70);
DQ=0;
delay(485);
DQ=1;
delay(50);
}
ReadOneChar(void) //18B20读一个字节
{
unsigned char i=0;
unsigned char dat=0;
for (i=8;i>0;i--)
{
DQ=1;
delay(1);
DQ=0;
dat>>=1;
DQ=1;
if(DQ)
dat|=0x80;
delay(4);
}
return(dat);
}
WriteOneChar(unsigned char dat) //18B20写一个字节
{
unsigned char i=0;
for(i=8;i>0;i--)
{
DQ=0;
DQ=dat&0x01;
delay(5);
DQ=1;
dat>>=1;
}
delay(4);
}
ReadTemperature(void) //读取温度函数定义,返回值为浮点数
{
Init_DS18B20();
WriteOneChar(0xcc);
WriteOneChar(0x44);
delay(125);
Init_DS18B20();
WriteOneCha