API summary
The SDK’s public core_ API in one place, with the curated hal_ / ll_ surface beneath it — grouped by subsystem in the same order as the guides and the implementation status. Switch the Core / HAL / LL toggle at the top of the sidebar to view a layer. Under each function is what it returns, where the header says; the Core view also lists the types, constants and macros a header documents. It’s generated from the same manifests the guides use, so it stays in sync with the headers automatically. Left out on purpose: internal helpers, undocumented macros (such as core_tiles_pal(), covered in the tile PAL guide), and the register-level plumbing inside the HAL and LL headers.
307 Core functions · across 18 subsystems
Systemguide →
| Function | Access | Description |
|---|---|---|
core_delay_ms(uint32_t ms) | Tier 2 | Blocking delay in milliseconds. Thread builds (Core.ST.W5): the wait runs Thread's scheduler, so Thread callbacks (UDP receive, state changes) can run inside it, and it can end up to ~1 ms late. |
core_delay_us(uint32_t us) | Tier 2 | Blocking delay in microseconds. For delays > 1 ms prefer `delay_ms` — it won't starve the rest of the system as long. |
core_millis(void) | Tier 2 | Milliseconds since boot (wraps at ~49 days). |
core_timeout(uint32_t start, uint32_t ms) | Tier 2 | Check if a timeout has elapsed. start: value returned by core_millis() at the beginning ms: timeout duration in milliseconds Returns 1 if expired, 0 otherwise. |
core_cycle_init(void) | Tier 1 | Enable the DWT cycle counter. Idempotent; call once at startup. |
core_delay_cycles(uint32_t cycles) | Tier 1 | Busy-wait `cycles` CPU cycles (≈ ±a few cycles of loop overhead). |
core_delay_ns(uint32_t ns) | Tier 1 | Busy-wait `ns` nanoseconds (rounds to whole CPU cycles). |
core_watchdog_start(uint32_t timeout_ms) | Tier 2 | Start the independent watchdog with a timeout in milliseconds. Selects the best prescaler/reload combination automatically. Common values: 1000, 2000, 5000, 10000 (max ~28000). WARNING: Once started, the IWDG cannot be stopped, and it keeps running in Stop and Standby. core_stop_for() wakes to feed it; core_standby_for() refuses sleeps longer than half the timeout. |
core_watchdog_feed(void) | Tier 2 | Feed the watchdog. Must be called before the timeout expires. On ROM-DFU builds this also retires the brick-recovery strike counter, once, after the app has run for max(10 s, 2 x the timeout) — no call needed. Thread builds (Core.ST.W5): also services Thread (at most one scheduler pass every 5 ms), so Thread callbacks can run inside this call. |
core_watchdog_running(void) | Tier 1 | Returns 1 if this firmware has started the watchdog (core_watchdog_start). |
core_watchdog_sleep_chunk_ms(void) | Tier 1 | The longest a sleep may run between feeds while the watchdog runs: half its timeout, in ms. 0 when the watchdog isn't running. The RTC that times the sleep and the IWDG share the LSI, so the margin holds even when the LSI is far from nominal (the L0's is 26-56 kHz). |
core_watchdog_caused_reset(void) | Tier 1 | Check if the last reset was caused by a watchdog (IWDG or WWDG): core_reset_reason() == CORE_RESET_WATCHDOG. core_init() reads the RCC flags once, keeps the cause and clears them, so this gives the same answer for the whole run (core_reset.h). |
core_watchdog_clear_flags(void) | Tier 1 | Clear the RCC reset flags. core_init() already does this at boot, and the cause it kept stays, so this is only needed by code that skips core_init. |
core_watchdog_debug_freeze(void) | Tier 1 | Freeze the IWDG while the core is halted under a debugger, so a breakpoint doesn't let the watchdog reset the chip out from under an SWD session. Firmware-side so it holds for any probe/toolchain (rev b exposes SWD on L4). |
core_fault_set_callback(hal_fault_callback_t cb) | Tier 1 | Register a fault callback, run in fault context before the SOS blink (and before the L4's USB register dump). Keep it minimal: no heap, no interrupts. |
core_recovery_note_boot(void) | Tier 1 | Account for this boot and return the running strike count. Call once, very early in core_init(), before clocks/user code. |
core_recovery_over_limit(uint32_t strikes) | Tier 1 | True once we've had enough consecutive watchdog resets to give up on the app. |
core_recovery_clear(void) | Tier 1 | Clear the strike counter now. core_watchdog_feed() already does this once the app has run healthy for max(10 s, 2x the timeout); call this only to declare health earlier than that. |
core_debug_init(void) | Tier 1 | Initialize SWO debug output using the project's SYSCLK_HZ (no-op on Core.ST.L0). |
core_debug_print(const char * str) | Tier 1 | Print a string via SWO (no formatting). |
Onboard LEDguide →
| Function | Access | Description |
|---|---|---|
core_led_on(void) | Tier 2 | Turn the onboard LED on. Simplest possible light control — useful alongside blink/heartbeat when the user wants explicit on/off state rather than a timed pattern. |
core_led_off(void) | Tier 2 | Turn the onboard LED off. |
core_led_toggle(void) | Tier 2 | Flip the onboard LED's current state. Non-blocking — no delay. Good for driving the LED from an event handler that fires at a rate you've already chosen (e.g., a pad-edge ISR on a button). |
core_led_blink(int n, int on_ms, int off_ms) | Tier 2 | Blink the LED n times. Each cycle turns the LED on for on_ms milliseconds and off for off_ms milliseconds. Blocking — returns after the last off period. A short on_ms (under 50 ms) is the on-time at 3.3 V: at lower supply voltages it is lengthened so the blink looks as bright (the off-time shrinks to keep the rhythm). See core_led_heartbeat(). |
core_led_heartbeat(int period_ms, int on_ms) | Tier 2 | Start a free-running, asymmetric heartbeat on the onboard LED. Set it up once (e.g. at startup) and it runs on its own — the LED turns on for on_ms, off for the rest of period_ms, repeating forever. Serviced from the 1 ms SysTick interrupt, so it does NOT block your main loop the way a toggle-and-delay would: the loop stays free for tile reads and logic. Call again at any time to change the rhythm; pass period_ms = 0 to stop the heartbeat (the LED is left off). on_ms is clamped to period_ms. Brightness holds across the supply: a short on_ms (under 50 ms, e.g. a 2 ms blip) is the on-time at 3.3 V, and is lengthened as VDD drops (about 32 ms at 1.8 V), because the LED carries far less current there and the eye adds up the light of a short flash. VDD is read through VREFINT when the heartbeat is set (at most once a minute) on the L0, L4 and W5; the H5 uses the on-time as given. Examples: heartbeat(1000, 2) — a 1 Hz 2 ms blip, the same brightness at any VDD. heartbeat(1000, 100) — a 1 Hz "blip": 100 ms on, 900 ms off. heartbeat(500, 250) — a steady 1 Hz, 50%-duty pulse. Defined in core_led.c (not header-only) because it owns persistent state shared with the SysTick handler. |
core_led_init(void) | Tier 1 | Enable the GPIO clock and configure the LED pin as a push-pull output. Call once after core_init(). The LED starts in the off state. |
core_led_sos(void) | Tier 1 | Blink the SOS pattern (3 short, 3 long, 3 short) in an infinite loop. Use for unrecoverable errors. This function never returns. |
GPIOguide →
| Function | Access | Description |
|---|---|---|
core_pad_write(uint8_t pad, int state) | Tier 2 | Set a pad high (ON) or low (OFF). |
core_pad_read(uint8_t pad) | Tier 2 | Read a pad. Returns 0 or 1. |
core_pad_toggle(uint8_t pad) | Tier 2 | Toggle a pad output. |
core_pad_output(uint8_t pad) | Tier 1 | Configure a pad as push-pull output (default). |
core_pad_output_od(uint8_t pad, uint32_t pull) | Tier 1 | Configure a pad as open-drain output with optional pull resistor. Use PULL_UP for a wired-AND / I2C-style bus, PULL_NONE for external pull. |
core_pad_input(uint8_t pad, uint32_t pull) | Tier 1 | Configure a pad as input with pull resistor. |
core_pad_resolve(uint8_t pad) | Tier 1 | Resolve a pad to a fast handle once, outside the hot loop. |
core_pad_fast_set(core_pad_fast_t h) | Tier 1 | Drive the resolved pad high — single BSRR write. |
core_pad_fast_clear(core_pad_fast_t h) | Tier 1 | Drive the resolved pad low — single BSRR write. |
core_pad_fast_write(core_pad_fast_t h, int state) | Tier 1 | Drive the resolved pad to `state` (0/1) — single BSRR write. |
core_pad_fast_read(core_pad_fast_t h) | Tier 1 | Read the resolved pad — single IDR load. Returns 0 or 1. |
core_pad_bulk_write(GPIO_TypeDef * port, uint32_t set_mask, uint32_t clear_mask) | Tier 1 | Bulk update many pins on ONE port in a single atomic BSRR write: pins in `set_mask` go high, pins in `clear_mask` go low (set wins on overlap, per BSRR). Build the masks from the generated PAD_n_MASK defines. Useful for synchronized buses / LED matrices without per-pad jitter. |
core_pad_analog(uint8_t pad) | Tier 1 | Configure a pad as analog (for ADC, DAC, comparator). |
core_pad_speed(uint8_t pad, uint32_t speed) | Tier 1 | Set the output speed (slew rate) of a pad. Use SPEED_LOW / SPEED_MED / SPEED_HIGH / SPEED_VHIGH. Only affects outputs — input pads ignore this setting. |
core_pad_on_change(uint8_t pad, uint32_t edge, hal_callback_t cb, void * ctx) | Tier 1 | Register an edge-triggered callback on a pad. The pad is automatically configured as input with a sensible pull resistor based on edge direction: rising → pull-down (idle low) falling → pull-up (idle high) both → no pull (external bias expected) |
core_pad_on_change_stop(uint8_t pad) | Tier 1 | Stop the interrupt on a pad. |
UARTguide →
| Function | Access | Description |
|---|---|---|
core_serial_print_bus(uint8_t bus, const char * str) | Tier 2 | Write a null-terminated string to `bus` (blocking). Returns 0 on success or -1 on undeclared bus. The most-common DSL pattern — "drop a debug line on UART2" — is one line: serial.print(2, "ready\\n"); |
core_serial_putc_bus(uint8_t bus, uint8_t byte) | Tier 2 | Write a single byte to `bus` (blocking). Returns 0 on success or -1 on undeclared bus. |
core_serial_init(hal_uart_t * h, USART_TypeDef * instance, const hal_uart_config_t * cfg) | Tier 1 | Initialize a UART instance. Clock is auto-resolved from PCLK1_HZ (core_config.h). |
core_serial_init_clk(hal_uart_t * h, USART_TypeDef * instance, uint32_t pclk_hz, const hal_uart_config_t * cfg) | Tier 1 | Deprecated. Use core_serial_init(h, instance, cfg) — clock auto-resolved. |
core_serial_write(hal_uart_t * h, const uint8_t * data, uint32_t len) | Tier 1 | Transmit a buffer (blocking). |
core_serial_print(hal_uart_t * h, const char * str) | Tier 1 | Transmit a null-terminated string (blocking). |
core_serial_putc(hal_uart_t * h, uint8_t byte) | Tier 1 | Transmit a single byte (blocking). |
core_serial_available(hal_uart_t * h) | Tier 1 | Number of bytes available in the RX buffer. |
core_serial_getc(hal_uart_t * h) | Tier 1 | Receive a single byte (blocking -- waits for data). |
core_serial_read(hal_uart_t * h, uint8_t * buf, uint16_t max_len) | Tier 1 | Read available bytes from the RX ring buffer (non-blocking). Returns number of bytes read. |
core_serial_handle_for_bus(uint8_t bus) | Tier 1 | The UART handle for a bus id declared in config.json, or NULL if the project doesn't declare that bus. Emitted per project by coregen into core_init.c (when the project declares any USART); the *_bus helpers below resolve their handle through it. Forward-declared here rather than `#include "core_init.h"` so this header compiles without a project (val tests, examples without config.json). The natives-side caller is gated on CORE_HAS_SERIAL_BUSES, so the linker never asks for the symbol unless the dispatcher exists. Returns: The coregen-initialized handle, or NULL. |
I2Cguide →
| Function | Access | Description |
|---|---|---|
core_i2c_write_byte_bus(uint8_t bus, uint8_t addr, uint16_t reg, uint8_t value) | Tier 2 | Write a single byte to a register on a device on `bus`. Returns I2C_OK on success, I2C_NACK / I2C_ERROR on bus failure, or I2C_ERROR if `bus` isn't declared in config.json. |
core_i2c_read_byte_bus(uint8_t bus, uint8_t addr, uint16_t reg) | Tier 2 | Read a single byte from a register on a device on `bus`. Returns the byte value (0..255) on success, or -1 on any error (bus undeclared, NACK, timeout). The signed return lets DSL programs branch on `< 0` without an out-pointer. |
core_i2c_probe_bus(uint8_t bus, uint8_t addr) | Tier 2 | Check if a device responds at `addr` on `bus`. Returns 1 if the device ACKs, 0 on NACK / timeout / undeclared bus. |
_core_i2c_timing(uint32_t speed_hz) | Tier 1 | Resolve the TIMINGR value for a given speed using the compile-time I2C kernel clock (I2C_KERNEL_CLK_MHZ from core_config.h). Returns 0 if no pre-computed value exists. |
core_i2c_init(core_i2c_t * h, I2C_TypeDef * instance, uint32_t speed_hz) | Tier 1 | Initialize an I2C bus with automatic timing resolution. core_i2c_t bus; core_i2c_init(&bus, I2C1, I2C_400K); Resolves the TIMINGR value from the compile-time kernel clock. Enables FMP mode automatically when speed is I2C_1M. |
core_i2c_init_cfg(core_i2c_t * h, I2C_TypeDef * instance, const hal_i2c_config_t * cfg) | Tier 1 | Initialize I2C with explicit config struct (advanced). For most use cases, prefer core_i2c_init(h, instance, speed_hz). |
core_i2c_write(core_i2c_t * h, uint8_t addr, const uint8_t * data, uint32_t len) | Tier 1 | Write data to a 7-bit address device. |
core_i2c_read(core_i2c_t * h, uint8_t addr, uint8_t * buf, uint32_t len) | Tier 1 | Read data from a 7-bit address device. |
core_i2c_write_reg(core_i2c_t * h, uint8_t addr, uint16_t reg, const uint8_t * data, uint32_t len) | Tier 1 | Write to a device register (8 or 16-bit register address). |
core_i2c_read_reg(core_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t * buf, uint32_t len) | Tier 1 | Read from a device register (repeated START). |
core_i2c_write_byte(core_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t value) | Tier 1 | Write a single byte to a register. |
core_i2c_read_byte(core_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t * value) | Tier 1 | Read a single byte from a register. |
core_i2c_probe(core_i2c_t * h, uint8_t addr) | Tier 1 | Check if a device responds at addr. Returns I2C_OK or I2C_NACK. |
core_i2c_scan(core_i2c_t * h, uint8_t * found, uint8_t * count, uint8_t max_count) | Tier 1 | Scan the I2C bus (0x08–0x77). Fills found[] with responding addresses. |
core_i2c_handle_for_bus(uint8_t bus) | Tier 1 | The I2C handle for a bus id declared in config.json, or NULL if the project doesn't declare that bus. Emitted per project by coregen into core_init.c (when the project declares any I2C bus); the *_bus helpers below resolve their handle through it. Forward-declared here rather than `#include "core_init.h"` so this header compiles without a project (val tests, examples without config.json). The natives-side caller is gated on CORE_HAS_I2C_BUSES, so the linker never asks for the symbol unless the dispatcher exists. Returns: The coregen-initialized handle, or NULL. |
core_i2c_target_init(core_i2c_target_t * t, I2C_TypeDef * instance, uint8_t addr, uint32_t speed, uint8_t * regs, uint16_t n_regs, hal_i2c_target_cb_t cb, void * ctx) | Tier 1 | Bring up an I2C peripheral as a read-only target at `addr`. |
core_i2c_target_init_rw(core_i2c_target_t * t, I2C_TypeDef * instance, uint8_t addr, uint32_t speed, uint8_t * regs, uint16_t n_regs, uint16_t first, uint16_t count, hal_i2c_target_cb_t cb, void * ctx) | Tier 1 | As core_i2c_target_init, but accepts host writes to the register range [first, first + count). Everything outside stays read-only. |
core_i2c_target_deinit(core_i2c_target_t * t) | Tier 1 | Stop answering on the bus. |
Types
| Type | Kind | Description |
|---|---|---|
core_i2c_t | typedef | Core-level I2C handle. Alias for hal_i2c_t — use this in application code.typedef hal_i2c_t core_i2c_t; |
core_i2c_target_t | typedef | Core-level I2C target handle. Alias for hal_i2c_target_t.typedef hal_i2c_target_t core_i2c_target_t; |
I3C (Core.ST.H5)guide →
| Function | Access | Description |
|---|---|---|
core_i3c_init(core_i3c_t * h, I3C_TypeDef * instance, uint32_t scl_hz) | Tier 1 | Bring an I3C instance up as bus controller at `scl_hz` (push-pull SCL). The kernel clock is the APB clock (rcc_pclk1 for I3C1, rcc_pclk3 for I3C2; both run at SYSCLK on the Cores), so the fastest SCL depends on the clock level: 12.4 MHz at "max" (248 MHz), 10.7 MHz at "medium" (64 MHz), 8 MHz at "low" (16 MHz). The open-drain phases get the longest low time the kernel can count, up to 2 µs. The pads must already be in their I3C alternate function, push-pull (coregen does this). Returns: I3C_OK, or I3C_ERROR for a bad instance. |
core_i3c_deinit(core_i3c_t * h) | Tier 1 | Disable an I3C instance and stop its clock. |
core_i3c_rstdaa(core_i3c_t * h) | Tier 1 | Broadcast RSTDAA: every target on the bus forgets its dynamic address. Tiles keep their state across a reflash (they stay powered, and flash-serial on the H5 starts the new image without a reset), so run this before assigning addresses. |
core_i3c_setdasa(core_i3c_t * h, uint8_t static_addr, uint8_t dyn_addr) | Tier 1 | SETDASA: give the target at I2C static address `static_addr` the dynamic address `dyn_addr` (0 = the first free one, 0x09 up). Never its static address: a target that loses its dynamic address answers the static one again as an I2C device, and the controller could not tell. Returns: I3C_OK, I3C_NACK when no target answers at that static address, I3C_ERROR when dyn_addr is taken or equals static_addr. |
core_i3c_entdaa(core_i3c_t * h, uint8_t * found) | Tier 1 | ENTDAA: assign dynamic addresses to every target that has none, recording each one's PID, BCR and DCR in h->targets[]. Returns: I3C_OK, or I3C_NACK when no target took part. |
core_i3c_attach(core_i3c_t * h, uint8_t static_addr) | Tier 1 | Dynamic address of the target known by its static address, giving it one with SETDASA first if it has none yet. Returns: The dynamic address, or 0 if the target does not answer. |
core_i3c_get_pid(core_i3c_t * h, uint8_t dyn_addr, uint8_t pid[6]) | Tier 1 | GETPID: the target's 48-bit provisioned ID, MSB first (MIPI manufacturer ID in the top 15 bits). |
core_i3c_get_bcr(core_i3c_t * h, uint8_t dyn_addr, uint8_t * bcr) | Tier 1 | GETBCR: bus characteristics (bit 1 IBI capable, bit 2 IBI payload). |
core_i3c_get_dcr(core_i3c_t * h, uint8_t dyn_addr, uint8_t * dcr) | Tier 1 | GETDCR: device characteristics (MIPI device-type code). |
core_i3c_get_status(core_i3c_t * h, uint8_t dyn_addr, uint16_t * status) | Tier 1 | GETSTATUS (format 1): the target's 16-bit status word. |
core_i3c_enec(core_i3c_t * h, uint8_t dyn_addr, uint8_t events) | Tier 1 | ENEC: let one target (or all, dyn_addr 0x7E) raise the given events (LL_I3C_EVT_INT for IBIs). |
core_i3c_disec(core_i3c_t * h, uint8_t dyn_addr, uint8_t events) | Tier 1 | DISEC: stop one target (or all, 0x7E) raising the given events. |
core_i3c_write(core_i3c_t * h, uint8_t dyn_addr, const uint8_t * data, uint16_t len) | Tier 1 | Private write of `len` bytes (len >= 1) to a target. |
core_i3c_read(core_i3c_t * h, uint8_t dyn_addr, uint8_t * buf, uint16_t len) | Tier 1 | Private read of up to `len` bytes (len >= 1) from a target. |
core_i3c_write_reg(core_i3c_t * h, uint8_t dyn_addr, uint16_t reg, const uint8_t * data, uint16_t len) | Tier 1 | Write `len` bytes starting at register `reg` (8-bit, or 16-bit MSB first when > 0xFF), in one private write. |
core_i3c_read_reg(core_i3c_t * h, uint8_t dyn_addr, uint16_t reg, uint8_t * buf, uint16_t len) | Tier 1 | Read `len` bytes starting at register `reg`: a private write of the register address, a repeated start, a private read, in one frame. |
core_i3c_ibi_enable(core_i3c_t * h, uint8_t dyn_addr, hal_i3c_ibi_cb_t cb, void * ctx) | Tier 1 | Acknowledge IBIs from a target and call `cb` for each (interrupt context: keep it short). Then enable them on the target with core_i3c_enec(). The target also has to route an event to IBI in its own registers. |
core_i3c_ibi_disable(core_i3c_t * h, uint8_t dyn_addr) | Tier 1 | Stop acknowledging IBIs from a target. |
Types
| Type | Kind | Description |
|---|---|---|
core_i3c_t | typedef | Core-level I3C handle. Alias for hal_i3c_t — use this in application code.typedef hal_i3c_t core_i3c_t; |
Constants
| Name | Value | Description |
|---|---|---|
I3C_12M5 | 12500000UL | SDR maximum (12.4 MHz at a 248 MHz kernel) |
SPIguide →
| Function | Access | Description |
|---|---|---|
core_spi_xfer_byte_bus(uint8_t bus, uint8_t cs_pad, uint8_t tx) | Tier 2 | Single-byte full-duplex transfer over `bus`, with CS auto-managed around the call (asserted before, deasserted after). Returns the received byte (0..255) on success or -1 on any error (bus undeclared, cs_pad undefined, transfer timed out). The signed return lets DSL programs branch on `< 0` without an out-pointer. Most chip protocols pair two of these (write a register address, then read or write the value). Multi-byte sequences that need CS held across them — display init streams, audio frame transfers — still need Tier 1 with manual select/deselect. |
core_spi_init(hal_spi_t * h, SPI_TypeDef * instance, const hal_spi_config_t * cfg) | Tier 1 | Initialize SPI in master mode (8-bit frames). Returns: HAL_OK, or HAL_ERROR on a NULL argument |
core_spi_configure(hal_spi_t * h, const hal_spi_config_t * cfg) | Tier 1 | Change prescaler, SPI mode or bit order, keeping the CS pad. Returns: HAL_OK, HAL_BUSY while a DMA transfer runs, HAL_ERROR on NULL |
core_spi_sck_hz(const hal_spi_t * h) | Tier 1 | The SCK frequency the handle is set to, in Hz. Returns: SYSCLK / (2..256); 0 for an uninitialized handle |
core_spi_set_cs(hal_spi_t * h, uint8_t pad) | Tier 1 | Assign a CS pin by tile pad number. |
core_spi_select(hal_spi_t * h) | Tier 1 | Assert CS. Everything until core_spi_deselect() is one transaction. |
core_spi_deselect(hal_spi_t * h) | Tier 1 | Deassert CS, ending the transaction. |
core_spi_transfer(hal_spi_t * h, uint8_t tx) | Tier 1 | Full-duplex single byte. CS untouched. Returns: The byte received (0xFF if the transfer failed) |
core_spi_exchange(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len) | Tier 1 | Full-duplex transfer of `len` bytes. CS untouched. Returns: HAL_OK, HAL_TIMEOUT (stalled), HAL_ERROR (overrun / mode fault), HAL_BUSY (a DMA transfer is running) |
core_spi_write(hal_spi_t * h, const uint8_t * data, uint32_t len) | Tier 1 | Write-only: send `len` bytes, discard what comes back. CS untouched. Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange) |
core_spi_read(hal_spi_t * h, uint8_t * buf, uint32_t len) | Tier 1 | Read-only: clock out the fill byte (0xFF) and capture `len` bytes. CS untouched. Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange) |
core_spi_xfer(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len) | Tier 1 | One CS-framed full-duplex transaction: select, exchange, deselect. Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange) |
core_spi_write_read(hal_spi_t * h, const uint8_t * tx, uint32_t tx_len, uint8_t * rx, uint32_t rx_len) | Tier 1 | One CS-framed command + data transaction. Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange) |
core_spi_exchange_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len) | Tier 1 | Full-duplex DMA transfer, blocking until the last frame is off the bus. CS untouched. Returns: HAL_OK, HAL_TIMEOUT (stalled; channels aborted, SPI reset), HAL_BUSY (another DMA transfer is running), HAL_ERROR (DMA error) |
core_spi_xfer_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len, hal_callback_t cb, void * ctx) | Tier 1 | Start a full-duplex DMA transfer and return at once. Returns: HAL_OK, HAL_BUSY if a DMA transfer is running, HAL_ERROR for a bad length |
core_spi_dma_wait(hal_spi_t * h) | Tier 1 | Wait for a core_spi_xfer_dma() transfer to finish (bounded). Returns: The transfer's result (HAL_OK if none was running), or HAL_TIMEOUT after aborting a stalled transfer |
core_spi_busy(hal_spi_t * h) | Tier 1 | Whether a DMA transfer is in progress on this handle. Returns: 1 while core_spi_xfer_dma() is running, else 0 |
core_spi_handle_for_bus(uint8_t bus) | Tier 1 | The SPI handle for a bus id declared in config.json, or NULL if the project doesn't declare that bus. Emitted per project by coregen into core_init.c (when the project declares any SPI bus); the *_bus helpers below resolve their handle through it. Forward-declared here rather than `#include "core_init.h"` so this header compiles without a project (val tests, examples without config.json). The natives-side caller is gated on CORE_HAS_SPI_BUSES, so the linker never asks for the symbol unless the dispatcher exists. Returns: The coregen-initialized handle, or NULL. |
Types
| Type | Kind | Description |
|---|---|---|
core_spi_t | typedef | SPI handle type — same as hal_spi_t under the hood.typedef hal_spi_t core_spi_t; |
core_spi_config_t | typedef | SPI configuration — prescaler, clock polarity, clock phase, bit order.typedef hal_spi_config_t core_spi_config_t; |
Timers & PWMguide →
| Function | Access | Description |
|---|---|---|
core_timer_init_freq(core_timer_t * h, TIM_TypeDef * instance, uint32_t freq_hz) | Tier 1 | Initialize a timer at a given overflow frequency. Use for PWM output and periodic tick — the frequency is how often the counter wraps (= the PWM frequency). core_timer_init_freq(&t, TIM2, 1000); // overflows at 1 kHz |
core_timer_set_trgo(core_timer_t * h, uint32_t mms) | Tier 1 | Route an internal timer event to TRGO so another peripheral can be paced by it, with no pin, no channel and no interrupt involved. LL_TIM_MMS_UPDATE is the periodic-pacer case: every counter overflow emits a trigger. Pair it with core_adc_set_trigger() to run the ADC at an exact rate instead of free-running. core_timer_init_freq(&t, TIM2, 800); core_timer_set_trgo(&t, LL_TIM_MMS_UPDATE); core_timer_start(&t); |
core_timer_init_tick(core_timer_t * h, TIM_TypeDef * instance, uint32_t tick_hz) | Tier 1 | Initialize a timer at a given tick rate, free-running to max count. Use for input capture — the tick rate sets the measurement resolution, and the counter runs as long as possible before wrapping (0xFFFF for 16-bit, 0xFFFFFFFF for 32-bit TIM2). core_timer_init_tick(&t, TIM2, 1000000); // 1 us per tick core_timer_capture_init(&t, 1); core_timer_start(&t); |
core_timer_start(core_timer_t * h) | Tier 1 | Start the timer counter. |
core_timer_stop(core_timer_t * h) | Tier 1 | Stop the timer counter. |
core_timer_set_freq(core_timer_t * h, uint32_t freq_hz) | Tier 1 | Change the timer frequency (recalculates PSC/ARR). |
core_timer_pwm_set(core_timer_t * h, uint8_t channel, uint16_t duty_permil) | Tier 1 | Set PWM duty cycle for a channel. channel: 1–4 duty_permil: 0–1000 (0 = off, 500 = 50%, 1000 = always on) |
core_timer_pwm_set_pad(core_timer_t * h, uint8_t pad, uint16_t duty_permil) | Tier 1 | Set PWM duty cycle by pad number (requires coregen: only defined when the project's config.json binds a timer to a pad). Resolves the channel from the pad's timer assignment; pads without a timer are ignored. |
core_timer_capture_init(core_timer_t * h, uint8_t channel) | Tier 1 | Configure a channel for input capture (rising edge). The timer must already be initialized with core_timer_init(). |
core_timer_capture_read(core_timer_t * h, uint8_t channel) | Tier 1 | Read the last captured value from a channel. |
core_timer_enable_tick(core_timer_t * h, core_callback_t cb, void * ctx) | Tier 1 | Enable periodic tick on an already-initialized timer. Does NOT touch PSC/ARR — the timer keeps its existing timebase. The callback fires on each counter overflow at the timer's frequency. Typical pattern: core_timer_init(&t, TIM2, 1000); // 1 kHz timebase core_timer_pwm_set(&t, 1, 500); // CH1 = 50% PWM (permil) core_timer_enable_tick(&t, on_tick, NULL); // also fire ISR at 1 kHz core_timer_start(&t); |
core_timer_disable_tick(core_timer_t * h) | Tier 1 | Disable the tick callback (clears UIE, keeps timer running). |
core_tick_init(core_timer_t * h, TIM_TypeDef * instance, uint32_t period_us, core_callback_t cb, void * ctx) | Tier 1 | Convenience: initialize a timer as a tick-only source. Sets up the timebase AND enables the update interrupt. Use this when the timer's only job is a periodic callback. core_tick_init(&t, TIM3, 500000, on_tick, NULL); // 2 Hz core_timer_start(&t); |
core_pwm_duty(uint8_t pad, uint16_t duty_permil) | Tier 2 | Set PWM duty cycle on a pad. Timer handle resolved from config.json. |
core_pwm_init(core_timer_t * h, TIM_TypeDef * instance, uint32_t freq_hz) | Tier 1 | Initialize a timer for PWM output at the given frequency. Clock is auto-resolved from SYSCLK_HZ (core_config.h). |
core_pwm_init_clk(core_timer_t * h, TIM_TypeDef * instance, uint32_t pclk_hz, uint32_t freq_hz) | Tier 1 | Deprecated. Use core_pwm_init(h, instance, freq_hz) — clock auto-resolved. |
core_pwm_set(core_timer_t * h, uint8_t channel, uint16_t duty_permil) | Tier 1 | Set PWM duty cycle for a channel. channel: 1–4 duty_permil: 0–1000 (0 = off, 500 = 50%, 1000 = 100%) |
core_pwm_set_freq(core_timer_t * h, uint32_t freq_hz) | Tier 1 | Change PWM frequency (recalculates PSC/ARR, resets all channel duties). |
core_pwm_start(core_timer_t * h) | Tier 1 | Start the PWM timer. |
core_pwm_stop(core_timer_t * h) | Tier 1 | Stop the PWM timer. |
core_pwm_init_pad(core_timer_t * h, uint8_t pad, uint32_t freq_hz) | Tier 1 | Initialize PWM on a pad. Timer instance resolved from config.json. |
core_pwm_set_pad(core_timer_t * h, uint8_t pad, uint16_t duty_permil) | Tier 1 | Set PWM duty on a pad (0–1000 permil). |
core_pwm_timer_for_pad(uint8_t pad) | Tier 1 | The timer driving a pad. Emitted per project by coregen into core_init.c when any TIM<n> pad is configured. Returns: The timer handle, or NULL for pads not bound to a timer in config.json. |
core_every_us(core_timer_t * h, TIM_TypeDef * instance, uint32_t period_us, hal_callback_t cb, void * ctx) | Tier 1 | Configure a periodic callback at a fixed interval. period_us: interval in microseconds (1 – 1000000) cb: callback function (called from ISR context!) ctx: user context passed to callback Clock is auto-resolved from SYSCLK_HZ (core_config.h). |
core_every_us_clk(core_timer_t * h, TIM_TypeDef * instance, uint32_t pclk_hz, uint32_t period_us, hal_callback_t cb, void * ctx) | Tier 1 | Deprecated. Use core_every_us(h, instance, period_us, cb, ctx) — clock auto-resolved. |
core_every_start(core_timer_t * h) | Tier 1 | Start the periodic timer. |
core_every_stop(core_timer_t * h) | Tier 1 | Stop the periodic timer. |
Types
| Type | Kind | Description |
|---|---|---|
core_timer_t | typedef | Core-level timer handle. Alias for hal_timer_t — use this in application code.typedef hal_timer_t core_timer_t; |
core_callback_t | typedef | Core-level callback type.typedef hal_callback_t core_callback_t; |
Stepper (STEP/DIR)guide →
| Function | Access | Description |
|---|---|---|
core_stepper_init(core_stepper_t * h, uint8_t step_pad, uint8_t dir_pad) | Tier 1 | Bind a stepper axis to a STEP pad (timer output) and a DIR pad. The timer and channel come from the pad's TIMx.y assignment in config.json, which is also what routes the pin to the timer. Loads conservative defaults: 1000 steps/s cruise, 100 steps/s start, 4000 steps/s^2 ramp, 8 us pulses. The timer is left stopped until a move. Do not share the timer with a PWM output — the module rewrites its period on every step. Returns: HAL_OK; HAL_ERROR if the STEP pad is not a timer pad in config.json, DIR has no GPIO, or the timer cannot interrupt. |
core_stepper_init_ch(core_stepper_t * h, TIM_TypeDef * instance, uint8_t channel, uint8_t step_pad, uint8_t dir_pad) | Tier 1 | Bind an axis with an explicit timer instance and channel (no coregen lookup). The caller must have configured the STEP pad's alternate function for that channel. Everything else as core_stepper_init(). Returns: HAL_OK, or HAL_ERROR if DIR has no GPIO or the timer cannot interrupt. |
core_stepper_set_enable_pad(core_stepper_t * h, uint8_t en_pad, bool active_low) | Tier 1 | Add an ENABLE output. The pad is driven to its disabled level immediately; call core_stepper_enable() to power the driver. Pass pad 0 to remove it. |
core_stepper_enable(core_stepper_t * h, bool on) | Tier 1 | Drive the ENABLE pad. No effect without core_stepper_set_enable_pad(). Motion is not interlocked: halt before disabling if you care where the rotor ends up. |
core_stepper_set_polarity(core_stepper_t * h, bool step_active_low, bool dir_invert) | Tier 1 | Set signal polarities. Call while idle. |
core_stepper_set_pulse_us(core_stepper_t * h, uint32_t pulse_us) | Tier 1 | Set the STEP pulse width; the low time between pulses is kept at least as long, which also bounds DIR setup. 8 us (default) satisfies the DM320T's 7.5 us; a DRV8428 is happy with 1 us. The maximum step rate is 1 / (2 * pulse_us). |
core_stepper_set_speed(core_stepper_t * h, uint32_t steps_per_s) | Tier 1 | Set the cruise rate for position moves and the cap for run(). Takes effect at the next step; a move in flight ramps to the new value. |
core_stepper_set_accel(core_stepper_t * h, uint32_t steps_per_s2) | Tier 1 | Set the acceleration used for every ramp, or 0 to switch instantly between the start rate and full speed (fine for small, unloaded motors at low rates; expect stalls otherwise). |
core_stepper_set_min_speed(core_stepper_t * h, uint32_t steps_per_s) | Tier 1 | Set the rate a ramp starts from and stops at. Steppers can start instantly at a few hundred full steps per second; starting from a crawl only wastes time. Clamped to at least 1 (16 on a 16-bit timer). |
core_stepper_move(core_stepper_t * h, int32_t steps) | Tier 1 | Move a signed number of microsteps relative to the current target (so two quick calls of +160 travel 320). Retargets a move in flight; a reversal decelerates to the start rate before flipping DIR. |
core_stepper_move_to(core_stepper_t * h, int32_t position) | Tier 1 | Move to an absolute microstep position. |
core_stepper_run(core_stepper_t * h, int32_t steps_per_s) | Tier 1 | Run continuously at a signed rate, ramping from the current speed. Rate 0 decelerates to a stop (same as core_stepper_stop()). The magnitude is capped at the cruise rate from core_stepper_set_speed(). |
core_stepper_stop(core_stepper_t * h) | Tier 1 | Decelerate to a stop using the configured acceleration. Position continues to count during the ramp. Idempotent. |
core_stepper_halt(core_stepper_t * h) | Tier 1 | Stop instantly: timer off, STEP forced to idle. A pulse whose rising edge the driver has already seen is counted. A loaded motor at speed will lose steps; the position counter reflects the pulses sent. |
core_stepper_busy(const core_stepper_t * h) | Tier 1 | true while pulses are being generated (including a ramp to zero). Returns: true if not idle. |
core_stepper_state(const core_stepper_t * h) | Tier 1 | Current motion state. Returns: CORE_STEPPER_IDLE / MOVING / RUNNING. |
core_stepper_position(const core_stepper_t * h) | Tier 1 | Current signed microstep position (pulses actually issued). Returns: Position. |
core_stepper_set_position(core_stepper_t * h, int32_t position) | Tier 1 | Redefine the current position without moving (homing). Only while idle; the target follows so the next move() is relative to it. |
core_stepper_target(const core_stepper_t * h) | Tier 1 | Target position of the current or last move. Returns: Target position. |
core_stepper_speed(const core_stepper_t * h) | Tier 1 | Current signed step rate. Returns: Microsteps per second, negative in the reverse direction, 0 when idle. |
core_stepper_stop_distance(const core_stepper_t * h, uint32_t steps_per_s) | Tier 1 | Microsteps needed to decelerate from `steps_per_s` to the start rate at the configured acceleration. Useful for planning where a run() will stop, e.g. to land on a pocket boundary. Returns: Stopping distance in microsteps (0 when accel is 0). |
Types
| Type | Kind | Description |
|---|---|---|
core_stepper_state_t | enum | Motion state. Read with core_stepper_state(); never write it.
|
core_stepper_t | struct | Stepper axis handle. Allocate one per motor; treat the fields as private. Fields tagged ISR are written from the timer interrupt.
|
Analog (ADC / DAC)guide →
| Function | Access | Description |
|---|---|---|
core_adc_read_pad(uint8_t pad) | Tier 2 | Read a pad as raw ADC counts (0–4095 at 12-bit resolution). |
core_adc_read_mv_pad(uint8_t pad) | Tier 2 | Read a pad as calibrated millivolts (uses VREFINT for per-chip accuracy). |
core_adc_temp_decidegc(void) | Tier 2 | Die temperature in tenths of deg C (e.g. 253 = 25.3 C). Dispatches to the default ADC instance. |
core_adc_vdd_mv(void) | Tier 2 | VDD supply voltage in millivolts (via VREFINT). Dispatches to the default ADC instance. |
core_adc_default(void) | Tier 1 | `core_adc1`, initialised on first use if no ADC pad set it up (core_adc.c): the internal channels (temperature, VDD) need no pad. |
core_adc_init(core_adc_t * adc, uint32_t resolution) | Tier 1 | Initialise the ADC at the given resolution. |
core_adc_add(core_adc_t * adc, uint8_t pad, uint32_t samp) | Tier 1 | Register a pad as an ADC input with the given sampling speed. |
core_adc_read(core_adc_t * adc, uint8_t pad) | Tier 1 | Single-shot read — returns raw ADC count. |
core_adc_read_mv(core_adc_t * adc, uint8_t pad) | Tier 1 | Single-shot read — returns calibrated millivolts. |
core_adc_temp(core_adc_t * adc) | Tier 1 | Die temperature in tenths of deg C (e.g. 253 = 25.3 C). |
core_adc_vdd(core_adc_t * adc) | Tier 1 | Actual VDD supply voltage in millivolts via VREFINT. |
core_adc_start_dma(core_adc_t * adc, uint16_t * buf, uint16_t len, hal_callback_t cb, void * ctx) | Tier 1 | Start continuous conversion with DMA circular buffer. |
core_adc_stop_dma(core_adc_t * adc) | Tier 1 | Stop continuous DMA conversion. |
core_adc_dma_read(core_adc_t * adc, uint8_t pad) | Tier 1 | Read most recent DMA result for a pad. |
core_adc_dma_read_mv(core_adc_t * adc, uint8_t pad) | Tier 1 | Read most recent DMA result for a pad, in calibrated millivolts. Prime VDDA before starting DMA (call core_adc_vdd(adc) once), since measuring VREFINT needs the regular sequence that DMA owns. |
core_adc_raw_to_mv(core_adc_t * adc, uint16_t raw) | Tier 1 | Convert a raw count from a DMA buffer to millivolts. |
core_adc_set_trigger(core_adc_t * adc, uint8_t extsel, uint32_t edge) | Tier 1 | Pace DMA conversions from a hardware trigger instead of free-running. Call before core_adc_start_dma. See hal_adc_set_trigger for why this matters to any slow output rate: it is what makes a DMA ring span the whole output period, turning the average into a real anti-alias filter instead of a burst average. core_timer_init_freq(&t, TIM2, 800); core_timer_set_trgo(&t, LL_TIM_MMS_UPDATE); core_adc_set_trigger(adc, LL_ADC_L0_TRG_TIM2_TRGO, ADC_TRIG_RISING); core_adc_start_dma(adc, buf, len, 0, 0); core_timer_start(&t); |
core_adc_dma_slot(core_adc_t * adc, uint8_t pad) | Tier 1 | Buffer slot a pad occupies in one DMA scan (-1 if not registered). Use the stride when the buffer holds several scans for averaging. |
core_dac_init(void) | Tier 2 | Initialize the DAC (clock, GPIO, peripheral). Call once from `on start` before any writes. |
core_dac_write(uint16_t val) | Tier 2 | Write a raw 12-bit value. Output = val / 4095 × VREF+. |
core_dac_write_mv(uint16_t mv) | Tier 2 | Write a voltage in millivolts. |
core_dac_read(void) | Tier 2 | Read back the current DAC output register value (12-bit). |
Types
| Type | Kind | Description |
|---|---|---|
core_adc_t | typedef | Core-level ADC handle. Alias for hal_adc_t — use this in application code.typedef hal_adc_t core_adc_t; |
Audio (PDM mic)guide →
| Function | Access | Description |
|---|---|---|
core_audio_init(core_audio_t * a, uint32_t kernel_clk_hz, uint32_t pcm_rate_hz, uint8_t data_line, uint8_t clock_line, uint8_t cic_order, uint8_t gain_shift) | Tier 1 | Configure the mic: SAI PDM block + decimator (capture not started). |
core_audio_start(core_audio_t * a, GPDMA_Channel_TypeDef * ch, uint8_t * pdm_buf, uint32_t pdm_len, int16_t * pcm_buf, core_audio_pcm_cb on_pcm, void * user_ctx) | Tier 1 | Start continuous capture; `on_pcm` fires per half-buffer with PCM. |
core_audio_stop(core_audio_t * a) | Tier 1 | Stop capture. |
core_audio_irq(core_audio_t * a) | Tier 1 | Route the capture channel's GPDMA IRQ here. |
core_pdm_init(core_pdm_cic_t * st, uint8_t order, uint16_t decimation, uint8_t gain_shift, uint8_t lsb_first) | Tier 1 | Initialise a CIC decimator. |
core_pdm_process(core_pdm_cic_t * st, const uint8_t * pdm, uint32_t nbytes, int16_t * out) | Tier 1 | Decimate a block of packed PDM bits into PCM samples. Returns: number of PCM samples written to `out`. |
Types
| Type | Kind | Description |
|---|---|---|
core_audio_pcm_cb | callback | Delivered finished PCM: `n` samples at the configured rate.typedef void (*core_audio_pcm_cb)(const int16_t *pcm, uint32_t n, void *ctx); |
core_pdm_cic_t | struct | CIC decimator state. Zero-initialise via core_pdm_init(); do not poke the fields directly. One instance per microphone stream.
|
Powerguide →
| Function | Access | Description |
|---|---|---|
core_sleep(void) | Tier 2 | Sleep until any interrupt (CPU stopped, peripherals running). |
core_stop_for(uint32_t seconds) | Tier 2 | 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). |
core_woke_from_standby(void) | Tier 2 | 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. |
core_reset(void) | Tier 2 | Reset the Core. Does not return. With a BLE central connected (Core.ST.W5), the link is dropped first so the central sees a clean disconnect (waits at most 200 ms). Pending USB serial output is flushed (Core.ST.L4 / H5: waits at most 50 ms for a terminal that stopped reading, longer only while a just-opened port settles). The watchdog is fed while it waits. Then a system reset (AIRCR SYSRESETREQ). Core.ST.L4: if the chip still believes its flash is empty (it was flashed blank by DFU or SWD and not power-cycled since), that stale verdict is corrected first, so the reset starts this program and not the ROM bootloader. Core.ST.H5 with BOOT0 held high resets into the ROM bootloader. Safe from an interrupt handler; the BLE disconnect is then left to the central's supervision timeout. |
core_reset_reason(void) | Tier 2 | Why the Core last reset: power-on (or brownout), the reset pin, the watchdog, a software reset (core_reset(), a remote reset from Studio, an update), or other. Read once by core_init() and kept, so it answers the same for the whole run. Returns: core_reset_reason_t: 0 power-on, 1 pin, 2 watchdog, 3 software, 4 other. |
core_clock_init(void) | Tier 1 | 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). |
core_stop_until_on_change_timeout(uint8_t pad, uint32_t edge, uint32_t timeout_ms) | Tier 1 | 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. Returns: 1 = woken by the pad (or it was already at the wake level), 0 = timed out, -1 = the pad can't take an edge interrupt. |
core_stop_until_on_change(uint8_t pad, uint32_t edge) | Tier 1 | 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. |
core_standby_for(uint32_t seconds) | Tier 1 | 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.) Returns: HAL_ERROR if refused because of the watchdog; does not return otherwise. |
core_standby_until_on_change(uint8_t pad, uint32_t edge) | Tier 1 | 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). |
core_stop(void) | Tier 1 | 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). |
core_standby(void) | Tier 1 | Enter Standby (caller manages wakeup source). Does not return. A running watchdog keeps counting through Standby and resets the Core at its timeout. |
core_clear_standby_flag(void) | Tier 1 | Clear the Standby wake flag. Call after core_woke_from_standby() returns 1. |
core_watchdog_start_seconds(uint32_t seconds) | Tier 1 | Start the independent watchdog with a timeout in seconds (convenience). For finer control use core_watchdog_start() from core_watchdog.h which accepts milliseconds. |
core_reset_flags(void) | Tier 1 | The raw RCC reset flags read at boot (CORE_RESET_FLAG_*; bits 31..24). 0 if no flag was set. |
core_reset_capture(void) | Tier 1 | Read and keep the reset flags, then clear them in RCC. core_init() calls this first thing; later calls do nothing. core_reset_reason() calls it too, for a program that never ran core_init(). |
core_rtc_init(void) | Tier 2 | 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). |
core_rtc_set_time(uint8_t h, uint8_t m, uint8_t s) | Tier 2 | Set the time (24h format). |
core_rtc_set_date(uint8_t y, uint8_t mo, uint8_t d, uint8_t wd) | Tier 2 | Set the date. |
core_rtc_wakeup(uint32_t seconds) | Tier 2 | Configure a periodic wakeup timer. |
core_rtc_wakeup_stop(void) | Tier 2 | Disable the periodic wakeup timer. |
core_rtc_set_alarm(uint8_t hours, uint8_t minutes, uint8_t seconds) | Tier 2 | 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. |
core_rtc_clear_alarm(void) | Tier 2 | Clear the alarm (disable Alarm A). |
core_rtc_alarm_fired(void) | Tier 2 | Check if the alarm has fired (poll mode). Clears the flag on read. |
core_rtc_get_time(uint8_t * h, uint8_t * m, uint8_t * s) | Tier 1 | Read the current time. |
core_rtc_get_date(uint8_t * y, uint8_t * mo, uint8_t * d, uint8_t * wd) | Tier 1 | Read the current date. |
core_backup_read(uint8_t index) | Tier 2 | Read a backup register. |
core_backup_write(uint8_t index, uint32_t value) | Tier 2 | Write a backup register. Backup domain write access is enabled automatically. |
_core_backup_ensure_clk(void) | Tier 1 | 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. |
Types
| Type | Kind | Description |
|---|---|---|
core_reset_reason_t | enum | Cause of the reset this program started from (core_reset_reason()).
|
Constants
| Name | Value | Description |
|---|---|---|
CORE_RESET_FLAG_LPWR | (1UL << 31) | Illegal Stop/Standby entry |
CORE_RESET_FLAG_WWDG | (1UL << 30) | Window watchdog |
CORE_RESET_FLAG_IWDG | (1UL << 29) | Independent watchdog |
CORE_RESET_FLAG_SFT | (1UL << 28) | Software (AIRCR SYSRESETREQ) |
CORE_RESET_FLAG_POR | (1UL << 27) | POR/PDR (L0), BOR (L4, W5, H5) |
CORE_RESET_FLAG_PIN | (1UL << 26) | NRST pin (set by every reset) |
CORE_RESET_FLAG_OBL | (1UL << 25) | Option-byte reload (not on the H5) |
CORE_RESET_FLAG_FW | (1UL << 24) | Firewall (L0, L4 only) |
Storage & IDguide →
| Function | Access | Description |
|---|---|---|
core_nvm_size(void) | Tier 2 | Returns the total NVM size in bytes for this Core. |
core_nvm_read_byte(uint32_t offset) | Tier 2 | Read a single byte from NVM at `offset`. Returns the byte (0..255) on success or -1 on any error (offset out of range, NVM disabled). A byte never written reads 255 (0xFF) on Core.ST.L4 / W5 / H5 and 0 on Core.ST.L0. The signed return lets DSL programs branch on `< 0` without an out-pointer. |
core_nvm_write_byte(uint32_t offset, uint8_t value) | Tier 2 | Write a single byte to NVM at `offset`. Returns 1 on success or -1 on any error (offset out of range, flash error, no NVM on this Core). Each call is one flash record on Core.ST.L4 / W5 / H5: fine for a setting or a boot counter, wasteful in a tight loop (see core_nvm.h on wear). |
core_nvm_read(uint32_t offset, void * buf, uint32_t len) | Tier 1 | Read bytes from NVM. Bytes never written read as CORE_NVM_ERASED (0xFF on Core.ST.L4 / W5 / H5, 0 on Core.ST.L0). Returns: 0 on success, CORE_NVM_ERR_RANGE (-1) if the range falls outside the NVM region, CORE_NVM_ERR_FLASH if the store can't be read. |
core_nvm_write(uint32_t offset, const void * data, uint32_t len) | Tier 1 | Write bytes to NVM. On Core.ST.L4 / W5 / H5 (1 KB, flash-emulated) a write is atomic: after a reset or power loss the range reads all old or all new, and the data survives reflashing. Most writes take well under a millisecond; about one small write in a hundred (L4; one in 500 on the W5, one in 350 on the H5) compacts the store, which erases a flash page and stalls the Core.ST.L4 for ~22 ms. Core.ST.L0 writes its EEPROM byte by byte (~3.2 ms each). Returns: 0 on success, or a negative CORE_NVM_ERR_* code: RANGE (-1) if the range falls outside the region or the Core has no NVM, FLASH (-2) if the hardware reported an error, BUSY (-3), LAYOUT (-4). |
core_nvm_erase_all(void) | Tier 1 | Erase the whole NVM region: every byte reads CORE_NVM_ERASED afterwards. Core.ST.L4 / W5 / H5: one page erase, or nothing if the region is already empty. Core.ST.L0: clears the EEPROM word by word (up to ~0.4 s; words already clear are skipped). Returns: 0 on success, or a negative CORE_NVM_ERR_* code. |
core_otp_size(void) | Tier 2 | Total OTP size in bytes for this Core (0 where unavailable). |
core_otp_slot_count(void) | Tier 2 | Number of write-once quad-word slots (CORE_OTP_SIZE / 16). |
core_otp_slot_is_blank(uint32_t slot) | Tier 2 | Return 1 if `slot` is entirely erased (all 0xFF), 0 if any bit is programmed, or -1 if the slot index is out of range / OTP unavailable. A blank slot is programmable; a non-blank slot is not (write-once). |
core_otp_slot_addr(uint32_t slot) | Tier 1 | Absolute address of a slot (no bounds check). |
core_otp_read(uint32_t offset, void * buf, uint32_t len) | Tier 1 | Read bytes from OTP into `buf`. Memory-mapped load; no unlock needed. Returns 0 on success, -1 if the range is out of bounds or OTP is unavailable on this Core. |
core_otp_program_slot(uint32_t slot, const void * rec) | Tier 1 | Program one 16-byte quad-word slot (write-once) and verify the read-back. IRREVERSIBLE. Refuses a slot that is not blank (returns -2) so a second write can't corrupt an existing record. On WBA55 this unlocks flash, issues a single 128-bit quad-word program, re-locks, and confirms the stored bytes match `rec`. There is no retry and no undo — callers should treat a failure as "advance to the next slot", not "try again here". slot: 0 .. CORE_OTP_SLOT_COUNT-1 rec: pointer to exactly CORE_OTP_SLOT_SIZE (16) bytes. Returns 0 on success, -1 on range/program/verify error, -2 if the slot is not blank, -3 if OTP programming is unavailable on this Core. Deliberately NOT `@studio expose`d: a destructive, irreversible burn does not belong in the DSL palette. It is escape-to-C only (see @studio unsupported below). |
core_uid_read(uint32_t out[3]) | Tier 1 | Read the 96-bit unique device ID as three 32-bit words. |
core_uid_hash(void) | Tier 1 | Fold the full 96-bit ID into a single stable 32-bit value (the three words XORed together). Better mixed than any single word — use it to derive short tokens (e.g. a 4-hex-digit name suffix) where the raw low bytes might be correlated across a manufacturing batch. Returns: A 32-bit value, stable for the life of the chip. |
core_uid_hex(char * buf, uint16_t buflen) | Tier 1 | Format the full 96-bit ID as an uppercase hex string (24 hex characters, word 0 first, most-significant nibble first within each word) plus a NUL terminator. The canonical form for a device serial number. Returns: Number of hex characters written (24), or -1 if buf is NULL or buflen is too small. |
Constants
| Name | Value | Description |
|---|---|---|
CORE_NVM_OK | 0 | Success. |
CORE_NVM_ERR_RANGE | (-1) | Offset / length outside the region, or this Core has no NVM. |
CORE_NVM_ERR_FLASH | (-2) | Erase or program failed (FLASH_SR error, or the read-back did not match). |
CORE_NVM_ERR_BUSY | (-3) | Called while another NVM call was in progress (e.g. from an interrupt). |
CORE_NVM_ERR_LAYOUT | (-4) | The linker script does not reserve the NVM pages (a custom .ld). |
CORE_NVM_ERASED | 0x00u | RM0377 §3.3.4: an erased EEPROM byte reads 0. |
Securityguide →
| Function | Access | Description |
|---|---|---|
core_rng_read(void) | Tier 2 | Read a single 32-bit random value (blocking). Returns 0 on timeout — check core_rng_error() if this happens. |
core_rng_init(void) | Tier 1 | Initialize the hardware RNG. Enables the peripheral clock, configures the RNG, and waits for the first random number to be ready. On Core.ST.L4: requires HSI48 to be running (auto-enabled if USB is used, otherwise call ll_rcc_hsi48_enable() first). |
core_rng_fill(uint32_t * buf, uint32_t count) | Tier 1 | Fill a buffer with random 32-bit values (blocking). Returns: Number of values successfully generated |
core_rng_error(void) | Tier 1 | Check if the RNG has a seed error (entropy source failure). |
core_rng_deinit(void) | Tier 1 | Power down the RNG peripheral. |
USBguide →
| Function | Access | Description |
|---|---|---|
core_usb_print(const char * s) | Tier 2 | Print a string followed by a newline. Thin wrapper for DSL-style callers. |
core_usb_print_int(int v) | Tier 2 | Print a signed integer followed by a newline. |
core_usb_print_float(double v) | Tier 2 | Print a double with a newline. %g trims trailing zeros for readability. |
core_usb_print_bool(int v) | Tier 2 | Print "true" / "false" followed by a newline. |
core_usb_init(void) | Tier 1 | Initialize USB CDC. Device appears as /dev/tty.usbmodem* on the host. |
core_usb_connected(void) | Tier 1 | Returns 1 if a host terminal is connected (DTR set). |
core_usb_wait_host(uint32_t timeout_ms) | Tier 1 | Wait up to `timeout_ms` for a host terminal to open the port (DTR set). Feeds the watchdog while it waits. Not needed just to see early output: text printed before the port opens is kept (up to HAL_USB_CDC_TX_QUEUE_SIZE bytes) and sent when it does. Returns: 1 if a terminal is connected, 0 on timeout. |
core_usb_write(const uint8_t * buf, uint16_t len) | Tier 1 | Transmit data: queued, never waits long (see hal_usb_cdc_write); returns bytes queued, -1 before init. |
core_usb_try_write(const uint8_t * buf, uint16_t len) | Tier 1 | Transmit without waiting: queues the whole buffer or none of it. Returns len if queued, 0 if there is no room right now (offer it again later), -1 if no terminal is connected. Main loop only. |
core_usb_on_receive(hal_usb_cdc_rx_cb_t cb, void * ctx) | Tier 1 | Set a callback for received data. Called from USB ISR. When set, data is NOT buffered for polling reads. |
core_usb_available(void) | Tier 1 | Returns the number of bytes available to read (ring buffer mode). |
core_usb_getc(void) | Tier 1 | Read a single byte (blocking — waits for data). |
core_usb_read(uint8_t * buf, uint16_t max) | Tier 1 | Read available bytes into buf (non-blocking). Returns bytes read. |
core_usb_try_read(uint8_t * byte) | Tier 1 | Non-blocking single byte read. Returns 1 if a byte was read, 0 if empty. |
core_usb_line_feed(core_usb_line_t * lr, uint8_t byte) | Tier 1 | Feed one received byte to a line assembler. A line ends at '\n'. '\r' and NUL bytes are dropped, so "\r\n" and "\n" endings both work. Empty lines are ignored. A line longer than CORE_USB_LINE_MAX characters is dropped whole: the assembler discards everything up to the next '\n' and starts clean after it. Returns: The completed line, NUL-terminated and without its ending, or NULL if this byte did not complete one. The text stays valid until the next call with the same `lr`. |
core_usb_poll_line(core_usb_line_t * lr) | Tier 1 | Read received bytes until a line completes (non-blocking). Takes bytes from the receive ring with core_usb_try_read(), at most 64 per call so a flood can't stall the main loop, and stops at the first complete line; later bytes stay in the ring for the next call. Call it from the main loop. Not for use with core_usb_on_receive(), which bypasses the ring, and not alongside other ring reads, which would steal bytes from the line. Studio programs don't call this: `on Core.USB.line(text)` gets each line from coregen's dispatcher, which feeds core_usb_line_feed() directly. Returns: The completed line, NUL-terminated and without its ending, or NULL if no line is complete yet. Valid until the next call with `lr`. |
core_usb_hid_send(const uint8_t * buf, uint16_t len) | Tier 1 | Send a HID report (up to 64 bytes, zero-padded automatically). Blocking — waits for the previous report to be read by the host. Returns bytes of user data sent, or -1 if USB not configured. |
core_usb_hid_set_rx_callback(hal_usb_hid_rx_cb_t cb, void * ctx) | Tier 1 | Register a callback for inbound HID reports (host -> device). Fires from the USB ISR with one report per call (up to 64 bytes), delivered via the EP3 OUT interrupt endpoint or HID SET_REPORT. Pass NULL to disable; unhandled reports are dropped. |
Types
| Type | Kind | Description |
|---|---|---|
core_usb_line_t | struct | Line-assembler state for core_usb_line_feed() / core_usb_poll_line(). Zero-initialize it (a static or global is).
|
BLEguide →
| Function | Access | Description |
|---|---|---|
core_ble_set_services(void) | Tier 1 | Register a service builder function. Called before core_ble_init(). The builder is invoked during init after the GATT server is ready. |
core_ble_add_services(void) | Tier 1 | Register an ADDITIONAL service builder, run after the application's own. For SDK modules that bring a GATT service of their own without taking over core_ble_set_services(), which the application — or coregen's generated contract — owns. Call before core_ble_init(). Registering the same builder twice is harmless. Returns 0, or -1 if the slots are full or the stack has already started. |
core_ble_init(void) | Tier 1 | Initialize the BLE stack. Call once after core_init(). |
core_ble_advertise(const char * name) | Tier 1 | Start advertising. Call after init + at least one core_ble_process(). |
core_ble_stop_advertise(void) | Tier 1 | Stop advertising. |
core_ble_process(void) | Tier 1 | Process BLE events. Call continuously from main loop. |
core_ble_add_service(const char * name) | Tier 1 | Add a GATT service. Returns a handle for adding characteristics. The UUID is auto-assigned SEQUENTIALLY by registration order (0000B000-…, 0000B100-…, …). Convenient, but the UUIDs shift if you reorder or insert services — so for a contract shared with client apps, prefer core_ble_add_service_id() to pin a stable, order-independent UUID. |
core_ble_add_service_id(const char * name, uint16_t id) | Tier 1 | Add a GATT service with an EXPLICIT 16-bit ID. The full UUID is 0000<id>-8E22-4541-9D4C-21EDAE82ED19. Pinning the ID makes the GATT contract independent of registration order — the recommended path when the service map is a source of truth shared with phone / desktop client apps. |
core_ble_add_char(core_ble_svc_t svc, const char * name, uint8_t access, uint8_t type, core_ble_write_cb on_write, void * ctx) | Tier 1 | Add a characteristic to a service. Returns: Characteristic handle for set_value/notify. |
core_ble_add_char_id(core_ble_svc_t svc, const char * name, uint16_t id, uint8_t access, uint8_t type, core_ble_write_cb on_write, void * ctx) | Tier 1 | Add a characteristic with an EXPLICIT 16-bit ID (0000<id>-8E22-…), the order-independent counterpart to core_ble_add_char(). Use for the stable contract shared with client apps. |
core_ble_add_service_sig(const char * name, uint16_t uuid16) | Tier 1 | Add a SIG-adopted service by its 16-bit Bluetooth UUID (e.g. 0x180F Battery Service, 0x180A Device Information). Use adopted services for standardised profiles so generic clients and the host OS recognise them; use the _id variants for custom application-specific data. |
core_ble_add_char_sig(core_ble_svc_t svc, const char * name, uint16_t uuid16, uint8_t access, uint8_t type, core_ble_write_cb on_write, void * ctx) | Tier 1 | Add a SIG-adopted characteristic by its 16-bit Bluetooth UUID (e.g. 0x2A19 Battery Level, 0x2A29 Manufacturer Name) to a service. |
core_ble_set_value(core_ble_char_t ch, const void * data, uint16_t len) | Tier 1 | Update a characteristic's value. For readable characteristics this is what the central reads. For notify characteristics the stack also sends the new value to a subscribed central in the same call: there is nothing else to do (core_ble_notify() is a no-op kept for source compatibility). Returns: 0 on success, -1 when the stack could not take the update (its TX queue is full): keep the data and call again on a later pass. |
core_ble_notify(core_ble_char_t ch) | Tier 1 | Kept for source compatibility: does nothing and returns 0. core_ble_set_value() already notifies a subscribed central. |
core_ble_subscribed(core_ble_char_t ch) | Tier 1 | Has a client subscribed to notifications on this characteristic? Returns: 1 when connected and the client has enabled notify/indicate, else 0. |
core_ble_connected(void) | Tier 1 | Returns 1 if a central is connected. |
core_ble_on_connect(void * ctx) | Tier 1 | Set callback for connection events. |
core_ble_on_disconnect(void * ctx) | Tier 1 | Set callback for disconnection events. |
core_ble_set_tx_power(uint8_t level) | Tier 1 | TX power: 0=low(-20dBm), 1=medium(0dBm), 2=high(+10dBm). Default: 1. |
core_ble_set_adv_interval(uint16_t min_ms, uint16_t max_ms) | Tier 1 | Advertising interval in ms (20-10240). Default: 100/150. |
core_ble_set_conn_params(uint16_t min_ms, uint16_t max_ms, uint16_t latency, uint16_t timeout_ms) | Tier 1 | Request a preferred connection parameter set from the central. The central ultimately owns the connection timing, but a peripheral can ask for parameters that suit its traffic. After each connection the SDK sends an L2CAP update request (retrying until the stack accepts it, since the link is busy with pairing/discovery right after connecting). May also be called while connected to re-request — e.g. tighten the interval while streaming, relax it when idle. For Apple hosts, keep within their guidelines: interval_min >= 15 ms, interval_max >= interval_min + 15 ms, interval_max * (latency + 1) <= 2 s, and timeout in 2000..6000 ms. A short interval lowers latency for high-rate notifications at the cost of power; latency > 0 saves power when the peripheral often has nothing to send. |
core_ble_set_auto_mtu(uint8_t on) | Tier 1 | Turn the automatic ATT MTU exchange on or off. After each connection the SDK starts an ATT Exchange MTU request from the peripheral side, offering 251 bytes, so a central that never asks for a larger MTU (Android without requestMtu(), or a central with no way to ask, such as iOS or Web Bluetooth) still ends up with large notifications instead of the 23-byte default (20 bytes of payload). The request retries while the link is busy right after connecting, and is dropped for that connection if the stack refuses it (for example because the central already exchanged). The negotiated MTU is the smaller of both sides' offers. On by default. Turn it off only for a central that misbehaves when the peripheral starts the exchange. Takes effect from the next connection (or the next core_ble_process() pass, if an exchange is still pending). |
core_ble_enable_pairing(void) | Tier 1 | Enable OS-level pairing + bonding (Just Works). When enabled, every characteristic is registered behind an encrypted link, so on first access the host runs its pairing flow (a "Pair?" prompt on most OSes; silent for Just Works on macOS). Keys are persisted in flash NVM, so the bond survives resets — the host doesn't re-pair on the next connection and the device stays listed in OS Bluetooth settings. (Reconnect itself is the central's choice; the peripheral simply re-advertises after a drop.) Call before core_ble_init(). Default: disabled (characteristics open, no pairing). Note that once enabled, *all* clients must pair to read/write. |
Types
| Type | Kind | Description |
|---|---|---|
core_ble_svc_t | typedef | Service handle, returned by core_ble_add_service*().typedef uint16_t core_ble_svc_t; |
core_ble_char_t | typedef | Characteristic handle, returned by core_ble_add_char*().typedef uint16_t core_ble_char_t; |
core_ble_write_cb | callback | Write callback: called when the central writes to a characteristic.typedef void (*core_ble_write_cb)(const uint8_t *data, uint16_t len, void *ctx); |
Threadguide →
| Function | Access | Description |
|---|---|---|
core_thread_start(void) | Tier 1 | Start Thread: bring up the radio and OpenThread, then join (or form) the network from config.json. Returns: 0 on success (also when Thread is already running), or a negative CORE_THREAD_ERR_* code. |
core_thread_process(void) | Tier 1 | Run Thread: radio events, OpenThread timers and tasks, received datagrams. |
core_thread_role(void) | Tier 1 | The device's current Thread role. Returns: CORE_THREAD_DISABLED before core_thread_start(), else the role. |
core_thread_on_role_change(core_thread_role_cb cb, void * ctx) | Tier 1 | Register a callback for role changes (detached, child, router, leader). |
core_thread_udp_open(uint16_t port, core_thread_udp_rx_cb rx_cb, void * ctx) | Tier 1 | Open a UDP socket bound to a port and receive datagrams sent to it. Returns: 0 on success, CORE_THREAD_ERR_FULL when all sockets are taken, or another negative code. |
core_thread_udp_send(const uint8_t addr6[16], uint16_t port, const void * buf, uint16_t len) | Tier 1 | Send a UDP datagram to an IPv6 address on the Thread network. Returns: 0 when queued, CORE_THREAD_ERR_STATE when not attached, or another negative code. |
core_thread_udp_broadcast(uint16_t port, const void * buf, uint16_t len) | Tier 1 | Send a UDP datagram to every node on the Thread network (ff03::1, realm-local all-nodes). Returns: 0 when queued, CORE_THREAD_ERR_STATE when not attached, or another negative code. |
core_thread_rloc16(void) | Tier 1 | The device's RLOC16 (its short address in the mesh; changes with the role). Returns: The RLOC16, or 0xFFFE when not attached. |
core_thread_ext_addr(uint8_t out[8]) | Tier 1 | Copy the device's IEEE 802.15.4 extended address (8 bytes). |
core_thread_partition_id(void) | Tier 1 | The ID of the Thread partition the device belongs to. Returns: The partition ID, or 0 when not attached. |
core_thread_neighbor_count(void) | Tier 1 | Number of neighbours in the device's neighbour table (routers and children it hears). Returns: The neighbour count. |
core_thread_parent_rssi(void) | Tier 1 | Average RSSI of frames from the parent, when the device is a child. Returns: RSSI in dBm, or 127 when there is no parent (not a child). |
core_thread_last_error(void) | Tier 1 | The last error OpenThread reported to this API (an otError value; 0 = none). Returns: The otError code (see openthread/error.h). |
core_thread_instance(void) | Tier 1 | The OpenThread instance, for C code that calls the OpenThread API directly. Returns: An otInstance pointer (as void*), or NULL before core_thread_start(). |
core_thread_get_network(core_thread_dataset_t * out) | Tier 1 | Read the network the device is set to (its active dataset), without the network key. Returns: 0 on success, CORE_THREAD_ERR_STATE before core_thread_start(), or another negative code. |
core_thread_set_enabled(int on) | Tier 1 | Stop or restart Thread: leave the network (detach, radio idle for Thread) or attach again. Returns: 0 on success, CORE_THREAD_ERR_STATE before core_thread_start(), or another negative code. |
core_thread_set_dataset(const core_thread_dataset_t * ds) | Tier 1 | Move the device to another Thread network and store it, so it wins over config.json from now on. Returns: 0 on success, CORE_THREAD_ERR_ARG for a bad dataset, CORE_THREAD_ERR_STATE before core_thread_start(), CORE_THREAD_ERR_UNSUPPORTED without "provisioning" in config.json, or another negative code. |
core_thread_factory_reset(void) | Tier 1 | Forget the stored (provisioned) network and return to config.json's, then attach again. Returns: 0 on success, CORE_THREAD_ERR_STATE before core_thread_start(), CORE_THREAD_ERR_UNSUPPORTED without "provisioning" in config.json, or another negative code. |
core_thread_is_provisioned(void) | Tier 1 | Is the current network a stored (provisioned) one rather than config.json's factory default? Returns: 1 when a provisioned network is in use, 0 when it is config.json's (always 0 before core_thread_start() and in builds without "provisioning"). |
core_thread_prov_allow_write(void) | Tier 1 | Hook: may a BLE client change the network now? Override to gate the provisioning service. Returns: Nonzero to accept writes to the BLE provisioning service, 0 to refuse them (THREAD_PROV_ERR_DENIED). The SDK's default (a weak function) returns 1: open writes. |
Types
| Type | Kind | Description |
|---|---|---|
core_thread_role_t | enum | Device role (the values of OpenThread's otDeviceRole).
|
core_thread_role_cb | callback | Role-change callback: the new role. Runs from core_thread_process().typedef void (*core_thread_role_cb)(core_thread_role_t role, void *ctx); |
core_thread_udp_packet_t | struct | A received UDP datagram, as handed to a core_thread_udp_rx_cb. One struct instead of separate arguments on purpose: the old (data, len, src, port, ctx) form had two pointer and two uint16_t parameters, so a callback with them in the wrong order compiled silently and read the sender's address as the payload. Valid only during the callback: copy what you keep.
|
core_thread_udp_rx_cb | callback | UDP receive callback. Runs from core_thread_process(). Reply with core_thread_udp_send(pkt->src, pkt->src_port, ...).typedef void (*core_thread_udp_rx_cb)(const core_thread_udp_packet_t *pkt, void *ctx); |
core_thread_dataset_t | struct | A Thread network: the operational dataset fields config.json sets. Byte strings are in the order they are written down (an ext PAN ID of dead00beef00cafe is { 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe }).
|
Constants
| Name | Value | Description |
|---|---|---|
CORE_THREAD_ERR_STATE | (-1) | Not started, or not attached |
CORE_THREAD_ERR_ARG | (-2) | Bad argument |
CORE_THREAD_ERR_NOBUFS | (-3) | Out of message buffers |
CORE_THREAD_ERR_FULL | (-4) | No free UDP socket |
CORE_THREAD_ERR_FAILED | (-5) | OpenThread refused; see core_thread_last_error() |
CORE_THREAD_ERR_UNSUPPORTED | (-6) | Not in this build: needs config.json "thread": {"provisioning": true} |
CORE_THREAD_UDP_SOCKETS | 4 | Sockets core_thread_udp_open() can hold |
CORE_THREAD_UDP_MAX_PAYLOAD | 256 | Received datagrams are cut to this many bytes |
Scope (live streaming)guide →
| Function | Access | Description |
|---|---|---|
core_scope_init(void) | Tier 1 | Start the module on the Core's default link. |
core_scope_set_link(const core_scope_link_t * link) | Tier 1 | Stream over a different link than the default. |
core_scope_add(const char * name, const volatile void * ptr, core_scope_type_t type) | Tier 1 | Register one variable as a channel. Returns: 0 on success, -1 if the table or the schema frame is full. |
core_scope_add_array(const char * name, const volatile void * ptr, core_scope_type_t type, uint8_t count) | Tier 1 | Register a fixed array as channels `name[0]` .. `name[count-1]`. Returns: 0 on success, -1 if they do not all fit (none are added). |
core_scope_set_interval_ms(uint32_t ms) | Tier 1 | Limit core_scope_update() to one sample per `ms` milliseconds. |
core_scope_declare_rate_hz(uint32_t hz) | Tier 1 | Promise that samples are taken at exactly this rate. |
core_scope_declare_rate_millihertz(uint32_t millihertz) | Tier 1 | As core_scope_declare_rate_hz(), in millihertz — for rates that are not a whole number of hertz (an IMU at 416.667 Hz = 416667). |
core_scope_sample(void) | Tier 1 | Snapshot the registered variables. ISR-safe. |
core_scope_pump(void) | Tier 1 | Frame the pending samples and hand them to the link. Main loop. |
core_scope_update(void) | Tier 1 | core_scope_sample() + core_scope_pump(), honoring the interval gate. The one call a simple main loop needs. |
core_scope_enable(int on) | Tier 1 | Pause or resume at run time (a debug button, a BLE command). |
core_scope_active(void) | Tier 1 | Is a host listening right now? Returns: Non-zero while samples are being streamed. |
core_scope_dropped(void) | Tier 1 | Samples dropped since boot because the ring was full. Returns: Running count; wraps at 2^32. |
Types
| Type | Kind | Description |
|---|---|---|
core_scope_type_t | enum | Channel value types — the wire type codes of docs/scope-protocol.md.
|
core_scope_link_t | struct | A link carries the framed byte stream to the host. USB and BLE ship with the SDK; anything with a write function (a UART, a radio module) can be a link too — see core_scope_set_link().
|
Macros
| Macro | Description |
|---|---|
core_scope_watch(var) | Register `var` as a channel named after itself. |
core_scope_watch_as(name, var) | Register `var` under a display name of your choosing. |
core_scope_watch_array(arr, count) | Register the first `count` elements of array `arr` as `arr[0]`, `arr[1]`, … |

