玩转 RTC时钟库 DS1302

1.前言

????最近博主在弄8266编程的时候,偶然发现两个全新时钟模块压仓货:

  • DS1302
  • DS3231

????为了避免资源浪费以及重复编写代码,博主还是抱着尝试的心态去寻找能够同时兼容 DS1302、DS3231甚至其他的时钟模块的第三方库。终于,还是被我找到了 —— Rtc 时钟库

2.RTC

2.1 简介

  • Arduino Real Time Clock library(Arduino平台的实时时钟库)
  • 支持DS1302、DS1307、DS3231、DS3234
  • 英文文档参考 wiki

2.2 安装

  • 打开Arduino IDE的库管理器,然后搜索“Rtc by Makuna”,选择版本安装

  • 为了更加了解该库的使用,博主会带着大家剖析源码。首先让我们看看代码结构:

????代码非常直接明了,分别支持了DS1302、DS1307、DS3231、DS3234。但是由于博主资源限制,本篇只会讨论DS1302以及DS3231,其他请读者自行学习。

3. DS1302 与 RTC库

3.1 DS1302设计时钟芯片

????网上介绍DS1302的帖子非常多,博主在这里就不从零开始讲解,这里推荐一个博主觉得写得还可以的参考文档 STM32与DS1302设计时钟芯片,超详细
????认真看完上面参考文档之后,博主得到几个我比较关心的点:

  • DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有闰年自动调整的能力,可以通过配置AM/PM来决定采用24小时格式还是12小时格式;
  • 仅需用到三个口线:(1)RES 复位(2)I/O 数据线(3)SCLK串行时钟;
  • 拥有31字节数据存储RAM(RTC Memory);
  • DS1302这种时钟芯片功耗一般都很低,它在工作电压2.0V的时候,工作电流小于300nA;
  • DS1302的工作电压比较宽,大概是2.0V~5.5V都可以正常工作。采用双电源供电,当主电源比备用电源高0.2V时,由主电源供电,否则采用备用电源,一般是一个纽扣电池;
  • 读/写时钟或RAM 数据时有两种传送方式单字节传送和多字节传送字符组方式;

3.2 DS1302电路图与引脚关系

????接下来看看常用电路图以及引脚定义:

3.3 DS1302具体操作

????操作DS1302的大致过程,就是将各种数据或者控制命令写入DS1302的寄存器来完成对应操作(比如设置时间、写保护操作)。然后使DS1302开始运作,DS1302时钟会按照设置情况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简易电子钟。

3.3.1 DS1302寄存器

????DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字见表。

????此外,DS1302 还有控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。

????DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。

3.3.2 RTCDS1302库

????上面只是介绍理论,在理解理论的基础上,我们开始讲解RTC库的使用。以下是RTCDS1302库的源码(参考源码注释):



#ifndef __RTCDS1302_H__
#define __RTCDS1302_H__

#include <Arduino.h>
#include "RtcDateTime.h"
#include "RtcUtility.h"

//DS1302 Register Addresses 寄存器地址
const uint8_t DS1302_REG_TIMEDATE   = 0x80;//秒寄存器
const uint8_t DS1302_REG_TIMEDATE_BURST = 0xBE;//时钟批量处理寄存器
const uint8_t DS1302_REG_TCR    = 0x90;//涓流充电控制寄存器
const uint8_t DS1302_REG_RAM_BURST = 0xFE;//RAM批量处理寄存器
const uint8_t DS1302_REG_RAMSTART   = 0xc0;//ram空间第一个字节
const uint8_t DS1302_REG_RAMEND     = 0xfd;//ram空间最后一个字节
// ram read and write addresses are interleaved
const uint8_t DS1302RamSize = 31;//ram空间大小

// DS1302 Trickle Charge Control Register Bits 以下跟涓流充电有关 可忽略
enum DS1302TcrResistor {
    DS1302TcrResistor_Disabled = 0,
    DS1302TcrResistor_2KOhm = B00000001,
    DS1302TcrResistor_4KOhm = B00000010,
    DS1302TcrResistor_8KOhm = B00000011,
    DS1302TcrResistor_MASK  = B00000011,
};

enum DS1302TcrDiodes {
    DS1302TcrDiodes_None = 0,
    DS1302TcrDiodes_One      = B00000100,
    DS1302TcrDiodes_Two      = B00001000,
    DS1302TcrDiodes_Disabled = B00001100,
    DS1302TcrDiodes_MASK     = B00001100,
};

enum DS1302TcrStatus {
    DS1302TcrStatus_Enabled  = B10100000,
    DS1302TcrStatus_Disabled = B01010000,
    DS1302TcrStatus_MASK     = B11110000,
};

const uint8_t DS1302Tcr_Disabled = DS1302TcrStatus_Disabled | DS1302TcrDiodes_Disabled | DS1302TcrResistor_Disabled;

// DS1302 Clock Halt Register & Bits
const uint8_t DS1302_REG_CH = 0x80; // bit in the seconds register 秒寄存器
const uint8_t DS1302_CH     = 7;

// Write Protect Register & Bits
const uint8_t DS1302_REG_WP = 0x8E; //写保护寄存器
const uint8_t DS1302_WP = 7;

template<class T_WIRE_METHOD> class RtcDS1302
{
public:
    RtcDS1302(T_WIRE_METHOD& wire) :
        _wire(wire)
    {
    }

    void Begin()
    {
       //会把三个引脚设置为输入状态
        _wire.begin();
    }

    /**
     * 判断是否写保护
     * @return bool true表示写保护
     */
    bool GetIsWriteProtected()
    {
        //获取写保护寄存器的值
        uint8_t wp = getReg(DS1302_REG_WP);
        //获取最高位的值
        return !!(wp & _BV(DS1302_WP));
    }

    /**
     * 设置是否写保护
     * @param isWriteProtected
     *        true 写保护
     *        false 去掉写保护
     */
    void SetIsWriteProtected(bool isWriteProtected)
    {
        //获取写保护寄存器的值
        uint8_t wp = getReg(DS1302_REG_WP);
        if (isWriteProtected)
        {
            wp |= _BV(DS1302_WP);
        }
        else
        {
            wp &= ~_BV(DS1302_WP);
        }
        setReg(DS1302_REG_WP, wp);
    }

    bool IsDateTimeValid()
    {
        return GetDateTime().IsValid();
    }

    /**
     * 判断时钟是否正在运行
     * @return bool
     *        true 时钟运行
     *        false 时钟停振,进入低功耗态
     */
    bool GetIsRunning()
    {
        uint8_t ch = getReg(DS1302_REG_CH);
        return !(ch & _BV(DS1302_CH));
    }

    void SetIsRunning(bool isRunning)
    {
        uint8_t ch = getReg(DS1302_REG_CH);
        if (isRunning)
        {
            ch &= ~_BV(DS1302_CH);
        }
        else
        {
            ch |= _BV(DS1302_CH);
        }
        setReg(DS1302_REG_CH, ch);
    }

    uint8_t GetTrickleChargeSettings()
    {
        uint8_t setting = getReg(DS1302_REG_TCR);
        return setting;
    }

    void SetTrickleChargeSettings(uint8_t setting)
    {
        if ((setting & DS1302TcrResistor_MASK) == DS1302TcrResistor_Disabled) {
            // invalid resistor setting, set to disabled
            setting = DS1302Tcr_Disabled;
            goto apply;
        }
        if ((setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_Disabled ||
            (setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_None) {
            // invalid diode setting, set to disabled
            setting = DS1302Tcr_Disabled;
            goto apply;
        }
        if ((setting & DS1302TcrStatus_MASK) != DS1302TcrStatus_Enabled) {
            // invalid status setting, set to disabled
            setting = DS1302Tcr_Disabled;
            goto apply;
        }

     apply:
        setReg(DS1302_REG_TCR, setting);
    }

    /**
     * 设置日期时间
     * @param RtcDateTime 日期时间对象
     */
    void SetDateTime(const RtcDateTime& dt)
    {
        // set the date time  批量设置时间
        _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST);

        _wire.write(Uint8ToBcd(dt.Second()));//秒数
        _wire.write(Uint8ToBcd(dt.Minute()));//分钟
        _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
        _wire.write(Uint8ToBcd(dt.Day()));//天数
        _wire.write(Uint8ToBcd(dt.Month()));//月份

        // RTC Hardware Day of Week is 1-7, 1 = Monday
        // convert our Day of Week to Rtc Day of Week
        uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());

        _wire.write(Uint8ToBcd(rtcDow));
        _wire.write(Uint8ToBcd(dt.Year() - 2000));//年份
        _wire.write(0); // no write protect, as all of this is ignored if it is protected

        _wire.endTransmission();//批量写入寄存器
    }

    /**
     * 获取日期时间
     * @return RtcDateTime 日期时间对象
     */
    RtcDateTime GetDateTime()
    {
        // set the date time  批量获取时间
        _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST | THREEWIRE_READFLAG);

        uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数
        uint8_t minute = BcdToUint8(_wire.read());//分钟
        uint8_t hour = BcdToBin24Hour(_wire.read());//小时
        uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数
        uint8_t month = BcdToUint8(_wire.read());//月份

        _wire.read();  // throwing away day of week as we calculate it

        uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份

        _wire.read();  // throwing away write protect flag

        _wire.endTransmission();//批量处理

        //返回日期时间对象
        return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
    }

    /***
     * 往RTC Memory写入数据
     * @param  memoryAddress 地址偏移量
     * @param  value 数据
     */
    void SetMemory(uint8_t memoryAddress, uint8_t value)
    {
        // memory addresses interleaved read and write addresses
        // so we need to calculate the offset
        uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
        if (address <= DS1302_REG_RAMEND)
        {
            setReg(address, value);
        }
    }

    uint8_t GetMemory(uint8_t memoryAddress)
    {
        uint8_t value = 0;
        // memory addresses interleaved read and write addresses
        // so we need to calculate the offset
        uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
        if (address <= DS1302_REG_RAMEND)
        {
            value = getReg(address);
        }
        return value;
    }

    uint8_t SetMemory(const uint8_t* pValue, uint8_t countBytes)
    {
        uint8_t countWritten = 0;
        //批量处理
        _wire.beginTransmission(DS1302_REG_RAM_BURST);

        while (countBytes > 0 && countWritten < DS1302RamSize)
        {
            _wire.write(*pValue++);
            countBytes--;
            countWritten++;
        }

        _wire.endTransmission();
        //返回写入数量
        return countWritten;
    }

    uint8_t GetMemory(uint8_t* pValue, uint8_t countBytes)
    {
        uint8_t countRead = 0;

        _wire.beginTransmission(DS1302_REG_RAM_BURST | THREEWIRE_READFLAG);

        while (countBytes > 0 && countRead < DS1302RamSize)
        {
            *pValue++ = _wire.read();
            countRead++;
            countBytes--;
        }

        _wire.endTransmission();

        return countRead;
    }

private:
    T_WIRE_METHOD& _wire;

    uint8_t getReg(uint8_t regAddress)
    {

        _wire.beginTransmission(regAddress | THREEWIRE_READFLAG);
        uint8_t regValue = _wire.read();
        _wire.endTransmission();
        return regValue;
    }

    void setReg(uint8_t regAddress, uint8_t regValue)
    {
        _wire.beginTransmission(regAddress);
        _wire.write(regValue);
        _wire.endTransmission();
    }
};

#endif // __RTCDS1302_H__

3.3.3 RTCDS1302库函数

3.3.3.1 Begin() —— 初始化

函数说明:

/**
 * 初始化,会把三个引脚设置为输入状态
 */
void Begin();
3.3.3.2 GetIsWriteProtected() —— 判断是否写保护

函数说明:

/**
 * 判断是否写保护
 * @return bool true表示写保护
 */
bool GetIsWriteProtected();

源码说明:

bool GetIsWriteProtected()
{
    //获取写保护寄存器的值
    uint8_t wp = getReg(DS1302_REG_WP);
    //获取最高位的值
    return !!(wp & _BV(DS1302_WP));
}
3.3.3.3 GetIsWriteProtected() —— 设置是否写保护

函数说明:

/**
 * 设置是否写保护
 * @param isWriteProtected
 *        true 写保护
 *        false 去掉写保护
 */
void SetIsWriteProtected(bool isWriteProtected)

源码说明:

void SetIsWriteProtected(bool isWriteProtected)
{
    //获取写保护寄存器的值
    uint8_t wp = getReg(DS1302_REG_WP);
    if (isWriteProtected)
    {
        wp |= _BV(DS1302_WP);
    }else{
        wp &= ~_BV(DS1302_WP);
    }
    setReg(DS1302_REG_WP, wp);
}
3.3.3.4 GetIsRunning() —— 判断时钟是否正在运行

函数说明:

/**
 * 判断时钟是否正在运行
 * @return bool
 *        true 时钟运行
 *        false 时钟停振,进入低功耗态
 */
bool GetIsRunning()

源码说明:

bool GetIsRunning()
{
    //判断秒寄存器的最高位数值
    uint8_t ch = getReg(DS1302_REG_CH);
    return !(ch & _BV(DS1302_CH));
}
3.3.3.5 SetIsRunning() —— 设置时钟是否运行

函数说明:

/**
 * 设置时钟是否运行
 * @param isRunning
 *        true 时钟运行
 *        false 时钟停振,进入低功耗态
 */
void SetIsRunning(bool isRunning)

源码说明:

void SetIsRunning(bool isRunning)
{
    uint8_t ch = getReg(DS1302_REG_CH);
    if (isRunning){
        ch &= ~_BV(DS1302_CH);
    }else{
        ch |= _BV(DS1302_CH);
    }
    //设置秒寄存器的最高位
    setReg(DS1302_REG_CH, ch);
}
3.3.3.6 SetDateTime() —— 设置日期时间

函数说明:

/**
 * 设置日期时间
 * @param RtcDateTime 日期时间对象
 */
void SetDateTime(const RtcDateTime& dt)

源码说明:

/**
 * 设置日期时间
 * @param RtcDateTime 日期时间对象
 */
void SetDateTime(const RtcDateTime& dt)
{
    // set the date time  批量设置时间
    _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST);

    _wire.write(Uint8ToBcd(dt.Second()));//秒数
    _wire.write(Uint8ToBcd(dt.Minute()));//分钟
    _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
    _wire.write(Uint8ToBcd(dt.Day()));//天数
    _wire.write(Uint8ToBcd(dt.Month()));//月份

    // RTC Hardware Day of Week is 1-7, 1 = Monday
    // convert our Day of Week to Rtc Day of Week
    uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());

    _wire.write(Uint8ToBcd(rtcDow));
    _wire.write(Uint8ToBcd(dt.Year() - 2000));//年份
    _wire.write(0); // no write protect, as all of this is ignored if it is protected

    _wire.endTransmission();//批量写入寄存器
}

参数说明:

  • RtcDateTime RTC日期时间对象
class RtcDateTime
{
public:
    RtcDateTime(uint32_t secondsFrom2000 = 0);
    RtcDateTime(uint16_t year,
        uint8_t month,
        uint8_t dayOfMonth,
        uint8_t hour,
        uint8_t minute,
        uint8_t second) :
        _yearFrom2000((year >= c_OriginYear) ? year - c_OriginYear : year),
        _month(month),
        _dayOfMonth(dayOfMonth),
        _hour(hour),
        _minute(minute),
        _second(second)
    {
    }

    // RtcDateTime compileDateTime(__DATE__, __TIME__);
    RtcDateTime(const char* date, const char* time);

    bool IsValid() const;

    uint16_t Year() const
    {
        return c_OriginYear + _yearFrom2000;
    }
    uint8_t Month() const
    {
        return _month;
    }
    uint8_t Day() const
    {
        return _dayOfMonth;
    }
    uint8_t Hour() const
    {
        return _hour;
    }
    uint8_t Minute() const
    {
        return _minute;
    }
    uint8_t Second() const
    {
        return _second;
    }
    // 0 = Sunday, 1 = Monday, ... 6 = Saturday
    uint8_t DayOfWeek() const;

    // 32-bit times as seconds since 1/1/2000
    uint32_t TotalSeconds() const;
    uint64_t TotalSeconds64() const;

    // add seconds
    void operator += (uint32_t seconds)
    {
        RtcDateTime after = RtcDateTime( TotalSeconds() + seconds );
        *this = after;
    }

    // remove seconds
    void operator -= (uint32_t seconds)
    {
        RtcDateTime before = RtcDateTime( TotalSeconds() - seconds );
        *this = before;
    }

    // allows for comparisons to just work (==, <, >, <=, >=, !=)
    operator uint32_t() const
    {
        return TotalSeconds();
    }

    // Epoch32 support
    uint32_t Epoch32Time() const
    {
        return TotalSeconds() + c_Epoch32OfOriginYear;
    }
    void InitWithEpoch32Time(uint32_t time)
    {
        _initWithSecondsFrom2000<uint32_t>(time - c_Epoch32OfOriginYear);
    }

    // Epoch64 support
    uint64_t Epoch64Time() const
    {
        return TotalSeconds64() + c_Epoch32OfOriginYear;
    }
    void InitWithEpoch64Time(uint64_t time)
    {
        _initWithSecondsFrom2000<uint64_t>(time - c_Epoch32OfOriginYear);
    }

    void InitWithIso8601(const char* date);

    // convert our Day of Week to Rtc Day of Week
    // RTC Hardware Day of Week is 1-7, 1 = Monday
    static uint8_t ConvertDowToRtc(uint8_t dow)
    {
        if (dow == 0)
        {
            dow = 7;
        }
        return dow;
    }

    // convert Rtc Day of Week to our Day of Week
    static uint8_t ConvertRtcToDow(uint8_t rtcDow)
    {
        return (rtcDow % 7);
    }

protected:
    uint8_t _yearFrom2000;
    uint8_t _month;
    uint8_t _dayOfMonth;
    uint8_t _hour;
    uint8_t _minute;
    uint8_t _second;

    template <typename T> void _initWithSecondsFrom2000(T secondsFrom2000)
    {
        _second = secondsFrom2000 % 60;
        T timeFrom2000 = secondsFrom2000 / 60;
        _minute = timeFrom2000 % 60;
        timeFrom2000 /= 60;
        _hour = timeFrom2000 % 24;
        T days = timeFrom2000 / 24;
        T leapDays;

        for (_yearFrom2000 = 0;; ++_yearFrom2000)
        {
            leapDays = (_yearFrom2000 % 4 == 0) ? 1 : 0;
            if (days < 365U + leapDays)
                break;
            days -= 365 + leapDays;
        }
        for (_month = 1;; ++_month)
        {
            uint8_t daysPerMonth = pgm_read_byte(c_daysInMonth + _month - 1);
            if (leapDays && _month == 2)
                daysPerMonth++;
            if (days < daysPerMonth)
                break;
            days -= daysPerMonth;
        }
        _dayOfMonth = days + 1;
    }

};
3.3.3.7 GetDateTime() —— 获取日期时间

函数说明:

/**
 * 获取日期时间
 * @return RtcDateTime 日期时间对象
 */
RtcDateTime GetDateTime()

源码说明:

RtcDateTime GetDateTime()
{
    // set the date time  批量获取时间
    _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST | THREEWIRE_READFLAG);

    uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数
    uint8_t minute = BcdToUint8(_wire.read());//分钟
    uint8_t hour = BcdToBin24Hour(_wire.read());//小时
    uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数
    uint8_t month = BcdToUint8(_wire.read());//月份

    _wire.read();  // throwing away day of week as we calculate it

    uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份

    _wire.read();  // throwing away write protect flag

    _wire.endTransmission();//批量处理

    //返回日期时间对象
    return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
3.3.3.8 SetMemory() —— 往RTC Memory写入数据

函数说明:

/***
 * 往RTC Memory写入数据
 * @param  memoryAddress 地址偏移量
 * @param  value 数据
 */
void SetMemory(uint8_t memoryAddress, uint8_t value)

/**
 * 往RTC Memory批量写入数据
 * @param pValue 批量数据
 * @param countBytes 数据字节数
 */
uint8_t SetMemory(const uint8_t* pValue, uint8_t countBytes)

源码说明:

    void SetMemory(uint8_t memoryAddress, uint8_t value)
    {
        // memory addresses interleaved read and write addresses
        // so we need to calculate the offset
        uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
        if (address <= DS1302_REG_RAMEND)
        {
            setReg(address, value);
        }
    }

    uint8_t SetMemory(const uint8_t* pValue, uint8_t countBytes)
    {
        uint8_t countWritten = 0;
        //批量处理
        _wire.beginTransmission(DS1302_REG_RAM_BURST);

        while (countBytes > 0 && countWritten < DS1302RamSize)
        {
            _wire.write(*pValue++);
            countBytes--;
            countWritten++;
        }

        _wire.endTransmission();
        //返回写入数量
        return countWritten;
    }
3.3.3.9 GetMemory() —— 读取RTC Memory

函数说明:

/***
 * 读取RTC Memory
 * @param  memoryAddress 地址偏移量
 * @return  数据
 */
uint8_t GetMemory(uint8_t memoryAddress)

/***
 * 批量读取RTC Memory
 * @param pValue 存储空间
 * @param countBytes 数据字节数
 */
uint8_t GetMemory(uint8_t* pValue, uint8_t countBytes)

源码说明:

    uint8_t GetMemory(uint8_t memoryAddress)
    {
        uint8_t value = 0;
        // memory addresses interleaved read and write addresses
        // so we need to calculate the offset
        uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
        if (address <= DS1302_REG_RAMEND)
        {
            value = getReg(address);
        }
        return value;
    }

    uint8_t GetMemory(uint8_t* pValue, uint8_t countBytes)
    {
        uint8_t countRead = 0;

        _wire.beginTransmission(DS1302_REG_RAM_BURST | THREEWIRE_READFLAG);

        while (countBytes > 0 && countRead < DS1302RamSize)
        {
            *pValue++ = _wire.read();
            countRead++;
            countBytes--;
        }

        _wire.endTransmission();

        return countRead;
    }

3.3.3 DS1302接线

DS1302采用三总线方式,SCLK(CLK), IO (DAT), CE (RST)。

3.3.4 测试用例

3.3.4.1 测试时间

实验内容

  • 设置时间并在串口上打印时间

实验器材

  • Mega2560 + DS1302

引脚连接

模块引脚 Mega2560引脚
VCC VCC
GND GND
DAT 6
CLK 5
RST 7

实验代码


// CONNECTIONS:
// DS1302 CLK/SCLK --> 5
// DS1302 DAT/IO --> 6
// DS1302 RST/CE --> 7
// DS1302 VCC --> 3.3v - 5v
// DS1302 GND --> GND

#include <ThreeWire.h>
#include <RtcDS1302.h>

ThreeWire myWire(/*IO*/6,/*SCLK*/5,/*CE*/7); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);

void setup ()
{
    Serial.begin(57600);

    Serial.print("compiled: ");
    Serial.print(__DATE__);
    Serial.println(__TIME__);

    Rtc.Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    printDateTime(compiled);
    Serial.println();

    if (Rtc.GetIsWriteProtected())
    {
        Serial.println("RTC was write protected, enabling writing now");
        Rtc.SetIsWriteProtected(false);
    }

    if (!Rtc.GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc.SetIsRunning(true);
    }

    RtcDateTime now = Rtc.GetDateTime();
    if (now < compiled)
    {
        Serial.println("RTC is older than compile time!  (Updating DateTime)");
        Rtc.SetDateTime(compiled);
    }
    else if (now > compiled)
    {
        Serial.println("RTC is newer than compile time. (this is expected)");
    }
    else if (now == compiled)
    {
        Serial.println("RTC is the same as compile time! (not expected but all is fine)");
    }
}

void loop ()
{
    RtcDateTime now = Rtc.GetDateTime();

    printDateTime(now);
    Serial.println();

    delay(10000); // ten seconds
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt)
{
    char datestring[20];

    snprintf_P(datestring,
            countof(datestring),
            PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
            dt.Month(),
            dt.Day(),
            dt.Year(),
            dt.Hour(),
            dt.Minute(),
            dt.Second() );
    Serial.print(datestring);
}

实验结果:

3.3.4.2 测试RTC Memory

实验内容

  • 往RTC Memory存入“danpianjicainiao”
  • 读取存储内容并打印到串口显示

实验器材

  • Mega2560 + DS1302

引脚连接

模块引脚 Mega2560引脚
VCC VCC
GND GND
DAT 6
CLK 5
RST 7

实验代码


// CONNECTIONS:
// DS1302 CLK/SCLK --> 5
// DS1302 DAT/IO --> 6
// DS1302 RST/CE --> 7
// DS1302 VCC --> 3.3v - 5v
// DS1302 GND --> GND

#include <ThreeWire.h>
#include <RtcDS1302.h>

ThreeWire myWire(/*IO*/6,/*SCLK*/5,/*CE*/7); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);

#define countof(a) (sizeof(a) / sizeof(a[0]))

const char data[] = "danpianjicainiao";

void setup ()
{
    Serial.begin(57600);

    Serial.print("compiled: ");
    Serial.print(__DATE__);
    Serial.println(__TIME__);

    Rtc.Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    printDateTime(compiled);
    Serial.println();

    if (Rtc.GetIsWriteProtected())
    {
        Serial.println("RTC was write protected, enabling writing now");
        Rtc.SetIsWriteProtected(false);
    }

    if (!Rtc.GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc.SetIsRunning(true);
    }

    RtcDateTime now = Rtc.GetDateTime();
    if (now < compiled)
    {
        Serial.println("RTC is older than compile time!  (Updating DateTime)");
        Rtc.SetDateTime(compiled);
    }

/* comment out on a second run to see that the info is stored long term */
    // Store something in memory on the RTC
    uint8_t count = sizeof(data);
    uint8_t written = Rtc.SetMemory((const uint8_t*)data, count); // this includes a null terminator for the string
    if (written != count)
    {
        Serial.print("something didn't match, count = ");
        Serial.print(count, DEC);
        Serial.print(", written = ");
        Serial.print(written, DEC);
        Serial.println();
    }
/* end of comment out section */
}

void loop ()
{
    RtcDateTime now = Rtc.GetDateTime();

    printDateTime(now);
    Serial.println(" +");

    delay(5000);

    // read data
    uint8_t buff[20];
    const uint8_t count = sizeof(buff);
    // get our data
    uint8_t gotten = Rtc.GetMemory(buff, count);

    if (gotten != count)
    {
        Serial.print("something didn't match, count = ");
        Serial.print(count, DEC);
        Serial.print(", gotten = ");
        Serial.print(gotten, DEC);
        Serial.println();
    }

    Serial.print("data read (");
    Serial.print(gotten);
    Serial.print(") = \"");
    // print the string, but terminate if we get a null
    for (uint8_t ch = 0; ch < gotten && buff[ch]; ch++)
    {
        Serial.print((char)buff[ch]);
    }
    Serial.println("\"");

    delay(5000);
}

void printDateTime(const RtcDateTime& dt)
{
    char datestring[20];

    snprintf_P(datestring,
            countof(datestring),
            PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
            dt.Month(),
            dt.Day(),
            dt.Year(),
            dt.Hour(),
            dt.Minute(),
            dt.Second() );
    Serial.print(datestring);
}

实验结果:

4.总结

本博文讲解如何使用RTC时钟库来操作DS1302,同时附带源码解析,请关注下一篇DS3231.

原文地址:https://www.cnblogs.com/danpianjicainiao/p/11048661.html

时间: 2024-10-06 22:07:57

玩转 RTC时钟库 DS1302的相关文章

玩转 RTC时钟库 DS3231

1.前言 ????接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块.没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里不再重复. DS3231 2.DS3231介绍 2.1 为什么使用DS3231 ????常用的DS1302需要使用外置晶振,且没有温度补偿,误差较大.这就促使了更高精度的时钟芯片 -- DS3231. ????DS3231内置晶振且有内部温度补偿,误差可做到1分钟每年.说白了,精度更高. 2.2 D

张高兴的 Windows 10 IoT 开发笔记:RTC 时钟模块 DS3231

原文:张高兴的 Windows 10 IoT 开发笔记:RTC 时钟模块 DS3231 GitHub:https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/DS3231 注意:不包含闹钟设置

STM32 RTC时钟的配置

1) 使能电源时钟和备份区域时钟. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能电源时钟和备份区域时钟. 2) 取消备份区写保护. 要向备份区域写入数据,就要先取消备份区域写保护 ,否则是无法向备份区域写入数据的. PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问 3) 复位备份区域,开启外部低速振荡器. BKP_DeInit();//复位备份

Linux应用层系统时间写入RTC时钟的方法

Linux内核版本:linux-3.0.35 开发板:i.MX6S MY-IMX6-EK200 系统:Ubuntu12 前言:之前写过一篇关于如何通过应用层程序读取系统时间的blog,今天再写一篇如何写入并保存RTC时钟的blog吧. 一.写入时间 1.预备知识: a.mktime 头文件:#include <time.h> 函数:time_t mktime(struct tm *timeptr) 函数说明:mktime()用来将timeptr所指的tm结构体数据换成从公元1970年1月1日0

GEC2440的RTC时钟

引用:http://www.cnblogs.com/Neddy/archive/2011/12/07/2278761.html 2440的RTC时钟 移植linux到2440上发现没有时钟信息,解决方法如下: 1.在linux kernel里面用make menuconfig打开配置画面,进Device Drivers选项 找到Real Time Clock画面,按下space键选择为“*”,再进入这个选项 选中Samsung S3C series SoC RTC 在Exit退出后保存 2.修改

WINCE7中RTC时钟芯片BQ32K的开发

最近客户反映我们提供的WINCE7系统的RTC时钟设置后,掉电不能保存,总是回到2000年的起始年份, 我们的工业触摸屏使用的是AM3354方案,查看了原来的WINCE7系统源码,打开文件: C:\WINCE700\PLATFORM\AM33X_BSP\SRC\OAL\OALLIB\init.c 发现在OEMInit函数中调用的是TI自己的RTC驱动函数OAL3XX_RTCInit,这个函数在路径为: C:\WINCE700\PLATFORM\COMMON\src\soc\COMMON_TI_V

树莓派配置RTC时钟(DS3231,I2C接口)

1.购买基于DS3231的RTC时钟模块,并且支持3.3V的那种 2.配置树莓派 a.打开树莓派的i2c接口 sudo raspi-config -->Interfacing Options -->I2C,全部选择yes b.添加i2c模块     sudo nano /etc/modules     然后添加以下两行内容:         i2c-bcm2708         i2c-dev c.安装i2c工具,查看i2c设备b sudo apt-get install i2c-tools

玩转u8g2 OLED库 MAX7219_32X8点阵模块

u8g2 OLED库 + MAX7219_32X8点阵模块 理论基础 玩转u8g2 OLED库,一篇就够 玩转u8g2 OLED库,一篇就够(字数太多 要分篇) 实验内容 Full screen buffer mode 显示"I??U" 实验器材 Mega2560 + MAX7219-32X8点阵模块 引脚连接 4线软件模拟SPI总线 模块引脚 Mega2560引脚 VCC VCC GND GND DIN 5 CS 6 CLK 7 实验代码 /* HelloWorld.ino */ #

(16)给树莓派B+ 安装一个实时时钟芯片DS1302

在这里首先感谢glgoo在google被墙的情况下提供搜索功能,还要感谢 http://wiringpi.com/ 以及github,尽管自己眼瞎,几经波折还是发现了尘封已久的少林"七十二绝技",没错,每种绝技都能克制一个树莓派GPIO控制外设的问题,七十二绝技的核心就是wiringpi这个为Pi GPIO写的C语言库了,而每种绝技则暗藏在wiringPi/examples/目录下,这里就不一一罗列了,大家想学什么招式,就去看每个example,都非常简短. 其实早在第9篇文章&quo