哈尔滨理工大学软件工程专业08-7李万鹏原创作品,转载请标明出处
http://blog.csdn.net/woshixingaaa/archive/2010/09/27/5910473.aspx
在许多电子设备中,通常会进行一些与时间有关的控制,如果用系统的定时器来设计时钟的话,偶然的掉电或晶振的误差都会造成时间的错乱,更糟糕的是,若完全用程序设计时钟还会占用大量的系统资源,从而严重影响系统的其他功能。为此,很多芯片制造公司都设计出了各种各样的实时时钟芯片。
常见的时钟芯片有两种。
一种是体积非常小的表贴是元件,通常用在高端小型手持式仪器或设备中。这种芯片在使用时需要外接备份电池和外部晶振,电池用来保持主系统在意外时为时钟芯片供电,外部晶振用来给时钟芯片所必须的震荡来源。
另一种体积相对较大,一般为直插式,它的内部有可充电锂电池,同时内部还集成了32.768KHZ的标准晶振。
DS18B20是由DALLAS公司生产的,采用普通的32.768KHZ。
DS18B20的内部结构:
DS18B20的读数据时序:
如图的数据序列,左边是低位,右边是高位,在负跳变沿进行读数据,上升沿进行写数据。
DS18B20的写数据时序:
数据在SCLK在上升沿输入,前8位指定访问地址命令,在之后的时钟周期,读操作时输出的数据,写操作时输入数据。时钟脉冲的个数在单字节方式下为8个地址加8个数据。
DS18B20的控制字:
日历时钟寄存器与控制字对照:
日历时钟寄存器:
寄存器功能说明:
万年历程序:
#include <reg52.h> #define uchar unsigned char #define uint unsigned int uint i; sbit SCLK = P1^3; sbit IO = P1^4; sbit RST = P1^5; sbit RS = P1^0; sbit RW = P1^1; sbit E = P1^2; uchar Time_Data[]={'0','1','2','3','4','5','6','7','8','9'}; void delay(uint t){ int i,j; for(i = 0; i < t; i++) for(j = 10; j > 0; j--) ; } void lcd_com(uchar s){ RS = 0; //低电平,写指令 P2 = s; //传数据 delay(14); //看时序图,数据需要稳定一段时间 E = 1; //给一个高脉冲,发送命令 delay(14); //如图,高脉冲延时一段时间,确保命令发送 E = 0; //发送结束E置为低电平 } void lcd_data(uchar s){ RS = 1; P2 = s; delay(14); E = 1; delay(14); E = 0; } uchar DS_Read(uchar command){ uchar value; RST = 0; SCLK = 0; RST = 1; value = 0x0; for(i = 0; i < 8; i++){ IO = command & 0x01; //写入控制字 SCLK = 0; SCLK = 1; command >>= 1; } for(i = 0; i < 8; i++){ SCLK = 1; SCLK = 0; if(IO) value |= (0x01<<i); //如果那位是1则value置1 } RST = 0; value = value/16*10 + value%16; //BCD码到十进制的转换 return value; } void DS_Write(uchar command,uchar value){ RST = 0; SCLK = 0; RST = 1; for(i = 0; i < 8; i++){ //写控制字 IO = command & 0x01; SCLK = 0; SCLK = 1; command >>= 1; } for(i = 0; i < 8; i++){ //向IO引脚写数据 IO = value & 0x01; SCLK = 0; SCLK = 1; value >>= 1; } } void Init_ds(){ RST = 0; SCLK = 0; RST = 1; DS_Write(0x8E,0x00); //打开写保护,WP位置0,这样就可以写数据了 DS_Write(0x84,0x00); //对日历时钟寄存器的初始化 DS_Write(0x8c,0x10); DS_Write(0x8a,0x05); DS_Write(0x88,0x10); DS_Write(0x86,0x01); DS_Write(0x84,0x04); DS_Write(0x82,0x22); DS_Write(0x80,0x00); //启动时钟 DS_Write(0x8e,0x80); //禁止寄存器写 } void Init_lcd(){ RS = 1; //先发指令,在初始时刻RS是高,E和RW是低 E = 0; RW = 0; lcd_com(0x38); //设置为16*2显示,5*7点阵,8位数据接口 lcd_com(0x0f); //开显示,显示光标,光标闪烁 lcd_com(0x06); //读写一个字符后地址指针加一 lcd_com(0x01); } void Display_lcd(uchar y, uchar x, uchar value){ if(y) lcd_com(0x80+0x40+x); //如果y为1,写在第二行 else lcd_com(0x80+x); lcd_data(value); //写到LCD602上 } void main(){ Init_ds(); Init_lcd(); while(1){ Display_lcd(0,0,'2'); Display_lcd(0,1,'0'); Display_lcd(0,2,Time_Data[DS_Read(0x8D)/10]); Display_lcd(0,3,Time_Data[DS_Read(0x8D)%10]); Display_lcd(0,4,'-'); Display_lcd(0,5,Time_Data[DS_Read(0x89)/10]); Display_lcd(0,6,Time_Data[DS_Read(0x89)%10]); Display_lcd(0,7,'-'); Display_lcd(0,8,Time_Data[DS_Read(0x87)/10]); Display_lcd(0,9,Time_Data[DS_Read(0x87)%10]); Display_lcd(0,10,'D'); Display_lcd(0,11,'a'); Display_lcd(0,12,'y'); Display_lcd(0,13,':'); Display_lcd(0,14,Time_Data[DS_Read(0x8b)/10]); Display_lcd(0,15,Time_Data[DS_Read(0x8b)%10]); Display_lcd(1,0,'C'); Display_lcd(1,1,'a'); Display_lcd(1,2,'n'); Display_lcd(1,3,'u'); Display_lcd(1,4,'t'); Display_lcd(1,5,'e'); Display_lcd(1,6,' '); Display_lcd(1,7,Time_Data[DS_Read(0x85)/10]); Display_lcd(1,8,Time_Data[DS_Read(0x85)%10]); Display_lcd(1,9,':'); Display_lcd(1,10,Time_Data[DS_Read(0x83)/10]); Display_lcd(1,11,Time_Data[DS_Read(0x83)%10]); Display_lcd(1,12,':'); Display_lcd(1,13,Time_Data[DS_Read(0x81)/10]); Display_lcd(1,14,Time_Data[DS_Read(0x81)%10]); } } //2010-10-1 Day:5 //Canute 04:22:00