一 DS13302时钟简介
DS1302是时钟的芯片,可以显示对应的时钟和日期,这里有一个前提,需要连接着电源,如果把程序在今天烧录到板子里,然后不连电,第二天的数据并不准确,所以说,如果想要准确的日期,那么需要一直给板子提供电源。
这里所说的数据,使用841BCD码来保存并传输,传输八位数据,这八位数据的最后一位,决定着是读还是写;1表示读,0表示写,如果想要真正运行DS1302,需要先写再读。
二 原理
上面这个是板子提供的原理图中的电路图,在这里,我们只需要关注三条线:RST、I/O、SCLK,这三根线决定着数据的传输,接下来详细的介绍一下:
- RST:是控制着整体写的过程,并且只有把RST的电压拉高才能往里面写数据
- SCLK:这是一个时钟,他一直以某一种速度发送频率,低电平高电平一直发送,他的作用是控制着节奏,并且是有一个上升沿读取一个。(RST必须是高电平)
- I/O:他的作用主要是发送地址 + 数据,并且他遵循先写低位,再写高位
三 写的过程
微观
现在,假设,我要写86,换成BCD码,1000 0110,先从最低位,从右边第一位的0开始写起,向右走,现在把1000 0110中的0拿出来,然后再将1000 0110向右移一位,(最后一位去掉,最前面加个零)变成了0100 0011,再重复循环,取八位值
宏观
先将RST拉低,等一会,SCLK拉低,等一会,再将RST拉高,等一会,把数据的地址+data写进去,最后再关闭RST
可视化:
四 写和读的区别
在读的时候,和写的过程有不同的地方,在写的时候,我们当SCLK有上升沿的时候,进行一次数据的写,但是在读的时候,读地址:SCLK是上升沿;读数据:SCLK是下降沿
可视化:
五 代码层面
不用慌张要背这么多代码,因为在比赛的时候,会有范例代码给到我们,我们只需要到对应的文件夹中赋值即可,接下来说一下详细过程:
在比赛提供的文件夹中,将ds1302.c和.h的两个文件放到我们的代码中,然后打开ds1302.c文件进行代码的编辑。
DS1302.c
写函数(set_RTC)
在该文件中,我们需要将想要设置的时分秒(假设是22点59分55秒),转化成8421BCD码,转化成计算机所能看得懂的数字,随后,再将计算机看的懂得数字再转成最后实现的时间。
这里的设置的时分秒它属于是一个数组,ucRTC[3]
在写的过程中,要先把高位赋值成0,因为高位(WP)决定着这个写的功能,WP的位置是在8Eh,随后我们看到write一列,我们想要设置时分秒,我们需要找到对应的寄存器的位置,84h、82h、80h。最后一定要记得把WP高位再给关闭了。
相应的写函数的代码为:
//设置1302时分秒函数
//入口参数是数组,包含时、分、秒数据
void Set_RTC(unsigned char *ucRTC) //这里的*,待定
{unsigned char temp; //中间局部变量,存放时分秒Write_Ds1302_Byte(0x8e,0); //WP=0 允许写write操作//这里需要记忆://0x8e WP=0,允许写的操作;0x80不允许//0x84 小时//0x82 分钟//0x80 秒temp = ((ucRTC[0] / 10 ) << 4) + (ucRTC[0] % 10) ; //数组的第零个数据,是小时,23 // 2(0010)对应的是高位,3(0011)对应的是低位Write_Ds1302_Byte(0x84,temp); temp = ((ucRTC[1] / 10 ) << 4) + (ucRTC[1] % 10) ; //数组的第一个数据,是分钟,59 Write_Ds1302_Byte(0x82,temp); temp = ((ucRTC[2] / 10 ) << 4) + (ucRTC[2] % 10) ; //数组的第一个数据,是秒,59 Write_Ds1302_Byte(0x80,temp); Write_Ds1302_Byte(0x8e,0x80); //WP=1 禁止写write操作}
在temp和ucRTC[]之间的转换我给大家举个例子,例如,我现在要写23这个数字,我们先把2这个数字提取出来,需要先让23除以10,得出来了一个2,但是这个2计算机是识别不了的,自动把2写成了8421BCD码,也就是0010,然后我们将0010向左移动四位,然后再加上“23除以10取余,也就得到了3,3转换为BCD码成了0011”,最后23这个人能看懂的数字,转换成了0010 0011,并赋值给temp。
读函数(Read_RTC)
在上面的写函数已经将23这个数字,转换成了0010 0011,我们现在又需要将这个8421BCD码转换成人能看得懂的形式,那么如何取高四位和第四位呢,先将这个BCD码向右移动四位,得到了0010,然后将0010如何将0010转换成十进制也就是我们能看得懂的代码呢,我们需要将他再乘以10,就成了十进制,随后将0010 0011和0x0F相与,就可以得到第四位的东西,也就是0011,把他们相加就是最后的呈现数字啦。当然,也要记得把对应的地址写出来哦,对应读的地址,时分秒分别在85h、83h、81h。
//读取DS1302时分秒的函数
//入口参数:将读取到的时分秒数据存放到数组指针当中
void Read_RTC(unsigned char *ucRTC)
{unsigned char temp;temp = Read_Ds1302_Byte ( 0x85); //读取小时ucRTC[0] = ((temp >> 4)*10 )+( temp & 0x0F ); //读取的是23(BCD),但是转成十进制的数是35,不能直接赋值,要进行BCD到十进制的转换temp = Read_Ds1302_Byte ( 0x83); //读取分钟ucRTC[1] = ((temp >> 4)*10 )+( temp & 0x0F );temp = Read_Ds1302_Byte ( 0x81); //读取秒ucRTC[2] = ((temp >> 4)*10 )+( temp & 0x0F ); }
main.c
在主函数中,我们想要实现的功能是按一下按键,变成计时器,再按一下按键,是时钟
要特别强调一点,这里虽然代码很长,但是这是基于上一篇文章“通过按键控制数码管显示数字”,如果大家有所看不懂的,请先看懂上一篇文章,再来本章学习。
#include <STC15F2K60S2.H>
#include "Timer.h"
#include "dsp_init.h"
#include "dsp_seg.h"
#include "stdio.h"
#include "key.h"
#include "ds1302.h"
#include "dsp_led.h"//函数声明,三个东西主体循环
void Key_Process(void); //按键处理,底层的数据
void Seg_Process(void); //显示处理,显示信息的生成
void Led_Process(void); //LED状态信息//全局变量
//显示专用
unsigned char seg_buf[8]; //放置字符串转换后的段码到数组
unsigned char seg_string[10]; //放置字符串
unsigned char pos = 0; //中断显示专用//按键专用
unsigned char Key_Val; //按键的数值存储变量
unsigned char Key_Down;
unsigned char Key_Old;//按键和显示函数减速专用,基本不变,我不写试一下
unsigned char Key_Slow_Down;
unsigned char Seg_Slow_Down;//可能会改变
//unsigned_char Seg_Show_Num; //准备显示出来的数值//运行时间
unsigned int ms_count = 0;
unsigned char s_count = 0;
//运行状态
unsigned char Change_State;
unsigned char ucled;//使用1302时,基本不变
unsigned char ucRTC[3] = {23,23,55}; //数组初始值,里面放的是时分秒void main()
{Cls_Peripheral(); //清除外设Timer1Init(); //定时器1,1ms进入一次Set_RTC(ucRTC); //23.59.55while(1){Key_Process(); //按键处理,底层的数据Seg_Process(); //显示处理,显示信息的生成Led_Process();}}//定时器初始化
//-----------------------------------------------/* Timer1_interrupt routine */
void tm1_isr() interrupt 3
{if(++Key_Slow_Down == 10) Key_Slow_Down = 0;if(++Seg_Slow_Down == 10) Seg_Slow_Down = 0;if(++ms_count == 1000) //记录运行时间{s_count++;ms_count = 0;}Seg_Disp( seg_buf,pos);//数码管显示if(++pos == 8){pos = 0;}Led_Disp(ucled);//ucled显示}//key_process的函数内部
void Key_Process(void) //按键处理,底层的数据
{//读取按键按下的编号数值Key_Val = Key_Read(); Key_Down = Key_Val & (Key_Old ^ Key_Val);Key_Old = Key_Val;//以上这五行,就是检验下降沿//根据代码改变if(Key_Down) //如果捕捉到下降沿跳变{//Seg_Show_Num = Key_Down;if(++Change_State == 2) Change_State = 0; //保证Change_State在0、1之间翻滚}}//seg_process的函数内部
void Seg_Process(void) //显示处理,显示信息的生成
{if(Seg_Slow_Down) return;Seg_Slow_Down = 1;switch(Change_State){case 0: Read_RTC(ucRTC); //读取1302内部时分秒的数据,放到预定义的数组空间中sprintf(seg_string,"%02d-%02d-%02d",(unsigned int)ucRTC[0],(unsigned int)ucRTC[1],(unsigned int)ucRTC[2]);break;case 1:sprintf(seg_string,"%8d",(unsigned int)s_count);break;}//永远不变Seg_Tran(seg_string, seg_buf);}//Led_Process的函数内部
void Led_Process(void)
{switch(Change_State){case 0: ucled = 0xF0;break;case 1:ucled = 0x0F;break;}}