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>
ds3231
Maxim DS3231 RTC driver on libavr:
clock and alarm read/write, alarm interrupts, oscillator-stop detection,
die temperature. One source runs on every libavr chip — TWI hardware on
the mega328P, open-drain software I2C on the tinies. All bus errors
surface as std::expected.
using bus = dev::i2c<{.frequency = 100_kHz}>;
using rtc = ds3231::device<bus>;
auto now = rtc::read_clock(); // result<date_time>
auto armed = rtc::set_alarm1({}, ds3231::alarm1_rate::once_per_second);
example/main.cpp is the full tour. libavr rides as the libavr/ submodule,
pinned to the commit this driver builds against; LIBAVR_ROOT (cache or
environment) overrides it for development against a working tree:
git submodule update --init libavr
cmake --preset attiny85-generated
cmake --build --preset attiny85-generated
Presets cover attiny85/atmega328p in both libavr modes (generated and
reflect). The legacy yazoalfa-based driver lives on the master branch.
Atmel Studio
master carries a Studio solution, so this branch does too: ide/ds3231.atsln
builds example/main.cpp for both parts the presets cover, each to a
byte-identical .text against the CMake build (check-flags.py below is
what holds the flag sets equal, so the sizes are the presets' own), with the
flags mirrored by hand. CMake remains the build system.
avrdevice is a project-level property in Studio, so a part means a project, not
a configuration: ide/atmega328p/ and ide/attiny85/. They need separate
directories rather than separate names — Studio builds into <project dir>/<Configuration> whatever OutputDirectory says, so two projects sharing
one directory would also share one example/main.o, and building one after the
other without a rebuild would link the other part's object.
Studio finds libavr in the submodule at
$(MSBuildProjectDirectory)\..\..\libavr\include — correct by construction,
and anchored to the project because a plain relative path is resolved against
the generated makefile's directory (the configuration's output directory), not
the project's. There is no LIBAVR_ROOT escape hatch: a variable exported in a
shell is invisible to Studio launched from the Start menu — which is what the
submodule answers. It also needs a GCC 16.1 toolchain registered as flavour
avr-g++-16.1.0, nothing older reaching -std=c++26.
One generated file is required before the project will load, and one command
checks the flags have not drifted (both from libavr's tools/atmelstudio/):
for mcu in atmega328p attiny85; do
case $mcu in atmega328p) device=ATmega328P;; attiny85) device=ATtiny85;; esac
python libavr/tools/atmelstudio/componentinfo.py \
"ide/$mcu/ds3231-$mcu.componentinfo.xml" --device "$device"
python libavr/tools/atmelstudio/check-flags.py --solution ide/ds3231.atsln \
--project "ds3231-$mcu" \
--compile-commands "build/$mcu-generated/compile_commands.json" \
--log "build/as-$mcu.log"
done
One reference describes one part, hence --project. Release is what the gate
compares, the presets defining no debug build; Debug carries the -Og -gdwarf-4 pair libavr's own debug preset uses.
Differences from legacy: weekday-rate alarms now actually set the DY bit
(the old setAlarmHelper always cleared it), reads/writes are single
coherent bus transactions, and errors are reported instead of ignored.