#ifndef __RTCDS3234_H__ #define __RTCDS3234_H__ #include #include #include "RtcDateTime.h" #include "RtcTemperature.h" #include "RtcUtility.h" //DS3234 Register Addresses const uint8_t DS3234_REG_WRITE_FLAG = 0x80; const uint8_t DS3234_REG_TIMEDATE = 0x00; const uint8_t DS3234_REG_ALARMONE = 0x07; const uint8_t DS3234_REG_ALARMTWO = 0x0B; const uint8_t DS3234_REG_CONTROL = 0x0E; const uint8_t DS3234_REG_STATUS = 0x0F; const uint8_t DS3234_REG_AGING = 0x10; const uint8_t DS3234_REG_TEMP = 0x11; const uint8_t DS3234_REG_RAM_ADDRESS = 0x18; const uint8_t DS3234_REG_RAM_DATA = 0x19; const uint8_t DS3234_RAMSTART = 0x00; const uint8_t DS3234_RAMEND = 0xFF; const uint8_t DS3234_RAMSIZE = DS3234_RAMEND - DS3234_RAMSTART; // DS3234 Control Register Bits const uint8_t DS3234_A1IE = 0; const uint8_t DS3234_A2IE = 1; const uint8_t DS3234_INTCN = 2; const uint8_t DS3234_RS1 = 3; const uint8_t DS3234_RS2 = 4; const uint8_t DS3234_CONV = 5; const uint8_t DS3234_BBSQW = 6; const uint8_t DS3234_EOSC = 7; const uint8_t DS3234_AIEMASK = (_BV(DS3234_A1IE) | _BV(DS3234_A2IE)); const uint8_t DS3234_RSMASK = (_BV(DS3234_RS1) | _BV(DS3234_RS2)); // DS3234 Status Register Bits const uint8_t DS3234_A1F = 0; const uint8_t DS3234_A2F = 1; const uint8_t DS3234_BSY = 2; const uint8_t DS3234_EN32KHZ = 3; const uint8_t DS3234_CRATE0 = 4; const uint8_t DS3234_CRATE1 = 5; const uint8_t DS3234_BB32KHZ = 6; const uint8_t DS3234_OSF = 7; const uint8_t DS3234_AIFMASK = (_BV(DS3234_A1F) | _BV(DS3234_A2F)); const uint8_t DS3234_CRATEMASK = (_BV(DS3234_CRATE0) | _BV(DS3234_CRATE1)); // seconds accuracy enum DS3234AlarmOneControl { // bit order: A1M4 DY/DT A1M3 A1M2 A1M1 DS3234AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00, DS3234AlarmOneControl_OncePerSecond = 0x17, DS3234AlarmOneControl_SecondsMatch = 0x16, DS3234AlarmOneControl_MinutesSecondsMatch = 0x14, DS3234AlarmOneControl_HoursMinutesSecondsMatch = 0x10, DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08, }; class DS3234AlarmOne { public: DS3234AlarmOne( uint8_t dayOf, uint8_t hour, uint8_t minute, uint8_t second, DS3234AlarmOneControl 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; } DS3234AlarmOneControl ControlFlags() const { return _flags; } bool operator == (const DS3234AlarmOne& other) const { return (_dayOf == other._dayOf && _hour == other._hour && _minute == other._minute && _second == other._second && _flags == other._flags); } bool operator != (const DS3234AlarmOne& other) const { return !(*this == other); } protected: DS3234AlarmOneControl _flags; uint8_t _dayOf; uint8_t _hour; uint8_t _minute; uint8_t _second; }; // minutes accuracy enum DS3234AlarmTwoControl { // bit order: A2M4 DY/DT A2M3 A2M2 DS3234AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00, DS3234AlarmTwoControl_OncePerMinute = 0x0b, DS3234AlarmTwoControl_MinutesMatch = 0x0a, DS3234AlarmTwoControl_HoursMinutesMatch = 0x08, DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04, }; class DS3234AlarmTwo { public: DS3234AlarmTwo( uint8_t dayOf, uint8_t hour, uint8_t minute, DS3234AlarmTwoControl 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; } DS3234AlarmTwoControl ControlFlags() const { return _flags; } bool operator == (const DS3234AlarmTwo& other) const { return (_dayOf == other._dayOf && _hour == other._hour && _minute == other._minute && _flags == other._flags); } bool operator != (const DS3234AlarmTwo& other) const { return !(*this == other); } protected: DS3234AlarmTwoControl _flags; uint8_t _dayOf; uint8_t _hour; uint8_t _minute; }; enum DS3234SquareWaveClock { DS3234SquareWaveClock_1Hz = 0b00000000, DS3234SquareWaveClock_1kHz = 0b00001000, DS3234SquareWaveClock_4kHz = 0b00010000, DS3234SquareWaveClock_8kHz = 0b00011000, }; enum DS3234SquareWavePinMode { DS3234SquareWavePin_ModeNone, DS3234SquareWavePin_ModeBatteryBackup, DS3234SquareWavePin_ModeClock, DS3234SquareWavePin_ModeAlarmOne, DS3234SquareWavePin_ModeAlarmTwo, DS3234SquareWavePin_ModeAlarmBoth }; enum DS3234TempCompensationRate { DS3234TempCompensationRate_64Seconds, DS3234TempCompensationRate_128Seconds, DS3234TempCompensationRate_256Seconds, DS3234TempCompensationRate_512Seconds, }; enum DS3234AlarmFlag { DS3234AlarmFlag_Alarm1 = 0x01, DS3234AlarmFlag_Alarm2 = 0x02, DS3234AlarmFlag_AlarmBoth = 0x03, }; const SPISettings c_Ds3234SpiSettings(1000000, MSBFIRST, SPI_MODE1); // CPHA must be used, so mode 1 or mode 3 are valid template class RtcDS3234 { public: RtcDS3234(T_SPI_METHOD& spi, uint8_t csPin) : _spi(spi), _csPin(csPin) { } void Begin() { UnselectChip(); pinMode(_csPin, OUTPUT); } bool IsDateTimeValid() { uint8_t status = getReg(DS3234_REG_STATUS); return !(status & _BV(DS3234_OSF)); } bool GetIsRunning() { uint8_t creg = getReg(DS3234_REG_CONTROL); return !(creg & _BV(DS3234_EOSC)); } void SetIsRunning(bool isRunning) { uint8_t creg = getReg(DS3234_REG_CONTROL); if (isRunning) { creg &= ~_BV(DS3234_EOSC); } else { creg |= _BV(DS3234_EOSC); } setReg(DS3234_REG_CONTROL, creg); } void SetDateTime(const RtcDateTime& dt) { // clear the invalid flag uint8_t status = getReg(DS3234_REG_STATUS); status &= ~_BV(DS3234_OSF); // clear the flag setReg(DS3234_REG_STATUS, status); // set the date time _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_TIMEDATE | DS3234_REG_WRITE_FLAG); _spi.transfer(Uint8ToBcd(dt.Second())); _spi.transfer(Uint8ToBcd(dt.Minute())); _spi.transfer(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()); _spi.transfer(Uint8ToBcd(rtcDow)); _spi.transfer(Uint8ToBcd(dt.Day())); _spi.transfer(Uint8ToBcd(dt.Month()) | centuryFlag); _spi.transfer(Uint8ToBcd(year)); UnselectChip(); _spi.endTransaction(); } RtcDateTime GetDateTime() { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_TIMEDATE); uint8_t second = BcdToUint8(_spi.transfer(0)); uint8_t minute = BcdToUint8(_spi.transfer(0)); uint8_t hour = BcdToBin24Hour(_spi.transfer(0)); _spi.transfer(0); // throwing away day of week as we calculate it uint8_t dayOfMonth = BcdToUint8(_spi.transfer(0)); uint8_t monthRaw = _spi.transfer(0); uint16_t year = BcdToUint8(_spi.transfer(0)) + 2000; UnselectChip(); _spi.endTransaction(); 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() { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_TEMP); // 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. int8_t ms = _spi.transfer(0); // MS byte, signed temperature uint8_t ls = _spi.transfer(0); // LS byte is r12h UnselectChip(); _spi.endTransaction(); return RtcTemperature(ms, ls); // LS byte is r12h } void Enable32kHzPin(bool enable) { uint8_t sreg = getReg(DS3234_REG_STATUS); if (enable == true) { sreg |= _BV(DS3234_EN32KHZ); } else { sreg &= ~_BV(DS3234_EN32KHZ); } setReg(DS3234_REG_STATUS, sreg); } void SetSquareWavePin(DS3234SquareWavePinMode pinMode) { uint8_t creg = getReg(DS3234_REG_CONTROL); // clear all relevant bits to a known "off" state creg &= ~(DS3234_AIEMASK | _BV(DS3234_BBSQW)); creg |= _BV(DS3234_INTCN); // set INTCN to disables SQW switch (pinMode) { case DS3234SquareWavePin_ModeNone: break; case DS3234SquareWavePin_ModeBatteryBackup: creg |= _BV(DS3234_BBSQW); // set battery backup flag creg &= ~_BV(DS3234_INTCN); // clear INTCN to enable SQW break; case DS3234SquareWavePin_ModeClock: creg &= ~_BV(DS3234_INTCN); // clear INTCN to enable SQW break; case DS3234SquareWavePin_ModeAlarmOne: creg |= _BV(DS3234_A1IE); break; case DS3234SquareWavePin_ModeAlarmTwo: creg |= _BV(DS3234_A2IE); break; case DS3234SquareWavePin_ModeAlarmBoth: creg |= _BV(DS3234_A1IE) | _BV(DS3234_A2IE); break; } setReg(DS3234_REG_CONTROL, creg); } void SetSquareWavePinClockFrequency(DS3234SquareWaveClock freq) { uint8_t creg = getReg(DS3234_REG_CONTROL); creg &= ~DS3234_RSMASK; // Set to 0 creg |= (freq & DS3234_RSMASK); // Set freq bits setReg(DS3234_REG_CONTROL, creg); } void SetAlarmOne(const DS3234AlarmOne& alarm) { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_ALARMONE | DS3234_REG_WRITE_FLAG); _spi.transfer(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) << 7)); _spi.transfer(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x02) << 6)); _spi.transfer(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x04) << 5)); // 24 hour mode only uint8_t rtcDow = alarm.DayOf(); if (alarm.ControlFlags() == DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch) { rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow); } _spi.transfer(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x18) << 3)); UnselectChip(); _spi.endTransaction(); } void SetAlarmTwo(const DS3234AlarmTwo& alarm) { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_ALARMTWO | DS3234_REG_WRITE_FLAG); _spi.transfer(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) << 7)); _spi.transfer(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x02) << 6)); // 24 hour mode only // convert our Day of Week to Rtc Day of Week if needed uint8_t rtcDow = alarm.DayOf(); if (alarm.ControlFlags() == DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch) { rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow); } _spi.transfer(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x0c) << 4)); UnselectChip(); _spi.endTransaction(); } DS3234AlarmOne GetAlarmOne() { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_ALARMONE); uint8_t raw = _spi.transfer(0); uint8_t flags = (raw & 0x80) >> 7; uint8_t second = BcdToUint8(raw & 0x7F); raw = _spi.transfer(0); flags |= (raw & 0x80) >> 6; uint8_t minute = BcdToUint8(raw & 0x7F); raw = _spi.transfer(0); flags |= (raw & 0x80) >> 5; uint8_t hour = BcdToBin24Hour(raw & 0x7f); raw = _spi.transfer(0); flags |= (raw & 0xc0) >> 3; uint8_t dayOf = BcdToUint8(raw & 0x3f); UnselectChip(); _spi.endTransaction(); if (flags == DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch) { dayOf = RtcDateTime::ConvertRtcToDow(dayOf); } return DS3234AlarmOne(dayOf, hour, minute, second, (DS3234AlarmOneControl)flags); } DS3234AlarmTwo GetAlarmTwo() { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_ALARMTWO); uint8_t raw = _spi.transfer(0); uint8_t flags = (raw & 0x80) >> 7; uint8_t minute = BcdToUint8(raw & 0x7F); raw = _spi.transfer(0); flags |= (raw & 0x80) >> 6; uint8_t hour = BcdToBin24Hour(raw & 0x7f); raw = _spi.transfer(0); flags |= (raw & 0xc0) >> 4; uint8_t dayOf = BcdToUint8(raw & 0x3f); UnselectChip(); _spi.endTransaction(); if (flags == DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch) { dayOf = RtcDateTime::ConvertRtcToDow(dayOf); } return DS3234AlarmTwo(dayOf, hour, minute, (DS3234AlarmTwoControl)flags); } // Latch must be called after an alarm otherwise it will not // trigger again DS3234AlarmFlag LatchAlarmsTriggeredFlags() { uint8_t sreg = getReg(DS3234_REG_STATUS); uint8_t alarmFlags = (sreg & DS3234_AIFMASK); sreg &= ~DS3234_AIFMASK; // clear the flags setReg(DS3234_REG_STATUS, sreg); return (DS3234AlarmFlag)alarmFlags; } void SetTemperatureCompensationRate(DS3234TempCompensationRate rate) { uint8_t sreg = getReg(DS3234_REG_STATUS); sreg &= ~DS3234_CRATEMASK; sreg |= (rate << DS3234_CRATE0); setReg(DS3234_REG_STATUS, sreg); } DS3234TempCompensationRate GetTemperatureCompensationRate() { uint8_t sreg = getReg(DS3234_REG_STATUS); return (sreg & DS3234_CRATEMASK) >> DS3234_CRATE0; } void ForceTemperatureCompensationUpdate(bool block) { uint8_t creg = getReg(DS3234_REG_CONTROL); creg |= _BV(DS3234_CONV); // Write CONV bit setReg(DS3234_REG_CONTROL, creg); while (block && (creg & _BV(DS3234_CONV)) != 0) { // Block until CONV is 0 creg = getReg(DS3234_REG_CONTROL); } } int8_t GetAgingOffset() { return getReg(DS3234_REG_AGING); } void SetAgingOffset(int8_t value) { setReg(DS3234_REG_AGING, value); } void SetMemory(uint8_t memoryAddress, uint8_t value) { SetMemory(memoryAddress, &value, 1); } uint8_t GetMemory(uint8_t memoryAddress) { uint8_t value; GetMemory(memoryAddress, &value, 1); return value; } uint8_t SetMemory(uint8_t memoryAddress, const uint8_t* pValue, uint8_t countBytes) { uint8_t countWritten = 0; setReg(DS3234_REG_RAM_ADDRESS, memoryAddress); _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_RAM_DATA | DS3234_REG_WRITE_FLAG); while (countBytes > 0) { _spi.transfer(*pValue++); countBytes--; countWritten++; } UnselectChip(); _spi.endTransaction(); return countWritten; } uint8_t GetMemory(uint8_t memoryAddress, uint8_t* pValue, uint8_t countBytes) { // set address to read from setReg(DS3234_REG_RAM_ADDRESS, memoryAddress); _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(DS3234_REG_RAM_DATA); // read the data uint8_t countRead = 0; while (countBytes-- > 0) { *pValue++ = _spi.transfer(0); countRead++; } UnselectChip(); _spi.endTransaction(); return countRead; } private: T_SPI_METHOD& _spi; uint8_t _csPin; void SelectChip() { digitalWrite(_csPin, LOW); } void UnselectChip() { digitalWrite(_csPin, HIGH); } uint8_t getReg(uint8_t regAddress) { uint8_t regValue; _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(regAddress); regValue = _spi.transfer(0); UnselectChip(); _spi.endTransaction(); return regValue; } void setReg(uint8_t regAddress, uint8_t regValue) { _spi.beginTransaction(c_Ds3234SpiSettings); SelectChip(); _spi.transfer(regAddress | DS3234_REG_WRITE_FLAG); _spi.transfer(regValue); UnselectChip(); _spi.endTransaction(); } }; #endif // __RTCDS3234_H__