来自:csqesz > 馆藏分类
配色: 字号:
单片机与高精度时钟芯片DS3231的接口应用
2014-12-11 | 阅:  转:  |  分享 
  
单片机与高精度时钟芯片DS3231的接口应用



[1].前言在许多单片机的测控系统和家用电器中都含有时钟显示部分,最廉价的是直接使用单片机中的定时器,辅以一定的中断服务程序,构成时钟显示部分,这种方式是几乎不需要增加新的硬件即可实现,缺点是计时误差大,同时电源掉电不能保持时钟继续运行。在相对要求较高的场合,则使用廉价的时钟芯片(如DS1302等等)辅以备用电池,计时精度略高,可满足一般的要求。

在这篇文章中,我们重点介绍高精度时钟电路DS3231的设计和应用。在DS1302普及型时钟芯片,晶体均需外置,由于晶体的离散性,很难选择到精度很高的器件,同时也没有温度补偿电路,不同的温度环境下,晶体的特性也在变化,直接影响着时钟的振荡频率,较大的误差在所在所难免。而DS3231高精度时钟芯片,将晶体和温度补偿均集成在芯片中,为提高计时精度提供了可能,实册证明,使用DS3231时钟芯片,误差可做到一年小于一分钟,甚至部分显示器误差可小于20秒/一年。这对有相对精确时钟要求的应用场合是个理想的选择。[2].DS3231时钟芯片结构原理DS3231是一款高精度I2C实时时钟(RTC)器件,具有集成的温度补偿晶体振荡器(TCXO)。该器件包含电池输入端,断开主电源时仍可保持精确计时。集成的晶体振荡器可提高器件的长期精确度。DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息。少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS3231与单片机通过I2C双向串行总线传输地址与数据。

下图为DS3231典型应用电路,图中可看出,DS3231几乎不需要外部元件。



[3].DS3231时钟芯片结构如图1所示,DS3231的主要组成部分有8个模块,划分为4个功能组:TCXO、电源控制、按钮复位和RTC。1.32kHz的TCXOTCXO包括温度传感器、振荡器和控制逻辑。控制器读取片上温度传感器输出,使用查表法确定所需的电容,加上AGE寄存器的老化修正。然后设置电容选择寄存器。仅在温度变化或者用户启动的温度转换完成时,才加载包括AGE寄存器变化的新值。VCC初次上电时就会读取温度值,然后每隔64s读取一次。



2.DS3231的内部寄存器及功能DS3231寄存器地址为00h~12h,分别用于存放秒、分、时、星期、日期及闹钟设置信息。在多字节访问期间,如果地址达到RAM空间的结尾12h处,将发生卷绕,此时定位到开始位置即00h单元。DS3231的时间和日历信息通过读取相应的寄存器来设置和初始化。用户辅助缓冲区用于防止内部寄存器更新时可能出现的错误。读取时间和日历寄存器时,用户缓冲区在任何START条件下或者寄存器指针返回到零时与内部寄存器同步。时间信息从这些辅助寄存器读取,此时时钟继续保持运行状态。这样在读操作期间发生主寄存器更新时可以避免重新读取寄存器。以控制寄存器(地址为0EH)为例,可以控制实时时钟、闹钟和方波输出。其各bit定义如下表。



BIT7位:使能振荡器(EOEC)。设定为逻辑0时,启动振荡器。如果设定为逻辑1,在DS3231电源切换至VBAT时,振荡器停止。初次上电时该位清零(逻辑0)。当DS3231由VCC供电时,振荡器与EOSC位的状态无关,始终保持工作状态。BIT6位:电池备份的方波使能(BBSOW)。当设定为逻辑1并且DS3231由VBAT引脚供电时,在没有加载VCC的情况下,该位使能方波输出。当BB-SQW设定为逻辑0时,若VCC降至低于电源故障门限值,则INT/SQW引脚变为高阻抗。初次上电时,该位清零(逻辑0)。BIT5位:转换温度(CONV)。该位置为1时,强制温度传感器将温度转换成数字,并执行TCXO算法更新振荡器的电容阵列。只在空闲期间有效。状态位BSY=1时,禁止设定转换温度位。用户在强制控制器开始新的TCXO操作之前。应检查状态位BSY。用户启动的温度转换不影响内部64s更新周期。用户启动的温度转换在大约2ms内不会影响BSY位。CONV位从写入开始直到转换完成一直保持为1,转换完后,CONV和BSY均变为0。在监视用户启动转换状态时,应使用CONV位。

BIT4和BIT3位:频率选择(RS2和RS1),初次上电时,BIT4和BIT3设置为逻辑1。方波使能时用于控制方波输出的频率。RS1、RS2的逻辑值与方波输出频率的关系如表2所列。

BIT2位:中断控制(INTCN)。该位控制INT/SQW信号。INTCN置为0时,INT/SQW引脚输出方波;INTCN置为1时,若计时寄存器与任一个闹钟寄存器相匹配,则会触发INT/SQW信号(如果也使能闹钟的话)。匹配时相应的闹钟标志总是置位,而与INTCN位的状态无关。初次上电时,INTCN位置为逻辑1。BIT1位:闹钟2中断使能(A2IE)。该位置为逻辑1时,允许状态寄存器中的闹钟2标志位(A2F)触发INT/SQW信号(当INTCN=1时)。当A2IE位置为0或者INTCN置为0时,A2F位不启动中断信号。初次上电时,A2IE位清零(逻辑0)。BIT0位:闹钟1中断使能(A1IE)。该位置为逻辑1时,允许状态寄存器中的闹钟1标志位(A1F)触发INT/SQW信号(当INTCN=1时)。当A1IE位置为0或者INTCN置为0时,A1F位不启动INT/SQW信号。初次上电时,A1IE位清零(逻辑0)。3.DS3231的电源控制电源控制功能由温度补偿电压基准(VPF)和监视VCC电平的比较器电路提供。当VCC高于VPF时,DS3231由VCC供电,当VCC低于VPF但高于VBAT时,DS3231由VCC供电;当VCC低于VPF并低于VBAT时,DS3231由VBAT供电。为保护电池,VBAT首次加到器件时振荡器并不启动,除非加载VCC,或者向器件写入一个有效的I2C地址。典型的振荡器启动时间在1s以内。在VCC加电后或者有效的I2C地址写入后大约2s,器件会测量一次温度,并使用计算的修正值校准振荡器。一旦振荡器运行,只要电源(VCC或者VBAT)有效就会一直保持工作状态。器件每隔64s进行一次温度测量并校准振荡器频率。4.DS3231的时钟和日历RTC可以通过读取适当的寄存器字节获得时钟和日历信息。通过写入适当的寄存器字节设定或者初始化时钟和日历数据。时钟和日历寄存器的内容采用二-十进制编码(BCD)格式。DS3231运行于12小时或者24小时模式。小时寄存器的第6位定义为12或24小时模式选择位。该位为高时,选择12小时模式。在12小时模式下,第5位为AM/PM指示位,逻辑高时为PM。5.DS3231的复位按钮DS3231具有连接至RST输出引脚的按钮开关功能。若DS3231不在复位周期,会持续监视RST信号的下降沿。如果检测到一个边沿转换,DS3231通过拉低RST完成开关去抖。内部定时器定时结束后,DS3231继续监视RST信号。如果信号依旧保持低电平,DS3231持续监视信号线以检测上升沿。一旦检测到按钮释放,DS3231强制RST为低电平并保持tRST。RST还可用于指示电源故障报警情况。当VCC低于VPF时,产生内部电源故障报警信号,并强制拉低RST引脚。当VCC返回至超过VPF电平时。RST保持低电平大约250ms(tREC),使供电电源达到稳定。如果在VCC加载时,振荡器不工作,将跳过tREC,RST立刻变为高电平。6.DS3231的闹钟和报警功能DS3231包含2个定时/日期闹钟。闹钟1可通过写入寄存器07h~0Ah设定。闹钟2可通过写入寄存器0Bh~0Dh设定。可对闹钟进行编程(通过控制寄存器的闹钟使能位和INTCN位),从而在闹钟匹配条件下触发INT/SQW输出。每个定时/日期闹钟寄存器的第7位是屏蔽位。当每个闹钟的屏蔽位均为逻辑0时,只有当计时寄存器中的值与存储于定时/日期闹钟寄存器中的对应值相匹配时才会告警。闹钟也可以编程为每秒、分、时、星期或日期重复告警。当RTC寄存器值与闹钟寄存器的设定值相匹配时,相应的闹钟标志位A1F或A2F置为逻辑1。如果对应的闹钟中断使能位A1IE或A2IE也置为逻辑1,并且INTCN位置为逻辑1,闹钟条件将会触发INT/SQW信号。RTC在时间和日期寄存器每秒更新时都会检测匹配情况。7.DS3231的I2C总线时序,数据交换及其格式及编程注意事项DS3231在I2C总线上作为从器件。通过执行START命令并且在验证器件地址后才可以访问。然后寄存器可以被访问直到执行一个STOP命令为止。所有在I2C总线上传输的地址包长度均为9位,它包括7个地址位,1个R/W控制位和1个应答位ACK,如果R/W为1,则执行读操作;如果R/W为0,则执行写操作。从机寻址后,必须在第9个SCL(ACK)周期通过拉低SDA做出应答,若从机忙或者无法响应主机,则应在ACK周期内保持SDA为高。然后主机发出STOP状态或者REPSTART状态重新开始发送。地址包包括从机地址和称为SLA+R或者SLA+W的READ或者WRITE位。地址字节的MSB首先被发送。所有1111xxxx的地址均保留。以便将来使用。所有在I2C总线上传送的数据包长度均为9位,它包括8个数据位和1个应答位。在数据传送中,主机产生时钟及START与STOP状态,而接收器响应接收。应答是由ACK在第9个SCL周期拉低SDA实现的。如果接收器拉高SDA,则发送NACK信号。如果接收器由于某种原因不能接收更多数据,应在最后一个数据字节后发出NACK信号告诉发送器停止发送,首先发送数据的MSB。下图为DS3231与MCUI2C总线数据交换时序:



DS3231通过双向数据线SDA和时钟线SCL与外界进行数据交换,从其时序关系可看出,DS3231有两种操作方式:写操作:把SDA数据线上的数据按RAM指定的首地址(WordAddress)依次写入N个字节数据。主器件首先传输从器件的地址字节,紧跟着是一系列数据字节。从器件每收到一个字节后返回一个应答位ACK。其格式下图所示。



读操作:按RAM指定的首地址依次读取N个字节数据,主器件首先传送从器件地址。从器件返回一个应答位。随后是从器件传输的一系列数据字节。主器件收到除最后一个字节外的所有字节后返回一个应答位。在收到最后一个字节后,返回一个“非应答位”NACK。其格式下图所示。



上述读写操作信号中:S为起始信号(START),1101000为DS3231的口地址,A为应答信号ACK,A为非应答信号NACK,P为停止信号(STOP)。主器件产生所有的串行时钟和START、STOP条件,通过传输STOP和重发START条件使其停止。4.DS3231与AT89C2051单片机的接口电路



其实,可以使用AT89C2051单片机的任何两个I/O口与DS3231相连接,复位部分也可取消。编程时需认真分析DS3231的时序,哪怕一个应答位的编程时序错误,也不能正确读写DS3231。同时需注意,因为C51单片机的特点,在将SDA拉低后(此时为输出口),如果下一步是当做输入口,结束任务后应将此端口拉高,否则DS3231的输出不能使该输出口置1。更详细的数据手册,请阅读[DS3231数据手册]English

下图为一款DS3231实验板实物图



作者:huqin???来源:本站原创???点击数:?376???更新时间:2014年04月08日??【字体:大?中?小】

本程序一共有2个文件,成功的用stc89c51单片机驱动,完整的代码从?http://www.51hei.com/f/ds3231code.rar?处下载下面是源码预览(第一个文件):?////////////////////////////////////////////////////////////////////////////////////DS3231.h文件

/////////////////////////////////////////////////////////////////////////////////////

#ifndef_ds3231_h_#define_ds3231_h_#defineuintunsignedint#defineucharunsignedchar//externvoidIIC_Init(void);externucharl_tmpdate[];externucharl_tmpdisplay[];externvoiddelay_IIC(void);externvoidIIC_start(void);externvoidIIC_stop(void);externbitIIC_Tack(void);externvoidRead_RTC(void);externvoidSet_RTC(void);externvoidDS1302_Initial();#endif////////////////////////////////////////////////////////////////////////////////////DS3231.C文件

/////////////////////////////////////////////////////////////////////////////////////#include#include#include"ds3231.h"sbitIIC_SDA=P0^3;sbitIIC_RST=P0^2;sbitIIC_SCL=P0^1;codeunsignedcharrtc_address[7]={0x00,0x01,0x02,0x04,0x05,0x03,0x06};//秒分时日月周年最低位读写位

//-------------------------------------------------------------------voiddelay_IIC(void)???{//IIC总线限速延时函数。//该函数是空函数,延时4个机器周期。_nop_();_nop_();_nop_();_nop_();}//-------------------------------------------------------------------voidIIC_Init(void){//IIC总线初始化函数???IIC_RST=1;???IIC_SDA=1;//释放IIC总线的数据线。???IIC_SCL=1;//释放IIC总线的时钟线。}//-------------------------------------------------------------------voidIIC_start(void){//IIC总线产生起始信号函数?????????IIC_SDA=1;//拉高数据线????IIC_SCL=1;//拉高时钟线????delay_IIC();????IIC_SDA=0;//在时钟线为高电平时,拉低数据线,产生起始信号。????delay_IIC();????IIC_SCL=0;//拉低时钟线}//-------------------------------------------------------------------voidIIC_stop(void){//IIC总线产生停止信号函数???IIC_SDA=0;//拉低数据线???delay_IIC();???IIC_SCL=1;//拉高时钟线。???delay_IIC();???IIC_SDA=1;//时钟时线为高电平时,拉高数据线,产生停止信号。???delay_IIC();}//-------------------------------------------------------------------bitIIC_Tack(void){//接收应答信号函数???bitack;//定义一个位变量,来暂存应答状态。???IIC_SDA=1;//释放数据总线,准备接收应答信号。???delay_IIC();???IIC_SCL=1;//拉高时钟线。???delay_IIC();???ack=IIC_SDA;//读取应答信号的状态。???delay_IIC();???IIC_SCL=0;//拉低时钟线。???delay_IIC();???returnack;//返回应答信号的状态,0表示应答,1表示非应答。}//-------------------------------------------------------------------voidIIC_write_byte(unsignedcharData){//向IIC总线写入一个字节的数据函数???unsignedchari;????for(i=0;i<8;i++)//有8位数据???{???????IIC_SDA=Data&0x80;//写最高位的数据?????????delay_IIC();?????????IIC_SCL=1;//拉高时钟线,将数写入到设备中。???????delay_IIC();???????IIC_SCL=0;//拉低时钟线,允许改变数据线的状态???????delay_IIC();???????Data=Data<<1;//数据左移一位,把次高位放在最高位,为写入次高位做准备???}}//-------------------------------------------------------------------unsignedcharIIC_read_byte(){//从IIC总线读取一个字节的数据函数???unsignedchari;???unsignedcharData;??????//定义一个缓冲寄存器。???for(i=0;i<8;i++)//有8位数据???{???????IIC_SCL=1;//拉高时钟线,为读取下一位数据做准备。???????delay_IIC();???????Data=Data<<1;//将缓冲字节的数据左移一位,准备读取数据。???????delay_IIC();??????????????if(IIC_SDA)//如果数据线为高平电平。???????????Data=Data|0x01;//则给缓冲字节的最低位写1。???????IIC_SCL=0;//拉低时钟线,为读取下一位数据做准备。???????delay_IIC();???}???returnData;//返回读取的一个字节数据。}//-------------------------------------------------------------------voidIIC_single_byte_write(unsignedcharWaddr,unsignedcharData){//向任意地址写入一个字节数据函数???IIC_start();//产生起始信号???IIC_write_byte(0xd0);//写入设备地址(写)???IIC_Tack();//等待设备的应答???IIC_write_byte(Waddr);//写入要操作的单元地址。???IIC_Tack();//等待设备的应答。???IIC_write_byte(Data);//写入数据。???IIC_Tack();//等待设备的应答。???IIC_stop();//产生停止符号。}//-------------------------------------------------------------------unsignedcharIIC_single_byte_read(unsignedcharWaddr){//从任意地址读取一个字节数据函数???unsignedcharData;//定义一个缓冲寄存器。???IIC_start();//产生起始信号???IIC_write_byte(0xd0);//写入设备地址(写)???IIC_Tack();//等待设备的应答???IIC_write_byte(Waddr);//写入要操作的单元地址。???IIC_Tack();//等待设备的应答。???IIC_stop();//产生停止符号。

???IIC_start();//产生起始信号???IIC_write_byte(0xd1);//写入设备地址(写)???IIC_Tack();//等待设备的应答???Data=IIC_read_byte();//写入数据。???delay_IIC();???IIC_stop();//产生停止符号。???//-------------------返回读取的数据--------------------???returnData;//返回读取的一个字节数据。}

voidRead_RTC(void){???unsignedchari,p;????p=rtc_address;????????//地址传递???for(i=0;i<7;i++)???????????//分7次读取秒分时日月周年???{?????l_tmpdate[i]=IIC_single_byte_read(p);?????p++;???}???}voidSet_RTC(void){???unsignedchari,tmp;???for(i=0;i<7;i++)???{??????//BCD处理???????tmp=l_tmpdate[i]/10;???????l_tmpdate[i]=l_tmpdate[i]%10;???????l_tmpdate[i]=l_tmpdate[i]+tmp16;???}?//???IIC_single_byte_write(0x0e,0X8c);????for(i=0;i<7;i++)???????//7次写入秒分时日月周年????{???????IIC_single_byte_write(rtc_address[i],l_tmpdate[i]);???}???IIC_single_byte_write(0x0e,0x0c);}

voidDS1302_Initial(){???IIC_RST=1;???IIC_SDA=1;//释放IIC总线的数据线。???IIC_SCL=1;???IIC_single_byte_write(0x0e,0x0c);}



献花(0)
+1
(本文系csqesz首藏)