上一节我们讲了DS1302的工作原理,这一节我们开始代码演示。
新创建一个工程写上框架
我们需要LCD1602进行显示,所以我们要将LCD1602调试工具那一节的LCD1602的模块化代码给添加进来
然后我们开始创建一个DS1302.c和DS1302.h
根据原理图,为了方便操作,我们要在DS1302.c文件重新定义我们的三个端口
定义好之后我们操作重命名的名字,就相当于操作单片机的这三个引脚。
然后我们就可以根据上节讲的工作原理模拟这个时序
首先我们要写一个函数,叫做单字节写DS1302,模拟这个时序
第二个函数是单字节读DS1302,模拟这个时序
然后初始化DS1302
开始写字节,第一步将CE置1
下一步把命令字的第0位放在IO线上
然后DS1302_SCLK置1再置0
这里置1后立马置0的话需要考虑我们芯片能够承受的时钟最快频率,因为置1后立马置0的过程中,可能时钟线操作的太快,而时钟芯片反应不过来。
所以要看看芯片手册上支持的最快速率是多少。
比如手册上给出了这个时序上每一段的操作时间表格
还画了图表示哪里到哪里对应的是表格中的哪一个时间
比如说tDC对应的是当VCC是2V的时候,操作时间最小不能小过200ns,如果VCC是5V的时候,时间最小是50ns。
但是由于我们51单片机的速率是比较慢的(一个机器周期是1微秒),所以置1到置0之间可以不用加延时。如果有的单片机的速度很快的话,置1到置0之间要加一段延时,等IO后完成数据交互之后再置0。
经过以上三步后我们的时序已经走完到这一步了
接下来我们接着写入命令字的第1位,第2位,第3位......数据(保持CE=1)
我们可以用for循环来实现
更正:图中应该是i<8
这个循环完成后整个时序就进行到这个地方
最后那一半时序其实和前面一半的时序操作几乎一模一样,只是它的数据不同
可以直接复制过来改一下
更正:图中应该是i<8
这个循环完成后我们时序已经完成到这里了
最后就是把CE给清零
更正:图中应该是i<8
这样单字节写的这整个时序就已经完成了。
数据写进去后,我们还要让单片机读出来,最后再显示出来才能看到我们写进去的数据。
接下来我们就操作单字节读的时序
操作命令字部分的时序几乎一样,可以直接复制过来
更正:图中应该是i<8
这样整个时序就进行到这里了
但是这里有个问题当时钟来一个下降沿的时候,时钟芯片就会向单片机输出数据了,这就意味着如果我们直接这样写的话,那进行到最后一轮for循环的时候就同时有一位数据出来了,我们应该想要时序进行到这一步才对
所以得调整一下顺序,先给0,再给1
更正:图中应该是i<8
这样当第一个for循环结束后,时序就不是到这里来了
而是到这里来了
最后一个for循环结束后就到这里来了
这样我们这个for循环就全部是与命令字有关的,
当这一部分时序进行完之后,这里单片机就要释放掉对IO口的控制,然后把IO口的控制权转交给DS1302。DS1302输出数据,然后单片机在这里直接读取IO口的状态。
因此接下来先给下降沿
更正:图中应该是i<8
那么数据就已经到了IO口的线上了,所以我们直接读
所以我们再给一个上升沿
更正:图中应该是i<8
这个时候时序就进行到这里了
这个时候DS1302操作IO口把它想要输出的数据的D0给到IO口线上了。
然后先定义一个变量Data,然后进行循环读出数据的每一位
这样就和这个时序完全相同了
PS:连续两次置1时序上的电平不会产生变化,并且单字节读的时序比单字节写的时序少了一个脉冲,那么我们这个地方重复置了一个1的话可以补充少了的这个脉冲。
最后别忘记CE清零
把Data返回(到时候主程序里面调用这个函数的话,返回值就可以打印出来了)
返回值类型也要改
这里有一个重点!敲黑板!重点来了!
读取完数据时候一定要给IO口清零!!!
如果不写上这一句代码的话效果是这样的,请看视频,读出来的数据是乱码的!
DS1302实时时钟2
这样读和写的两个时序已经模拟完成,我们需要再DS1302.h文件里面进行声明一下这几个函数
到这里我们的读写程序基本已经完成了
BCD码
然后还需要补充一个知识:BCD码
DS1302的内部寄存器不是以正常的二进制进行存储的,而是以BCD码进行存储的。
BCD码(Binary Coded Decimal),用4位二进制数来表示1位十进制数
高四位表示十进制的十位,低四位表示十进制的个位
例:0001 0011表示13,1000 0101表示85,0001 1010不合法
在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法
(10的BCD码是0001 0000,转换成正常的十进制是16)
BCD码转十进制:DEC=BCD/16(把高四位提取出来)*10+BCD%16(把低四位提取出来); (2位BCD)
十进制转BCD码:BCD=DEC/10(得到十位)*16+DEC%10(得到个位); (2位BCD)
所以我们回头这一下这张表格就能看明白了
以秒寄存器为例,这张表格上写明低四位存的是秒,而高位的那三位(0-50就只能用到三位(0)101)存的是10秒,CH置为1的话,秒就会停止,整个时钟就会停止。CH=0的话时钟时钟是运行的。
所以想要最后的结果显示成正常的十进制数的话就要将结果转换成十进制再显示。
如果还是想转换成十进制再显示的话可以利用这一个转换公式
BCD码转十进制:DEC=BCD/16(把高四位提取出来)*10+BCD%16(把低四位提取出来); (2位BCD)
设置时间时,要将十进制转换成BCD码写进时钟芯片,可以利用这个公式
十进制转BCD码:BCD=DEC/10(得到十位)*16+DEC%10(得到个位); (2位BCD)
我们要将年月日,时分秒,星期显示在液晶屏上的话还要在DS1302.c文件中定义是个数组,然后再写两个函数,方便我们后续设置时间
为了方便后面不用每次都查找这个表上的地址码
我们可以把这个表格上的写入地址重定义
然后我们知道,写入时的命令字最低位肯定是0,读出时的命令字最低位肯定是1
所以我们在ReadByte()这里加一行这样的代码
因为这个读的命令字一定是1了,将Command或0x01之后,那它的最低位肯定是1,也就是肯定是读,然后我们给地址的时候,可以直接给写的地址,因为写的地址或了0x01之后就是读的地址,这样我们就不需要重复定义那么多地址了。因此我们就不需要把读的地址也弄成这样了
要想获得读的地址就直接将写的这个地址|0x01就变成读的地址了!
于是两个函数写完之后是这样
将这两个函数和数组都声明一下
最后DS1302.c的完整代码如下:
最后我们在主程序中调用函数然后运行
运行后的效果,请看视频:
DS1302实时时钟1
本节的代码演示就到这里。
之后有时间再演示一个可调时钟的代码!
源码放在评论区,自取!有什么问题,可以评论区留言。