DS1302概述:
数据:
DS1302是一个可充电实时时钟芯片,包含时钟(24小时格式或12小时格式)、日历(年,月,日,星期)、31字节RAM(断电数据丢失)。
供电:
DS1302采用两组供电,一组主电,由外部供电,一组备电,一般由纽扣电池供电。当主电存在时,由主电供电,当主电不存在时由备电供电。
功能:
DS1302,通过外部晶振自动对时间进行计数,对月末日期进行调整,以及闰年矫正。MCU可对其时间日期进行设置及读取,显示正确的当前时间。同时可借用31字节的RAM临时保存重要数据(因为外部断电后,纽扣电池供电保证数据不丢失)。
连接:
与MCU连接只需要三个普通GPIO口即可,DS1302芯片的通讯方式接近于SPI,其中将SPI的MISO和MOSI合并为I/O引脚。但是并不需要使用SPI复用引脚进行连接,只需要普通GPIO引脚即可。
DS1302型号说明:
这里是常用国产有替代型号:DS1302,插件;DS1302Z+T,贴片;DS1302N温度范围从(0 ~ +70℃)提高到 (-40~ +85℃),插件;DS1302ZN+T温度范围从(0 ~ +70℃)提高到 (-40~ +85℃),贴片。
DS1302引脚说明:
MCU连接:
正常使用时完全可以安照下图进行连接,非常简单,任意的3个GPIO口连接CE、I/O、SCLK(这里的SCLK只是提供一个上升沿做时钟信号,设低再设高之后数据即被DS1302读取,并非SPI或IIC的CLK)。晶振直接近距离连接DS1302即可,不需要接对地的匹配电容。VCC2接外部供电,VCC1接纽扣电池。
下图,晶振近距离连接DS1302,斜线部分的不要走信号线,会对晶振信号产生影响。
DS1302寄存器定义:
首先是寄存器地址:
寄存地址最后一位表示读写,前边按正常数值。如秒钟地址,读地址为0b1000 0001 = 0x81;写地址为0b1000 0000 = 0x80,所以读地址比写地址大1。
寄存器定义:
一个字节的8位二进制数分为十位和个位,并不是直接对应的十六进制数,这点要注意。例如23秒,读出的是0x23,而不是0x17。采用右移和与运算可对数据进行分离。
控制寄存器进行写保护操作,只对最高位写保护为进行操作即可。
RAM操作:
和时间寄存器一样,最后一位表示读写,前边按正常数值。如RAM 0地址,读地址为0b1100 0001 = 0xC1;写地址为0b1100 0000 = 0xC0。数据内容不限,任意单字节数值。
DS1302寄存器读写:
首先是读写时序图:
RST 即CE,为数据传输控制,数据传输时设高电平,不用时设低电平;
SCLK即DS1302 读写数值控制信号,上升沿触发, 即SCLK的GPIO由低电平设置到高电平时,DS1302读取或写出一个数据。
I/O即DS1302读取或写出的数据。在MCU写入数据时,连接的GPIO设置为一般输出模式,通过高低电平切换,写入数据;在MCU写入数据时,连接的GPIO设置为一般读取电平模式,通过获取DS1302 I/O输出的高低电平,读取数据。
I/O数据的寄存器地址由低位到高位的顺序进行写入;数据也是由低位到高位写入,或读出。 其中R/W 即为读写控制位0为读,1为写,A0-A4为地址数据,R/C为时间寄存器还是RAM寄存器控制位,D0-D7为写入数据,读取时该位置为读出数据。
在进行写时间数据操作时,MCU先向DS1302输出写寄存器地址,再紧跟着输出时间数据;在进行读时间数据操作时,MCU先向DS1302输出读寄存器地址,再紧跟着读取DS1302输出的时间数据;该数据输入输出流程时序由SCLK上升沿控制。
DS1302代码样例(基于HC32L130):
ds1302.h文件
#ifndef __DS1302_H__
#define __DS1302_H__
#include "ddl.h"
#include "gpio.h"
#include "hc32l13x.h"
//DS1302 GPIO定义
#define DS1302_RSTPORT GpioPortB
#define DS1302_RSTPIN GpioPin12
#define DS1302_DATAPORT GpioPortB
#define DS1302_DATAPIN GpioPin11
#define DS1302_CLKPORT GpioPortB
#define DS1302_CLKPIN GpioPin10
//DS1302地址定义
#define ds1302_sec_add 0x80 //秒数据地址
#define ds1302_min_add 0x82 //分数据地址
#define ds1302_hr_add 0x84 //时数据地址
#define ds1302_date_add 0x86 //日数据地址
#define ds1302_month_add 0x88 //月数据地址
#define ds1302_day_add 0x8a //星期数据地址
#define ds1302_year_add 0x8c //年数据地址
#define ds1302_control_add 0x8e //控制数据地址
#define ds1302_charger_add 0x90
#define ds1302_clkburst_add 0xbe
//初始时间定义
extern uint8_t time_buf[8]; //设置时间
extern uint8_t readtime[14];//当前时间
void ds1302_init(void);
void ds1302ioinit(void);
void ds1302_write_byte(uint8_t data);
uint8_t ds1302_read_byte(void);
void ds1302_write_reg_byte(uint8_t addr,uint8_t data);
uint8_t ds1302_read_reg_byte(uint8_t addr);
void ds1302_read_time(void);
void ds1302_write_time(void);
uint8_t ds1302_read_hour(void);
uint8_t ds1302_read_minute(void);
uint8_t ds1302_read_second(void);
#endif /* __DS1302_H__ */
ds1302.c文件
#include "ds1302.h"
#include "hc32l13x.h"
//定义GPIO操作
#define DS1302_SCK_PIN_LOW Gpio_ClrIO(DS1302_CLKPORT,DS1302_CLKPIN)
#define DS1302_SCK_PIN_HIGH Gpio_SetIO(DS1302_CLKPORT,DS1302_CLKPIN)
#define DS1302_RST_PIN_LOW Gpio_ClrIO(DS1302_RSTPORT,DS1302_RSTPIN)
#define DS1302_RST_PIN_HIGH Gpio_SetIO(DS1302_RSTPORT,DS1302_RSTPIN)
#define DS1302_SDA_PIN_LOW Gpio_ClrIO(DS1302_DATAPORT,DS1302_DATAPIN)
#define DS1302_SDA_PIN_HIGH Gpio_SetIO(DS1302_DATAPORT,DS1302_DATAPIN)
#define DS1302_SDA_PIN_READ Gpio_GetInputIO(DS1302_DATAPORT,DS1302_DATAPIN)
//GPIO定义 SCLK和RST(CE)
void ds1302ioinit(void)
{
stc_gpio_cfg_t GpioInitStruct;
DDL_ZERO_STRUCT(GpioInitStruct);
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口驱动能力配置->低驱动能力
GpioInitStruct.enDrv = GpioDrvL;
///< 端口方向配置->输出
GpioInitStruct.enDir = GpioDirOut;
///< 端口上下拉配置->下拉
GpioInitStruct.enPu = GpioPuDisable;
GpioInitStruct.enPd = GpioPdEnable;
///< 端口输出关闭
Gpio_ClrIO(DS1302_CLKPORT, DS1302_CLKPIN);
Gpio_ClrIO(DS1302_RSTPORT, DS1302_RSTPIN);
///< 端口初始化
Gpio_Init(DS1302_CLKPORT,DS1302_CLKPIN,&GpioInitStruct);
Gpio_Init(DS1302_RSTPORT,DS1302_RSTPIN,&GpioInitStruct);
}
//GPIO定义 I/O输出
void ds1302_dataOut_init(void)
{
stc_gpio_cfg_t GpioInitStruct;
DDL_ZERO_STRUCT(GpioInitStruct);
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口驱动能力配置->低驱动能力
GpioInitStruct.enDrv = GpioDrvL;
///< 端口方向配置->输出
GpioInitStruct.enDir = GpioDirOut;
///< 端口上下拉配置->下拉
GpioInitStruct.enPu = GpioPuDisable;
GpioInitStruct.enPd = GpioPdEnable;
///< 端口输出关闭
Gpio_ClrIO(DS1302_DATAPORT, DS1302_DATAPIN);
///< 端口初始化
Gpio_Init(DS1302_DATAPORT,DS1302_DATAPIN,&GpioInitStruct);
}
//GPIO定义 I/O输入
void ds1302_dataIn_init(void)
{
stc_gpio_cfg_t GpioInitStruct;
DDL_ZERO_STRUCT(GpioInitStruct);
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口驱动能力配置->低驱动能力
GpioInitStruct.enDrv = GpioDrvL;
///< 端口方向配置->输入
GpioInitStruct.enDir = GpioDirIn;
///< 端口上下拉配置->无
GpioInitStruct.enPu = GpioPuDisable;
GpioInitStruct.enPd = GpioPdDisable;
///< 端口开漏输出配置->开漏输出关闭
GpioInitStruct.enOD = GpioOdDisable;
///< 端口输入/输出值寄存器总线控制模式配置->AHB
GpioInitStruct.enCtrlMode = GpioAHB;
Gpio_Init(DS1302_DATAPORT,DS1302_DATAPIN,&GpioInitStruct); ///< 端口初始化
}
//DS1302初始化函数
void ds1302_init(void)
{
ds1302ioinit();
ds1302_dataOut_init();
}
//向DS1302写入一字节数据
void ds1302_write_byte(uint8_t data)
{
uint8_t i;
ds1302_dataOut_init();
for (i = 0; i < 8; i ++)
{
DS1302_SCK_PIN_LOW;
if (data & 0x01)
DS1302_SDA_PIN_HIGH;
else
DS1302_SDA_PIN_LOW;
DS1302_SCK_PIN_HIGH;
data = data >> 1;
}
}
//从DS1302读出一字节数据
uint8_t ds1302_read_byte(void)
{
uint8_t i, data = 0;
ds1302_dataIn_init();
for (i = 0; i < 8; i ++)
{
DS1302_SCK_PIN_LOW;
data >>= 1;
if(DS1302_SDA_PIN_READ)
data |= 0x80;
DS1302_SCK_PIN_HIGH;
}
return data;
}
//向DS1302写入对应寄存器数据
void ds1302_write_reg_byte(uint8_t addr,uint8_t data)
{
DS1302_RST_PIN_LOW;
DS1302_SCK_PIN_LOW;
DS1302_RST_PIN_HIGH;
ds1302_write_byte(addr);
ds1302_write_byte(data);
DS1302_SCK_PIN_HIGH;
DS1302_RST_PIN_LOW;
}
//从DS1302读出对应寄存器数据
uint8_t ds1302_read_reg_byte(uint8_t addr)
{
uint8_t data;
DS1302_RST_PIN_LOW;
DS1302_SCK_PIN_LOW;
DS1302_RST_PIN_HIGH;
ds1302_write_byte(addr);
data=ds1302_read_byte();
DS1302_SCK_PIN_HIGH;
DS1302_RST_PIN_LOW;
return data;
}
//向DS1302写入时间数据
void ds1302_write_time(void)
{
ds1302_write_reg_byte(ds1302_control_add,0x00); //关闭写保护
ds1302_write_reg_byte(ds1302_charger_add,0x00); //禁止充电
ds1302_write_reg_byte(ds1302_year_add,time_buf[1]); //年
ds1302_write_reg_byte(ds1302_month_add,time_buf[2]); //月
ds1302_write_reg_byte(ds1302_date_add,time_buf[3]); //日
ds1302_write_reg_byte(ds1302_hr_add,time_buf[4]); //时
ds1302_write_reg_byte(ds1302_min_add,time_buf[5]); //分
ds1302_write_reg_byte(ds1302_sec_add,time_buf[6]); //秒
ds1302_write_reg_byte(ds1302_day_add,time_buf[7]); //周
ds1302_write_reg_byte(ds1302_control_add,0x80); //打开写保护
}
//从DS1302读出时间数据
void ds1302_read_time(void)
{
time_buf[1]=ds1302_read_reg_byte(ds1302_year_add+1); //年
time_buf[2]=ds1302_read_reg_byte(ds1302_month_add+1); //月
time_buf[3]=ds1302_read_reg_byte(ds1302_date_add+1); //日
time_buf[4]=ds1302_read_reg_byte(ds1302_hr_add+1); //时
time_buf[5]=ds1302_read_reg_byte(ds1302_min_add+1); //分
time_buf[6]=(ds1302_read_reg_byte(ds1302_sec_add+1))&0x7f;//秒,屏蔽秒的第7位,避免超出59
time_buf[7]=ds1302_read_reg_byte(ds1302_day_add+1); //周
readtime[0]=(time_buf[0]>>4); //分离出年千位
readtime[1]=(time_buf[0]&0x0F); //分离出年百位
readtime[2]=(time_buf[1]>>4); //分离出年十位
readtime[3]=(time_buf[1]&0x0F); //分离出年个位
readtime[4]=(time_buf[2]>>4); //分离出月十位
readtime[5]=(time_buf[2]&0x0F); //分离出月个位
readtime[6]=(time_buf[3]>>4); //分离出日十位
readtime[7]=(time_buf[3]&0x0F); //分离出日个位
readtime[8]=(time_buf[4]>>4); //分离出小时十位
readtime[9]=(time_buf[4]&0x0F); //分离出小时个位
readtime[10]=(time_buf[5]>>4); //分离出分钟十位
readtime[11]=(time_buf[5]&0x0F); //分离出分钟个位
readtime[12]=(time_buf[6]>>4); //分离出秒钟十位
readtime[13]=(time_buf[6]&0x0F); //分离出秒钟个位
}
//从DS1302读出小时数据
uint8_t ds1302_read_hour(void)
{
uint8_t time_hour;
time_buf[4]=ds1302_read_reg_byte(ds1302_hr_add+1); //时
readtime[8]=(time_buf[4]>>4); //分离出小时十位
readtime[9]=(time_buf[4]&0x0F); //分离出小时个位
time_hour = readtime[8]*10+readtime[9];
return time_hour;
}
//从DS1302读出分钟数据
uint8_t ds1302_read_minute(void)
{
uint8_t time_minute;
time_buf[5]=ds1302_read_reg_byte(ds1302_min_add+1); //分
readtime[10]=(time_buf[5]>>4); //分离出分钟十位
readtime[11]=(time_buf[5]&0x0F); //分离出分钟个位
time_minute = readtime[10]*10+readtime[11];
return time_minute;
}
//从DS1302读出秒钟数据
uint8_t ds1302_read_second(void)
{
uint8_t time_second;
time_buf[6]=(ds1302_read_reg_byte(ds1302_sec_add+1))&0x7f;//秒,屏蔽秒的第7位,避免超出59
readtime[12]=(time_buf[6]>>4); //分离出秒钟十位
readtime[13]=(time_buf[6]&0x0F); //分离出秒钟个位
time_second = readtime[12]*10+readtime[13];
return time_second;
}
main.c文件
#include "gpio.h"
#include "ddl.h"
#include "ds1302.h"
//读写时间列表定义
uint8_t time_buf[8]; //设置时间
uint8_t readtime[14];//当前时间
//读取时间
uint8_t ds1302_hour = 0;
uint8_t ds1302_minute = 0;
uint8_t ds1302_second = 0;
void init()
{
time_buf[0] = 0x00; //年
time_buf[1] = 0x24; //年
time_buf[2] = 0x07; //月
time_buf[3] = 0x19; //日
time_buf[4] = 0x17; //时
time_buf[5] = 0x16; //分
time_buf[6] = 0x20; //秒
time_buf[7] = 0x05; //周
}
int main(void)
{
init();
//DS1302初始化
ds1302_init();
//DS1302写入时间数据
ds1302_write_time();
while(1)
{
ds1302_hour = ds1302_read_hour(); //读取小时数据
ds1302_minute = ds1302_read_minute(); //读取分钟数据
ds1302_second = ds1302_read_second(); //读取秒钟数据
//DS1302读取时间数据
ds1302_read_time();
}
}
调试效果: