Compare commits
11 Commits
a946746960
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a6170cb10 | |||
| 21f9215bba | |||
| dfd2289aef | |||
| 14e608d397 | |||
| 1bc7e66389 | |||
| ff52f4f152 | |||
| 3029c3cfe0 | |||
| c3f9aa6a13 | |||
| db5197b3b1 | |||
| 2a90cdee18 | |||
| 1388412d70 |
24
alarms.hpp
Normal file
24
alarms.hpp
Normal 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
|
||||
242
ds3231.hpp
242
ds3231.hpp
@@ -15,19 +15,74 @@ struct Date {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
|
||||
inline bool operator==(const Date &rhs) const
|
||||
{
|
||||
if (day != rhs.day)
|
||||
return false;
|
||||
if (month != rhs.month)
|
||||
return false;
|
||||
if (year != rhs.year)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Date &rhs)
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct Time {
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
|
||||
inline bool operator==(const Time &rhs) const
|
||||
{
|
||||
if (second != rhs.second)
|
||||
return false;
|
||||
if (minute != rhs.minute)
|
||||
return false;
|
||||
if (hour != rhs.hour)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Time &rhs)
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct DateTime : Date, Time {
|
||||
inline bool operator==(const DateTime &rhs) const
|
||||
{
|
||||
if (second != rhs.second)
|
||||
return false;
|
||||
if (minute != rhs.minute)
|
||||
return false;
|
||||
if (hour != rhs.hour)
|
||||
return false;
|
||||
if (day != rhs.day)
|
||||
return false;
|
||||
if (month != rhs.month)
|
||||
return false;
|
||||
if (year != rhs.year)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const DateTime &rhs)
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename I2cDriver, bool SetDayOfWeek = true>
|
||||
class DS3231 {
|
||||
using i2c_t = i2c::I2c<I2cDriver>;
|
||||
|
||||
public:
|
||||
static constexpr auto I2C_ADDRESS = 0x68;
|
||||
|
||||
@@ -50,7 +105,7 @@ class DS3231 {
|
||||
|
||||
static inline void init()
|
||||
{
|
||||
I2cDriver::init();
|
||||
i2c_t::init();
|
||||
}
|
||||
|
||||
static auto getDate()
|
||||
@@ -87,6 +142,15 @@ class DS3231 {
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
static DateTime getAlarm1()
|
||||
{
|
||||
return getAlarmHelper<ALARM1_REG_ADDR>();
|
||||
}
|
||||
static DateTime getAlarm2()
|
||||
{
|
||||
return getAlarmHelper<ALARM2_REG_ADDR>();
|
||||
}
|
||||
|
||||
static void setDate(const Date &date)
|
||||
{
|
||||
detail::TimeReg timeReg;
|
||||
@@ -99,7 +163,7 @@ class DS3231 {
|
||||
constexpr auto DATE_START_OFFSET = offsetof(detail::TimeReg, day);
|
||||
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)
|
||||
{
|
||||
@@ -111,7 +175,7 @@ class DS3231 {
|
||||
constexpr auto TIME_START_OFFSET = offsetof(detail::TimeReg, seconds);
|
||||
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)
|
||||
{
|
||||
@@ -129,21 +193,52 @@ class DS3231 {
|
||||
constexpr auto START_OFFSET = offsetof(detail::TimeReg, seconds);
|
||||
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:
|
||||
template <uint8_t Address, typename Register>
|
||||
static Register readRegisterHelper()
|
||||
{
|
||||
I2cDriver::template start<I2C_ADDRESS>(false);
|
||||
I2cDriver::write(Address);
|
||||
I2cDriver::stop();
|
||||
i2c_t::template start<I2C_ADDRESS>(false);
|
||||
i2c_t::write(Address);
|
||||
i2c_t::stop();
|
||||
|
||||
Register reg;
|
||||
I2cDriver::template start<I2C_ADDRESS>(true);
|
||||
I2cDriver::template readBytes<sizeof(Register)>(reinterpret_cast<uint8_t *>(®));
|
||||
I2cDriver::stop();
|
||||
i2c_t::template start<I2C_ADDRESS>(true);
|
||||
i2c_t::template readBytes<sizeof(Register)>(reinterpret_cast<uint8_t *>(®));
|
||||
i2c_t::stop();
|
||||
return reg;
|
||||
}
|
||||
|
||||
@@ -169,9 +264,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 ®)
|
||||
{
|
||||
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 < sizeof(Register), "Start offset out of bounds");
|
||||
static_assert(EndOffset < sizeof(Register), "End offset out of bounds");
|
||||
@@ -179,28 +296,89 @@ class DS3231 {
|
||||
constexpr auto WRITE_SIZE = EndOffset + 1 - StartOffset;
|
||||
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::write(ADDRESS + StartOffset);
|
||||
i2c_t::template writeBytes<WRITE_SIZE>(reinterpret_cast<const uint8_t *>(®) + StartOffset);
|
||||
i2c_t::stop();
|
||||
}
|
||||
|
||||
I2cDriver::template start<I2C_ADDRESS>(false);
|
||||
I2cDriver::write(Address + StartOffset);
|
||||
I2cDriver::template writeBytes<WRITE_SIZE>(reinterpret_cast<const uint8_t *>(®) + StartOffset);
|
||||
I2cDriver::stop();
|
||||
template <typename Register>
|
||||
static void writeRegister(const Register ®)
|
||||
{
|
||||
writePartialRegister<0, sizeof(Register) - 1>(reg);
|
||||
}
|
||||
|
||||
template <uint8_t Address>
|
||||
static inline auto getAlarmHelper()
|
||||
{
|
||||
constexpr auto IsAlarm1 = Address == ALARM1_REG_ADDR;
|
||||
static_assert(IsAlarm1 || Address == ALARM2_REG_ADDR, "Must use valid alarm address");
|
||||
|
||||
const auto alarmReg = readRegister<Address>();
|
||||
|
||||
DateTime alarmTime = {};
|
||||
alarmReg.getDate(alarmTime.day);
|
||||
alarmTime.hour = alarmReg.getHours();
|
||||
alarmTime.minute = alarmReg.getMinutes();
|
||||
if constexpr (IsAlarm1)
|
||||
alarmTime.second = alarmReg.getSeconds();
|
||||
|
||||
return alarmTime;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
17
flags.hpp
17
flags.hpp
@@ -37,17 +37,34 @@ struct [[gnu::packed]] FlagsImpl
|
||||
return *this;
|
||||
}
|
||||
|
||||
FlagsImpl &operator|=(const uint8_t &flag)
|
||||
{
|
||||
data |= flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FlagsImpl &operator&=(const FlagsT &flag)
|
||||
{
|
||||
data &= static_cast<uint8_t>(flag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FlagsImpl &operator&=(const uint8_t &flag)
|
||||
{
|
||||
data &= flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FlagsImpl &operator~()
|
||||
{
|
||||
data = ~data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const FlagsT &flag) const
|
||||
{
|
||||
return data & static_cast<uint8_t>(flag);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename FlagsT>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "alarms.hpp"
|
||||
#include "flags.hpp"
|
||||
|
||||
namespace rtc {
|
||||
@@ -194,15 +195,6 @@ struct [[gnu::packed]] Alarm1Reg
|
||||
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
|
||||
@@ -225,7 +217,7 @@ struct [[gnu::packed]] Alarm1Reg
|
||||
{
|
||||
return getEncodedDate(date, day_date);
|
||||
}
|
||||
inline AlarmRate getAlarmRate() const
|
||||
inline Alarm1Rate getAlarmRate() const
|
||||
{
|
||||
constexpr auto M_FLAG = 7;
|
||||
const auto m1 = (seconds >> M_FLAG) & 1;
|
||||
@@ -236,9 +228,9 @@ struct [[gnu::packed]] Alarm1Reg
|
||||
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<Alarm1Rate>(dayFormat);
|
||||
}
|
||||
return static_cast<AlarmRate>(m);
|
||||
return static_cast<Alarm1Rate>(m);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -263,7 +255,7 @@ struct [[gnu::packed]] Alarm1Reg
|
||||
{
|
||||
setEncodedDate(day_date, date);
|
||||
}
|
||||
inline void setAlarmRate(const AlarmRate &alarmRate)
|
||||
inline void setAlarmRate(const Alarm1Rate &alarmRate)
|
||||
{
|
||||
const auto alarmRateFlags = static_cast<uint8_t>(alarmRate);
|
||||
constexpr auto M_FLAG = 7;
|
||||
@@ -288,14 +280,6 @@ struct [[gnu::packed]] Alarm2Reg
|
||||
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
|
||||
@@ -314,7 +298,7 @@ struct [[gnu::packed]] Alarm2Reg
|
||||
{
|
||||
return getEncodedDate(date, day_date);
|
||||
}
|
||||
inline AlarmRate getAlarmRate() const
|
||||
inline Alarm2Rate getAlarmRate() const
|
||||
{
|
||||
constexpr auto M_FLAG = 7;
|
||||
const auto m2 = (minutes >> M_FLAG) & 1;
|
||||
@@ -324,9 +308,9 @@ struct [[gnu::packed]] Alarm2Reg
|
||||
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<Alarm2Rate>(dayFormat);
|
||||
}
|
||||
return static_cast<AlarmRate>(m);
|
||||
return static_cast<Alarm2Rate>(m);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -347,7 +331,7 @@ struct [[gnu::packed]] Alarm2Reg
|
||||
{
|
||||
setEncodedDate(day_date, date);
|
||||
}
|
||||
inline void setAlarmRate(const AlarmRate &alarmRate)
|
||||
inline void setAlarmRate(const Alarm2Rate &alarmRate)
|
||||
{
|
||||
const auto alarmRateFlags = static_cast<uint8_t>(alarmRate);
|
||||
constexpr auto M_FLAG = 7;
|
||||
@@ -375,6 +359,11 @@ enum class ControlRegFlags : uint8_t {
|
||||
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");
|
||||
@@ -389,6 +378,11 @@ enum class ControlStatusRegFlags : uint8_t {
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user