refactor: one alarm encoder, named register bits, and the example 14 B smaller
Both alarms' registers come from one encoder that derives DY/DT and the mask bits from the rate, and the battery checks the bytes each Table 2 row writes instead of restating the enum values. Register addresses and bits are named, the year register is the year less 2000 rather than a division, and an alarm day reads back with one decode for both forms. Over the whole API the image is 10 B smaller on both parts; a rate only known at run time costs set_alarm1 6 B more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4df536d0a5
commit
f9bc2cd8e2
4 files changed
+156
-134
No files matched your search
+35
-24
@@ -1,9 +1,10 @@
|
||||
// Compile-only battery, built with the cross compiler so the target's 16-bit
|
||||
// int is exercised. Everything in this driver that is arithmetic rather than a
|
||||
// bus transaction lives here: the BCD both ways, the hours register's two
|
||||
// formats, Sakamoto's weekday, and the alarm rate encodings as the datasheet
|
||||
// tabulates them.
|
||||
// formats, Sakamoto's weekday, the alarm registers each rate writes, and the
|
||||
// control and status values written back.
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include <ds3231/ds3231.hpp>
|
||||
@@ -53,31 +54,41 @@ static_assert(weekday(2024, 3, 1) == 6); // Friday, the day after it
|
||||
static_assert(weekday(2100, 3, 1) == 2); // Monday - 2100 is not a leap year
|
||||
static_assert(weekday(2026, 8, 12) == 4); // Wednesday
|
||||
|
||||
// The alarm rate encodings (19-5170 Table 2), transcribed: the low bits are
|
||||
// A1M4..A1M1 in register order and the high one is DY/DT. A rate is the mask
|
||||
// pattern its row names, and a transcription slip here arms the wrong alarm.
|
||||
// The alarm registers each rate writes (19-5170 Table 2): bit 7 of the n-th
|
||||
// register is AxM(n+1), and DY/DT is bit 6 of the day register. Rows are read
|
||||
// off the table, so a slip in the rate values or in their distribution over
|
||||
// the registers arms the wrong alarm here first.
|
||||
using ds3231::alarm1_rate;
|
||||
static_assert(static_cast<std::uint8_t>(alarm1_rate::once_per_second) == 0b01111);
|
||||
static_assert(static_cast<std::uint8_t>(alarm1_rate::seconds_match) == 0b01110);
|
||||
static_assert(static_cast<std::uint8_t>(alarm1_rate::minutes_seconds_match) == 0b01100);
|
||||
static_assert(static_cast<std::uint8_t>(alarm1_rate::time_match) == 0b01000);
|
||||
static_assert(static_cast<std::uint8_t>(alarm1_rate::date_time_match) == 0b00000);
|
||||
static_assert(static_cast<std::uint8_t>(alarm1_rate::weekday_time_match) == 0b10000);
|
||||
|
||||
using ds3231::alarm2_rate;
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::once_per_minute) == 0b0111);
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::minutes_match) == 0b0110);
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::time_match) == 0b0100);
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::date_time_match) == 0b0000);
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::weekday_time_match) == 0b1000);
|
||||
using ds3231::detail::alarm_registers;
|
||||
constexpr ds3231::date_time at{{2026, 1, 15}, {12, 30, 45}};
|
||||
constexpr ds3231::date_time on_weekday{{2026, 1, 3}, {7, 5, 0}};
|
||||
|
||||
// Alarm 2 has no seconds register, so its mask bits are one place lower than
|
||||
// alarm 1's throughout - the property that makes one distribution loop wrong
|
||||
// for the other.
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::once_per_minute) ==
|
||||
static_cast<std::uint8_t>(alarm1_rate::once_per_second) >> 1);
|
||||
static_assert(static_cast<std::uint8_t>(alarm2_rate::weekday_time_match) ==
|
||||
static_cast<std::uint8_t>(alarm1_rate::weekday_time_match) >> 1);
|
||||
template <typename Rate>
|
||||
constexpr auto alarm1(const ds3231::date_time &value, Rate rate)
|
||||
{
|
||||
return alarm_registers<ds3231::detail::alarm1_registers>(value, static_cast<std::uint8_t>(rate));
|
||||
}
|
||||
|
||||
template <typename Rate>
|
||||
constexpr auto alarm2(const ds3231::date_time &value, Rate rate)
|
||||
{
|
||||
return alarm_registers<ds3231::detail::alarm2_registers>(value, static_cast<std::uint8_t>(rate));
|
||||
}
|
||||
|
||||
static_assert(alarm1(at, alarm1_rate::once_per_second) == std::to_array<std::uint8_t>({0xc5, 0xb0, 0x92, 0x95}));
|
||||
static_assert(alarm1(at, alarm1_rate::seconds_match) == std::to_array<std::uint8_t>({0x45, 0xb0, 0x92, 0x95}));
|
||||
static_assert(alarm1(at, alarm1_rate::minutes_seconds_match) == std::to_array<std::uint8_t>({0x45, 0x30, 0x92, 0x95}));
|
||||
static_assert(alarm1(at, alarm1_rate::time_match) == std::to_array<std::uint8_t>({0x45, 0x30, 0x12, 0x95}));
|
||||
static_assert(alarm1(at, alarm1_rate::date_time_match) == std::to_array<std::uint8_t>({0x45, 0x30, 0x12, 0x15}));
|
||||
static_assert(alarm1(on_weekday, alarm1_rate::weekday_time_match) ==
|
||||
std::to_array<std::uint8_t>({0x00, 0x05, 0x07, 0x43}));
|
||||
|
||||
static_assert(alarm2(at, alarm2_rate::once_per_minute) == std::to_array<std::uint8_t>({0xb0, 0x92, 0x95}));
|
||||
static_assert(alarm2(at, alarm2_rate::minutes_match) == std::to_array<std::uint8_t>({0x30, 0x92, 0x95}));
|
||||
static_assert(alarm2(at, alarm2_rate::time_match) == std::to_array<std::uint8_t>({0x30, 0x12, 0x95}));
|
||||
static_assert(alarm2(at, alarm2_rate::date_time_match) == std::to_array<std::uint8_t>({0x30, 0x12, 0x15}));
|
||||
static_assert(alarm2(on_weekday, alarm2_rate::weekday_time_match) == std::to_array<std::uint8_t>({0x05, 0x07, 0x43}));
|
||||
|
||||
// A1F and A2F ignore a written 1 and OSF holds until written 0 (19-5170,
|
||||
// Status Register): clearing one alarm must write the other as 1, or an alarm
|
||||
|
||||
Reference in new issue
Block a user