大家好,最近发现IDE自身的库没有DS1302,因此依据网上的资料写了DS1302的库。这里将分享DS1302库的原理。
DS1302 是美国 DALLAS 公司推出的一种高性能、低功耗、带 RAM 的实时时钟电路, 它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能。且能自动对少于31天的月份日期进行调整,支持12小时制和24小时制。简而言之,利用DS1302时钟模块能帮助我们得到实时的时间。
首先创建新的库。利用Stduino IDE创建库还是很方便的。如下,点击菜单栏中的“库开发”,在下拉菜单栏中选择新建库:
接下来选择库开发的语言,这里我们选择C++。
之后输入我们创建的库的名称,建议名称简明扼要。我这里直接写的DS1302.h。(名称只能包含英文字母和数字)
点击确定后,IDE就自动生成了文件目录,还是很方便的。其中.cpp文件包含具体的功能实现代码、.h头文件包含库文件的预定义函数和变量,还包括库属性描述文件、关键词文件、示例文件。
我们在左侧的文件树中,打开“libraries”文件夹,发现已经生成了DS1302相关的文件目录。
接下来我们来完善库的代码。
DS1302的内部寄存器构成如下: 首先,我们需要明确DS1302的操作顺序: 第一步,选择需要访问的存器(利用地址),并设置需要进行的操作(读/写 )。 第二步,访问相应时钟/日历寄存器或者RAM,并传出/传入数据。
而访问地址、传输数据通过命令字节来实现。 命令字节如下图所示。每次数据传输均由命令字节启动。 当需要读写时,最高位必须为逻辑1(即Bit 7),否则无法读写。Bit 6代表访问的内容是时钟和日历(逻辑0),还是RAM(逻辑1)。剩余的Bit 1到Bit 5指定要输入/输出的寄存器。最低位指定具体的操作(读:逻辑1;写:逻辑0)。始终从最低位(bit 0)开始输入命令字节。 具体而言,RAM命令与数据内容: 日历时钟寄存器命令与数据内容。因此,可以定义操作寄存器的地址: - #define DS1302_SEC 0x80//秒寄存器地址
- #define DS1302_MIN 0x82//分寄存器地址
- #define DS1302_HR 0x84//时寄存器地址
- #define DS1302_DAY 0x86//日寄存器地址
- #define DS1302_MON 0x88//月寄存器地址
- #define DS1302_WEEK 0x8a//星期寄存器地址
- #define DS1302_YEAR 0x8c//年份寄存器地址
复制代码以及整体的时间数据: - typedef struct __TIME__
- {
- unsigned char Second;
- unsigned char Minute;
- unsigned char Hour;
- unsigned char Week;
- unsigned char Day;
- unsigned char Month;
- unsigned char Year;
- }TIME; //定义时间类型
复制代码因此我们需要用到的函数,包括读写操作,还需要定义字节操作的函数(初始只能读写比特)。 - extern void DS1302_Write(unsigned char ucAddr, unsigned char ucDa);//写操作,包括地址与写的数据(字节)两个参数
- extern unsigned char DS1302_Read(unsigned char ucAddr);//读操作
- extern void DS1302_InputByte(unsigned char dat);//字节传入
- extern unsigned char DS1302_OutputByte(void) ;//字节传出
- extern void DS1302_SetProtect(unsigned char flag) ;//设置位保护
- extern void DS1302_SetTime(unsigned char Address, unsigned char Value);//设置时间
- extern void DS1302_GetTime(TIME *Time);//获取时间
- extern void DS1302_Init(void);//初始化
复制代码数据传输可以通过命令字节来控制,但是需要遵循一定时序,从而保证数据有条不紊的传输。 数据传输时序如下图所示。 简而言之,需要遵循: 数据传输前,需要设置RST为高电平,直到传输完毕,设置RST为低电平,结束传输。
RST输入有两个作用。第一,打开控制逻辑(该部分允许访问地址/命令序列的移位寄存器。)第二,RST信号提供了终止单字节或者多字节数据传输的方法。 数据开始传输前,在设置RST为高电平前,需将SCLK设置为低电平。
时钟周期是下降沿跟着上升沿的序列。在时钟处于上升沿时数据才被传输,在时钟的下降沿时停止输出。因此当RST设置为高电平开始传送数据前,SCLK必须设置为低电平。 如果RST输入为低电平,则所有的数据传输终止,并且IO引脚变为高阻抗状态,因此数据传输过程中,RST要一直置为1。 传输完毕时,先把CLK置为高电平,确保无问题;再将RST置0,终止传输。 上电时,RST必须为逻辑0,直到VCC> 2.0V。此时RST也必须被置为逻辑1状态时,SCLK为逻辑0。
因此,可以再添加一些高低电平设置的预定义: - #define DS1302_RST 2 //RST复位线引脚
- #define DS1302_IO 3 //IO数据线引脚
- #define DS1302_CLK 4 //SCLK时钟线引脚
- #define DS1302_RST_0 digitalWrite(DS1302_RST,LOW)//复位置为低电平
- #define DS1302_RST_1 digitalWrite(DS1302_RST,HIGH)//复位置为高电平
- #define DS1302_IO_0 digitalWrite(DS1302_IO,LOW)//IO置为低电平
- #define DS1302_IO_1 digitalWrite(DS1302_IO,HIGH)//IO置为高电平
- #define DS1302_CLK_0 digitalWrite(DS1302_CLK,LOW)//时钟置为低电平
- #define DS1302_CLK_1 digitalWrite(DS1302_CLK,HIGH)//时钟置为高电平
复制代码DS1302.h的基本内容写完啦,如下: - /***********************************************************************
- * @代码说明:DS1302的库函数
- * @作者:admin
- * @日期:2020/07/27
- * @开发指导:wiki.stduino.com
- ***********************************************************************/
- #ifndef __DS1302_H__
- #define __DS1302_H__
- #include "Stduino.h"
- //DS1302 IO设置
- #define DS1302_RST 2 //RST复位线引脚
- #define DS1302_IO 3 //IO数据线引脚
- #define DS1302_CLK 4 //SCLK时钟线引脚
- #define DS1302_RST_0 digitalWrite(DS1302_RST,LOW)//复位置为低电平
- #define DS1302_RST_1 digitalWrite(DS1302_RST,HIGH)//复位置为高电平
- #define DS1302_IO_0 digitalWrite(DS1302_IO,LOW)//IO置为低电平
- #define DS1302_IO_1 digitalWrite(DS1302_IO,HIGH)//IO置为高电平
- #define DS1302_CLK_0 digitalWrite(DS1302_CLK,LOW)//时钟置为低电平
- #define DS1302_CLK_1 digitalWrite(DS1302_CLK,HIGH)//时钟置为高电平
- //宏定义
- #define DS1302_SEC 0x80//秒寄存器地址
- #define DS1302_MIN 0x82//分寄存器地址
- #define DS1302_HR 0x84//时寄存器地址
- #define DS1302_DAY 0x86//日寄存器地址
- #define DS1302_MON 0x88//月寄存器地址
- #define DS1302_WEEK 0x8a//星期寄存器地址
- #define DS1302_YEAR 0x8c//年份寄存器地址
- typedef struct __TIME__
- {
- unsigned char Second;
- unsigned char Minute;
- unsigned char Hour;
- unsigned char Week;
- unsigned char Day;
- unsigned char Month;
- unsigned char Year;
- }TIME; //定义时间类型
- extern void DS1302_Write(unsigned char ucAddr, unsigned char ucDa);//写操作,包括地址与写的数据(字节)两个参数
- extern unsigned char DS1302_Read(unsigned char ucAddr);//读操作
- extern void DS1302_InputByte(unsigned char dat);//字节传入
- extern unsigned char DS1302_OutputByte(void) ;//字节传出
- extern void DS1302_SetProtect(unsigned char flag) ;//设置位保护
- extern void DS1302_SetTime(unsigned char Address, unsigned char Value);//设置时间
- extern void DS1302_GetTime(TIME *Time);//获取时间
- extern void DS1302_Init(void);//初始化
- extern void DS1302_ON_OFF(bool FLAG_ON_OFF);//
- extern TIME DS1302Data;//用于储存时间数据的整体
- extern bool Flag_Time_Refresh;
- #endif
复制代码
接下来实现每个操作函数,这一部分在DS1302.cpp中完成。 首先是,字节输入与输出:- //****************************************************
- //写入一字节:上升沿写入数据
- //****************************************************
- void DS1302_InputByte(unsigned char dat)
- {
- unsigned char i;
- pinMode(DS1302_IO,OUTPUT);
- for(i=0; i<8; i++)
- {
- if((dat & 0x01) == 1)
- {
- DS1302_IO_1;
- }
- else
- {
- DS1302_IO_0;
- }
-
-
- DS1302_CLK_0; //低电平
-
- delayMicroseconds(2);
-
- DS1302_CLK_1; //高电平,结合低电平,一次上升沿,写入数据
-
- delayMicroseconds(2);
- dat >>= 1;
- }
- }
- //****************************************************
- //读取一字节:下降沿读取数据
- //****************************************************
- unsigned char DS1302_OutputByte(void)
- {
- unsigned char i;
- unsigned char dat;
- pinMode(DS1302_IO,INPUT);
- for(i=0; i<8; i++)
- {
- DS1302_CLK_1; //高电平
- delayMicroseconds(2);
- DS1302_CLK_0; //低电平,结合高电平,一次下降沿读出数据
- delayMicroseconds(2);
- dat >>= 1;
- if( digitalRead(DS1302_IO) == HIGH )
- dat |= 0x80; //或运算,最高位置1
- else
- dat &= 0x7F; //与运算,最高位清0
- }
- return(dat);
- }
复制代码 读/写操作:
- //****************************************************
- //写操作函数 Addr: DS1302地址, Data: 要写的数据
- //读写操作,都需要RST置高电平,在这之前,CLK需要为低电平
- //****************************************************
- void DS1302_Write(unsigned char Addr, unsigned char Data)
- {
- DS1302_RST_0;
- delayMicroseconds(2);
- DS1302_CLK_0; //CLK置为低电平
- delayMicroseconds(2);
- DS1302_RST_1; //RST置1
- delayMicroseconds(2);
- DS1302_InputByte(Addr); // 访问地址
- DS1302_InputByte(Data); // 写1字节数据
- DS1302_CLK_1; // 将时钟电平置于高电平状态 ,处于已知状态
- delayMicroseconds(2);
- DS1302_RST_0; //RST置0,终止传输
- delayMicroseconds(2);
- }
- //****************************************************
- //读操作函数:包括读取数据的地址,时序同上
- //****************************************************
- unsigned char DS1302_Read(unsigned char Addr)
- {
- unsigned char Data;
- DS1302_RST_0;
- delayMicroseconds(2);
- DS1302_CLK_0;
- delayMicroseconds(2);
- DS1302_RST_1;
- delayMicroseconds(2);
- DS1302_InputByte(Addr|0x01); // 地址,命令
- ucData = DS1302_OutputByte(); // 读1Byte数据
- DS1302_CLK_1;
- delayMicroseconds(2);
- DS1302_RST_0;
- delayMicroseconds(2);
- return(Data);
- }
复制代码 写保护函数:
控制寄存器(CONTROL,上图左边第八个,地址为0x8E),定义位7是写保护位。前七个位(位0 – 6)被强制为0,并且在读取时始终为0。在对时钟或RAM进行任何写操作之前,位7必须为0。当高电平时,写保护位可防止对其他任何寄存器的写操作。初始开机状态并未定义,因此,在尝试写入设备之前,控制寄存器最高位置0。
- //****************************************************
- //写保护:
- //****************************************************
- void DS1302_SetProtect(unsigned char flag)
- {
- if(flag)
- DS1302_Write(0x8E,0x80); //保护,此时无法对其他寄存器写数据
- else
- DS1302_Write(0x8E,0x00); //不保护,此时可对其他寄存器写数据
- }
复制代码 时钟停止标志:秒寄存器(sec)的bit 7定义为时钟停止标志。当该位置1时,时钟振荡器停止工作,DS1302进入低功耗待机模式,电流消耗小于100纳安。当该位写入逻辑0时,时钟将启动工作。利用该位来关闭振荡器。
- //****************************************************
- //DS1302振荡器停止
- //****************************************************
- void DS1302_HALT(bool FLAG_ON)
- {
- unsigned char Second;
- Second = DS1302_Read(DS1302_SEC);
- if(FLAG_ON == 0)
- DS1302_Write(DS1302_SEC,Second | 0x80); //停止振荡
- else
- DS1302_Write(DS1302_SEC,Second & 0x7f); //开启振荡
- }
复制代码 在此基础上,我们可以检查时钟是否在振荡,在开始的时候,需要保证时钟正在正常工作因此,需要检验秒寄存器的最高位HALT标志状态。如果处于停止,则需要启动,如果在运行就不需要管。
- //****************************************************
- //初始化:检查HALT标志
- //****************************************************
- void DS1302_Init(void)
- {
- unsigned char Second;
- pinMode(DS1302_RST,OUTPUT);
- pinMode(DS1302_IO,OUTPUT);
- pinMode(DS1302_CLK,OUTPUT);
- Second = DS1302_Read(DS1302_SEC);
- if(Second&0x80)
- DS1302_SetTime(DS1302_SEC,Second & 0x7f); //开启振荡
- }
复制代码 因此,整个DS1302.cpp的代码如下:
- #include "DS1302.h"
- /***********************************************************************
- * @代码说明:本代码内容用于快速使用DS1302
- * @作者:Astilbe
- * @日期:2020/07/26
- * @开发指导:wiki.stduino.com
- ***********************************************************************/
- /* Includes ------------------------------------------------------------------*/
- //例如:#include "stm32f10x_abc.h"
- /*上面不用动,开始你的表演 ------------------------------------------------------------------*/
- #include "DS1302.h"
- TIME DS1302Data;
- bool Flag_Time_Refresh = 1;
- //****************************************************
- //写入一字节:上升沿写入数据
- //****************************************************
- void DS1302_InputByte(unsigned char dat)
- {
- unsigned char i;
- pinMode(DS1302_IO,OUTPUT);
- for(i=0; i<8; i++)
- {
- if((dat & 0x01) == 1)
- {
- DS1302_IO_1;
- }
- else
- {
- DS1302_IO_0;
- }
-
-
- DS1302_CLK_0; //低电平
-
- delayMicroseconds(2);
-
- DS1302_CLK_1; //高电平,结合低电平,一次上升沿,写入数据
-
- delayMicroseconds(2);
- dat >>= 1;
- }
- }
- //****************************************************
- //读取一字节:下降沿读取数据
- //****************************************************
- unsigned char DS1302_OutputByte(void)
- {
- unsigned char i;
- unsigned char dat;
- pinMode(DS1302_IO,INPUT);
- for(i=0; i<8; i++)
- {
- DS1302_CLK_1; //高电平
- delayMicroseconds(2);
- DS1302_CLK_0; //低电平,结合高电平,一次下降沿读出数据
- delayMicroseconds(2);
- dat >>= 1;
- if( digitalRead(DS1302_IO) == HIGH )
- dat |= 0x80; //或运算,最高位置1
- else
- dat &= 0x7F; //与运算,最高位清0
- }
- return(dat);
- }
复制代码
|