《电子技术应用》

DS1302菜鸟级讲解

2015/11/4 13:56:00

DS1302;12.1 概述;DS1302实时时钟芯片我真的为她着迷呀;12.2芯片介绍;VCC2;GND;VCC1;SCLK;I/O;RST工作电源电源地后备电源时钟 信号数据输入输出;我在看视频教程时,老时觉得很奇怪的,为什么老师会;SCLK是串行时钟信号的输入,而I/O则是串行数;12.3时序分析;以上是 DS1302一个字节写入的时序图;上升沿有效

第12章DS1302

12.1概述

DS1302 实时时钟芯片我真的为她着迷呀。什么原因导致我为她着迷?自己接触后您自然而然会明白的。本片笔记的手法与前几章写笔记的手法明显而外的不同,因为介绍 DS1302芯片不像是介绍单片机内部的资源一样,都有参考步骤可言,但设置DS1302的方法太多样化了,让新手很不容易。

12.2芯片介绍

VCC2

GND

VCC1

SCLK

I/O

RST工作电源电源地后备电源时钟信号数据输入输出复位信号|片选信号

我 在看视频教程时,老时觉得很奇怪的,为什么老师会把RST信号说成片选信号呢?这也难怪的,因为在DS1302的时序图,RST信号的角色有如片选信号。 众多的芯片中,往往最后的PIN都是工作电源的输入。可是DS1302却是很奇特,VCC2是工作电源的输入而VCC1却是后备电源的输入,或者是充电的 电流的经过,更详细的介绍在后面的内容会继续介绍。

SCLK是串行时钟信号的输入,而I/O则是串行数据输入输出。X1,X2是晶振32.768kHz的输入,数据手册中有记录晶振的两端需无极电容,手册中的记录是6pf,但是在众多的AVR学习板中会看到23pf,27pf的出现。

12.3时序分析

以上是DS1302一个字节写入的时序图。第一个字节是地址字节,第二个字节是数据字节。RST信号必须拉高,否则数据的输入是无效的。换一句话说,RST信号控制数据|时间信号输入的开始和结束。地址字节和数据字节的读取时

上升沿有效,而且是由LSB开始读入。

读一个字节和写一个字节有明显的不一样,先是写地址字节,然后再读数据字节,写地址字节时上升沿有效,而读数据字节时下降沿有效,当然前提是RST信号必须拉高。写地址字节和读数据字节同是LSB开始。

再 重申一次,读一个字节和写一个字节是不一样,在写一个字节的时候,AVR的IO口一直保持输出状态,相反的在读一个字节的时候AVR的IO口先是输出状 态,然后是输入状态,且必须改变时钟信号的顺序。(补充一点题外话,我在编辑时序的时候,由于疏忽了一点“小错误”,后果却是很严重。)

12.3DS1302时钟|日期|控制|爆发寄存器

在介绍DS1302的时序图中不都是,先地址字节,然后数据字节码?那么地址字节和数据字节又有什么关系呢?(看看下面的图)

上面的图说明了每个寄存器的定义和地址字节,而每个地址字节的LSB可以是0或者1,逻辑0代表写,逻辑1代表读。如果忽略每个地址字节的LSB,十六进制则是0x80+i,而i每一次累加2。我们一个一个寄存器来看吧:

第一:秒钟寄存器地址字节;0x80

其 实呀,我很佩服该芯片的设计人员,将芯片设计得很贴心,为什么呢?因为秒钟寄存器,除了记录秒钟以外,还控制了DS1302的时钟开关(晶振开始工作,或 者晶振禁止工作)。该位第7位CH,当写入逻辑1时DS1302停止工作,时间的计时保持最后一次的状态,如果写入逻辑0DS1302则开始工作,时间从 最后一次状态中继续计时。

换成另一句话说,每一次写入秒钟,都会使DS1302工作,但这又是为什么呢?秒钟寄存器是八位寄存器,高四位中 的BIT4~BIT6(BIT7除外)记录十位,而低四位记录个位。秒钟的计算最多也是59,如果换成十六进制的话是0x59。所以呢,最高位基本上都是 用不到,但我们每一次向秒钟寄存器进行初始化的时候,都会很自然的把最高位BIT7记录成0,CH位为逻辑0,DS1302就开始工作。

第二:分钟寄存器地址字节;0x82

八位寄存器,高四位记录十位(BIT7除外),低四位记录个位

第三:时钟寄存器地址字节;0x84

八 位寄存器,高四位记录十位(BIT7,BIT6除外),低四位记录个位。时钟寄存器的最高位,决定了时间是以24小时制,还是12小时制。逻辑1为12小 时致,逻辑0为24小时致。至于24小时致是默认的,为什么这么说呢?该解释与秒钟寄存器很相似,因为每一次我们为时钟寄存器赋值的时候,由于时钟最大值 是23小时(0x23),还是11小时(0x11),自然而然最高位我们都会赋0值。

第四:日寄存器地址字节:0x86

八位寄存器,高四位记录十位(实际上仅有BIT4~5被使用),低四位记录个位。第五:月寄存器地址字节:0x88

八位寄存器,高四位记录十位(实际上仅有BIT4被使用),低四位记录个位。第六:周寄存器地址字节:0x8A

八位寄存器,仅有低四位被使用(BIT0~3),用来记录个位。

第七:年寄存器地址字节:0x8C

八位寄存器,高四位记录十位,低四位记录个位。

第八:控制寄存器地址字节:0x8E

八位寄存器,仅BIT7有用,BIT7亦即WP位(WriteProtect),逻辑0解除写保护,逻辑1开启写保护。换一句话说,每一次写其他寄存器WP位必须先置0。

第九:充电寄存器?(TrickleCharge)地址字节:0x90

这是开启细流充电的寄存器,写保留。后面有详细的解释。

第十:爆发寄存器?(BurstMode)地址字节:0x92

这个寄存器的功能可以用软件来模拟,无视他把。

.4DS1302的RAM1212.4

DS1302真的很厚道,还设立了31个字节的RAM空间,RAM空间的开始地址字节是0x94。RAM空间可以让使用着任意发挥,你可以把它当做外存储器,但是前提DS1302必须一直供电。要访问任意空间也很简单,如下表:

嗯,使用RAM空间的方法很自由,自己发挥想象力吧。

12.5细流充电TrickleCharge

在前几个Article,介绍了DS1302有后备供电的输入,亦即VCC1引脚,DS1302允许透过控制内部的充电寄存器,经VCC2向VCC1

流入的充电细流(很小很小的电流)。我们看一看Hj-2G的硬件布局吧:

VCC2连接的5v是工作电压,VCC1连接的3v是后备工作电压。充电寄存器就控制VCC2流向VCC1之间的“阻值”和“二极管的降压”。

在这里补充一些题外话:当VCC2有电源输入时,VCC1是停止供电的,但同一时间也可以为VCC1进行细流充电(这要看VCC1是否连接着可充电电池)。一旦

VCC2停止供电,VCC1就开始工作,与此同时细流充电就变成没有意义了。

以 上是TrickleCharge的概念图,充电控制寄存器的高四位(BIT7~4)是TCS,TrickleChargerSelect位,仅1010才 会开启充电功能。而BIT3~2是DS,DiodeSelect位,01选择一个diode,10选择两个diode串联。BIT1~0是 RS,ResistorSelect位,两位组成了3个阻值的选择。浏览下表:

RS位组合的阻值选择表细流公式

渴求细流的公式可以由上右表求出,阻值位R1,diodedrop为二极管的降压,0.7v(一个二极管),1.4(串联二极管)。不过我比较有爱,将他分成六个等级,为了编程更方便。#defineLV6

#defineLV5

#defineLV4

#defineLV3

#defineLV2

#defineLV10xA50xA90xA60xAA0xA70xAB//0.7降压,2k阻值,2.15mA//1.4降压,2k阻值,1.80mA

//0.7降压,4k阻值,1.07mA//1.7降压,4k

阻值,0.90mA

//0.7降压,8k阻值,0.50mA//1.4降压,8k阻值,0.45mA

12.6DS1302的地址字节

DS1302的地址字节,也可以作为命令字节。A0~A4是用来选择地址的位,而BIT0是制定该地址字节是写入还是读取,BIT6是作为选择RAM还是CLOCK的区别。其实这也没有什么困难的啦。

慧 争电子免费共享资料、欢迎复制共享、没有版权;12.7简单归纳;控制DS1302的是RST信号,拉低无效,拉高有;12.8DS1302头文件的理 解;自带来的DS1302头文件,没有自己定义的好;首先是按寄存器的地址字节,声明的宏定义;#defineCLKOFF0x80#define;以上 的宏定义,是作为配置时使用的;IO口的宏定义,可以使程式的可达性更高;接

慧争电子免费共享资料、欢迎复制共享、没有版权。HJ-2G多功能AVR/51二合一开发板学习笔记

12.7简单归纳

控 制DS1302的是RST信号,拉低无效,拉高有效。而DS1302是串行输入,串行读入。写入一个字节和读取一个字节的时序不同。写入一个字节都是上跳 沿有效,读入一个字节先是上跳沿有效,然后下降沿有效。地址字节也可称为命令字节,BIT6控制了对RAM/CLOCK的访问控制,而BIT0决定了地 址|命令字节的写入还是读出的特性。DS1302有31个字节的RAM空间。除此之外,还有细流充电的功能。秒寄存器决定了DS1302开始工作与否,而 时钟寄存器有12小时制和24小时制之分。每当要写入任何一个寄存器|RAM空间,写入控制寄存器的WP位必须置0,解除写保护。

12.8DS1302头文件的理解

自 带来的DS1302头文件,没有自己定义的好。这一章笔记,主要是以头文件的方式解释。这是我认为,新手最简单习得的办法。//命令,地 址#defineSEC0x80#defineMIN0x82#defineHOUR0x84#defineDAY0x86#defineMONTH0x88#defineWEEK0x8A#defineYEAR0x8C#defineCTRL0x8E#defineCHRG0x90#defineBRUST0xBE#defineRAM0xC0

首先是按寄存器的地址字节,声明的宏定义。SEC为开始,亦即0x80,随后都是+2。宏定义中的地址字节没有包括了,BIT0写/读的控制位。//配置

#defineCLKOFF0x80#defineCLKON0x00#defineT120x80#defineT240x00#defineLOCK0x80#defineUNLOCK0x00#defineLV60xA5#defineLV50xA9#defineLV40xA6#defineLV30xAA#defineLV20xA7#defineLV10xAB

以上的宏定义,是作为配置时使用的。自己看着明白吧,很简单而已。

IO口的宏定义,可以使程式的可达性更高。

接下来要为写一个字节函数,和读一个字节函数做介绍。

//DS1302写一个字节函数

voidDS1302_Write(unsignedcharAdd,unsignedcharData){

unsignedchari;

DDRB|=BIT(IO);

PORTB&=~BIT(SCLK);PORTC|=BIT(RST);Add&=0xfe;

//IO为输出

//时钟信号拉低<==这个很重要//拉高RST信号

//将Add亦即地址字节的LSB设置为0

for(i=0;i<8;i++)//写地址|命令字节,上跳沿有效{

PORTB&=~BIT(SCLK); //时钟信号拉低if(Add&0x01)PORTB|=BIT(IO);//判断地址字节的最低位,1位拉高IOelsePORTB& amp;=~BIT(IO);//否则拉低IOPORTB|=BIT(SCLK);//时钟信号拉高Add>>=1;//地址字节左移一位

}

for(i=0;i<8;i++)//写数据字节,上跳沿有效{

PORTB&=~BIT(SCLK);

if(Data&0x01)PORTB|=BIT(IO);elsePORTB&=~BIT(IO);PORTB|=BIT(SCLK);Data>>=1;

}

PORTC&=~BIT(RST);//拉低RST信号,结束写一个字节。}

//时钟信号拉低

//判断数据字节的最低位,1位拉高IO//否则拉低IO//时钟信号拉高//数据字节左移一位

函 数完全是依照时序图写的,该函数带有两个参数,Add和Data,顾名思义就是如字面上的意思,地址字节和数据字节。地址字节需经过0xfe与云散,取得 xxxxxxx0写字节|命令的特征。写地址和写数据字节都是下降沿有效。有一点需要注意的是:当RST信号还没有拉高之前,必须把时钟信号初始化|拉 第,这一点很重要请注意。(我就是在这一点犯了“小错误”)

这是读一个字节的函数,带unsignedchar返回型,但仅有一个参数,那 即是Add:地址字节。Add写入之前必须经过0x01的与运算,为了就是取得xxxxxxx1的地址|命令的读特征。在RST拉高之前,和写一个字节函 数一样的注意点,就是必须先将时钟信号拉低,不然的话函数会失败。

还有另一个注意点就是IO的状态,在写地址|命令字节的时候必须设置位输出状态,而当读取数据之前必须将IO设置位高祖态输入状态。程式的最后就是返回从IO读到的数据。

补充,写一个字节函数,和读一个字节函数是最基础和最重要的函数,不允许有错误出现,不然的话,一切的设定都徒劳无功。

//模拟BURST函数,读取数据|时间

voidDS1302_Get_Timer(unsignedchar*pTimer){

unsignedchari,j;

for(i=0,j=0;i<7;i++,j+=2)

pTimer[i]=DS1302_Read((0x80+j));

}

//i循环次数,j地址

//将数据一一读入时间数组

上 面两个函数是连续写时间,和连续读时间函数。只要明白数组传递的规则,基本上好似很简单就看懂了。但是有一个条件,就是必须按照以下的数组顺 序:unsignedcharTimer[]={0x40,0x59,0x23,0x31,0x12,0x07,0x09};//秒分时日月周年 unsignedcharBuffer[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};//缓冲数组Timer数组是 用于连续写入,而Buffer数组是用于连续读取。

//开启写保护函数voidProtect(){

DS1302_Write(CTRL,LOCK);}

//解除写保护函数voidUnprotect(){

DS1302_Write(CTRL,UNLOCK);}

启动写保护和关闭写保护的函数也很容易明白。自己看着办吧。

细流充电启动和关闭函数,关闭方没有什么特别,而启动方比较特别,带有Level的参数,实际上是对细流充电控制寄存器写入相关的值而已,而Level已经宏定义了,就是Lv1~Lv6.

//写入RAM函数

voidDS1302_RAM_Write(unsignedcharN,unsignedcharData){

if(N<32)//RAM地址不可以超过31

DS1302_Write(RAM+N,Data);

}

//读取RAM函数

unsignedcharDS1302_RAM_Read(unsignedcharN){

if(N<32)//RAM地址不可以超过31

returnDS1302_Read(RAM+N);

}

依然是那句话,很容易明白的函数。部比较不同的是,if语句预防万一了访问无效地址字节和越界访问。

DS1302 应用IO口的初始化函数,很简单,而且;//DS1302启动函数voidClock_On;DS1302_Write(SEC,CLKON);; //DS1302停止函数voidClock_Of;DS1302_Write(SEC,CLKOFF);启动工作和关闭工作的函数;//设时间为12小 时制函数;//该函数没有什么用处,24小时制为默认,为了演;DS1

DS1302应用IO口的初始化函数,很简单,而且程式也注释了。

//DS1302启动函数voidClock_On(){

DS1302_Write(SEC,CLKON);}

//DS1302停止函数voidClock_Off(){

DS1302_Write(SEC,CLKOFF);}

启动工作和关闭工作的函数。

//设时间为12小时制函数

//该函数没有什么用处,24小时制为默认,为了演示voidSet12(){

DS1302_Write(HOUR,T12);}

//设时间为24小时制函数

//该函数没有什么用处,24小时制为默认,为了演示voidSet24(){

DS1302_Write(HOUR,T24);}

该函数没有实际的用处,编辑出来时为了演示·解释·明白而已。

慧争电子免费共享资料、欢迎复制共享、没有版权。HJ-2G多功能AVR/51二合一开发板学习笔记

.9实例程式:1212.9

这里我就不编辑什么程式了,写了几个实例,自己看看吧~很简单而已,因为使用DS1302的方法太自由了。

实例程式1:

==============================================================//1200-DS1302_Driver.c

//DS1302最基本的驱动程式//akuei202-01-10#include"iom16v.h"#include"macros.h"#include"DS1302.h"#include"USART.h"voidDelay(unsignedcharx){

while(x--);}

voidmain(){

unsignedcharTimer[]= {0x02,0x35,0x23,0x15,0x11,0x07,0x33};//秒分时日月周年unsignedcharBuffer[]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00};unsignedcharTemp,Temp1,Temp2;inti;

DS1302_IO_Init();USART_Init();

//初始化DS1302的设置//Unprotect();Unprotect();Clock_Off();Set24();

//DS1302_Set_Timer(Timer);Protect();

//连续读时间

/*//读任意一个寄存器while(1){

Temp=DS1302_Read(YEAR);Temp1=((Temp>>4)&0x07);Temp2=(Temp&0x0f);

USART_Send('0'+Temp1);

//读年寄存器//取十位//取个位//串口发送

一个很简单的实例程式,介绍了很多的设置办法。

慧争电子免费共享资料、欢迎复制共享、没有版权。HJ-2G多功能AVR/51二合一开发板学习笔记

实例程式2

==============================================================//1201-DS1302_RTC.c

//DS1302最基本的驱动程式//akuei204-01-10#include"iom16v.h"#include"macros.h"#include"DS1302.h"#include"USART.h"//延迟函数

voidDelay(unsignedcharx){

while(x--);}

//主函数voidmain(){

unsignedcharTimer[]= {0x40,0x59,0x23,0x31,0x12,0x07,0x09};//秒分时日月周年unsignedcharBuffer[]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00};//缓冲数组unsignedcharTemp,Temp1,Temp2; //缓冲变量inti,j,k;//for循环变量

DS1302_IO_Init();USART_Init();

//初始化DS1302的设置Unprotect();Clock_Off();Set24();

DS1302_Set_Timer(Timer);//Charge_On(LV3);Clock_On();Protect();

//DS1302引脚初始化//USART初始化

//解除写保护//时钟关闭

//设置为24小时制

//设定时间

//充电开启,等级3,大约1mA//时钟启动,秒种从00计数开始//开启写保护

while(1){

for(k=0,j=0;k<7;k++,j+=2)

{

Temp=DS1302_Read(0x80+j);Temp1=((Temp>>4)&0x07);Temp2=(Temp&0x0f);

//读取时间//取十位//取个位

//循环7次,分别读取秒分时日月周

这是另一个的实例程式。怎样编写程式都不重要,最重要是编程的目的。以上两个实例程式我都使用了串口发送函数,而一般的实例程式都是使用1602液晶。见仁见智吧。

最后附上USART.h和完整的DS1302.h头文件(注意:只是适合HJ-2G)

==============================================================

//DS1302.h//命令,地址#defineSEC#defineMIN#defineDAY#define

0x800x820x86

#defineHOUR0x84

MONTH0x88

#defineWEEK0x8A#defineYEAR0x8C#defineCTRL0x8E#defineCHRG0x90#defineBRUST0xBE#defineRAM//配置

#defineCLKOFF0x80#defineCLKON0x00#defineT12#defineT24#defineLOCK#defineLV6#defineLV5#defineLV4#defineLV3#defineLV2

0x800x000x800xA50xA90xA60xAA0xA7

//0.7降压,2k阻值,2.15mA//1.4降压,2k阻值,1.80mA//0.7降压,4k阻值,1.07mA//1.7降压,4k阻值,0.90mA//0.7降压,8k阻值,0.50mA

0xC0

#defineUNLOCK0x00

//USART.h; //波特率,晶振;#defineBAUD9600;#defineFXTAL11059200;//串口接收完毕中断触发声 明;#pragmainterrupt_handler;//变量定义:接收缓冲变量,接收标志位;unsignedcharRX_Buffer=0x; //函数声明;voidUSART_Send(unsignedc;//串口IO初

//USART.h

//波特率,晶振

#defineBAUD9600

#defineFXTAL11059200

//串口接收完毕中断触发声明

#pragmainterrupt_handlerUSART_Received_Ir:12

//变量定义:接收缓冲变量,接收标志位

unsignedcharRX_Buffer=0x00,RX_Flag=0;

//函数声明

voidUSART_Send(unsignedchar);

//串口IO初始化函数

voidUSART_IO_Init()

{

DDRD|=BIT(PD1);//PD1:TX为输出状态

}

继续阅读>>