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.

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

  1. 玩转 RTC时钟库 DS3231

    1.前言     接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块.没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里 ...

  2. 轻松吃透实时时钟芯片DS1302软硬件设计,看完秒懂

    今天我们来讨论一款老掉牙的实时时钟芯片DS1302.什么是实时时钟(RealTime Clock, RTC)呢?为什么我们需要它呢?假设你使用单片机实现万年历应用,一般的做法是这样的:设置中断后判断1 ...

  3. S3C2440上RTC时钟驱动开发实例讲解(转载)

    嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤.一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便.如有错误之处,谢请指正. 共享资源,欢迎转载:http:/ ...

  4. 基于STM32F1的时钟芯片DS1302驱动

    目录 DS1302.h源代码 DS1302.c源代码 DS1302.h源代码 /** ********************************************************* ...

  5. STM32 RTC时钟的配置

    1) 使能电源时钟和备份区域时钟. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能电源时钟和 ...

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

    1.购买基于DS3231的RTC时钟模块,并且支持3.3V的那种 2.配置树莓派 a.打开树莓派的i2c接口 sudo raspi-config -->Interfacing Options - ...

  7. 教你在树莓派使用上RTC实时时钟,不用再担心断电后时间归零的问题,开机后自动同步RTC时钟!!!

    准备工作:1.系统建议使用官方最新的镜像文件 2.RTC时钟模块板(I2C接口)建议使用DS1307时钟模块,或者RTC时钟模块RTC时钟模块: 大家知道arduino的电平是5V,树莓派是3.3V, ...

  8. RTC时钟和BKP的配置stm32

    摘自:https://blog.csdn.net/gtkknd/article/details/52233605 RTC和后备寄存器通过一个开关供电,在VDD有效的时候选择VDD供电,否则选择VBAT ...

  9. 玩转u8g2 OLED库 MAX7219_32X8点阵模块

    u8g2 OLED库 + MAX7219_32X8点阵模块 理论基础 玩转u8g2 OLED库,一篇就够 玩转u8g2 OLED库,一篇就够(字数太多 要分篇) 实验内容 Full screen bu ...

随机推荐

  1. SpringBoot的注解注入功能移植到.Net平台(开源)

    *:first-child { margin-top: 0 !important; } .markdown-body>*:last-child { margin-bottom: 0 !impor ...

  2. (三)Spring 高级装配 bean的作用域@Scope

    1.默认情况下,spring通过@Autowared注入的bean是单例的bean,但有些情况是不满足的,例如:购物车,每个会话,或每个用户登录使用的购物车都是独立的 spring的定义的作用域: a ...

  3. Introduction to ES6上课笔记

    课程链接:https://scrimba.com/g/gintrotoes6 这个网站有几个热门的前端技术栈的免费课程,上课体验除了英语渣只能看代码理解听老师讲的一知半解之外,是极佳的学编程的网站了. ...

  4. 四、springBoot 优雅的创建定时任务

    前言 好几天没写了,工作有点忙,最近工作刚好做一个定时任务统计的,所以就将springboot 如何创建定时任务整理了一下. 总的来说,springboot创建定时任务是非常简单的,不用像spring ...

  5. WordPress 文章目录插件 Easy Table of Contents 配置教程

    今天介绍一款目录插件,本站已经使用一段时间,感觉不错,实现效果可以看文章页右侧边栏有目录,点击目录内容会快速定位.推荐给大家. 一.安装Easy Table of Contents 插件主页搜索Eas ...

  6. [技术栈]CRC校验原理及C#代码实现CRC16、CRC32计算FCS校验码

    1.CRC.FCS是什么 CRC,全称Cyclic Redundancy Check,中文名称为循环冗余校验,是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检 ...

  7. spark运行信息及报错问题解决集锦

    错误1: ERROR client.RemoteDriver: Failed to start SparkContext: java.lang.IllegalArgumentException: Ex ...

  8. Kotlin学习系列(二)

    IF表达式 if在kotlin可以当做表达式使用跟java的三元操作符类似: var max = if( a > b ) a else b if分支可以使用代码块,最后一个表达式是返回值: va ...

  9. How to setup Electrum testnet mode and get BTC test coins

    For some reason we need to use BTC test coins, but how to set up the Bitcoin testnet wallet and get ...

  10. CentOS7下mongodb忘记密码后重置密码

    新装mongodb后,结果一段时间没有用,密码给忘记了,只能重置密码了. 步骤如下: 1.找到mongodb的配置文件 通过ps -ef|grep mongod找到mongodb的配置文件mongod ...