标题: DIY高精度时钟、温度显示器Ds3231+12864+ds18b20+89c51 [打印本页]
作者: forgetname 时间: 2015-5-11 16:48
标题: DIY高精度时钟、温度显示器Ds3231+12864+ds18b20+89c51
DIY高精度时钟、温度显示器Ds3231+12864+ds18b20+89c51
硬件:
程序:
#include<reg52.h>
#include<intrins.h>
#include<stdlib.h>
#define uchar unsigned char
#define uint unsigned int
/*端口定义*/
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^4;
sbit LCD_PSB=P2^3;
sbit DQ=P3^7; //18b20
sbit SDA=P1^4; //ds32321 //模拟I2C数据传送位SDA
sbit SCL=P1^3; //模拟I2C时钟控制位SCL
//***按键功能****//
////***K1停止时间显示****//
////***K2选择修改位置****//
////***K3进行加1的修改****//
////***K4将修改写入ds3231,同时启动时间显示****//
sbit K1=P3^2;
sbit K2=P3^3;
sbit K3=P3^4;
sbit K4=P3^5;
//定义变量
uchar numbr[10]="0123456789"; //字模
uchar dis4[]=" "; // 第四行显示 自己添加
uchar t[]=" . ℃" ; //18b20
uint sdata,xiaoshu1,xiaoshu2; //整数、小数1位、小数2位
bit fg=1; //温度正负标志
uchar tempL=0,tempH=0; // 变量
uchar year,month,date,hour,min,sec; // ds3231
uchar a[]="2011年22月33日";
uchar b[]="11时22分33秒";
///函数
//******************延时子程序 *******************************
//这个延时程序的具体延时时间是time=i*8+10,适用于小于2ms的延时
//************************************************************
void delay(unsigned char i)
{
for(i;i>0;i--);
}
//***********************************************************
// 延时子程序
//************************************************************
void delay1ms(uchar j)
{
while(j!=0)
{uchar i;
for(i=124;i>0;i--); //延时124*8+10=1002us
j--;
}
}
/**************************12864部分*************************************/
/**************************12864部分*************************************/
/*写指令数据到LCD
RS=L——表示DB0-DB7为显示指令数据
RW=L——表示DB0-DB7数据被write(当E=“H-L”,指令数据被写到IR或DR)
E=高脉冲
此时DB0-DB7=指令码 */
void write_cmd(uchar cmd)
{
LCD_RS=0;
LCD_RW=0;
LCD_EN=0;
P0=cmd;
delay1ms(5);
LCD_EN=1;
delay1ms(5);
LCD_EN=0;
}
/*设定显示位置*/
void lcd_pos(uchar X, uchar Y)
{
ucharpos;
if(X== 0)
{
X= 0x80;
}
elseif(X == 1)
{
X= 0x90;
}
elseif(X == 2)
{
X= 0x88;
}
elseif(X == 3)
{
X= 0x98;
}
pos= X + Y;
write_cmd(pos); //显示地址
}
/*写显示数据到LCD*/
/*
RS=H——表示DB0-DB7为显示数据
RW=L——RW=L,E='H-L',DB0-DB7数据被写到IR或DR
E=高脉冲
DB0-DB7=显示数据 */
void write_dat(uchar dat)
{
LCD_RS=1;
LCD_RW=0;
LCD_EN=0;
P0=dat;
delay1ms(5);
LCD_EN=1;
delay1ms(5);
LCD_EN=0;
}
/*LCD初始化*/
void lcd_init()
{ uinti;
LCD_PSB=1; //并口方式
write_cmd(0x30); //基本操作指令
delay1ms(5);
write_cmd(0x0c); //打开显示,光标关闭
delay1ms(5);
write_cmd(0x01); //清除LCD显示类容
delay1ms(5);
lcd_pos(3,0);
i=0;
while(dis4[ i]!='\0')
{
delay1ms(1);
write_dat(dis4);
delay1ms(1);
i++;
}
}
/**********************************18b20************************************************/
/**********************************18b20************************************************/
void Init_DS18B20(void) //初始化
{
uchar x=0;
DQ=1; //DQ先置高
delay(8); //稍延时
DQ=0; //发送复位脉冲
delay(80); //延时(>480us)
DQ=1; //拉高数据线
delay(5); //等待(15~60us)
x=DQ; //用X的值来判断初始化有没有成功,18B20存在的话X=0,否则X=1
delay(20);
}
//**********读一个字节************//
ReadOneChar(void) //主机数据线先从高拉至低电平1us以上,再使数据线升为高电平,从而产生读信号
{
unsigned char i=0; //每个读周期最短的持续时间为60us,各个读周期之间必须有1us以上的高电平恢复期
unsigned char dat=0;
for (i=8;i>0;i--) //一个字节有8位
{
DQ=1;
delay(1);
DQ=0;
dat>>=1;
DQ=1;
if(DQ)
dat|=0x80;
delay(4);
}
return(dat);
}
//*********************** **写一个字节**************************//
void WriteOneChar(unsigned char dat)
{
unsigned char i=0; //数据线从高电平拉至低电平,产生写起始信号。15us之内将所需写的位送到数据线上,
for(i=8;i>0;i--) //在15~60us之间对数据线进行采样,如果是高电平就写1,低写0发生。
{
DQ=0; //在开始另一个写周期前必须有1us以上的高电平恢复期。
DQ=dat&0x01;
delay(5);
DQ=1;
dat>>=1;
}
delay(4);
}
void ReadTemperature(void) //读温度值(低位放tempL;高位放tempH;)//
{
Init_DS18B20(); //初始化
WriteOneChar(0xcc); //跳过读序列号的操作
WriteOneChar(0x44); //启动温度转换
delay(125); //转换需要一点时间,延时
Init_DS18B20(); //初始化
WriteOneChar(0xcc); //跳过读序列号的操作
WriteOneChar(0xbe); //读温度寄存器(头两个值分别为温度的低位和高位)
tempL=ReadOneChar(); //读出温度的低位LSB
tempH=ReadOneChar(); //读出温度的高位MSB
if(tempH>0x7f) //最高位为1时温度是负
{
tempL=~tempL; //补码转换,取反加一
tempH=~tempH+1;
fg=0; //读取温度为负时fg=0
}
sdata= tempL/16+tempH*16; //整数部分
xiaoshu1= (tempL&0x0f)*10/16; //小数第一位
xiaoshu2= (tempL&0x0f)*100/16%10;//小数第二位
t[0]=numbr[sdata/10];
t[1]=numbr[sdata%10];
t[3]=numbr[xiaoshu1];
t[4]=numbr[xiaoshu2];
}
/*****************************************ds3231********************************************/
#define ADDRTW 0xD0 //器件写地址
#define ADDRTD 0xD1 //器件读地址
#define DS3231_SEC 0x00 //秒
#define DS3231_MIN 0x01 //分
#define DS3231_HOUR 0x02 //时
#define DS3231_DAY 0x03 //星期
#define DS3231_DATE 0x04 //日
#define DS3231_MONTH 0x05 //月
#define DS3231_YEAR 0x06 //年
//闹铃1
#define DS3231_Al1SEC 0x07 //秒
#define DS3231_AL1MIN 0x08 //分
#define DS3231_AL1HOUR 0x09 //时
#define DS3231_AL1DAY 0x0A //星期/日
//闹铃2
#define DS3231_AL2MIN 0x0b //分
#define DS3231_AL2HOUR 0x0c //时
#define DS3231_AL2DAY 0x0d //星期/日
#define DS3231_CONTROL 0x0e //控制寄存器
#define DS3231_STATUS 0x0f //状态寄存器
bit ack;
uchar BCD2HEX(uchar val) //BCD转换为Byte
{ uchari;
i= val&0x0f;
val >>= 4;
val &= 0x0f;
val *= 10;
i+= val;
return i;
}
uchar HEX2BCD(uchar val)//B码转换为BCD码
{
uchari,j,k;
i=val/10;
j=val%10;
k=j+(i<<4);
return k;
}
void Start()
{
SDA=1; //发送起始条件的数据信号
delay(1);
SCL=1;
delay(5); //起始条件建立时间大于4.7us,延时
SDA=0; //发送起始信号
delay(5); // 起始条件锁定时间大于4μs
SCL=0; //钳住I2C总线,准备发送或接收数据
delay(2);
}
void Stop()
{
SDA=0; //发送结束条件的数据信号
delay(1); //发送结束条件的时钟信号
SCL=1; //结束条件建立时间大于4us
delay(5);
SDA=1; //发送I2C总线结束信号
delay(4);
}
/********************************************************/
/*******************************************************************
字节数据发送函数
函数原型: void SendByte(uchar Dat);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0)
ack=1 发送数据正常,
ack=0 被控器无应答或损坏。
********************************************************************/
void SendByte(uchar Dat)
{
uchar BitCnt;
for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位
{
if((Dat<<BitCnt)&0x80)
SDA=1; //判断发送位
else
SDA=0;
delay(1);
SCL=1; //置时钟线为高,通知被控器开始接收数据位
delay(5); //保证时钟高电平周期大于4μs
SCL=0;
}
delay(2);
SDA=1; //8位发送完后释放数据线,准备接收应答位
delay(2);
SCL=1;
delay(3);
if(SDA==1)
ack=0;
else
ack=1; //判断是否接收到应答信号
SCL=0;
delay(2);
}
uchar RcvByte() //功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),发完后请用应答函数应答从机。
{
uchar retc;
uchar BitCnt;
retc=0;
SDA=1; //置数据线为输入方式
for(BitCnt=0;BitCnt<8;BitCnt++)
{
delay(1);
SCL=0; //置时钟线为低,准备接收数据位
delay(5); //时钟低电平周期大于4.7μs
SCL=1; //置时钟线为高使数据线上数据有效
delay(3);
retc=retc<<1;
if(SDA==1)
retc=retc+1; //读数据位,接收的数据位放入retc中
delay(2);
}
SCL=0;
delay(2);
return(retc);
}
void I2CACK(bit a) // 功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
{
if(a==0)
SDA=0; //在此发出应答或非应答信号
else
SDA=1;
delay(3);
SCL=1;
delay(5); //时钟低电平周期大于4μs
SCL=0; //清时钟线,钳住I2C总线以便继续接收
delay(2);
}
uchar I2CRead() /************从DS3231当前地址读一个字节************/
{
uchar read_data;
Start();
SendByte(ADDRTD);
if(ack==0)
{
return(0);
}
read_data = RcvByte();
I2CACK(1);
Stop();
return read_data;
}
uchar I2CReadAdd(uchar addr) /************从DS3231指定地址读一个字节************/
{
Start();
SendByte(ADDRTW);
if(ack==0)
{
return(0);
}
SendByte(addr);
if(ack==0)
{
return(0);
}
return(I2CRead());
}
void Readtime() /*********************读取时间**********************/
{
uchar temp;
temp=I2CReadAdd(DS3231_SEC);//秒
sec=BCD2HEX(temp);
temp=I2CReadAdd(DS3231_MIN);//分
min=BCD2HEX(temp);
temp=I2CReadAdd(DS3231_HOUR); //时
hour=BCD2HEX(temp);
temp=I2CReadAdd(DS3231_DATE); //日
date=BCD2HEX(temp);
temp=I2CReadAdd(DS3231_MONTH); //月
month=BCD2HEX(temp);
temp=I2CReadAdd(DS3231_YEAR); //年
year=BCD2HEX(temp);
}
void InitDS3231() //ds3231初始化
{SCL=1;
delay(5);
SDA=1;
delay(5);
}
void TimeDisplay(uchar Dhour,ucharDmin,uchar Dsec) //时分秒数组赋值
{ b[0]=numbr[Dhour / 10]; // 时十位
b[1]=numbr[Dhour % 10]; // 时个位
b[4]=numbr[Dmin / 10]; // 分十位
b[5]=numbr[Dmin % 10]; // 分个位
b[8]=numbr[Dsec / 10]; // 秒十位
b[9]=numbr[Dsec % 10]; // 秒个位
}
void DateDisplay(uchar Dyear,ucharDmonth,uchar Dday) //年月天数组赋值
{ a[2]=numbr[Dyear / 10]; // 年十位
a[3]=numbr[Dyear % 10]; // 年个位
a[6]=numbr[Dmonth / 10]; // 月十位
a[7]=numbr[Dmonth % 10]; // 月个位
a[10]=numbr[Dday / 10]; // 天十位
a[11]=numbr[Dday % 10]; // 天个位
}
void Start_I2C()
{
SDA=1; //发送起始条件的数据信号
delay(1);
SCL=1;
delay(5); //起始条件建立时间大于4.7us,延时
SDA=0; //发送起始信号
delay(5); // 起始条件锁定时间大于4μs
SCL=0; //钳住I2C总线,准备发送或接收数据
delay(2);
}
void Stop_I2C()
{
SDA=0; //发送结束条件的数据信号
delay(1); //发送结束条件的时钟信号
SCL=1; //结束条件建立时间大于4us
delay(5);
SDA=1; //发送I2C总线结束信号
delay(4);
}
uchar write_byte(uchar addr, ucharwrite_data)
{
Start_I2C();
SendByte(ADDRTW); //////*******************************************************************///////////
if (ack == 0)
return 0;
SendByte(addr);
if (ack == 0)
return 0;
SendByte(write_data);
if (ack == 0)
return 0;
Stop_I2C();
delay1ms(10);
return 1;
}
void ModifyTime(uchar yea,uchar mon,ucharda,uchar hou,uchar min,uchar sec)
{ uchar temp=0;
temp=HEX2BCD(yea);
write_byte(DS3231_YEAR,temp); //修改年
temp=HEX2BCD(mon);
write_byte(DS3231_MONTH,temp); //修改月
temp=HEX2BCD(da); /////////////////////
write_byte(DS3231_DATE,temp); //修改日
temp=HEX2BCD(hou);
write_byte(DS3231_HOUR,temp); //修改时
temp=HEX2BCD(min);
write_byte(DS3231_MIN,temp); //修改分
temp=HEX2BCD(sec);
write_byte(DS3231_SEC,temp); //修改秒
}
void xianshi(void)
{
{uint i;
TimeDisplay(hour,min,sec);
lcd_pos(1,1); //时间
i=0;
while(b[ i]!='\0')
{
delay1ms(1);
write_dat(b);
delay1ms(1);
i++;
}
DateDisplay(year,month,date); //显示日期
delay1ms(1);
lcd_pos(0,0);
i=0;
while(a[ i]!='\0')
{
delay1ms(1);
write_dat(a);
delay1ms(1);
i++;
}
ReadTemperature(); //显示温度
delay1ms(1);
lcd_pos(2,1);
delay1ms(1);
i=0;
while(t[ i]!='\0')
{
delay1ms(1);
write_dat(t);
delay1ms(2);
i++;
}
}
}
void shuaxin(void)
{ uint i;
TimeDisplay(hour,min,sec);
lcd_pos(1,1); //时间
i=0;
while(b[ i]!='\0')
{
delay1ms(1);
write_dat(b);
delay1ms(1);
i++;
}
DateDisplay(year,month,date); //显示日期
delay1ms(1);
lcd_pos(0,0);
i=0;
while(a[ i]!='\0')
{
delay1ms(1);
write_dat(a);
delay1ms(1);
i++;
}
}
void sotp(void)
{
uinti;
while(1)
{
duan1: if(K1==0)
{
delay1ms(100);
if(K1==0)
{
Readtime();
shuaxin();
i=0;
gotoduan2;
}
}
else
{gotoduan5;}
duan2: if(K2==0)
{
delay1ms(100);
if(K2==0)
{
i++;
if(i>6){i=0;}
shuaxin();
}
}
switch(i)
{
case 0: if(K3==0){delay1ms(100);if(K3==0){year=year+1;} if(year>=100){year=0;} shuaxin();}break;
case 1: if(K3==0){delay1ms(100);if(K3==0){month=month+1;} if(month>=13){month=0;} shuaxin();}break;
case 2: if(K3==0){delay1ms(100);if(K3==0){date=date+1;} if(date>=32){date=0;} shuaxin();}break;
case 3: if(K3==0){delay1ms(100);if(K3==0){hour=hour+1;} if(hour>=25){hour=0;} shuaxin();}break;
case 4: if(K3==0){delay1ms(100);if(K3==0){min=min+1;} if(min>=61){min=0;} shuaxin();}break;
case 5: if(K3==0){delay1ms(100);if(K3==0){sec=sec+1;} if(sec>=61){sec=0;} shuaxin();} break;
}
duan4: if(K4==0)
{
delay1ms(100);
if(K4==0)
{
ModifyTime(year,month,date,hour,min,sec);
gotoduan5; ///////////////////*******************//////////
}
}
else
{gotoduan2;}
duan5: Readtime();
xianshi();
}
}
main() /*主程序*/
{
lcd_init();
delay1ms(5);
InitDS3231();
delay1ms(5);
delay1ms(5);
sotp();
}
作者: admin 时间: 2015-5-11 17:28
非常好的设计 谢谢分享,楼主最好把 代码用代码工具发布 以防发生乱码,代码工具在工具栏的 <> 这个标志 点击即可出现.
作者: ardu 时间: 2015-5-12 05:26
谢谢分享,51hei有你更精彩!!
作者: wang215322 时间: 2015-5-12 18:53
谢谢分享,学习了
作者: jjbboox 时间: 2015-5-18 09:43
貌似DS3231内部就有温度寄存器,所以不需要另外再加一个ds18b20吧。
另外问一下楼主,DS3231的计时精度如何?据说内部集成了温补晶振。年误差很小的说。
作者: forgetname 时间: 2015-5-19 15:50
DS3231内部就有温度寄存器,测温精度低
作者: jjbboox 时间: 2015-5-20 09:01
楼主是经验者,正好咨询一下。
DS3231真的有标称上说的误差那么小吗?
根据我的观察,就算是电脑,如果不进行时间同步的话,每天也会有几秒钟的误差。
累积下来年误差绝对超过十分钟了。
作者: jjbboox 时间: 2015-5-20 11:57
精度有多低?一般家用差不多能精确到个位数也就OK了。
没用过3231,个位数的精度也没有吗?
作者: forgetname 时间: 2015-5-27 16:43
我说的测温精度低,时间精度很高现在27号,还没有感觉出时间偏差。
作者: dely2009 时间: 2015-6-14 13:50
非常不错,学习中.......
作者: nieli12345 时间: 2015-9-25 13:23
万年历
作者: fushandz 时间: 2015-11-24 21:51
很不错收藏..................
作者: 血一 时间: 2016-5-26 09:00
谢谢分享,找了好久的DS3231帖子,能把电路程序都发给我一份吗,研究一下1107456443@qq.com。

作者: 双赢电子 时间: 2016-6-5 21:57
够厉害,今天是长知识了,谢谢分享
作者: stxlpeng 时间: 2016-6-8 17:30
谢谢分享,找了好久的DS3231帖子,能把电路程序都发给我一份吗,研究一下625756413@qq.com谢谢
作者: wangjianb01978 时间: 2016-11-15 23:07
谢谢分享,学习了
作者: hghjksfh 时间: 2017-10-2 16:14
谢谢分享,找了好久的DS3231帖子,能把电路程序都发给我一份吗,研究一下2607431632@qq.com谢谢
作者: luxianjun 时间: 2017-10-3 19:36
12864显示温度时间
-
-
12864显示温度时间(修改可以改时间).zip
98.1 KB, 下载次数: 83, 下载积分: 黑币 -5
作者: lsgzq 时间: 2020-2-3 17:33
有源程序吗
作者: 不吃草的牛 时间: 2020-2-7 23:44
好资料,谢谢楼主分享
作者: xdl0129 时间: 2020-2-13 14:25
谢谢楼主分享
作者: rayin 时间: 2020-11-15 16:21
不错,值得学习。51hei有你更精彩!!
作者: wengjiamin 时间: 2021-1-2 10:22
51hei有你更精彩!!
作者: zhyopi 时间: 2021-1-6 19:57
值得学习。51hei有你更精彩
作者: 刘工程 时间: 2021-1-12 13:29
在这里对于初学者来说是个聚宝盆园地哦。
作者: 网络不安全 时间: 2021-1-18 23:44
如果能加上星期和农历就完美了,看了其他的有星期和农历,但没18b02
作者: icjsb 时间: 2021-2-1 13:51
已收藏。正准备做一个,可以好好借鉴一下
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |