BERGSONNE

Power

Battery life is about being asleep most of the time. core_power exposes the three low-power modes and the ways to wake from them — a timed RTC interval, a pin edge — plus backup registers that survive the deepest sleep. It sits on ll_pwr (there’s no HAL layer). Use the Core / HAL / LL toggle at the top of the sidebar to switch.

Overview

The modes trade wakeup latency and retained state against current draw. Picking one is mostly about what you need alive while asleep and how you’ll wake:

  • Sleep — CPU halted, peripherals and clocks running. Wakes on any interrupt, ~instantly. The everyday idle.
  • Stop — clocks halted, RAM retained. Wakes from EXTI or the RTC; the clock tree is rebuilt on wake.
  • Standby — almost everything off; RAM is lost and wakeup is a full reset. Lowest draw.

Sleep, stop & standby

The Core calls bundle entry, wakeup configuration, and (for stop) clock recovery into one call. core_sleep returns on the next interrupt; core_stop_for wakes itself after N seconds, restores the clocks, and advances core_millis() by the time it slept:

#include "core.h"

  core_sleep();                 // idle until any interrupt

  core_stop_for(30);            // Stop for 30 s (RTC), then resume with clocks restored

  if (core_standby_for(2) != HAL_OK) {
      // refused: the watchdog is running and 2 s is more than half its timeout
  }                             // on success: never returns, the Core resets on wake
Sleeping with the watchdog on
The watchdog keeps counting in Stop and Standby (on the L0 it can’t be paused at all), and Studio projects start it at 5 s. So core_stop_for sleeps in chunks of half the watchdog timeout and wakes briefly to feed it: any length is safe. Standby can’t be chunked, so with the watchdog running core_standby_for refuses anything longer than half the timeout (it returns HAL_ERROR without sleeping) and core_standby_until_on_change returns at once. Use core_stop_for for long sleeps. Core.ST.H5 still runs the older sleep code: no chunking and no Standby check, until its fix session.
Standby resets on wake
When it does sleep, core_standby_* never returns — the MCU resets and RAM is lost. Detect it at the top of main() with core_woke_from_standby().

Wakeup & the RTC

Beyond a timed interval, you can wake on a pin edge, and the RTC gives you a wall clock with periodic wakeups and a calendar alarm:

// Wake from Stop on a falling edge on pad 8, or after 10 s:
  int why = core_stop_until_on_change_timeout(8, EDGE_FALLING, 10000);
  // 1 = the pad (or it was already low), 0 = timed out, -1 = pad can't wake the Core

  // No timeout: wait for the edge however long it takes
  core_stop_until_on_change(8, EDGE_FALLING);

  // RTC wall clock + alarm:
  core_rtc_init();
  core_rtc_set_time(12, 0, 0);
  core_rtc_set_alarm(0xFF, 0xFF, 0);   // 0xFF = wildcard → fire every minute at :00
  if (core_rtc_alarm_fired()) { ... }

A pin wake returns at once if the pad is already at its wake level (a sensor holding its interrupt line low until serviced would otherwise never wake the Core); EDGE_BOTH always waits for a change. On the H5 the timeout isn’t implemented yet: a non-zero one returns -1.

Check core_woke_from_standby() first thing in main() to branch on a Standby return, then core_clear_standby_flag() to acknowledge it.

Backup registers

Backup registers are 32-bit slots that survive reset and Standby (and power loss, with VBAT). Use them for crash counters, boot flags, or carrying state across a Standby cycle:

core_backup_write(0, 0x12345678);
  uint32_t saved = core_backup_read(0);   // persists across reset / Standby
Count varies by Core
CORE_BACKUP_COUNT is 32 on most Cores, 5 on the M0+ (Core.ST.L0).

Cross-architecture support

Sleep is verified across the family. Stop passed a bench test of two back-to-back 10 s core_stop_for calls with a 5 s watchdog on the L4 and the W5 (25 September 2026). The L0’s sleep, RTC and backup code was rewritten against its reference manual the same day and awaits its bench pass. The H5 ran Stop and RTC wake-up before, but keeps the older sleep code until its fix session. Hover a chip for each feature:

●L0M0+7/8●L4M48/8●W5M337/8●H5M337/8WCH (RISC-V) · Nordic (nRF54) — in development

See the implementation status for the full matrix.

Known gaps

What the SDK itself lists as missing here, straight from the headers:

Power
mediumDSLStandby family stays Tier 1 (noreturn)

core_standby_for / core_standby reset on wake — they never return on success and can't be safely exposed to the DSL, since the simulator would deadlock on a call that never returns. Recoverable variants (sleep, stop_for) are Tier 2; reaching Standby from a DSL program needs an "escape-to-C" fallthrough or a separate "would have entered Standby" event the simulator can model.

mediumDSLNo wake-on-pad Tier 2

core_stop_until_on_change blocks until a configured pad edge wakes the chip. The DSL surface is straightforward in spirit but needs the EXTI handler bridge to express "block until" cleanly across the host-call boundary. Tracked alongside the broader DSL event- model story.

highC APIStop / Standby not bench-verified on Core.ST.L0; Standby not on Core.ST.W5

The register maps behind stop_for / standby_for / RTC / backup were corrected against RM0377 and RM0493 on 2026-09-25 (the L0 enabled the LSE instead of the LSI and hung; the WBA wrote CCIPR instead of BDCR1, had no RTC bus clock and a reserved Standby LPMS). The W5's Stop passed tests/hw-sleep-cycle on 2026-09-25; its Standby and all of the L0 are compile-only until they do. Core.ST.L4 passed the same test; Core.ST.H5 keeps its older sleep code until its own fix session.

mediumC APIStandby can't outlast the watchdog

The IWDG keeps counting in Standby (always on the L0, by option byte on the L4/WBA) and nothing feeds it until main() runs again, so with the watchdog on (the Studio default, 5 s) core_standby_for refuses anything longer than half the timeout and returns HAL_ERROR, and core_standby_until_on_change returns without sleeping. Long sleeps use core_stop_for, which wakes to feed. Changing the IWDG_STDBY option byte would lift this on the L4/WBA; the SDK doesn't touch option bytes.

mediumC APIWKUP-pin Standby is L4-only

core_standby_until_on_change is implemented for STM32L422 only — Core.ST.L4.2's PA0/PA2 WKUP pins. Other Cores hit the (void) fallback and return without entering Standby.

mediumC APINo LPTIM (low-power timer) wakeup

The SDK roadmap flags LPTIM as Tier 1 high-impact: it runs in Stop without the RTC and gives finer wakeup granularity than seconds. Not wrapped here yet.

lowC APINo autonomous-mode peripherals (Core.ST.W5)

The WBA's Stop2 autonomous mode (ADC / SPI / I2C / UART operating while the CPU sleeps) is on the roadmap as Tier 3 — none of it is surfaced through core_power yet.

RTC
mediumDSLNo DSL get_time / get_date

The C surface has core_rtc_get_time / get_date taking out-pointers; they're not exposed because the host-call ABI doesn't map cleanly to multi-out values. The newer multi-scalar-out ABI (see DSL Capability Coverage close, 2026-05-03) could land them.

mediumC APILSE / external crystal not exposed

core_rtc_init always selects LSI (~32 kHz RC, ±5% typical drift on the L4/WBA; the L0's is 26-56 kHz part to part and is not calibrated against HSI16, so its RTC second can be off by tens of percent). Cores with an LSE crystal need ll_rtc_init(1) — no Tier 2 wrapper that takes a clock-source argument.

lowC APINo subsecond / millisecond accuracy

The wrapper exposes whole-second resolution. STM32 RTCs have a subsecond register (1/PREDIV_S) that's reachable from ll_rtc but not from this header — wall-clock timestamps round to the second.

Backup
lowDSLTwin backup state wipes on Reset

read/write round-trip within a single run, but the per-slot store is cleared on every project reload — DSL programs that rely on backup state surviving a soft reset (e.g., boot counters across the user clicking Reset in the IDE) can't be exercised end-to-end.

lowC APINo tamper / anti-tamper integration

The TAMP block on WBA/H5 hosts the backup registers but also drives tamper detection (active edge, anti-tamper erase, time-stamping on tamper). None of that is wrapped — backup is read/write only.

From the @studio unsupported notes in core_power.h, core_rtc.h, core_backup.h — tiles@6af026f.

API reference

Power modes

Default-instance · Tier 2
void core_sleep(void);
Sleep until any interrupt (CPU stopped, peripherals running).
void core_stop_for(uint32_t seconds);
Enter Stop mode for a number of seconds, then wake and restore clocks. Uses the RTC wakeup timer (LSI). Returns after wake with PLL + SysTick restored; core_millis() advances by the time slept, as the RTC measured it. If the watchdog is running, sleeps in chunks of half its timeout and feeds it in between. Core.ST.L4 with a USB host awake on the bus: waits in Sleep mode so the host can still reach it (see the USB note at the top).
int core_woke_from_standby(void);
Returns 1 if the MCU woke from Standby. The flag survives later resets until core_clear_standby_flag() — call that once you've handled the wake.
Lower-level · Tier 1
void core_clock_init(void);
Bring up the clock tree for the project's `clock` level (oscillator, PLL, flash wait states, bus prescalers) and start SysTick. Generated per project by coregen into core_init.c and called by core_init(); call it again only to restore the clocks by hand (core_stop_for already does after a wake).
int core_stop_until_on_change_timeout(uint8_t pad, uint32_t edge, uint32_t timeout_ms);
Enter Stop mode until a GPIO edge on the given pad, or until `timeout_ms` has passed (0 = no timeout). Configures the pad as input, arms its EXTI, enters Stop, and restores clocks on wake. If the watchdog is running, wakes every half timeout to feed it and goes back to sleep. Core.ST.L4 with a USB host awake on the bus: waits in Sleep mode (see the USB note at the top). Already at the wake level: EXTI latches only edges that happen after it is armed, so a line that is already at its wake level — a sensor INT held low until serviced, for EDGE_FALLING — would never wake the Core. After arming, this reads the pad and returns 1 at once if it is already low (EDGE_FALLING) or high (EDGE_RISING). EDGE_BOTH always waits for a change.
void core_stop_until_on_change(uint8_t pad, uint32_t edge);
Enter Stop mode until a GPIO edge occurs on the given pad (no timeout). Returns right away if the pad can't take an edge interrupt, or if it is already at the level the edge wakes on (see core_stop_until_on_change_timeout()). If the watchdog is running, wakes every half timeout to feed it and goes back to sleep.
hal_status_t core_standby_for(uint32_t seconds);
Enter Standby mode with RTC wakeup after the given seconds. On success it does not return — the MCU resets on wake. Check core_woke_from_standby() at the top of main() to detect a Standby wake. Watchdog: the IWDG keeps counting through Standby and nothing feeds it until main() runs again, and Standby can't be split into fed chunks. So while the watchdog is running this refuses — returns HAL_ERROR without sleeping — unless seconds is at most half the watchdog timeout (2 s with the default 5 s watchdog). Use core_stop_for() for longer sleeps. (Core.ST.H5: no check.)
void core_standby_until_on_change(uint8_t pad, uint32_t edge);
Enter Standby mode until a GPIO edge on the given pad (via WKUP pin). Does not return — MCU resets on wake. Not all pads support WKUP; returns without entering Standby if the pad has no WKUP capability, or if the watchdog is running (it would reset the Core long before most edges). On Core.ST.L4.2: pad 8 (PA0 = WKUP1) and pad 7 (PA2 = WKUP4).
void core_stop(void);
Enter Stop mode (caller manages wakeup source + clock recovery). A running watchdog keeps counting and is not fed: wake within its timeout. On the WBA the caller also owns the Stop 1 wait-state/HDIV5 rules (RM0493 §11.7.7).
void core_standby(void);
Enter Standby (caller manages wakeup source). Does not return. A running watchdog keeps counting through Standby and resets the Core at its timeout.
void core_clear_standby_flag(void);
Clear the Standby wake flag. Call after core_woke_from_standby() returns 1.
void core_watchdog_start_seconds(uint32_t seconds);
Start the independent watchdog with a timeout in seconds (convenience). For finer control use core_watchdog_start() from core_watchdog.h which accepts milliseconds.

Generated from core_power.h — tiles@f70bca2.

RTC

Default-instance · Tier 2
void core_rtc_init(void);
Initialize the RTC using LSI (internal RC, ~32 kHz; ~37 kHz on Core.ST.L0, scaled so a second is still ~1 s). Call once from `on start` before setting time, date, or alarms. For LSE, call ll_rtc_init(1) directly in hand-written C (Core.ST.L0 has no LSE and stays on LSI).
void core_rtc_set_time(uint8_t h, uint8_t m, uint8_t s);
Set the time (24h format).
void core_rtc_set_date(uint8_t y, uint8_t mo, uint8_t d, uint8_t wd);
Set the date.
void core_rtc_wakeup(uint32_t seconds);
Configure a periodic wakeup timer.
void core_rtc_wakeup_stop(void);
Disable the periodic wakeup timer.
void core_rtc_set_alarm(uint8_t hours, uint8_t minutes, uint8_t seconds);
Set Alarm A to trigger at a specific time. Pass 0xFF for hours, minutes, or seconds to ignore that field. The alarm fires when all non-masked fields match the RTC time.
void core_rtc_clear_alarm(void);
Clear the alarm (disable Alarm A).
int core_rtc_alarm_fired(void);
Check if the alarm has fired (poll mode). Clears the flag on read.
Lower-level · Tier 1
void core_rtc_get_time(uint8_t * h, uint8_t * m, uint8_t * s);
Read the current time.
void core_rtc_get_date(uint8_t * y, uint8_t * mo, uint8_t * d, uint8_t * wd);
Read the current date.

Generated from core_rtc.h — tiles@8f1266b.

Backup registers

Default-instance · Tier 2
uint32_t core_backup_read(uint8_t index);
Read a backup register.
void core_backup_write(uint8_t index, uint32_t value);
Write a backup register. Backup domain write access is enabled automatically.
Lower-level · Tier 1
void _core_backup_ensure_clk(void);
Ensure the backup registers are reachable. L0: RTC_BKPxR live inside the RTC (RM0377 §22.7.20), so the RTC clock must be selected, enabled and running (LSI; never the LSE here). L4 (L422): TAMP_BKPxR (RM0394 §36.6.8) on the RTC APB clock; the RTC kernel clock is brought up too, as before, in case nothing else has. WBA: TAMP_BKPxR (RM0493 §37.6.18) need RCC_APB7ENR.RTCAPBEN, which is off at reset — without it TAMP read 0 and ignored writes unless the BLE stack happened to have set it. H5: TAMP on RCC_APB3ENR.RTCAPBEN. All of them need PWR + DBP for writes.

Generated from core_backup.h — tiles@4b95d38.