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. vue-router钩子函数实现路由守卫

    接上一篇,我们一起学习了vue路由的基本使用以及动态路由.路由嵌套以及路由命名等知识,今天我们一起来学习记录vue-router的钩子函数实现路由守卫: 何为路由守卫?路由守卫有点类似于ajax的请求 ...

  2. 记一次处理mysql数据库无故锁表的经历

    某日,生产环境上的用户表突然无故锁表,原以为只是偶发的bug.所以第一时间想到的解决方案简单粗暴:重启数据库(service mysqld restart).问题得以解决. 10min后,该表再次锁表 ...

  3. Xampp error:Port 80 in use by "Unable to open process" with PID 4

    今天打开Apache的时候报错: Port 80 in use by "Unable to open process" with PID 4 通过点击与“Apache”模块同一行上 ...

  4. 机器学习之SVM调参实例

    一.任务 这次我们将了解在机器学习中支持向量机的使用方法以及一些参数的调整.支持向量机的基本原理就是将低维不可分问题转换为高维可分问题,在前面的博客具体介绍过了,这里就不再介绍了. 首先导入相关标准库 ...

  5. asp.net core IdentityServer4 实现 implicit(隐式许可)实现第三方登录

    前言 OAuth 2.0默认四种授权模式(GrantType) 授权码模式(authorization_code) 简化模式(implicit) 密码模式(resource owner passwor ...

  6. 【PCIE-1】---Pcie基本概念普及(扫盲篇--巨适合新手)

    PCIE由早期得PCI扩展衍生而来并且对兼容PCI,两者得主要区别在于并行到串行得切换,且速率更快.目前主板上越来越多得设备都挂载到PCI总线下面,甚至部分硬盘也会挂载PCI总线下面,可见PCIE得应 ...

  7. layui-table与layui-rate评分转换成星级的使用

    需求:将layui-table中的某一列,例如:评分,从数据库中查找出来之后,进行layui-rate评分转换显示效果,为星星.显示效果如下: 实现代码: 1.layui中引入rate 2.table ...

  8. 阿里云服务器CentOS6.9 nexus私服使用

    exus安装成功之后,我们开始使用. 登录nexus: 访问地址:http:ip地址:8081/nexus 默认登录账号:admin 密码:admin123 登录成功之后页面: 点击[Reposito ...

  9. freemarker模版引擎技术总结

    FreeMarker语言概述 FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写. FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式 ...

  10. asp.netcore 自动挡Docker Nginx Redis(滴滴滴,自动挡)

    前言 上一章介绍了Docker通过多条命令创建启动运行Docker容器,由此可见这样一个个去创建单独的容器也是相当麻烦的,比如要在某个复杂项目中用DB.缓存.消息等等,这样我们还要去一个个再创建,为此 ...