BlackMarkandClaude Opus 5.5 081f2e74cd example: the code shows what the LED does, and a wrong comment goes
The comment on the failed alarm clear claimed it holds the LED dark; the
next clock read rewrites the LED a tenth of a second later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:56:05 +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%