深入学习 RTC时钟库 DS3231 您所在的位置:网站首页 arduino计时函数的精度 深入学习 RTC时钟库 DS3231

深入学习 RTC时钟库 DS3231

2024-06-12 12:23| 来源: 网络整理| 查看: 265

❤️博主介绍❤️ 😬 作者:单片机菜鸟哥 👉火爆博客:ESP8266 Arduino教程

零基础从入门到熟悉Arduino平台下开发ESP8266,同时会涉及网络编程知识。专栏文章累计超过60篇,分为基础篇、网络篇、应用篇、高级篇,涵盖ESP8266大部分开发技巧。

👻最近更新:ESP32 MicroPython应用

讲解Python在esp32上的应用,包括网络请求、爬虫

快速导航 单片机菜鸟的博客快速索引(快速找到你要的)

如果觉得有用,麻烦点赞收藏,您的支持是博主创作的动力。

文章目录 1.前言2.DS3231介绍2.1 为什么使用DS32312.2 DS3231概述2.3 DS3231电路图&引脚关系2.4 DS3231寄存器2.5 RTCDS3231库2.5.1 Begin() —— 初始化2.5.2 LastError() —— 获取上次错误编码2.5.3 IsDateTimeValid() —— 判断时间是否有效2.5.4 GetIsRunning() —— 判断时钟是否正在运行2.5.5 SetIsRunning() —— 设置时钟是否运行2.5.6 SetDateTime() —— 设置日期时间2.5.7 GetDateTime() —— 获取日期时间2.5.8 Enable32kHzPin() —— 使能32kHz引脚输出2.5.10 SetSquareWavePin() —— 设置方波输出2.5.11 SetSquareWavePinClockFrequency() —— 设置方波时钟频率2.5.12 SetAlarmOne() —— 设置闹钟12.5.13 GetAlarmOne() —— 获取闹钟12.5.14 SetAlarmTwo() —— 设置闹钟22.5.15 GetAlarmTwo() —— 获取闹钟22.5.16 LatchAlarmsTriggeredFlags() —— 处理闹钟触发 2.6 EepromAt24c32库2.6.1 Begin() —— 初始化2.6.2 LastError() —— 获取上次错误编码2.6.3 SetMemory() —— 存储数据2.6.4 GetMemory() —— 读取数据 2.7 DS3231接线 3.测试用例3.1 测试时间3.2 测试闹钟3.3 测试存储 4.总结

1.前言

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

DS3231 2.DS3231介绍 2.1 为什么使用DS3231

    常用的DS1302需要使用外置晶振,且没有温度补偿,误差较大。这就促使了更高精度的时钟芯片 —— DS3231。     DS3231内置晶振且有内部温度补偿,误差可做到1分钟每年。说白了,精度更高。

2.2 DS3231概述 DS3231是一款高精度I2C实时时钟器件,具有集成的温度补偿晶体振荡器,集成的晶体振荡器可提高器件的长期精确度;该器件包含电池输入端(也就是普通纽扣电池),断开主电源时仍可保持精确计时;DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息(除了和DS1302有一样的寄存器之外,还额外闹钟寄存器,这是可以做闹钟应用的一个关键点);少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式;DS3231提供两个可编程日历闹钟和一路可编程方波输出;DS3231与单片机通过I2C双向串行总线传输地址与数据;自带存储芯片:AT24C32 EEPROM芯片(存储容量32K,可以存不少东西); 2.3 DS3231电路图&引脚关系 电路图

image

引脚关系

image

2.4 DS3231寄存器

    跟DS1302一样,对于DS3231的操作就是操作对应的寄存器,其寄存器对应关系如下:

image

2.5 RTCDS3231库

    老规矩,先看看源码,博主在源码中加入了部分代码注释:

#ifndef __RTCDS3231_H__ #define __RTCDS3231_H__ #include #include "RtcDateTime.h" #include "RtcTemperature.h" #include "RtcUtility.h" //I2C Slave Address const uint8_t DS3231_ADDRESS = 0x68; //DS3231 Register Addresses const uint8_t DS3231_REG_TIMEDATE = 0x00;//日期时间相关寄存器的第一个地址 const uint8_t DS3231_REG_ALARMONE = 0x07;//闹钟1寄存器 const uint8_t DS3231_REG_ALARMTWO = 0x0B;//闹钟2寄存器 const uint8_t DS3231_REG_CONTROL = 0x0E;//控制寄存器 const uint8_t DS3231_REG_STATUS = 0x0F;//状态寄存器 const uint8_t DS3231_REG_AGING = 0x10; const uint8_t DS3231_REG_TEMP = 0x11; //DS3231 Register Data Size if not just 1 const uint8_t DS3231_REG_TIMEDATE_SIZE = 7;//日期时间相关寄存器的数量 const uint8_t DS3231_REG_ALARMONE_SIZE = 4;//闹钟1寄存器占用空间大小 4字节 const uint8_t DS3231_REG_ALARMTWO_SIZE = 3;//闹钟2寄存器占用空间大小 3字节 const uint8_t DS3231_REG_TEMP_SIZE = 2; // DS3231 Control Register Bits const uint8_t DS3231_A1IE = 0; const uint8_t DS3231_A2IE = 1; const uint8_t DS3231_INTCN = 2; const uint8_t DS3231_RS1 = 3; const uint8_t DS3231_RS2 = 4; const uint8_t DS3231_CONV = 5; const uint8_t DS3231_BBSQW = 6; const uint8_t DS3231_EOSC = 7; const uint8_t DS3231_AIEMASK = (_BV(DS3231_A1IE) | _BV(DS3231_A2IE)); const uint8_t DS3231_RSMASK = (_BV(DS3231_RS1) | _BV(DS3231_RS2)); // DS3231 Status Register Bits const uint8_t DS3231_A1F = 0; const uint8_t DS3231_A2F = 1; const uint8_t DS3231_BSY = 2; const uint8_t DS3231_EN32KHZ = 3; const uint8_t DS3231_OSF = 7; const uint8_t DS3231_AIFMASK = (_BV(DS3231_A1F) | _BV(DS3231_A2F)); // seconds accuracy enum DS3231AlarmOneControl { // bit order: A1M4 DY/DT A1M3 A1M2 A1M1 DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00, DS3231AlarmOneControl_OncePerSecond = 0x17, DS3231AlarmOneControl_SecondsMatch = 0x16, DS3231AlarmOneControl_MinutesSecondsMatch = 0x14, DS3231AlarmOneControl_HoursMinutesSecondsMatch = 0x10, DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08, }; class DS3231AlarmOne { public: DS3231AlarmOne( uint8_t dayOf, uint8_t hour, uint8_t minute, uint8_t second, DS3231AlarmOneControl controlFlags) : _flags(controlFlags), _dayOf(dayOf), _hour(hour), _minute(minute), _second(second) { } uint8_t DayOf() const { return _dayOf; } uint8_t Hour() const { return _hour; } uint8_t Minute() const { return _minute; } uint8_t Second() const { return _second; } DS3231AlarmOneControl ControlFlags() const { return _flags; } bool operator == (const DS3231AlarmOne& other) const { return (_dayOf == other._dayOf && _hour == other._hour && _minute == other._minute && _second == other._second && _flags == other._flags); } bool operator != (const DS3231AlarmOne& other) const { return !(*this == other); } protected: DS3231AlarmOneControl _flags; uint8_t _dayOf; uint8_t _hour; uint8_t _minute; uint8_t _second; }; // minutes accuracy enum DS3231AlarmTwoControl { // bit order: A2M4 DY/DT A2M3 A2M2 DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00, DS3231AlarmTwoControl_OncePerMinute = 0x0b, DS3231AlarmTwoControl_MinutesMatch = 0x0a, DS3231AlarmTwoControl_HoursMinutesMatch = 0x08, DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04, }; class DS3231AlarmTwo { public: DS3231AlarmTwo( uint8_t dayOf, uint8_t hour, uint8_t minute, DS3231AlarmTwoControl controlFlags) : _flags(controlFlags), _dayOf(dayOf), _hour(hour), _minute(minute) { } uint8_t DayOf() const { return _dayOf; } uint8_t Hour() const { return _hour; } uint8_t Minute() const { return _minute; } DS3231AlarmTwoControl ControlFlags() const { return _flags; } bool operator == (const DS3231AlarmTwo& other) const { return (_dayOf == other._dayOf && _hour == other._hour && _minute == other._minute && _flags == other._flags); } bool operator != (const DS3231AlarmTwo& other) const { return !(*this == other); } protected: DS3231AlarmTwoControl _flags; uint8_t _dayOf; uint8_t _hour; uint8_t _minute; }; enum DS3231SquareWaveClock { DS3231SquareWaveClock_1Hz = 0b00000000, DS3231SquareWaveClock_1kHz = 0b00001000, DS3231SquareWaveClock_4kHz = 0b00010000, DS3231SquareWaveClock_8kHz = 0b00011000, }; enum DS3231SquareWavePinMode { DS3231SquareWavePin_ModeNone, DS3231SquareWavePin_ModeBatteryBackup, DS3231SquareWavePin_ModeClock, DS3231SquareWavePin_ModeAlarmOne, DS3231SquareWavePin_ModeAlarmTwo, DS3231SquareWavePin_ModeAlarmBoth }; enum DS3231AlarmFlag { DS3231AlarmFlag_Alarm1 = 0x01, DS3231AlarmFlag_Alarm2 = 0x02, DS3231AlarmFlag_AlarmBoth = 0x03, }; template class RtcDS3231 { public: RtcDS3231(T_WIRE_METHOD& wire) : _wire(wire), _lastError(0) { } void Begin() { //会把三个引脚设置为输入状态 _wire.begin(); } uint8_t LastError() { return _lastError; } bool IsDateTimeValid() { uint8_t status = getReg(DS3231_REG_STATUS); return !(status & _BV(DS3231_OSF)); } /** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning() { //判断控制寄存器 DS3231_EOSC bit位置 uint8_t creg = getReg(DS3231_REG_CONTROL); return !(creg & _BV(DS3231_EOSC)); } /** * 设置时钟是否运行 * @param isRunning * true 时钟运行 * false 时钟停振,进入低功耗态 */ void SetIsRunning(bool isRunning) { uint8_t creg = getReg(DS3231_REG_CONTROL); if (isRunning) { creg &= ~_BV(DS3231_EOSC); } else { creg |= _BV(DS3231_EOSC); } setReg(DS3231_REG_CONTROL, creg); } /** * 设置日期时间 * @param RtcDateTime 日期时间对象 */ void SetDateTime(const RtcDateTime& dt) { // clear the invalid flag uint8_t status = getReg(DS3231_REG_STATUS); status &= ~_BV(DS3231_OSF); // clear the flag setReg(DS3231_REG_STATUS, status); // set the date time 批量设置时间 _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_TIMEDATE); _wire.write(Uint8ToBcd(dt.Second()));//秒数 _wire.write(Uint8ToBcd(dt.Minute()));//分钟 _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only uint8_t year = dt.Year() - 2000; uint8_t centuryFlag = 0; if (year >= 100) { year -= 100; centuryFlag = _BV(7); } // 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.Day()));//天数 _wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);//月份 _wire.write(Uint8ToBcd(year));//年份 _lastError = _wire.endTransmission(); } /** * 获取日期时间 * @return RtcDateTime 日期时间对象 */ RtcDateTime GetDateTime() { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_TIMEDATE); _lastError = _wire.endTransmission(); if (_lastError != 0) { return RtcDateTime(0); } _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TIMEDATE_SIZE); uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数 uint8_t minute = BcdToUint8(_wire.read());//分钟 uint8_t hour = BcdToBin24Hour(_wire.read());//小时 _wire.read(); // throwing away day of week as we calculate it uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数 uint8_t monthRaw = _wire.read();//月份 uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份 if (monthRaw & _BV(7)) // century wrap flag { year += 100; } uint8_t month = BcdToUint8(monthRaw & 0x7f); return RtcDateTime(year, month, dayOfMonth, hour, minute, second); } RtcTemperature GetTemperature() { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_TEMP); _lastError = _wire.endTransmission(); if (_lastError != 0) { return RtcTemperature(0); } // Temperature is represented as a 10-bit code with a resolution // of 1/4th �C and is accessable as a signed 16-bit integer at // locations 11h and 12h. // // | r11h | DP | r12h | // Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2 // s i i i i i i i . f f 0 0 0 0 0 0 // // As it takes (8) right-shifts to register the decimal point (DP) to // the right of the 0th bit, the overall word scaling equals 256. // // For example, at +/- 25.25�C, concatenated registers = // 256 * (+/- 25+(1/4)) = +/- 6464, or 1940h / E6C0h. _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TEMP_SIZE); int8_t r11h = _wire.read(); // MS byte, signed temperature return RtcTemperature( r11h, _wire.read() ); // LS byte is r12h } void Enable32kHzPin(bool enable) { uint8_t sreg = getReg(DS3231_REG_STATUS); if (enable == true) { sreg |= _BV(DS3231_EN32KHZ); } else { sreg &= ~_BV(DS3231_EN32KHZ); } setReg(DS3231_REG_STATUS, sreg); } /** * 设置方波输出 */ void SetSquareWavePin(DS3231SquareWavePinMode pinMode) { uint8_t creg = getReg(DS3231_REG_CONTROL); // clear all relevant bits to a known "off" state creg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW)); creg |= _BV(DS3231_INTCN); // set INTCN to disables SQW switch (pinMode) { case DS3231SquareWavePin_ModeNone: break; case DS3231SquareWavePin_ModeBatteryBackup: creg |= _BV(DS3231_BBSQW); // set battery backup flag creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW break; case DS3231SquareWavePin_ModeClock: creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW break; case DS3231SquareWavePin_ModeAlarmOne: creg |= _BV(DS3231_A1IE); break; case DS3231SquareWavePin_ModeAlarmTwo: creg |= _BV(DS3231_A2IE); break; case DS3231SquareWavePin_ModeAlarmBoth: creg |= _BV(DS3231_A1IE) | _BV(DS3231_A2IE); break; } setReg(DS3231_REG_CONTROL, creg); } void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq) { uint8_t creg = getReg(DS3231_REG_CONTROL); creg &= ~DS3231_RSMASK; // Set to 0 creg |= (freq & DS3231_RSMASK); // Set freq bits setReg(DS3231_REG_CONTROL, creg); } /** * 设置闹钟1 */ void SetAlarmOne(const DS3231AlarmOne& alarm) { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_ALARMONE); _wire.write(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_ALARMTWO); _wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_ALARMONE); _lastError = _wire.endTransmission(); if (_lastError != 0) { return DS3231AlarmOne(0, 0, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch); } _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMONE_SIZE); uint8_t raw = _wire.read(); uint8_t flags = (raw & 0x80) >> 7; uint8_t second = BcdToUint8(raw & 0x7F); raw = _wire.read(); flags |= (raw & 0x80) >> 6; uint8_t minute = BcdToUint8(raw & 0x7F); raw = _wire.read(); flags |= (raw & 0x80) >> 5; uint8_t hour = BcdToBin24Hour(raw & 0x7f); raw = _wire.read(); flags |= (raw & 0xc0) >> 3; uint8_t dayOf = BcdToUint8(raw & 0x3f); if (flags == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch) { dayOf = RtcDateTime::ConvertRtcToDow(dayOf); } return DS3231AlarmOne(dayOf, hour, minute, second, (DS3231AlarmOneControl)flags); } /** * 获取闹钟2 */ DS3231AlarmTwo GetAlarmTwo() { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_ALARMTWO); _lastError = _wire.endTransmission(); if (_lastError != 0) { return DS3231AlarmTwo(0, 0, 0, DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch); } _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMTWO_SIZE); uint8_t raw = _wire.read(); uint8_t flags = (raw & 0x80) >> 7; uint8_t minute = BcdToUint8(raw & 0x7F); raw = _wire.read(); flags |= (raw & 0x80) >> 6; uint8_t hour = BcdToBin24Hour(raw & 0x7f); raw = _wire.read(); flags |= (raw & 0xc0) >> 4; uint8_t dayOf = BcdToUint8(raw & 0x3f); if (flags == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch) { dayOf = RtcDateTime::ConvertRtcToDow(dayOf); } return DS3231AlarmTwo(dayOf, hour, minute, (DS3231AlarmTwoControl)flags); } // Latch must be called after an alarm otherwise it will not // trigger again DS3231AlarmFlag LatchAlarmsTriggeredFlags() { uint8_t sreg = getReg(DS3231_REG_STATUS); uint8_t alarmFlags = (sreg & DS3231_AIFMASK); sreg &= ~DS3231_AIFMASK; // clear the flags setReg(DS3231_REG_STATUS, sreg); return (DS3231AlarmFlag)alarmFlags; } void ForceTemperatureCompensationUpdate(bool block) { uint8_t creg = getReg(DS3231_REG_CONTROL); creg |= _BV(DS3231_CONV); // Write CONV bit setReg(DS3231_REG_CONTROL, creg); while (block && (creg & _BV(DS3231_CONV)) != 0) { // Block until CONV is 0 creg = getReg(DS3231_REG_CONTROL); } } int8_t GetAgingOffset() { return getReg(DS3231_REG_AGING); } void SetAgingOffset(int8_t value) { setReg(DS3231_REG_AGING, value); } private: T_WIRE_METHOD& _wire; uint8_t _lastError; uint8_t getReg(uint8_t regAddress) { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(regAddress); _lastError = _wire.endTransmission(); if (_lastError != 0) { return 0; } // control register _wire.requestFrom(DS3231_ADDRESS, (uint8_t)1); uint8_t regValue = _wire.read(); return regValue; } void setReg(uint8_t regAddress, uint8_t regValue) { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(regAddress); _wire.write(regValue); _lastError = _wire.endTransmission(); } }; #endif // __RTCDS3231_H__ 2.5.1 Begin() —— 初始化

函数说明:

/** * 初始化,会把三个引脚设置为输入状态 */ void Begin() 2.5.2 LastError() —— 获取上次错误编码

函数说明:

/** * 获取上次错误编码 * @return 返回错误编码 */ uint8_t LastError()

注意:

错误编码请参考 https://www.arduino.cc/en/Reference/WireEndTransmission 2.5.3 IsDateTimeValid() —— 判断时间是否有效

函数说明:

/** * 判断时间是否有效 * @return false 通常意味着电池没电或日期和时间从未设置 * true 意味时间有效 */ bool IsDateTimeValid() 2.5.4 GetIsRunning() —— 判断时钟是否正在运行

函数说明:

/** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning()

源码说明:

/** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning() { //判断控制寄存器 DS3231_EOSC bit位置 uint8_t creg = getReg(DS3231_REG_CONTROL); return !(creg & _BV(DS3231_EOSC)); } 2.5.5 SetIsRunning() —— 设置时钟是否运行

函数说明:

/** * 设置时钟是否运行 * @param isRunning * true 时钟运行 * false 时钟停振,进入低功耗态 */ void SetIsRunning(bool isRunning)

源码说明:

/** * 设置时钟是否运行 * @param isRunning * true 时钟运行 * false 时钟停振,进入低功耗态 */ void SetIsRunning(bool isRunning) { uint8_t creg = getReg(DS3231_REG_CONTROL); if (isRunning) { creg &= ~_BV(DS3231_EOSC); } else { creg |= _BV(DS3231_EOSC); } setReg(DS3231_REG_CONTROL, creg); } 2.5.6 SetDateTime() —— 设置日期时间

函数说明:

/** * 设置日期时间 * @param RtcDateTime 日期时间对象 */ void SetDateTime(const RtcDateTime& dt)

源码说明:

/** * 设置日期时间 * @param RtcDateTime 日期时间对象 */ void SetDateTime(const RtcDateTime& dt) { // clear the invalid flag uint8_t status = getReg(DS3231_REG_STATUS); status &= ~_BV(DS3231_OSF); // clear the flag setReg(DS3231_REG_STATUS, status); // set the date time 批量设置时间 _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_TIMEDATE); _wire.write(Uint8ToBcd(dt.Second()));//秒数 _wire.write(Uint8ToBcd(dt.Minute()));//分钟 _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only uint8_t year = dt.Year() - 2000; uint8_t centuryFlag = 0; if (year >= 100) { year -= 100; centuryFlag = _BV(7); } // 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.Day()));//天数 _wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);//月份 _wire.write(Uint8ToBcd(year));//年份 _lastError = _wire.endTransmission(); } 2.5.7 GetDateTime() —— 获取日期时间

函数说明:

/** * 获取日期时间 * @return RtcDateTime 日期时间对象 */ RtcDateTime GetDateTime()

源码说明:

/** * 获取日期时间 * @return RtcDateTime 日期时间对象 */ RtcDateTime GetDateTime() { _wire.beginTransmission(DS3231_ADDRESS); _wire.write(DS3231_REG_TIMEDATE); _lastError = _wire.endTransmission(); if (_lastError != 0) { return RtcDateTime(0); } _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TIMEDATE_SIZE); uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数 uint8_t minute = BcdToUint8(_wire.read());//分钟 uint8_t hour = BcdToBin24Hour(_wire.read());//小时 _wire.read(); // throwing away day of week as we calculate it uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数 uint8_t monthRaw = _wire.read();//月份 uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份 if (monthRaw & _BV(7)) // century wrap flag { year += 100; } uint8_t month = BcdToUint8(monthRaw & 0x7f); return RtcDateTime(year, month, dayOfMonth, hour, minute, second); } 2.5.8 Enable32kHzPin() —— 使能32kHz引脚输出

函数说明:

/** * 使能32kHz引脚输出 * @param enable true 使能 * false 禁止 */ void Enable32kHzPin(bool enable) 2.5.10 SetSquareWavePin() —— 设置方波输出

函数说明:

/** * 设置方波输出 * @param DS3231SquareWavePinMode 方波引脚模式 */ void SetSquareWavePin(DS3231SquareWavePinMode pinMode)

DS3231SquareWavePinMode 参数说明:

DS3231SquareWavePin_ModeNone 禁止引脚输出DS3231SquareWavePin_ModeBatteryBackup 如果外部电源电压低于电池电压,该引脚会触发中断DS3231SquareWavePin_ModeClock 该引脚触发频率由SetSquareWavePinClockFrequency方法定义DS3231SquareWavePin_ModeAlarmOne 闹钟1会触发DS3231SquareWavePin_ModeAlarmTwo 闹钟2会触发DS3231SquareWavePin_ModeAlarmBoth 闹钟1或者闹钟2都会触发

源码说明:

/** * 设置方波输出 */ void SetSquareWavePin(DS3231SquareWavePinMode pinMode) { uint8_t creg = getReg(DS3231_REG_CONTROL); // clear all relevant bits to a known "off" state creg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW)); creg |= _BV(DS3231_INTCN); // set INTCN to disables SQW switch (pinMode) { case DS3231SquareWavePin_ModeNone: break; case DS3231SquareWavePin_ModeBatteryBackup: creg |= _BV(DS3231_BBSQW); // set battery backup flag creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW break; case DS3231SquareWavePin_ModeClock: creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW break; case DS3231SquareWavePin_ModeAlarmOne: creg |= _BV(DS3231_A1IE); break; case DS3231SquareWavePin_ModeAlarmTwo: creg |= _BV(DS3231_A2IE); break; case DS3231SquareWavePin_ModeAlarmBoth: creg |= _BV(DS3231_A1IE) | _BV(DS3231_A2IE); break; } setReg(DS3231_REG_CONTROL, creg); } 2.5.11 SetSquareWavePinClockFrequency() —— 设置方波时钟频率

函数说明:

/** * 设置方波时钟频率 * @param DS3231SquareWaveClock 方波时钟频率 */ void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq)

DS3231SquareWaveClock 参数说明:

DS3231SquareWaveClock_1HzDS3231SquareWaveClock_1kHzDS3231SquareWaveClock_4kHzDS3231SquareWaveClock_8kHz 2.5.12 SetAlarmOne() —— 设置闹钟1

函数说明:

/** * 设置闹钟1 * @param DS3231AlarmOne 闹钟1 */ void SetAlarmOne(const DS3231AlarmOne& alarm)

注意点:

当达到闹钟定义的条件,就会触发中断,INT/SQW输出低电平信号;

DS3231AlarmOne源码解析:

class DS3231AlarmOne { public: DS3231AlarmOne( uint8_t dayOf, uint8_t hour, uint8_t minute, uint8_t second, DS3231AlarmOneControl controlFlags) : _flags(controlFlags), _dayOf(dayOf), _hour(hour), _minute(minute), _second(second) { } /** * 返回一周的一天或者一个月中的一天 */ uint8_t DayOf() const { return _dayOf; } /** * 返回一天的小时 24h制 */ uint8_t Hour() const { return _hour; } /** * 返回分钟 */ uint8_t Minute() const { return _minute; } /** * 返回秒数 */ uint8_t Second() const { return _second; } DS3231AlarmOneControl ControlFlags() const { return _flags; } bool operator == (const DS3231AlarmOne& other) const { return (_dayOf == other._dayOf && _hour == other._hour && _minute == other._minute && _second == other._second && _flags == other._flags); } bool operator != (const DS3231AlarmOne& other) const { return !(*this == other); } protected: DS3231AlarmOneControl _flags; uint8_t _dayOf; uint8_t _hour; uint8_t _minute; uint8_t _second; };

重点看构造函数:

/** * 建立闹钟1对象 * @param dayOf - (0-6) (1-31) day of the week or the day of the month , see flags below * @param hour - (0-23) the hour of the day * @param minute - (0-59) the minute of the hour * @param second - (0-59) the second of the minute * @param controlFlags * -- DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch 月天时分秒都匹配才会触发中断 * -- DS3231AlarmOneControl_OncePerSecond 每一秒都触发 * -- DS3231AlarmOneControl_SecondsMatch 每一分钟的秒数匹配才触发 * -- DS3231AlarmOneControl_MinutesSecondsMatch 每小时里面的分秒都匹配才触发 * -- DS3231AlarmOneControl_HoursMinutesSecondsMatch 一天中时分秒都匹配才触发 * -- DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch 一个星期中天时分秒都匹配才触发 */ DS3231AlarmOne( uint8_t dayOf, uint8_t hour, uint8_t minute, uint8_t second, DS3231AlarmOneControl controlFlags) 2.5.13 GetAlarmOne() —— 获取闹钟1

函数说明:

/** * 获取闹钟1 * @return DS3231AlarmOne 闹钟1 */ DS3231AlarmOne GetAlarmOne() 2.5.14 SetAlarmTwo() —— 设置闹钟2

函数说明:

/** * 设置闹钟2 * @param DS3231AlarmTwo 闹钟2 */ void SetAlarmTwo(const DS3231AlarmTwo& alarm)

注意点:

当达到闹钟定义的条件,就会触发中断,INT/SQW输出低电平信号;

DS3231AlarmTwo源码解析:

class DS3231AlarmTwo { public: DS3231AlarmTwo( uint8_t dayOf, uint8_t hour, uint8_t minute, DS3231AlarmTwoControl controlFlags) : _flags(controlFlags), _dayOf(dayOf), _hour(hour), _minute(minute) { } /** * 返回一周的一天或者一个月中的一天 */ uint8_t DayOf() const { return _dayOf; } /** * 返回一天的小时 24h制 */ uint8_t Hour() const { return _hour; } /** * 返回分钟 */ uint8_t Minute() const { return _minute; } DS3231AlarmTwoControl ControlFlags() const { return _flags; } bool operator == (const DS3231AlarmTwo& other) const { return (_dayOf == other._dayOf && _hour == other._hour && _minute == other._minute && _flags == other._flags); } bool operator != (const DS3231AlarmTwo& other) const { return !(*this == other); } protected: DS3231AlarmTwoControl _flags; uint8_t _dayOf; uint8_t _hour; uint8_t _minute; };

重点看构造函数:

/** * 建立闹钟2对象 * @param dayOf - (0-6) (1-31) day of the week or the day of the month , see flags below * @param hour - (0-23) the hour of the day * @param minute - (0-59) the minute of the hour * @param controlFlags * -- DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch 每月天时分都匹配才会触发中断 * -- DS3231AlarmTwoControl_OncePerMinute 每一分钟都触发 * -- DS3231AlarmTwoControl_MinutesMatch 每一小时的分钟匹配才触发 * -- DS3231AlarmTwoControl_HoursMinutesMatch 每天里面的时分都匹配才触发 * -- DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch 每星期的天时分匹配才触发 */ DS3231AlarmTwo( uint8_t dayOf, uint8_t hour, uint8_t minute, DS3231AlarmTwoControl controlFlags) 2.5.15 GetAlarmTwo() —— 获取闹钟2

函数说明:

/** * 获取闹钟2 * @return DS3231AlarmTwo 闹钟2 */ DS3231AlarmTwo GetAlarmTwo() 2.5.16 LatchAlarmsTriggeredFlags() —— 处理闹钟触发

函数说明:

/** * 处理闹钟触发 * @return DS3231AlarmFlag * --- DS3231AlarmFlag_Alarm1 闹钟1触发 * --- DS3231AlarmFlag_Alarm2 闹钟2触发 * --- DS3231AlarmFlag_AlarmBoth 闹钟1、2触发 */ DS3231AlarmFlag LatchAlarmsTriggeredFlags()

注意点:

当闹钟触发之后必须要调用该方法,不然不会再次触发,用来确保我们处理了闹钟事件; 2.6 EepromAt24c32库

    前面说到了,DS3231时钟模块集成了AT24c32 eeprom存储芯片,如果我们需要用到存储数据功能,就得引入 EepromAt24c32库。那么,我们来看看该库有什么方法。

2.6.1 Begin() —— 初始化

函数说明:

/** * 初始化引脚 */ void Begin() 2.6.2 LastError() —— 获取上次错误编码

函数说明:

/** * 获取上次错误编码 * @return 返回错误编码 */ uint8_t LastError()

注意:

错误编码请参考 https://www.arduino.cc/en/Reference/WireEndTransmission 2.6.3 SetMemory() —— 存储数据

函数说明:

/*** * 写入数据 * @param memoryAddress 地址偏移量 * @param value 数据 */ void SetMemory(uint16_t memoryAddress, uint8_t value) /** * 批量写入数据 * @param pValue 批量数据 * @param countBytes 数据字节数 */ uint8_t SetMemory(uint16_t memoryAddress, const uint8_t* pValue, uint8_t countBytes) 2.6.4 GetMemory() —— 读取数据

函数说明:

/*** * 读取数据 * @param memoryAddress 地址偏移量 * @return 数据 */ uint8_t GetMemory(uint16_t memoryAddress) /*** * 批量读取数据 * @param memoryAddress 地址偏移量 * @param pValue 存储空间 * @param countBytes 数据字节数 */ uint8_t SetMemory(uint16_t memoryAddress, const uint8_t* pValue, uint8_t countBytes) 2.7 DS3231接线

DS3231采用I2C总线方式,SCLK、SDA。

3.测试用例

测试用例分为三个:

测试时间测试闹钟测试存储 3.1 测试时间

实验内容

设置时间并在串口上打印时间

实验器材

Mega2560 + DS3231

引脚连接

模块引脚Mega2560引脚VCCVCC5VGNDGNDSDASDA(20)SCLSCL(21)

实验代码

// CONNECTIONS: // DS3231 SDA --> SDA // DS3231 SCL --> SCL // DS3231 VCC --> 3.3v or 5v // DS3231 GND --> GND /* for software wire use below #include // must be included here so that Arduino library object file references work #include SoftwareWire myWire(SDA, SCL); RtcDS3231 Rtc(myWire); for software wire use above */ /* for normal hardware wire use below */ #include // must be included here so that Arduino library object file references work #include RtcDS3231 Rtc(Wire); /* for normal hardware wire use above */ void setup () { Serial.begin(57600); Serial.print("compiled: "); Serial.print(__DATE__); Serial.println(__TIME__); //--------RTC SETUP ------------ // if you are using ESP-01 then uncomment the line below to reset the pins to // the available pins for SDA, SCL // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL Rtc.Begin(); RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); printDateTime(compiled); Serial.println(); if (!Rtc.IsDateTimeValid()) { if (Rtc.LastError() != 0) { // we have a communications error // see https://www.arduino.cc/en/Reference/WireEndTransmission for // what the number means Serial.print("RTC communications error = "); Serial.println(Rtc.LastError()); } else { // Common Cuases: // 1) first time you ran and the device wasn't running yet // 2) the battery on the device is low or even missing Serial.println("RTC lost confidence in the DateTime!"); // following line sets the RTC to the date & time this sketch was compiled // it will also reset the valid flag internally unless the Rtc device is // having an issue Rtc.SetDateTime(compiled); } } if (!Rtc.GetIsRunning()) { Serial.println("RTC was not actively running, starting now"); Rtc.SetIsRunning(true); } RtcDateTime now = Rtc.GetDateTime(); if (now 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)"); } // never assume the Rtc was last configured by you, so // just clear them to your needed state Rtc.Enable32kHzPin(false); Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); } void loop () { if (!Rtc.IsDateTimeValid()) { if (Rtc.LastError() != 0) { // we have a communications error // see https://www.arduino.cc/en/Reference/WireEndTransmission for // what the number means Serial.print("RTC communications error = "); Serial.println(Rtc.LastError()); } else { // Common Cuases: // 1) the battery on the device is low or even missing and the power line was disconnected Serial.println("RTC lost confidence in the DateTime!"); } } RtcDateTime now = Rtc.GetDateTime(); printDateTime(now); Serial.println(); RtcTemperature temp = Rtc.GetTemperature(); temp.Print(Serial); // you may also get the temperature as a float and print it // Serial.print(temp.AsFloatDegC()); Serial.println("C"); 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.2 测试闹钟

实验内容

设置时间并设置闹钟

实验器材

Mega2560 + DS3231

引脚连接

模块引脚Mega2560引脚VCCVCC5VGNDGNDSDASDA(20)SCLSCL(21)SQW19

实验代码

// CONNECTIONS: // DS3231 SDA --> SDA // DS3231 SCL --> SCL // DS3231 VCC --> 3.3v or 5v // DS3231 GND --> GND // SQW ---> (Pin19) Don't forget to pullup (4.7k to 10k to VCC) /* for software wire use below #include // must be included here so that Arduino library object file references work #include SoftwareWire myWire(SDA, SCL); RtcDS3231 Rtc(myWire); for software wire use above */ /* for normal hardware wire use below */ #include // must be included here so that Arduino library object file references work #include RtcDS3231 Rtc(Wire); /* for normal hardware wire use above */ // Interrupt Pin Lookup Table // (copied from Arduino Docs) // // CAUTION: The interrupts are Arduino numbers NOT Atmel numbers // and may not match (example, Mega2560 int.4 is actually Atmel Int2) // this is only an issue if you plan to use the lower level interupt features // // Board int.0 int.1 int.2 int.3 int.4 int.5 // --------------------------------------------------------------- // Uno, Ethernet 2 3 // Mega2560 2 3 21 20 [19] 18 // Leonardo 3 2 0 1 7 #define RtcSquareWavePin 19 // Mega2560 #define RtcSquareWaveInterrupt 4 // Mega2560 // marked volatile so interrupt can safely modify them and // other code can safely read and modify them volatile uint16_t interuptCount = 0; volatile bool interuptFlag = false; void InteruptServiceRoutine() { // since this interupted any other running code, // don't do anything that takes long and especially avoid // any communications calls within this routine interuptCount++; interuptFlag = true; } void setup () { Serial.begin(57600); // set the interupt pin to input mode pinMode(RtcSquareWavePin, INPUT); //--------RTC SETUP ------------ // if you are using ESP-01 then uncomment the line below to reset the pins to // the available pins for SDA, SCL // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL Rtc.Begin(); RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); if (!Rtc.IsDateTimeValid()) { if (Rtc.LastError() != 0) { // we have a communications error // see https://www.arduino.cc/en/Reference/WireEndTransmission for // what the number means Serial.print("RTC communications error = "); Serial.println(Rtc.LastError()); } else { Serial.println("RTC lost confidence in the DateTime!"); Rtc.SetDateTime(compiled); } } if (!Rtc.GetIsRunning()) { Serial.println("RTC was not actively running, starting now"); Rtc.SetIsRunning(true); } RtcDateTime now = Rtc.GetDateTime(); if (now if (!Rtc.IsDateTimeValid()) { if (Rtc.LastError() != 0) { // we have a communications error // see https://www.arduino.cc/en/Reference/WireEndTransmission for // what the number means Serial.print("RTC communications error = "); Serial.println(Rtc.LastError()); } else { Serial.println("RTC lost confidence in the DateTime!"); } } RtcDateTime now = Rtc.GetDateTime(); printDateTime(now); Serial.println(); // we only want to show time every 10 seconds // but we want to show responce to the interupt firing for (int timeCount = 0; timeCount Serial.print(">>Interupt Count: "); Serial.print(interuptCount); Serial.println("


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有