DS1302菜鸟级讲解
2015-11-04
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为输出状态
}