#pragma once #include <stdint.h> #include "alarms.hpp" #include "flags.hpp" namespace rtc { namespace detail { ////////////////////////////////////////////////////////////////////////// template <uint8_t Mask = 0xFF> static inline uint8_t toBcd(const uint8_t &data) { return ((data / 10 * 16) + (data % 10)) & Mask; } template <uint8_t Mask = 0xFF> static inline uint8_t fromBcd(const uint8_t &data) { const auto maskedData = data & Mask; return ((maskedData / 16 * 10) + (maskedData % 16)); } static inline uint8_t convertTo24Hour(const uint8_t &hoursReg) { constexpr auto FLAG_12_HOUR = 6; constexpr auto FLAG_PM = 5; const bool time12HourFormat = (hoursReg >> FLAG_12_HOUR) & 1; if (time12HourFormat) { const auto pmFlag = (hoursReg >> FLAG_PM) & 1; constexpr auto HOUR_12_MASK = 0b00011111; const auto hour12 = fromBcd<HOUR_12_MASK>(hoursReg); if (hour12 == 12 && !pmFlag) return 0; return hour12 + pmFlag ? 12 : 0; } else // 24 hour format { constexpr auto HOUR_MASK = 0b00111111; return fromBcd<HOUR_MASK>(hoursReg); } } template <uint8_t Mask = 0b01111111> static inline uint8_t getMaskedBcd(const uint8_t ®) { return fromBcd<Mask>(reg); } template <uint8_t Mask = 0b01111111> static inline void setMaskedBcd(uint8_t ®, const uint8_t &value) { reg &= ~Mask; reg |= toBcd<Mask>(value); } static inline bool getEncodedDay(uint8_t &value, const uint8_t ®) { constexpr auto DAY_FLAG = 6; if ((reg >> DAY_FLAG) & 1) { constexpr auto DAY_MASK = 0b00001111; value = reg & DAY_MASK; return true; } return false; } static inline bool getEncodedDate(uint8_t &value, const uint8_t ®) { constexpr auto DAY_FLAG = 6; if (!((reg >> DAY_FLAG) & 1)) { value = getMaskedBcd<0b00111111>(reg); return true; } return false; } static inline void setEncodedDay(uint8_t ®, const uint8_t &value) { constexpr auto DAY_MASK = 0b11110000; reg &= DAY_MASK; reg |= value & DAY_MASK; constexpr auto DAY_FLAG = 6; reg |= (1 << DAY_FLAG); } static inline void setEncodedDate(uint8_t ®, const uint8_t &value) { setMaskedBcd<0b00111111>(reg, value); constexpr auto DAY_FLAG = 6; reg &= ~(1 << DAY_FLAG); } ////////////////////////////////////////////////////////////////////////// struct [[gnu::packed]] TimeReg { uint8_t seconds = 0; uint8_t minutes = 0; uint8_t hours = 0; uint8_t day = 1; // Range 1-7 according to datasheet uint8_t date = 0; uint8_t month_century = 0; uint8_t year = 0; ////////////////////////////////////////////////////////////////////////// inline uint8_t getSeconds() const { return getMaskedBcd(seconds); } inline uint8_t getMinutes() const { return getMaskedBcd(minutes); } inline uint8_t getHours() const { return convertTo24Hour(hours); } inline uint8_t getDay() const { return getMaskedBcd<0b00000111>(day); } inline uint8_t getDate() const { return getMaskedBcd<0b00111111>(date); } inline uint8_t getMonth() const { return getMaskedBcd<0b00011111>(month_century); } inline bool getCentury() const { constexpr auto CENTURY_FLAG = 7; return (month_century >> CENTURY_FLAG) & 1; } inline uint16_t getYear() const { return 2000 + fromBcd(year); } ////////////////////////////////////////////////////////////////////////// inline void setSeconds(uint8_t seconds) { setMaskedBcd(this->seconds, seconds); } inline void setMinutes(uint8_t minutes) { setMaskedBcd(this->minutes, minutes); } inline void setHours(uint8_t hours) { setMaskedBcd(this->hours, hours); } inline void setDay(uint8_t day) { this->day = day & 0b111; } inline void setDate(uint8_t date) { setMaskedBcd<0b00111111>(this->date, date); } inline void setMonth(uint8_t month) { setMaskedBcd<0b00011111>(month_century, month); } inline void setCentury(bool century) { constexpr auto CENTURY_POS = 7; month_century &= ~(1 << CENTURY_POS); month_century |= (century << CENTURY_POS); } inline void setYear(uint16_t year) { year = year % 100; this->year = toBcd(year); } }; static_assert(sizeof(TimeReg) == 7, "Invalid time register size"); ////////////////////////////////////////////////////////////////////////// struct [[gnu::packed]] Alarm1Reg { uint8_t seconds = 0; uint8_t minutes = 0; uint8_t hours = 0; uint8_t day_date = 0; ////////////////////////////////////////////////////////////////////////// inline uint8_t getSeconds() const { return getMaskedBcd(seconds); } inline uint8_t getMinutes() const { return getMaskedBcd(minutes); } inline uint8_t getHours() const { return convertTo24Hour(hours); } inline bool getDay(uint8_t & day) const { return getEncodedDay(day, day_date); } inline bool getDate(uint8_t & date) const { return getEncodedDate(date, day_date); } inline Alarm1Rate getAlarmRate() const { constexpr auto M_FLAG = 7; const auto m1 = (seconds >> M_FLAG) & 1; const auto m2 = (minutes >> M_FLAG) & 1; const auto m3 = (hours >> M_FLAG) & 1; const auto m4 = (day_date >> M_FLAG) & 1; const auto m = (m4 << 3) | (m3 << 2) | (m2 << 1) | (m1 << 0); if (m == 0) { constexpr auto DAY_FLAG = 6; const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 4; return static_cast<Alarm1Rate>(dayFormat); } return static_cast<Alarm1Rate>(m); } ////////////////////////////////////////////////////////////////////////// inline void setSeconds(uint8_t seconds) { setMaskedBcd(this->seconds, seconds); } inline void setMinutes(uint8_t minutes) { setMaskedBcd(this->minutes, minutes); } inline void setHours(uint8_t hours) { setMaskedBcd(this->hours, hours); } inline void setDay(uint8_t day) { setEncodedDay(day_date, day); } inline void setDate(uint8_t date) { setEncodedDate(day_date, date); } inline void setAlarmRate(const Alarm1Rate &alarmRate) { const auto alarmRateFlags = static_cast<uint8_t>(alarmRate); constexpr auto M_FLAG = 7; seconds &= ~(1 << M_FLAG); seconds |= (alarmRateFlags & 1) << M_FLAG; minutes &= ~(1 << M_FLAG); minutes |= ((alarmRateFlags >> 1) & 1) << M_FLAG; hours &= ~(1 << M_FLAG); hours |= ((alarmRateFlags >> 2) & 1) << M_FLAG; day_date &= ~(1 << M_FLAG); day_date |= ((alarmRateFlags >> 3) & 1) << M_FLAG; } }; static_assert(sizeof(Alarm1Reg) == 4, "Invalid alarm1 register size"); ////////////////////////////////////////////////////////////////////////// struct [[gnu::packed]] Alarm2Reg { uint8_t minutes = 0; uint8_t hours = 0; uint8_t day_date = 0; ////////////////////////////////////////////////////////////////////////// inline uint8_t getMinutes() const { return getMaskedBcd(minutes); } inline uint8_t getHours() const { return convertTo24Hour(hours); } inline bool getDay(uint8_t & day) const { return getEncodedDay(day, day_date); } inline bool getDate(uint8_t & date) const { return getEncodedDate(date, day_date); } inline Alarm2Rate getAlarmRate() const { constexpr auto M_FLAG = 7; const auto m2 = (minutes >> M_FLAG) & 1; const auto m3 = (hours >> M_FLAG) & 1; const auto m4 = (day_date >> M_FLAG) & 1; const auto m = (m4 << 2) | (m3 << 1) | (m2 << 0); if (m == 0) { constexpr auto DAY_FLAG = 6; const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 3; return static_cast<Alarm2Rate>(dayFormat); } return static_cast<Alarm2Rate>(m); } ////////////////////////////////////////////////////////////////////////// inline void setMinutes(uint8_t minutes) { setMaskedBcd(this->minutes, minutes); } inline void setHours(uint8_t hours) { setMaskedBcd(this->hours, hours); } inline void setDay(uint8_t day) { setEncodedDay(day_date, day); } inline void setDate(uint8_t date) { setEncodedDate(day_date, date); } inline void setAlarmRate(const Alarm2Rate &alarmRate) { const auto alarmRateFlags = static_cast<uint8_t>(alarmRate); constexpr auto M_FLAG = 7; minutes &= ~(1 << M_FLAG); minutes |= (alarmRateFlags & 1) << M_FLAG; hours &= ~(1 << M_FLAG); hours |= ((alarmRateFlags >> 1) & 1) << M_FLAG; day_date &= ~(1 << M_FLAG); day_date |= ((alarmRateFlags >> 2) & 1) << M_FLAG; } }; static_assert(sizeof(Alarm2Reg) == 3, "Invalid alarm2 register size"); ////////////////////////////////////////////////////////////////////////// enum class ControlRegFlags : uint8_t { N_EOSC = 1 << 7, BBSQW = 1 << 6, CONV = 1 << 5, RS2 = 1 << 4, RS1 = 1 << 3, INTCN = 1 << 2, A2IE = 1 << 1, A1IE = 1 << 0, }; static inline uint8_t operator~(const ControlRegFlags &flag) { return ~static_cast<uint8_t>(flag); } struct [[gnu::packed]] ControlReg : FlagsImpl<ControlRegFlags>{}; static_assert(sizeof(ControlReg) == 1, "Invalid control register size"); ////////////////////////////////////////////////////////////////////////// enum class ControlStatusRegFlags : uint8_t { OSF = 1 << 7, EN32KHZ = 1 << 3, BSY = 1 << 2, A2F = 1 << 1, A1F = 1 << 0, }; static inline uint8_t operator~(const ControlStatusRegFlags &flag) { return ~static_cast<uint8_t>(flag); } struct [[gnu::packed]] ControlStatusReg : FlagsImpl<ControlStatusRegFlags>{}; static_assert(sizeof(ControlStatusReg) == 1, "Invalid control/status register size"); ////////////////////////////////////////////////////////////////////////// struct [[gnu::packed]] AgingOffsetReg { uint8_t data = 0; }; static_assert(sizeof(AgingOffsetReg) == 1, "Invalid aging offset register size"); ////////////////////////////////////////////////////////////////////////// struct [[gnu::packed]] TempReg { uint8_t msb_temp = 0; uint8_t lsb_temp = 0; }; static_assert(sizeof(TempReg) == 2, "Invalid temperature register size"); ////////////////////////////////////////////////////////////////////////// } // namespace detail } // namespace rtc