最近博主在弄8266编程的时候,偶然发现两个全新时钟模块压仓货:git
为了不资源浪费以及重复编写代码,博主仍是抱着尝试的心态去寻找可以同时兼容 DS130二、DS3231甚至其余的时钟模块的第三方库。终于,仍是被我找到了 —— Rtc 时钟库。github
代码很是直接明了,分别支持了DS130二、DS130七、DS323一、DS3234。可是因为博主资源限制,本篇只会讨论DS1302以及DS3231,其余请读者自行学习。编程
网上介绍DS1302的帖子很是多,博主在这里就不从零开始讲解,这里推荐一个博主以为写得还能够的参考文档 STM32与DS1302设计时钟芯片,超详细。
认真看完上面参考文档以后,博主获得几个我比较关心的点:app
接下来看看经常使用电路图以及引脚定义:函数
操做DS1302的大体过程,就是将各类数据或者控制命令写入DS1302的寄存器来完成对应操做(好比设置时间、写保护操做)。而后使DS1302开始运做,DS1302时钟会按照设置状况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是咱们常说的简易电子钟。oop
DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字见表。学习
此外,DS1302 还有控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的全部寄存器内容。测试
DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每一个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操做,偶数为写操做;另外一类为突发方式下的RAM寄存器,此方式下可一次性读写全部的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。ui
上面只是介绍理论,在理解理论的基础上,咱们开始讲解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__
函数说明:
/** * 初始化,会把三个引脚设置为输入状态 */ void Begin();
函数说明:
/** * 判断是否写保护 * @return bool true表示写保护 */ bool GetIsWriteProtected();
源码说明:
bool GetIsWriteProtected() { //获取写保护寄存器的值 uint8_t wp = getReg(DS1302_REG_WP); //获取最高位的值 return !!(wp & _BV(DS1302_WP)); }
函数说明:
/** * 设置是否写保护 * @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); }
函数说明:
/** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning()
源码说明:
bool GetIsRunning() { //判断秒寄存器的最高位数值 uint8_t ch = getReg(DS1302_REG_CH); return !(ch & _BV(DS1302_CH)); }
函数说明:
/** * 设置时钟是否运行 * @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); }
函数说明:
/** * 设置日期时间 * @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();//批量写入寄存器 }
参数说明:
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; } };
函数说明:
/** * 获取日期时间 * @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); }
函数说明:
/*** * 往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; }
函数说明:
/*** * 读取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; }
DS1302采用三总线方式,SCLK(CLK), IO (DAT), CE (RST)。
实验内容
实验器材
引脚链接
模块引脚 | 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); }
实验结果:
实验内容
实验器材
引脚链接
模块引脚 | 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); }
实验结果:
本博文讲解如何使用RTC时钟库来操做DS1302,同时附带源码解析,请关注下一篇DS3231.