Compare commits

..

7 Commits

4 changed files with 179 additions and 48 deletions

24
alarms.hpp Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
namespace rtc {
enum class Alarm1Rate : uint8_t {
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,
};
enum class Alarm2Rate : uint8_t {
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,
};
} // namespace rtc

View File

@@ -154,7 +154,7 @@ class DS3231 {
constexpr auto DATE_START_OFFSET = offsetof(detail::TimeReg, day); constexpr auto DATE_START_OFFSET = offsetof(detail::TimeReg, day);
constexpr auto DATE_END_OFFSET = offsetof(detail::TimeReg, year); constexpr auto DATE_END_OFFSET = offsetof(detail::TimeReg, year);
writePartialRegister<TIME_REG_ADDR, DATE_START_OFFSET, DATE_END_OFFSET>(timeReg); writePartialRegister<DATE_START_OFFSET, DATE_END_OFFSET>(timeReg);
} }
static void setTime(const Time &time) static void setTime(const Time &time)
{ {
@@ -166,7 +166,7 @@ class DS3231 {
constexpr auto TIME_START_OFFSET = offsetof(detail::TimeReg, seconds); constexpr auto TIME_START_OFFSET = offsetof(detail::TimeReg, seconds);
constexpr auto TIME_END_OFFSET = offsetof(detail::TimeReg, hours); constexpr auto TIME_END_OFFSET = offsetof(detail::TimeReg, hours);
writePartialRegister<TIME_REG_ADDR, TIME_START_OFFSET, TIME_END_OFFSET>(timeReg); writePartialRegister<TIME_START_OFFSET, TIME_END_OFFSET>(timeReg);
} }
static void setDateTime(const DateTime &dateTime) static void setDateTime(const DateTime &dateTime)
{ {
@@ -184,7 +184,38 @@ class DS3231 {
constexpr auto START_OFFSET = offsetof(detail::TimeReg, seconds); constexpr auto START_OFFSET = offsetof(detail::TimeReg, seconds);
constexpr auto END_OFFSET = offsetof(detail::TimeReg, year); constexpr auto END_OFFSET = offsetof(detail::TimeReg, year);
writePartialRegister<TIME_REG_ADDR, START_OFFSET, END_OFFSET>(timeReg); writePartialRegister<START_OFFSET, END_OFFSET>(timeReg);
}
static void setAlarm1(const DateTime &alarmTime, const Alarm1Rate &alarmRate, bool enableInterrupt = true)
{
setAlarmHelper(alarmTime, alarmRate);
if (enableInterrupt)
enableInterruptHelper<detail::ControlRegFlags::A1IE>();
}
static void setAlarm2(const DateTime &alarmTime, const Alarm2Rate &alarmRate, bool enableInterrupt = true)
{
setAlarmHelper(alarmTime, alarmRate);
if (enableInterrupt)
enableInterruptHelper<detail::ControlRegFlags::A2IE>();
}
static bool checkAlarm1()
{
return checkAlarmHelper<detail::ControlStatusRegFlags::A1F>();
}
static bool checkAlarm2()
{
return checkAlarmHelper<detail::ControlStatusRegFlags::A2F>();
}
static void clearAlarm1()
{
clearAlarmHelper<detail::ControlStatusRegFlags::A1F>();
}
static void clearAlarm2()
{
clearAlarmHelper<detail::ControlStatusRegFlags::A2F>();
} }
private: private:
@@ -224,9 +255,31 @@ class DS3231 {
} }
} }
template <uint8_t Address, uint8_t StartOffset, uint8_t EndOffset, typename Register> template <uint8_t StartOffset, uint8_t EndOffset, typename Register>
static void writePartialRegister(const Register &reg) static void writePartialRegister(const Register &reg)
{ {
constexpr auto getRegisterAddress = []() {
if constexpr (util::is_same_v<Register, detail::TimeReg>) {
return TIME_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::Alarm1Reg>) {
return ALARM1_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::Alarm2Reg>) {
return ALARM2_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::ControlReg>) {
return CONTROL_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::ControlStatusReg>) {
return CONTROL_STATUS_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::AgingOffsetReg>) {
return AGING_OFFSET_REG_ADDR;
} else if constexpr (util::is_same_v<Register, detail::TempReg>) {
return TEMP_REG_ADDR;
} else {
static_assert(util::always_false_v<Register>, "Invalid register type");
}
};
constexpr auto ADDRESS = getRegisterAddress();
static_assert(StartOffset <= EndOffset, "Invalid offset range"); static_assert(StartOffset <= EndOffset, "Invalid offset range");
static_assert(StartOffset < sizeof(Register), "Start offset out of bounds"); static_assert(StartOffset < sizeof(Register), "Start offset out of bounds");
static_assert(EndOffset < sizeof(Register), "End offset out of bounds"); static_assert(EndOffset < sizeof(Register), "End offset out of bounds");
@@ -234,30 +287,73 @@ class DS3231 {
constexpr auto WRITE_SIZE = EndOffset + 1 - StartOffset; constexpr auto WRITE_SIZE = EndOffset + 1 - StartOffset;
static_assert(StartOffset + WRITE_SIZE <= sizeof(Register), "Writing out of bounds"); static_assert(StartOffset + WRITE_SIZE <= sizeof(Register), "Writing out of bounds");
if constexpr (Address == TIME_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::TimeReg>, "Invalid register type");
} else if constexpr (Address == ALARM1_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::Alarm1Reg>, "Invalid register type");
} else if constexpr (Address == ALARM2_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::Alarm2Reg>, "Invalid register type");
} else if constexpr (Address == CONTROL_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::ControlReg>, "Invalid register type");
} else if constexpr (Address == CONTROL_STATUS_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::ControlStatusReg>, "Invalid register type");
} else if constexpr (Address == AGING_OFFSET_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::AgingOffsetReg>, "Invalid register type");
} else if constexpr (Address == TEMP_REG_ADDR) {
static_assert(util::is_same_v<Register, detail::TempReg>, "Invalid register type");
} else {
static_assert(util::always_false_v<Register>, "Invalid register address");
}
i2c_t::template start<I2C_ADDRESS>(false); i2c_t::template start<I2C_ADDRESS>(false);
i2c_t::write(Address + StartOffset); i2c_t::write(ADDRESS + StartOffset);
i2c_t::template writeBytes<WRITE_SIZE>(reinterpret_cast<const uint8_t *>(&reg) + StartOffset); i2c_t::template writeBytes<WRITE_SIZE>(reinterpret_cast<const uint8_t *>(&reg) + StartOffset);
i2c_t::stop(); i2c_t::stop();
} }
template <typename Register>
static void writeRegister(const Register &reg)
{
writePartialRegister<0, sizeof(Register) - 1>(reg);
}
template <typename AlarmRate>
static inline void setAlarmHelper(const DateTime &alarmTime, const AlarmRate &alarmRate)
{
constexpr auto IsAlarm1 = util::is_same_v<AlarmRate, Alarm1Rate>;
static_assert(IsAlarm1 || util::is_same_v<AlarmRate, Alarm2Rate>, "Must use valid alarm rate");
using alarm_reg_t = util::conditional_t<IsAlarm1, detail::Alarm1Reg, detail::Alarm2Reg>;
alarm_reg_t alarmReg;
alarmReg.setAlarmRate(alarmRate);
alarmReg.setDate(alarmTime.day);
alarmReg.setHours(alarmTime.hour);
alarmReg.setMinutes(alarmTime.minute);
if constexpr (IsAlarm1)
alarmReg.setSeconds(alarmTime.second);
writeRegister(alarmReg);
}
template <detail::ControlRegFlags AlarmFlag>
static inline void enableInterruptHelper()
{
constexpr auto IsAlarm1Flag = AlarmFlag == detail::ControlRegFlags::A1IE;
constexpr auto IsAlarm2Flag = AlarmFlag == detail::ControlRegFlags::A2IE;
static_assert(IsAlarm1Flag || IsAlarm2Flag, "Must use valid alarm flag");
auto controlReg = readRegister<CONTROL_REG_ADDR>();
using Flags = typename decltype(controlReg)::Flags;
controlReg &= ~Flags::BBSQW;
controlReg |= Flags::INTCN | AlarmFlag;
writeRegister(controlReg);
}
template <detail::ControlStatusRegFlags AlarmFlag>
static inline bool checkAlarmHelper()
{
constexpr auto IsAlarm1Flag = AlarmFlag == detail::ControlStatusRegFlags::A1F;
constexpr auto IsAlarm2Flag = AlarmFlag == detail::ControlStatusRegFlags::A2F;
static_assert(IsAlarm1Flag || IsAlarm2Flag, "Must use valid alarm flag");
const auto alarmStatus = readRegister<CONTROL_STATUS_REG_ADDR>();
return alarmStatus == AlarmFlag;
}
template <detail::ControlStatusRegFlags AlarmFlag>
static inline void clearAlarmHelper()
{
constexpr auto IsAlarm1Flag = AlarmFlag == detail::ControlStatusRegFlags::A1F;
constexpr auto IsAlarm2Flag = AlarmFlag == detail::ControlStatusRegFlags::A2F;
static_assert(IsAlarm1Flag || IsAlarm2Flag, "Must use valid alarm flag");
auto controlStatusReg = readRegister<CONTROL_STATUS_REG_ADDR>();
controlStatusReg &= ~AlarmFlag;
writeRegister(controlStatusReg);
}
static uint8_t calcDayOfWeek(uint16_t year, uint8_t month, uint16_t day) static uint8_t calcDayOfWeek(uint16_t year, uint8_t month, uint16_t day)
{ {
day += month < 3 ? year-- : year - 2; day += month < 3 ? year-- : year - 2;

View File

@@ -37,17 +37,34 @@ struct [[gnu::packed]] FlagsImpl
return *this; return *this;
} }
FlagsImpl &operator|=(const uint8_t &flag)
{
data |= flag;
return *this;
}
FlagsImpl &operator&=(const FlagsT &flag) FlagsImpl &operator&=(const FlagsT &flag)
{ {
data &= static_cast<uint8_t>(flag); data &= static_cast<uint8_t>(flag);
return *this; return *this;
} }
FlagsImpl &operator&=(const uint8_t &flag)
{
data &= flag;
return *this;
}
FlagsImpl &operator~() FlagsImpl &operator~()
{ {
data = ~data; data = ~data;
return *this; return *this;
} }
bool operator==(const FlagsT &flag) const
{
return data & static_cast<uint8_t>(flag);
}
}; };
template <typename FlagsT> template <typename FlagsT>

View File

@@ -2,6 +2,7 @@
#include <stdint.h> #include <stdint.h>
#include "alarms.hpp"
#include "flags.hpp" #include "flags.hpp"
namespace rtc { namespace rtc {
@@ -194,15 +195,6 @@ struct [[gnu::packed]] Alarm1Reg
uint8_t hours = 0; uint8_t hours = 0;
uint8_t day_date = 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 inline uint8_t getSeconds() const
@@ -225,7 +217,7 @@ struct [[gnu::packed]] Alarm1Reg
{ {
return getEncodedDate(date, day_date); return getEncodedDate(date, day_date);
} }
inline AlarmRate getAlarmRate() const inline Alarm1Rate getAlarmRate() const
{ {
constexpr auto M_FLAG = 7; constexpr auto M_FLAG = 7;
const auto m1 = (seconds >> M_FLAG) & 1; const auto m1 = (seconds >> M_FLAG) & 1;
@@ -236,9 +228,9 @@ struct [[gnu::packed]] Alarm1Reg
if (m == 0) { if (m == 0) {
constexpr auto DAY_FLAG = 6; constexpr auto DAY_FLAG = 6;
const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 4; const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 4;
return static_cast<AlarmRate>(dayFormat); return static_cast<Alarm1Rate>(dayFormat);
} }
return static_cast<AlarmRate>(m); return static_cast<Alarm1Rate>(m);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@@ -263,7 +255,7 @@ struct [[gnu::packed]] Alarm1Reg
{ {
setEncodedDate(day_date, date); setEncodedDate(day_date, date);
} }
inline void setAlarmRate(const AlarmRate &alarmRate) inline void setAlarmRate(const Alarm1Rate &alarmRate)
{ {
const auto alarmRateFlags = static_cast<uint8_t>(alarmRate); const auto alarmRateFlags = static_cast<uint8_t>(alarmRate);
constexpr auto M_FLAG = 7; constexpr auto M_FLAG = 7;
@@ -288,14 +280,6 @@ struct [[gnu::packed]] Alarm2Reg
uint8_t hours = 0; uint8_t hours = 0;
uint8_t day_date = 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 inline uint8_t getMinutes() const
@@ -314,7 +298,7 @@ struct [[gnu::packed]] Alarm2Reg
{ {
return getEncodedDate(date, day_date); return getEncodedDate(date, day_date);
} }
inline AlarmRate getAlarmRate() const inline Alarm2Rate getAlarmRate() const
{ {
constexpr auto M_FLAG = 7; constexpr auto M_FLAG = 7;
const auto m2 = (minutes >> M_FLAG) & 1; const auto m2 = (minutes >> M_FLAG) & 1;
@@ -324,9 +308,9 @@ struct [[gnu::packed]] Alarm2Reg
if (m == 0) { if (m == 0) {
constexpr auto DAY_FLAG = 6; constexpr auto DAY_FLAG = 6;
const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 3; const auto dayFormat = ((day_date >> DAY_FLAG) & 1) << 3;
return static_cast<AlarmRate>(dayFormat); return static_cast<Alarm2Rate>(dayFormat);
} }
return static_cast<AlarmRate>(m); return static_cast<Alarm2Rate>(m);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@@ -347,7 +331,7 @@ struct [[gnu::packed]] Alarm2Reg
{ {
setEncodedDate(day_date, date); setEncodedDate(day_date, date);
} }
inline void setAlarmRate(const AlarmRate &alarmRate) inline void setAlarmRate(const Alarm2Rate &alarmRate)
{ {
const auto alarmRateFlags = static_cast<uint8_t>(alarmRate); const auto alarmRateFlags = static_cast<uint8_t>(alarmRate);
constexpr auto M_FLAG = 7; constexpr auto M_FLAG = 7;
@@ -375,6 +359,11 @@ enum class ControlRegFlags : uint8_t {
A1IE = 1 << 0, A1IE = 1 << 0,
}; };
static inline uint8_t operator~(const ControlRegFlags &flag)
{
return ~static_cast<uint8_t>(flag);
}
struct [[gnu::packed]] ControlReg : FlagsImpl<ControlRegFlags>{}; struct [[gnu::packed]] ControlReg : FlagsImpl<ControlRegFlags>{};
static_assert(sizeof(ControlReg) == 1, "Invalid control register size"); static_assert(sizeof(ControlReg) == 1, "Invalid control register size");
@@ -389,6 +378,11 @@ enum class ControlStatusRegFlags : uint8_t {
A1F = 1 << 0, A1F = 1 << 0,
}; };
static inline uint8_t operator~(const ControlStatusRegFlags &flag)
{
return ~static_cast<uint8_t>(flag);
}
struct [[gnu::packed]] ControlStatusReg : FlagsImpl<ControlStatusRegFlags>{}; struct [[gnu::packed]] ControlStatusReg : FlagsImpl<ControlStatusRegFlags>{};
static_assert(sizeof(ControlStatusReg) == 1, "Invalid control/status register size"); static_assert(sizeof(ControlStatusReg) == 1, "Invalid control/status register size");