玩转 RTC时钟库 DS1302

授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

你如果想学常见的Arduino第三方库

1.玩转u8g2 OLED库,一篇就够
2.玩转u8g2 OLED库,一篇就够(字数太多 要分篇)
3.玩转u8g2 OLED库 + MAX7219_32X8点阵模块 (绝对被拒绝的表白神器)
4.玩转 RTC时钟库 + DS1302
5.玩转 RTC时钟库 + DS3231
6.玩转ArduinoJson库 V5版本
7.玩转ArduinoJson库 V6版本
8.玩转PubSubClient MQTT库

文章目录

    • 你如果想学常见的Arduino第三方库
    • 1.前言
    • 2.RTC
      • 2.1 简介
      • 2.2 安装
    • 3. DS1302 与 RTC库
      • 3.1 DS1302设计时钟芯片
      • 3.2 DS1302电路图与引脚关系
      • 3.3 DS1302具体操作
        • 3.3.1 DS1302寄存器
        • 3.3.2 RTCDS1302库
        • 3.3.3 RTCDS1302库函数
          • 3.3.3.1 Begin() —— 初始化
          • 3.3.3.2 GetIsWriteProtected() —— 判断是否写保护
          • 3.3.3.3 GetIsWriteProtected() —— 设置是否写保护
          • 3.3.3.4 GetIsRunning() —— 判断时钟是否正在运行
          • 3.3.3.5 SetIsRunning() —— 设置时钟是否运行
          • 3.3.3.6 SetDateTime() —— 设置日期时间
          • 3.3.3.7 GetDateTime() —— 获取日期时间
          • 3.3.3.8 SetMemory() —— 往RTC Memory写入数据
          • 3.3.3.9 GetMemory() —— 读取RTC Memory
        • 3.3.3 DS1302接线
        • 3.3.4 测试用例
          • 3.3.4.1 测试时间
          • 3.3.4.2 测试RTC Memory
    • 4.总结

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”,选择版本安装

玩转 RTC时钟库 DS1302_第1张图片

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

玩转 RTC时钟库 DS1302_第2张图片

    代码非常直接明了,分别支持了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电路图与引脚关系

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

玩转 RTC时钟库 DS1302_第3张图片

玩转 RTC时钟库 DS1302_第4张图片

3.3 DS1302具体操作

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

3.3.1 DS1302寄存器

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

玩转 RTC时钟库 DS1302_第5张图片

    此外,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 
#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   
#include 

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);
}

实验结果:
玩转 RTC时钟库 DS1302_第6张图片

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   
#include 

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);
}

实验结果:
玩转 RTC时钟库 DS1302_第7张图片

4.总结

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

你可能感兴趣的:(Arduino学习笔记)