1 DS1302是什么
DS1302由两块存储器组成,一个是日历时钟寄存器还有一个是31位的静态RAM存储器。
而在蓝桥杯中常考的就是日历时钟寄存器,故这里只介绍日历时钟寄存器。简单来说,其就是一个“电子表”,他会自动的实时记录时间,而不需要像我们之前运用定时器做的时钟一样,要自己来设计时钟怎么运行,DS1302是已经自动设计好了,我们只需要写入从哪个时刻开始计时,以及读取相应的时间即可。
2 DS1302的结构
2.1 硬件原理图(就是单片机上DS1302芯片的引脚连接情况)
这里比较重要的就是SCK,RST,I/O(SDA)这三个引脚,其在头文件中需要定义,要学会看原理图这三个口连接哪三个引脚。
2.2 内部寄存器结构图
2.2.1 控制字的格式
控制字简单理解就是控制写入或读取哪个地址啊,是读取还是写入啊,用的是RAM还是CK啊这些操作,各个位的要求如图。
(再往下看,其实这个控制字就是下个图中左侧读寄存器和写寄存器的那些地址为什么是这样写。)
比如:当选择日历时钟给模式。
读寄存器的第一个地址为81H, BIT7=1,BIT6=0,BIT5~BIT1=0,读操作故BIT0=1,
即1000 0001,写成16进制为81H
写寄存器的第一个地址为80H,BIT7=1,BIT6=0,BIT5~BIT1=0,写操作故BIT0=0,
即1000 0000,写成16进制为80H。
读寄存器的第二个地址为83H, BIT7=1,BIT6=0,BIT5~BIT2=0,BIT1=1,读操作故BIT0=1,
即1000 0001,写成16进制为83H
......
2.2.2 日历时钟寄存器
(以下图来源小蜜蜂老师,注意:有个错误,CH为1的时候时钟是停止为0的时候是开始运行,这里小蜜蜂老师写反了。)
日历时钟寄存器中一共包含8个8位寄存器,前7个分别为秒——分——时——日——月——周——年,(黄色高亮部位即为可以用于表示时间数据的位)
最后一个为控制寄存器,有效位只有BIT7,其他强制为0。(具体操作如图)
第一个秒寄存器:(CH为1的时候时钟是停止为0的时候是开始运行,这里小蜜蜂老师写反了。)
第三个时寄存器BIT7位表示可以选择是12小时模式还是24小时模式,当选择24小时时,BIT5位用于选择AM还是PM模式。
注意:时间数据是用BCD码表示,(以八位数为例,前四个为十位,后四个为个位,且每四个数只能表示0~9),因此,图中黄色高亮部分有的写10秒,10分,指的是秒的十位和分的十位,(由于秒和时十位最多为5,故只需要用三个位就可以表示,最高位恒为0,只是秒的最高位被利用成了CH位而已。)
3 DS1302代码编写即使用
(1):地址写成数组
首先,日历时钟寄存器经过控制字格式来写,地址是固定的,因此在代码编写中将读写寄存器地址写成一个固定的数组会方便后续操作。
(与秒——分——时——日——月——周——年即上一个图中的顺序一 一对应,由于最后一个寄存器只有首位有效,我们就放到后面单独写,就不写进数组里了)
///写寄存器地址
unsigned char code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};//读寄存器地址
unsigned char code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
(2):时间参数写成数组
DS1302的基本操作实际上非常简单,只有两个操作:其一是设定时间参数,其二是读取实时时间。不管是那个操作,MCU都要通过SPI接口进行数据交互,而SPI接口有其规定的时序,这个必须参考数据手册(这个在官方给的底层代码中.c文件有写,有空可以去看看,所以在这里只需要调用里面函数即可。)
要设定时间参数就要写进时间,因此把要写进去的时间参数也写成一个数组会比较方便
比如:注意写入顺序,秒——分——时——日——月——周——年
这里写成16进制时没有进行下一节(第四节)中讲的数据的转换,而是直接写成了16进制(已转换为为二进制的形式),这是因为若每四个单元都只是0~9,那么BCD和16进制(已转换为二进制的形式)是一样的,故直接写即可。
(3)调用函数来进行读取实时时间
调用以下两个函数(底层代码.c文件)可能有时候名称会有改变具体看你用的底层代码,传入数组的地址以及数据即可进行读取。
unsigned char Read_Ds1302_Byte ( unsigned char address )
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
(注意:千万别漏了还有几个寄存器要单独写的,在执行函数中记得写!!)
4 DS1302读写数据时的转换 !!!!!!
这是个难点!
因为DS1302储存是按BCD来储存的,然后我们需要知道一个概念,就是单片机内的数据都是以0,1这两种形式储存(不论是二进制还是BCD等本质还是01),但是在计算时,单片机就直接默认以这些数据(不论是二进制数还是BCD等等)通通运用二进制的转换规则来转换成十进制,在编写代码进行运算时,就直接把这些数据看成是十进制数来进行运算公式的编写。
但是读取显示出来的数据是以16进制的数来表示的(这个就不要管,你在编写公式时就想着这是个十进制数来写公式转化就行了!!!!)
4.1 然后就要解释一下各种进制的转换关系:
(图源:BCD码以及各进制之间的相互转换-CSDN博客)
特别要说一下十六进制和BCD码的区别。
16进制其实就只有0~F这些单个数字,平时我们运算时的每四位作为一个单元,然后每个单元对应0~F这16个数,写成01形式后就是16进制转换为二进制后的结果(即16进制本身为单个数字,而写成每四个是已经转换为2进制的结果),为了不易弄混,其转化为十进制数记成先转成二进制数在以二进制数的方法转十进制(即2^0 * _+ 2^1 * _ +....)
而BCD码本身就是以每四个数字为一个单元,只是每个单元对应的只有0~9这十个数字,而每个单元作为十进制的一位,比如11,BCD码为0001 0001(前四个数转化为十进制是1,为十进制的十位数,后四个数转化为十进制是1,为十进制的个位数,)因此BCD码还要继续转换为二进制。(比如下图中10的表达,二进制是1010,而BCD是0001 0000)
(来源bcd码和16进制的区别_bcd和16进制的区别-CSDN博客)
但是,当四个为1个单元只表示0~9时,此时BCD和16进制(已转换为二进制的形式)是一样的,此时写入数据就可以直接用16进制表达,而不需要在进行转换。
所以可以看到,对于同一个十进制数,用BCD表达,十六进制表达二进制表达都是不一样的。
4.2 DS1302中的转换
在DS1302中数据是以BCD储存的,但是我们讲到在单片机运算时,单片机是默认运用二进制的转换规则来把这些0101形式转换为十进制来进行运算(无论是写入单片机还是读取的单片机的数据),那么在写入和读写数据时(通常只用16进制来表示写入和读写的数据),就要进行BCD码的转换。
(参考:DS1302内码和10进制二进制十六进制之间转换.doc (book118.com),图片来源也是这个文档。)
写入DS1302:
要写入一个十进制为45的数,如果直接把45写进单片机的DS1302,(前面说到,单片机默认是以转换二进制的规则来把二进制转十进制,或者十进制转二进制),那么单片机就会运用2进制规则转换45后储存起来,即等于0010 1101(2^5 * 1 + 2^3 *1 +2^2 * 1 + 2^0 * 1),但是DS1302又是以BCD码的形式储存的,0010 1101对应的BCD码是完全不存在的(因为1101对应>9了,而且单看前四位为2,说明十进制数的十位为2,也是不对的),因此,我们要进行数据的转换,使得写进去的数最终表示为BCD码的形式转换回来十进制是我们想要的十进制数。简单说,我们想要45,但我们不能直接写45,我们要写69,这样单片机按2进制规则储存起来的就是0100 0101,按BCD方式转化为十进制就是我们想要的45。
以下例子的公式就是把所谓45转换为69的过程。
读取DS1302:
同样,当BCD存储为0100 0101,如果我们直接读取这个数据,出来的数对应十进制应该是69,因此,我们要把BCD码转换成45对应的二进制,再进行读取,最终显示的是以16进制来显示(转化为十进制是45)。
以下例子的公式就是把69转换为45的过程。