玩轉 RTC時鐘庫 DS1302

1.前言

    最近博主在弄8266編程的時候,偶然發現兩個全新時鐘模塊壓倉貨:git

  • DS1302
  • DS3231

    爲了不資源浪費以及重複編寫代碼,博主仍是抱着嘗試的心態去尋找可以同時兼容 DS130二、DS3231甚至其餘的時鐘模塊的第三方庫。終於,仍是被我找到了 —— Rtc 時鐘庫github

2.RTC

2.1 簡介

  • Arduino Real Time Clock library(Arduino平臺的實時時鐘庫)
  • 支持DS130二、DS130七、DS323一、DS3234
  • 英文文檔參考 wiki

2.2 安裝

  • 打開Arduino IDE的庫管理器,而後搜索「Rtc by Makuna」,選擇版本安裝

image

  • 爲了更加了解該庫的使用,博主會帶着你們剖析源碼。首先讓咱們看看代碼結構:

image

    代碼很是直接明瞭,分別支持了DS130二、DS130七、DS323一、DS3234。可是因爲博主資源限制,本篇只會討論DS1302以及DS3231,其餘請讀者自行學習。編程

3. DS1302 與 RTC庫

3.1 DS1302設計時鐘芯片

    網上介紹DS1302的帖子很是多,博主在這裏就不從零開始講解,這裏推薦一個博主以爲寫得還能夠的參考文檔 STM32與DS1302設計時鐘芯片,超詳細
    認真看完上面參考文檔以後,博主獲得幾個我比較關心的點:app

  • 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電路圖與引腳關係

    接下來看看經常使用電路圖以及引腳定義:函數

image

image

3.3 DS1302具體操做

    操做DS1302的大體過程,就是將各類數據或者控制命令寫入DS1302的寄存器來完成對應操做(好比設置時間、寫保護操做)。而後使DS1302開始運做,DS1302時鐘會按照設置狀況運轉,再用單片機將其寄存器內的數據讀出。再用液晶顯示,就是咱們常說的簡易電子鐘。oop

3.3.1 DS1302寄存器

    DS1302有12個寄存器,其中有7個寄存器與日曆、時鐘相關,存放的數據位爲BCD碼形式,其日曆、時間寄存器及其控制字見表。學習

image

    此外,DS1302 還有控制寄存器、充電寄存器、時鐘突發寄存器及與RAM相關的寄存器等。時鐘突發寄存器可一次性順序讀寫除充電寄存器外的全部寄存器內容。測試

    DS1302與RAM相關的寄存器分爲兩類:一類是單個RAM單元,共31個,每一個單元組態爲一個8位的字節,其命令控制字爲C0H~FDH,其中奇數爲讀操做,偶數爲寫操做;另外一類爲突發方式下的RAM寄存器,此方式下可一次性讀寫全部的RAM的31個字節,命令控制字爲FEH(寫)、FFH(讀)。ui

3.3.2 RTCDS1302庫

    上面只是介紹理論,在理解理論的基礎上,咱們開始講解RTC庫的使用。如下是RTCDS1302庫的源碼(參考源碼註釋):this

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

實驗結果:
image

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

實驗結果:
image

4.總結

本博文講解如何使用RTC時鐘庫來操做DS1302,同時附帶源碼解析,請關注下一篇DS3231.

相關文章
相關標籤/搜索