Commit Graph
6 Commits
Author SHA1 Message Date
BlackMarkandClaude Opus 5.5 f9bc2cd8e2 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>
2026-10-23 21:55:02 +02:00
BlackMarkandClaude Opus 5.5 4df536d0a5 fix: an alarm clear keeps the other alarm's flag, and arming keeps BBSQW
A1F and A2F ignore a written 1, so the status write-back now writes the
flag not being cleared as 1: an alarm firing between the read and the
write was cleared with the other. BBSQW gates the alarm interrupt on
battery as well as the square wave (19-5170 rev. 4), so arming an alarm
no longer clears it. Both register computations are consteval-tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:42:32 +02:00
BlackMarkandClaude Opus 5 2e9c929a19 fix: an alarm's interrupt could be turned on and never off, and nothing tested any of it
`set_alarm1/2`'s `enable_interrupt = false` did nothing at all - it skipped the
control-register write rather than clearing A1IE/A2IE - and there was no other
way to clear them, so an alarm armed once with its interrupt on drove INT/SQW
for good. It is `alarm_interrupt::off` now and it clears the bit, with
`listen_alarm1/2()` and `mute_alarm1/2()` beside it for a caller that arms once
and changes its mind later. The bare bool went with it (rule 37): `set_alarm1(at,
rate, false)` said nothing at the point of use.

`bool SetWeekday` was the same shape one level up - `device<bus, false>` names
nothing - and its two writers disagreed about what it meant: `write_date`
skipped the weekday register, `write_clock` stamped a placeholder 1 into it, so
a program that turned the maintenance off still had the register overwritten
and no way to set it. `weekday_source::external` now means the register is the
program's, in both writers.

test/consteval.cpp is the battery this driver never had, over the arithmetic
that has no bus in it: the BCD round trip across every representable value and
its nibble layout, the hours register in both formats including all four
noon/midnight cases, Sakamoto's weekday against six calendar dates (leap day,
the day after it, and 2100's missing leap), and both alarms' rate encodings
transcribed against 19-5170 Table 2 - including that alarm 2's masks sit one
place below alarm 1's, which is the property that makes one distribution loop
wrong for the other. Red-green: two assertions fire on a flipped DY bit.

Beside it, one decode rather than two: `read_clock` and `read_time` spelled the
seconds/minutes/hours triplet out separately (rule 6).

Every image byte-identical on both chips in both modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 15:40:52 +02:00
BlackMarkandClaude Opus 5 572d38feef build: the libavr pin advances past the audit sweep, and the extents get counted
The pin crosses libavr's phase-6 close and the guideline sweep behind it;
the example is byte-identical on both chips in both modes.

The port's own sweep found rule 36 in seven places, and the fix had to be
measured rather than applied. Sakamoto's month offsets as a `std::to_array`
local cost **+348 bytes** on a 1480-byte image - an automatic class-type
constant inside a `constexpr` function is given an address and built per
call, which outlined `weekday()` whole and pulled in `__do_copy_data` and
`__udivmodhi4`; `static constexpr` is what folds it, and emits no symbol at
all. The six register buffers cost +4 as `to_array`, which materialises its
argument array before copying where an aggregate initialiser stores each
element as it is computed - so those take CTAD, which counts the extent and
keeps the direct initialisation. Both spellings are rule 36; only one is
free.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:40:20 +02:00
BlackMarkandClaude Fable 5 87e1f5e8a1 build: the libavr pin advances past phase 6, and the driver takes the bus whole
The register sugar now rides the bus's own master role - device<Bus>
resolves avr::i2c::device<typename Bus::master, bus_address>, so a
consumer keeps handing over its declared dev::i2c<...> unchanged - and
the DS3231's hardwired 0x68 is a named constant. The example's four
discards became the LED's own error signal (a failed seed or a failed
alarm clear holds it dark, the same word a stuck bus says), the tree is
reformatted under InsertBraces, the sources are ASCII, and the README's
stale Studio byte counts are replaced by the claim its check-flags gate
holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:46:10 +02:00
BlackMarkandClaude cdca5219d0 Rewrite on libavr
Same driver surface as the yazoalfa version — clock and alarm get/set,
alarm interrupts, flag check/clear — plus oscillator-stop detection and
die temperature. One source for tiny85 (software I2C) and mega328P (TWI),
built against libavr in both generated and reflect mode, byte-identical
.text across modes. Errors surface as std::expected instead of being
dropped; weekday-rate alarms now really set the DY bit (legacy cleared
it); multi-register access is one coherent bus transaction. Legacy stays
on master.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-18 01:09:43 +02:00