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

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 a complete program. 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); ctest runs the consteval battery over the BCD, the hours register's two formats, the weekday, the alarm registers each rate writes and the control and status write-backs, and holds the example to its stated size — 1468 B on the mega328P and 1256 B on the tiny85, byte-identical across the two modes. The legacy yazoalfa-based driver lives on the master branch.

The weekday register is derived from the date by default. A program that assigns its own meaning to the device's 1..7 takes ownership of it, and no write here touches it:

using rtc = ds3231::device<bus, ds3231::weekday_source::external>;

An alarm arms its INT/SQW output unless told otherwise, and can be muted and re-wired without re-arming:

auto quiet = rtc::set_alarm2(at, ds3231::alarm2_rate::minutes_match,
                             ds3231::alarm_interrupt::off);
auto again = rtc::listen_alarm2();

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 set the DY bit (the legacy driver always cleared it), each register block is read or written in one coherent bus transaction, and errors are reported instead of ignored.

S
Description
Zero overhead template library driver for DS3231 RTC module.
Readme
317 KiB
0 Stars 1 Watchers 0 Forks
Languages
C++ 100%