# ds3231 Maxim DS3231 RTC driver on [libavr](https://git.blackmark.me/avr/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`. ```cpp using bus = dev::i2c<{.frequency = 100_kHz}>; using rtc = ds3231::device; auto now = rtc::read_clock(); // result 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: ```sh 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 and the alarm rate encodings. 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: ```cpp using rtc = ds3231::device; ``` An alarm arms its INT/SQW output unless told otherwise, and can be muted and re-wired without re-arming: ```cpp 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 `/` 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/`): ```sh 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.