430 lines
9.8 KiB
C++
430 lines
9.8 KiB
C++
#pragma once
|
|
|
|
#include <stdint.h>
|
|
|
|
#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;
|
|
|
|
enum class AlarmRate {
|
|
ONCE_PER_S = 0b1111,
|
|
WHEN_S_MATCH = 0b1110,
|
|
WHEN_M_S_MATCH = 0b1100,
|
|
WHEN_H_M_S_MATCH = 0b1000,
|
|
WHEN_DATE_H_M_S_MATCH = 0b00000,
|
|
WHEN_DAY_H_N_S_MATCH = 0b10000,
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
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 AlarmRate 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<AlarmRate>(dayFormat);
|
|
}
|
|
return static_cast<AlarmRate>(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 AlarmRate &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;
|
|
|
|
enum class AlarmRate {
|
|
ONCE_PER_M = 0b111,
|
|
WHEN_M_MATCH = 0b110,
|
|
WHEN_H_M_MATCH = 0b100,
|
|
WHEN_DATE_H_M_MATCH = 0b0000,
|
|
WHEN_DAY_H_N_MATCH = 0b1000,
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
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 AlarmRate 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<AlarmRate>(dayFormat);
|
|
}
|
|
return static_cast<AlarmRate>(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 AlarmRate &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
|