问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

DS1302实时时钟模块详解:从原理到代码实现

创作时间:
作者:
@小白创作中心

DS1302实时时钟模块详解:从原理到代码实现

引用
CSDN
1.
https://blog.csdn.net/qq_45217381/article/details/146173925

DS1302是一种常用的实时时钟模块,广泛应用于各种嵌入式系统中。本文将详细介绍DS1302的使用方法,包括其功能简介、引脚说明以及具体的程序设计,帮助读者快速掌握DS1302的使用技巧。

一、DS1302简介

我们首先来简单介绍一下DS1302,首先DS1302具有实时时钟的功能,DS1302能够精确记录当前的时间,包括秒、分钟、小时、日期、月份以及年份,并支持闰年的自动调整。

除了实时时钟功能外,DS1302还具备数据保存功能,DS1302内置有31字节静态RAM用于数据备份,即使外部供电中断也能依靠后备电池保持数据不丢失。

DS1302采用简单的三线SPI兼容串行接口进行通讯,仅需三条信号线即可完成全部操作命令传输及与MCU的数据交互。DS1302使用一跟数据线实现双向数据传输,类似于我们之前介绍过的DS18B20和DHT11。

这里插一小段,在使用DS1302之前不知道大家有没有使用过STM32的RTC功能,对于像一些STM32F103ZET6这种核心板上有这个3V电池的板子来说,用起来还是比较友好的,但是像我们平时用的STM32F103C8T6这种比较小巧的核心板来讲,没有这个3V电池,如果使用到RTC功能,想要做到断电能够继续计时的话需要给单片机的VBAT引脚接上3V电池供电。

二、引脚介绍

DS1302总共有五个引脚

引脚
功能
VCC
电源引脚,接3.3V/5V
CLK
时钟引脚
DAT
数据线,与MCU实现双向数据交互
RST
复位引脚,可以理解成类似片选

三、程序设计

3.1 DAT引脚处理

DAT引脚由于需要完成MCU和DS1302的双向数据传输,所以在不同情况下需要配置成不同的状态,我们这里封装一个函数来方便处理,当然也可以使用宏定义函数来实现

/*
 *==============================================================================
 *函数名称:Drv_Ds1302_DatIoCtrl
 *函数功能:DS1302 DAT引脚控制
 *输入参数:state:状态(0输入/1输出)
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void Drv_Ds1302_DatIoCtrl (u8 state)
{
    // 结构体定义
    GPIO_InitTypeDef GPIO_InitStructure;
    // 使能时钟
    RCC_APB2PeriphClockCmd (DS1302_DAT_CLK, ENABLE);
    
    if (state)
    {
        // 配置结构体
        GPIO_InitStructure.GPIO_Pin = DS1302_DAT_PIN;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_Init(DS1302_DAT_PORT, &GPIO_InitStructure);
    }
    else
    {
        // 配置结构体
        GPIO_InitStructure.GPIO_Pin = DS1302_DAT_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(DS1302_DAT_PORT, &GPIO_InitStructure);
    }
}

3.2 DS1302读写寄存器

/*
 *==============================================================================
 *函数名称:Med_Ds1302_WriteByte
 *函数功能:DS1302写入寄存器
 *输入参数:
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void Med_Ds1302_WriteByte (u8 addr,u8 data)
{
    u8 i;
    
    RST = 0;   // 关闭数据传输
    CLK = 0;   // 拉低时钟
    RST = 1;   // 启动数据传输
    Drv_Ds1302_DatIoCtrl(1);   // DAT引脚设置成输出模式
    addr = addr & 0xFE;   // 最低位置零,寄存器0位为0时写,为1时读
    
    // 写入目标地址
    for(i = 0;i < 8;i ++)
    {
        if (addr & 0x01)
            DAT = 1;
        else
            DAT = 0;
        
        CLK = 1;   // 时钟上升沿写入数据
        CLK = 0;
        addr = addr >> 1;
    }
    
    // 写入数据
    for (i = 0;i < 8;i ++)
    {
        if(data & 0x01)
            DAT = 1;
        else
            DAT = 0;
        
        CLK = 1;   // 时钟上升沿写入数据
        CLK = 0;
        data = data >> 1;
    }
    CLK = 1;    // 将时钟电平置于高电平状态
    RST = 0;   // 关闭数据传输
}
/*
 *==============================================================================
 *函数名称:Med_Ds1302_ReadByte
 *函数功能:DS1302读取寄存器
 *输入参数:
 *返回值:无
 *备  注:无
 *==============================================================================
*/
u8 Med_Ds1302_ReadByte (u8 addr)
{
    u8 i,data;
    
    RST = 0;   // 关闭数据传输
    CLK = 0;   // 拉低时钟
    RST = 1;   // 启动数据传输
    Drv_Ds1302_DatIoCtrl(1);   // DAT引脚设置成输出模式
    addr = addr | 0x01;   // 最低位置高,寄存器0位为0时写,为1时读
    
    // 写入目标地址
    for(i = 0;i < 8;i ++) 
    {
        if (addr & 0x01)
            DAT = 1;
        else
            DAT = 0;
        
        CLK = 1;   // 时钟上升沿写入数据
        CLK = 0;
        addr = addr >> 1;
    }
    
    // 从DS1302读出数据
    Drv_Ds1302_DatIoCtrl(0);   // DAT引脚设置为输入模式
    for(i = 0;i < 8;i ++)
    {
        data = data >> 1;
        if (DS1302_DAT_IN())
            data |= 0x80;
        else
            data &= 0x7F;
        
        CLK = 1;   // 时钟上升沿写入数据
        CLK = 0;
    }	
    CLK = 1;    // 将时钟电平置于高电平状态
    RST = 0;   // 关闭数据传输
    
    return data;
}

3.3 DS1302设置时间

/*
 *==============================================================================
 *函数名称:Med_Ds1302_SetTime
 *函数功能:DS1302设置时间
 *输入参数:writeTime:要设置的时间
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void Med_Ds1302_SetTime (uint8_t *targetTime)
{
    Med_Ds1302_WriteByte(DS1302_CONTROL_ADDR,0x00);        // 关闭写入
    Med_Ds1302_WriteByte(DS1302_SEC_ADDR,0x80);            // 暂停时钟 
//	Med_Ds1302_WriteByte(DS1302_CHARGER_ADDR,0xa9);   // 涓流充电 
    Med_Ds1302_WriteByte(DS1302_YEAR_ADDR,targetTime[1]);   // 年 
    Med_Ds1302_WriteByte(DS1302_MONTH_ADDR,targetTime[2]);  // 月 
    Med_Ds1302_WriteByte(DS1302_DAY_ADDR,targetTime[3]);    // 日 
    Med_Ds1302_WriteByte(DS1302_HOUR_ADDR,targetTime[4]);   // 时 
    Med_Ds1302_WriteByte(DS1302_MIN_ADDR,targetTime[5]);    // 分
    Med_Ds1302_WriteByte(DS1302_SEC_ADDR,targetTime[6]);    // 秒
    Med_Ds1302_WriteByte(DS1302_WEEK_ADDR,targetTime[7]);	 // 周 
    Med_Ds1302_WriteByte(DS1302_CHARGER_ADDR,0xA5);        // 打开充电功能 选择2K电阻充电方式
    Med_Ds1302_WriteByte(DS1302_CONTROL_ADDR,0x80);        // 打开写入 
}

3.4 DS1302获取当前时间

/*
 *==============================================================================
 *函数名称:Med_Ds1302_GetTime
 *函数功能:DS1302获取时间
 *输入参数:writeTime:要设置的时间
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void Med_Ds1302_GetTime (DS1302TimeStruct *timeData)
{ 
    u8 readtime[8];   // 当前时间
    
  readtime[1] = Med_Ds1302_ReadByte(DS1302_YEAR_ADDR);    // 年 
    readtime[2] = Med_Ds1302_ReadByte(DS1302_MONTH_ADDR);   // 月 
    readtime[3] = Med_Ds1302_ReadByte(DS1302_DAY_ADDR);     // 日 
    readtime[4] = Med_Ds1302_ReadByte(DS1302_HOUR_ADDR);    // 时 
    readtime[5] = Med_Ds1302_ReadByte(DS1302_MIN_ADDR);     // 分 
    readtime[6] = (Med_Ds1302_ReadByte(DS1302_SEC_ADDR)) & 0x7f;   // 秒,屏蔽秒的第7位,避免超出59
    readtime[7] = Med_Ds1302_ReadByte(DS1302_WEEK_ADDR);   // 周 	
    timeData -> year = (readtime[0] >> 4) * 1000 + (readtime[0] & 0x0F) * 100 + (readtime[1] >> 4) * 10 + (readtime[1] & 0x0F);   // 计算年份
    timeData -> month = (readtime[2] >> 4) * 10 + (readtime[2] & 0x0F);    // 计算月份
    timeData -> day = (readtime[3] >> 4) * 10 + (readtime[3] & 0x0F);      // 计算日期
    timeData -> hour = (readtime[4] >> 4) * 10 + (readtime[4] & 0x0F);     // 计算小时
    timeData -> minute = (readtime[5] >> 4) * 10 + (readtime[5] & 0x0F);   // 计算分钟
    timeData -> second = (readtime[6] >> 4) * 10 + (readtime[6] & 0x0F);   // 计算秒钟
    timeData -> week = (readtime[7] & 0x0F);   // 计算星期		
}
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号