# Bergsonne Labs — full documentation corpus > Bergsonne Tiles are millimeter-scale, fully-encapsulated microelectronic modules — each a complete subsystem behind a standard pad interface. Composable across processing, sensing, actuation, power, memory, and communication, Tiles bring embedded intelligence to physical AI, from ideation to benchtop prototype to mass production. Bergsonne Studio is the browser IDE that composes and programs them. This file inlines every page that is generated from our manifests: tile driver references, the SDK reference, and the pad / I²C lookup tables. Hand-authored guides are not included — see /llms.txt for those. Tiles are identified as `Family.Name` (`Sense.BP`, `Drive.H`). Each driver section carries a **Driver gaps** list: chip capabilities the driver does NOT expose, tagged hardware-gated or driver-deferred. Trust it — do not infer an API from the chip datasheet alone. Generated 2026-08-12 from tiles@842d43e. ## Contents - Core.ST.H5.1 datasheet — https://bergsonne.io/catalog/core/sth51 - Core.ST.L0.1 datasheet — https://bergsonne.io/catalog/core/stl01 - Core.ST.L4.1 datasheet — https://bergsonne.io/catalog/core/stl41 - Core.ST.L4.2 datasheet — https://bergsonne.io/catalog/core/stl42 - Core.ST.W5 datasheet — https://bergsonne.io/catalog/core/stw5 - Display.RGBW datasheet — https://bergsonne.io/catalog/display/rgbw - Drive.A.2 datasheet — https://bergsonne.io/catalog/drive/a2 - Drive.DC.H datasheet — https://bergsonne.io/catalog/drive/dch - Drive.H datasheet — https://bergsonne.io/catalog/drive/h - Drive.P datasheet — https://bergsonne.io/catalog/drive/p - Power.BB datasheet — https://bergsonne.io/catalog/power/bb - Power.L.1N datasheet — https://bergsonne.io/catalog/power/l1n - Power.L.1T datasheet — https://bergsonne.io/catalog/power/l1t - Sense.ACP datasheet — https://bergsonne.io/catalog/sense/acp - Sense.I.6P6 datasheet — https://bergsonne.io/catalog/sense/i6p6 - Sense.I.9 datasheet — https://bergsonne.io/catalog/sense/i9 - Sense.MIC datasheet — https://bergsonne.io/catalog/sense/mic - Sense.T.C datasheet — https://bergsonne.io/catalog/sense/tc - Sense.TOF datasheet — https://bergsonne.io/catalog/sense/tof - Store.O.128 datasheet — https://bergsonne.io/catalog/store/o128 - Display.RGBW driver — https://bergsonne.io/docs/tiles/display/rgbw - Drive.A.2 driver — https://bergsonne.io/docs/tiles/drive/a2 - Drive.DC.H driver — https://bergsonne.io/docs/tiles/drive/dch - Drive.H driver — https://bergsonne.io/docs/tiles/drive/h - Drive.P driver — https://bergsonne.io/docs/tiles/drive/p - Power.L.1N driver — https://bergsonne.io/docs/tiles/power/l1n - Power.L.1T driver — https://bergsonne.io/docs/tiles/power/l1t - Sense.ACP driver — https://bergsonne.io/docs/tiles/sense/acp - Sense.BP driver — https://bergsonne.io/docs/tiles/sense/bp - Sense.CAM.P driver — https://bergsonne.io/docs/tiles/sense/camp - Sense.CAP driver — https://bergsonne.io/docs/tiles/sense/cap - Sense.I.6P6 driver — https://bergsonne.io/docs/tiles/sense/i6p6 - Sense.I.9 driver — https://bergsonne.io/docs/tiles/sense/i9 - Sense.M.3G driver — https://bergsonne.io/docs/tiles/sense/m3g - Sense.MIC driver — https://bergsonne.io/docs/tiles/sense/mic - Sense.T.C driver — https://bergsonne.io/docs/tiles/sense/tc - Sense.TOF driver — https://bergsonne.io/docs/tiles/sense/tof - Store.O.128 driver — https://bergsonne.io/docs/tiles/store/o128 - Analog (ADC / DAC) — SDK reference — https://bergsonne.io/docs/sdk/adc - Audio (PDM mic) — SDK reference — https://bergsonne.io/docs/sdk/audio - BLE — SDK reference — https://bergsonne.io/docs/sdk/ble - Clocks — SDK reference — https://bergsonne.io/docs/sdk/clocks - GPIO — SDK reference — https://bergsonne.io/docs/sdk/gpio - I2C — SDK reference — https://bergsonne.io/docs/sdk/i2c - Onboard LED — SDK reference — https://bergsonne.io/docs/sdk/led - Power — SDK reference — https://bergsonne.io/docs/sdk/power - SPI — SDK reference — https://bergsonne.io/docs/sdk/spi - Timers & PWM — SDK reference — https://bergsonne.io/docs/sdk/timers - UART — SDK reference — https://bergsonne.io/docs/sdk/uart - USB — SDK reference — https://bergsonne.io/docs/sdk/usb - Tile I²C addresses — https://bergsonne.io/resources/i2c-addresses - Tile pad assignments — https://bergsonne.io/resources/pad-assignments ============================================================================== ## Core.ST.H5.1 > 250MHz Cortex-M33 | | | | --- | --- | | Tile | `Core.ST.H5.1` | | Revision | `a` | | Family | Core | | Package | T44 · 14 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.41` | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-H5-1-a-0-41.pdf | | Canonical page | https://bergsonne.io/catalog/core/sth51 | ### Overview The Core.ST.H5.1 is built around the high-performance STM32H523 250-MHz 32-bit Cortex-M33 processor into a single T44 tile, providing a user-configurable combination of multiple communication interfaces (USB 2.0 full-speed, I2C/I3C, SPI, FDCAN, and UART), along with two 12-bit 5-Msps ADC inputs, one DAC output, and multiple timers. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ST Microelectronics | `STM32H523HE` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/11/ST_Microelectronics-STM32H523HE.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.71 | 3.6 | | +10, GND 1 | yes | ### Interfaces #### I2C1 (I2C) - Modes: master, slave - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C1.CLK` | bus | yes | | 5 | `I2C1.DAT` | bus | yes | #### I2C3 (I2C) - Modes: master, slave - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `I2C3.CLK` | bus | yes | | 3 | `I2C3.DAT` | bus | yes | #### SPI1 (SPI) - Modes: master - Max clock: 45MHz | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `SPI1.RDY` | bus | no | | 3 | `SPI1.MISO` | bus | yes | | 8 | `SPI1.MOSI` | bus | yes | | 9 | `SPI1.CLK` | bus | yes | #### UART4 (UART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `UART4.RX` | bus | no | | 6 | `UART4.TX` | bus | no | | 7 | `UART4.RX` | bus | no | #### FDCAN1 (FD CAN) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `FDCAN1.RX` | bus | no | | 5 | `FDCAN1.TX` | bus | no | | 6 | `FDCAN1.TX` | bus | no | | 7 | `FDCAN1.RX` | bus | no | #### USB (USB) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 6 | `USB.DP` | bus | yes | | 7 | `USB.DM` | bus | yes | | 2 | `USB.SOF` | bus | no | #### I3C2 (I3C) - Modes: master, slave | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `I3C2.CLK` | bus | yes | | 3 | `I3C2.DAT` | bus | yes | #### I3C1 (I3C) - Modes: master, slave | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I3C1.CLK` | bus | yes | | 5 | `I3C1.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `A8` | digital | `USB.SOF`, `I2C3.CLK`, `I3C2.CLK`, `SPI1.RDY`, `TIM1.1`, `TIM8.BKIN2` | | 3 | `B4` | digital | `I2C3.DAT`, `I3C2.DAT`, `SPI1.MISO`, `TIM3.1`, `LPTIM1.2` | | 4 | `B8` | digital | `I2C1.CLK`, `I3C1.CLK`, `TIM4.3`, `UART4.RX`, `FDCAN1.RX` | | 5 | `B7` | digital | `I2C1.DAT`, `I3C1.DAT`, `TIM4.2`, `FDCAN1.TX` | | 6 | `A12` | digital | `USB.DP`, `TIM1.ETR`, `UART4.TX`, `FDCAN1.TX` | | 7 | `A11` | digital | `USB.DM`, `TIM1.4`, `UART4.RX`, `FDCAN1.RX` | | 8 | `A7` | digital | `SPI1.MOSI`, `ADC7+`, `ADC3-`, `TIM1.1N`, `TIM3.2`, `TIM8.1N` | | 9 | `A5` | digital | `SPI1.CLK`, `ADC19+`, `ADC18-`, `TIM2.1`, `TIM8.1N`, `TIM2.ETR`, `DAC1.OUT` | | 10 | `V+` | power | — | | 11 | `BOOT0` | system | — | | 12 | `NRST` | system | — | | 13 | `A14` | digital | `SWCLK` | | 14 | `A13` | digital | `SWDIO` | ### Application notes #### USB 2.0 Full-Speed Port To utilize the USB 2.0 Full-Speed (12Mbit/s) port, the supply voltage needs to be at least 3.0V. The Core.H.1 can serve as either a peripheral or a host. #### USB Bootloading Similar to the Core.U tiles, when the chip is blank, it will default into the bootloader when connected over USB. Once there is code in the program space, you need to hold the BOOT0 pin HIGH during reset (either power-on or via the NRST pin) to enter the bootloader. #### Single-Wire Debug & Bootloading The single-wire debug port is available on pads 13 (SWCLK) and 14 (SWDIO). While not absolutely required, it is often helpful to have the ability to hold pad 12 (NRST) low when connecting to the debugger. You can likely also use BOOT0 to help the debugger connect. #### LED The onboard LED is connected to PA15 in an active-high configuration. --- Generated 2026-08-12 from the canonical tile definition (version 0.41). Canonical HTML: https://bergsonne.io/catalog/core/sth51 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-H5-1-a-0-41.pdf ============================================================================== ## Core.ST.L0.1 > ultra-low-power Cortex-M0+ | | | | --- | --- | | Tile | `Core.ST.L0.1` | | Revision | `a` | | Family | Core | | Package | T44 · 14 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.33` | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-L0-1-a-0-33.pdf | | Canonical page | https://bergsonne.io/catalog/core/stl01 | ### Overview Ultra-low-power ARM Cortex-M0+ core tile built around the STM32L011E4 with 16KB flash, 2KB RAM, and 512 bytes of data EEPROM. Optimized for battery-powered and energy-harvesting applications, with flexible I/O across 14 pads including I2C, USART, and LPUART interfaces, 6 ADC inputs, multiple timer channels, and an analog comparator. Runs from 1.8–3.6V with bootloading and hardware debug via SWD. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ST Microelectronics | `STM32L011E4` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/12/ST_Microelectronics-STM32L011.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 3.6 | | +10, GND 1 | yes | ### Interfaces #### I2C1 (I2C) - Modes: master, slave - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C1.CLK` | bus | no | | 5 | `I2C1.DAT` | bus | no | | 6 | `I2C1.SMBA` | bus | no | | 13 | `I2C1.SMBA` | bus | no | | 14 | `I2C1.DAT` | bus | no | #### USART2 (USART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `USART2.RX` | bus | no | | 3 | `USART2.RX` | bus | no | | 3 | `USART2.CRS` | bus | no | | 4 | `USART2.TX` | bus | no | | 5 | `USART2.RX` | bus | no | | 6 | `USART2.RTS_DE` | bus | no | | 7 | `USART2.TX` | bus | no | | 9 | `USART2.RTS_DE` | bus | no | | 13 | `USART2.TX` | bus | no | #### LPUART1 (LPUART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `LPUART1.RX` | bus | no | | 3 | `LPUART1.RX` | bus | no | | 4 | `LPUART1.TX` | bus | no | | 5 | `LPUART1.RX` | bus | no | | 6 | `LPUART1.TX` | bus | no | | 7 | `LPUART1.TX` | bus | no | | 13 | `LPUART1.TX` | bus | no | | 14 | `LPUART1.RX` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `A3` | digital | `ADC3`, `TIM21.2`, `TIM2.4`, `USART2.RX`, `LPUART1.RX` | | 3 | `A0` | digital | `ADC0`, `TIM2.1`, `TIM2.ETR`, `LPTIM1.1`, `USART2.RX`, `USART2.CRS`, `LPUART1.RX`, `COMP1.OUT`, `COMP1.IN-` | | 4 | `B6` | digital | `I2C1.CLK`, `TIM2.3`, `LPTIM1.ETR`, `USART2.TX`, `LPUART1.TX` | | 5 | `B7` | digital | `I2C1.DAT`, `TIM2.4`, `LPTIM1.2`, `USART2.RX`, `LPUART1.RX` | | 6 | `A1` | digital | `I2C1.SMBA`, `ADC1`, `TIM2.2`, `TIM21.ETR`, `LPTIM1.2`, `USART2.RTS_DE`, `LPUART1.TX`, `COMP1.IN+` | | 7 | `A2` | digital | `ADC2`, `TIM21.1`, `TIM2.3`, `USART2.TX`, `LPUART1.TX` | | 8 | `A5` | digital | `ADC5`, `TIM2.1`, `TIM2.ETR`, `LPTIM1.2`, `COMP1.IN-` | | 9 | `B0` | digital | `ADC8`, `TIM2.2`, `TIM2.3`, `USART2.RTS_DE` | | 10 | `V+` | power | — | | 11 | `B9` | digital | `BOOT0` | | 12 | `NRST` | system | — | | 13 | `A14` | digital | `I2C1.SMBA`, `LPTIM1.OUT`, `USART2.TX`, `LPUART1.TX`, `SWCLK` | | 14 | `A13` | digital | `I2C1.DAT`, `LPTIM1.ETR`, `LPUART1.RX`, `COMP1.OUT`, `SWDIO` | ### Application notes #### Single-Wire Debug & Bootloading The single-wire debug port is available on pads 13 (SWCLK) and 14 (SWDIO). While not absolutely required, it is often helpful to have the ability to hold pads 22 (NRST) low when connecting to the debugger. You can likely also use BOOT0 to help the debugger connect. #### LED The onboard LED is connected to PA8 in an active-high configuration. --- Generated 2026-08-12 from the canonical tile definition (version 0.33). Canonical HTML: https://bergsonne.io/catalog/core/stl01 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-L0-1-a-0-33.pdf ============================================================================== ## Core.ST.L4.1 > USB-programmable Cortex-M4 | | | | --- | --- | | Tile | `Core.ST.L4.1` | | Revision | `b` | | Family | Core | | Package | T44 · 14 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.40` | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-L4-1-b-0-40.pdf | | Canonical page | https://bergsonne.io/catalog/core/stl41 | ### Overview The USB-programmable Core.ST.L4.1 is based on the 80-MHz 32-bit Cortex-M4 STM32L422 with 128KB flash, 40KB SRAM, and a CoreMark score of 273.55. The exposed pads provide a user-configurable combination of two fast-mode-plus I2C ports, one SPI port, three 12-bit ADCs, multiple timers, USART, an onboard comparator, and capacitive-touch input. A programmable onboard LED provides a convenient status indicator. If more I/O is needed, consider the larger Core.ST.L4.2 in a T48-22 package. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ST Microelectronics | `STM32L422TB` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/1/ST_Microelectronics-STM32L422TB.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.71 | 3.6 | | +10, GND 1 | yes | ### Interfaces #### I2C1 (I2C) - Modes: master, slave - Max clock: 1MHz - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 3 | `I2C1.SMBA` | bus | no | | 4 | `I2C1.CLK` | bus | yes | | 5 | `I2C1.DAT` | bus | yes | #### I2C3 (I2C) - Modes: master, slave - Max clock: 1MHz - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `I2C3.CLK` | bus | yes | | 8 | `I2C3.DAT` | bus | yes | #### SPI1 (SPI) - Modes: master, slave - Max clock: 40Mhz (master), 20MHz (slave) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 3 | `SPI1.CLK` | bus | yes | | 7 | `SPI1.MISO` | bus | no | | 8 | `SPI1.MISO` | bus | no | | 9 | `SPI1.CS` | other | no | | 2 | `SPI1.MOSI` | bus | no | | 6 | `SPI1.MOSI` | bus | no | #### USB (USB) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 6 | `USB.DP` | bus | yes | | 7 | `USB.DM` | bus | yes | #### USART1 (USART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `USART1.TX` | bus | yes | | 5 | `USART1.RX` | bus | yes | | 6 | `USART1.RTS_DE` | bus | no | | 7 | `USART1.CTS` | bus | no | | 8 | `USART1.CTS` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `A7` | digital | `I2C3.CLK`, `SPI1.MOSI`, `ADC12`, `TIM1.1N` | | 3 | `A1` | digital | `I2C1.SMBA`, `SPI1.CLK`, `ADC6`, `TIM2.2`, `TIM15.1N`, `COMP1.+` | | 4 | `B6` | digital | `I2C1.CLK`, `TIM16.1N`, `LPTIM1.ETR`, `USART1.TX`, `G2.IO3` | | 5 | `B7` | digital | `I2C1.DAT`, `LPTIM1.2`, `USART1.RX`, `G2.IO4` | | 6 | `A12` | digital | `SPI1.MOSI`, `TIM1.ETR`, `USART1.RTS_DE`, `USB.DP` | | 7 | `A11` | digital | `SPI1.MISO`, `TIM1.4`, `TIM1.BKIN2`, `TIM1.BKIN2_COMP1`, `USART1.CTS`, `COMP1.OUT`, `USB.DM` | | 8 | `B4` | digital | `I2C3.DAT`, `SPI1.MISO`, `USART1.CTS`, `G2.IO1` | | 9 | `A4` | digital | `SPI1.CS`, `LPTIM2.OUT`, `COMP1.-` | | 10 | `V+` | power | — | | 11 | `BOOT0` | system | `H3` | | 12 | `NRST` | system | — | | 13 | `A14` | digital | `SWCLK`, `I2C1.SMBA`, `LPTIM1.OUT` | | 14 | `A13` | digital | `SWDIO` | ### Application notes #### Bootloading When the chip is blank, it will default into the bootloader when connected over USB. Once there is code in the program space, you need to hold the BOOT0 pin HIGH during reset (either power-on or via the NRST pin) to enter the bootloader. #### LED The onboard LED is connected to PA8 in an active-high configuration. #### Single-Wire Debug & Bootloading The single-wire debug port is available on pads 13 (SWCLK) and 14 (SWDIO). While not absolutely required, it is often helpful to have the ability to hold pads 22 (NRST) low when connecting to the debugger. You can likely also use BOOT0 to help the debugger connect. --- Generated 2026-08-12 from the canonical tile definition (version 0.40). Canonical HTML: https://bergsonne.io/catalog/core/stl41 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-L4-1-b-0-40.pdf ============================================================================== ## Core.ST.L4.2 > expanded-I/0 USB-prog. Cortex-M4 | | | | --- | --- | | Tile | `Core.ST.L4.2` | | Revision | `a` | | Family | Core | | Package | T48 · 22 pads | | Dimensions | 4 mm × 8 mm | | Definition version | `0.61` | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-L4-2-a-0-61.pdf | | Canonical page | https://bergsonne.io/catalog/core/stl42 | ### Overview The expanded-I/O USB-programmable Core.U.2 is based on the same 80-MHz Cortex-M4 STM32L422 as the smaller Core.ST.L4.1, with the top 12 pads (1-5 and 16-22) matching the pad layout of the Core.U.1. The additional I/O pads provide a user-configurable combination of single-wire and trace debugging, two I2C ports, one SPI port, one quad SPI port, ten 12-bit ADC inputs, multiple timers, capacitive-touch inputs, and an on-board comparator. The board also includes an onboard LED and a physical button for reset and bootloading (see Application Notes). ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ST Microelectronics | `STM32L422TB` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/7/ST_Microelectronics-STM32L422TB.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 3.6 | | +20, GND 1 | yes | ### Interfaces #### I2C1 (I2C) - Modes: master, slave - Max clock: 1MHz - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 3 | `I2C1.SMBA` | bus | no | | 4 | `I2C1.CLK` | bus | yes | | 5 | `I2C1.DAT` | bus | yes | | 13 | `I2C1.SMBA` | bus | no | #### I2C3 (I2C) - Modes: master, slave - Max clock: 1MHz - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `I2C3.CLK` | bus | yes | | 18 | `I2C3.DAT` | bus | yes | #### SPI1 (SPI) - Modes: master, slave - Max clock: 40Mhz (master), 20MHz (slave) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 3 | `SPI1.CLK` | bus | no | | 2 | `SPI1.MOSI` | bus | no | | 9 | `SPI1.MISO` | bus | no | | 10 | `SPI1.CLK` | bus | no | | 11 | `SPI1.CS` | other | no | | 12 | `SPI1.CLK` | bus | no | | 16 | `SPI1.MOSI` | bus | no | | 17 | `SPI1.MISO` | bus | no | | 18 | `SPI1.MISO` | bus | no | | 19 | `SPI1.CS` | other | no | #### USB (USB) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 16 | `USB.DP` | bus | yes | | 17 | `USB.DM` | bus | yes | #### USART1 (USART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `USART1.TX` | bus | yes | | 5 | `USART1.RX` | bus | yes | | 16 | `USART1.RTS_DE` | bus | no | | 17 | `USART1.CTS` | bus | no | | 18 | `USART1.CTS` | bus | no | | 12 | `USART1.RTS_DE` | bus | no | #### USART2 (USART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 3 | `USART2.RTS_DE` | bus | no | | 6 | `USART2.RX` | bus | no | | 7 | `USART2.TX` | bus | no | | 8 | `USART2.CTS` | bus | no | #### LPUART1 (LPUART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 6 | `LPUART1.RX` | bus | no | | 7 | `LPUART1.TX` | bus | no | | 9 | `LPUART1.CTS` | bus | no | | 15 | `LPUART1.RTS_DE` | bus | no | #### QSPI (QSPI) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `QSPI.IO2` | bus | yes | | 6 | `QSPI.CLK` | bus | yes | | 7 | `QSPI.CS` | other | yes | | 9 | `QSPI.IO3` | bus | yes | | 11 | `QSPI.IO1` | bus | yes | | 15 | `QSPI.IO0` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `A7` | digital | `I2C3.CLK`, `SPI1.MOSI`, `ADC12`, `TIM1.1N`, `QSPI.IO2` | | 3 | `A1` | digital | `I2C1.SMBA`, `SPI1.CLK`, `ADC6`, `TIM2.2`, `TIM15.1N`, `COMP1.+`, `USART2.RTS_DE` | | 4 | `B6` | digital | `I2C1.CLK`, `TIM16.1N`, `LPTIM1.ETR`, `USART1.TX`, `G2.IO3`, `TRACE.D3` | | 5 | `B7` | digital | `I2C1.DAT`, `LPTIM1.2`, `USART1.RX`, `TRACE.CK`, `G2.IO4` | | 6 | `A3` | digital | `QSPI.CLK`, `ADC8`, `TIM2.4`, `TIM15.2`, `USART2.RX`, `LPUART1.RX` | | 7 | `A2` | digital | `QSPI.CS`, `ADC7`, `TIM2.3`, `TIM15.1`, `USART2.TX`, `LPUART1.TX` | | 8 | `A0` | digital | `ADC5`, `TIM2.1`, `TIM2.ETR`, `USART2.CTS`, `COMP1.IN-`, `COMP1.OUT` | | 9 | `A6` | digital | `SPI1.MISO`, `QSPI.IO3`, `ADC11`, `TIM1.BKIN`, `TIM16.1`, `LPUART1.CTS`, `COMP1.OUT` | | 10 | `A5` | digital | `SPI1.CLK`, `ADC10`, `TIM2.1`, `TIM2.ETR`, `LPTIM2.ETR`, `COMP1.IN-` | | 11 | `B0` | digital | `TRACE.D1`, `SPI1.CS`, `QSPI.IO1`, `ADC15`, `TIM1.2N`, `COMP1.OUT` | | 12 | `B3` | digital | `TRACE.SWO`, `SPI1.CLK`, `TIM2.2`, `USART1.RTS_DE` | | 13 | `A14` | digital | `SWCLK`, `I2C1.SMBA`, `LPTIM1.OUT` | | 14 | `A13` | digital | `SWDIO` | | 15 | `B1` | digital | `TRACE.D1`, `QSPI.IO0`, `ADC16`, `TIM1.3N`, `LPTIM2.1`, `LPUART1.RTS_DE` | | 16 | `A12` | digital | `SPI1.MOSI`, `TIM1.ETR`, `USART1.RTS_DE`, `USB.DP` | | 17 | `A11` | digital | `SPI1.MISO`, `TIM1.4`, `TIM1.BKIN2`, `TIM1.BKIN2_COMP1`, `USART1.CTS`, `COMP1.OUT`, `USB.DM`, `G2.IO1` | | 18 | `B4` | digital | `I2C3.DAT`, `SPI1.MISO`, `USART1.CTS`, `G2.IO1` | | 19 | `A4` | digital | `SPI1.CS`, `LPTIM2.OUT`, `COMP1.-` | | 20 | `V+` | power | — | | 21 | `BOOT0` | system | `PH3` | | 22 | `NRST` | system | — | ### Application notes #### USB Bootloading Similar to the Core.U.1, when the chip is blank, it will default into the bootloader when connected over USB. Once there is code in the program space, you need to hold the BOOT0 pin low during reset (either power-on or via the NRST pin) to enter the bootloader. The physical button will also execute this behavior. #### Single-Wire Debug & Bootloading The single-wire debug port is available on pads 13 (SWCLK) and 14 (SWDIO). While not absolutely required, it is often helpful to have the ability to hold pads 22 (NRST) low when connecting to the debugger. You can likely also use BOOT0 (via either the pad or the button) to help the debugger connect. #### Button The board button will issue a software reset via the NRST pin when pressed for a short (less than ~1sec) time. When held for more than ~2 seconds and released, the system will enter the bootloader. #### LED The onboard LED is connected to PA8 in an active-high configuration. --- Generated 2026-08-12 from the canonical tile definition (version 0.61). Canonical HTML: https://bergsonne.io/catalog/core/stl42 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-L4-2-a-0-61.pdf ============================================================================== ## Core.ST.W5 > BLE-enabled 100MHz Cortex M33 | | | | --- | --- | | Tile | `Core.ST.W5` | | Revision | `b` | | Family | Core | | Package | T48 · 16 pads | | Dimensions | 4 mm × 8 mm | | Definition version | `0.39` | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-W5-b-0-39.pdf | | Canonical page | https://bergsonne.io/catalog/core/stw5 | ### Overview The Core.W tile combines the powerful STM32WBA55 100-MHz 32-bit Cortex-M33 processor together with an integrated 2.4GHz antenna on double-size T48 (4.0 x 8.0 mm) SMD package. The tile provides a user-configurable combination of: SW debug & bootloading; 1MB flash & 128KB SRAM; wireless communications (BLE 5.4, Thread, Matter, Zigbee, proprietary); two FM+ (1MHz) I2C ports; two SPI ports; one USART; five 12-bit 2.5Msps ADC inputs (16-bit with oversampling); many timers; ten capacitive touch inputs; one serial-audio interface; IR output; and an on-board LED ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ST Microelectronics | `STM32WBA55HGF6` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/8/ST_Microelectronics-STM32WBA55.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 3.6 | | +14, GND 1 | yes | ### Interfaces #### I2C1 (I2C) - Modes: master, slave - Max clock: 1MHz - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 10 | `I2C1.CLK` | bus | yes | | 11 | `I2C1.DAT` | bus | yes | #### I2C3 (I2C) - Modes: master, slave - Max clock: 1MHz - Addresses: `programmable` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C3.CLK` | bus | yes | | 5 | `I2C3.DAT` | bus | yes | #### SPI1 (SPI) - Modes: master, slave | Pad | Function | Role | Required | | --- | --- | --- | --- | | 8 | `SPI1.CS` | other | no | | 9 | `SPI1.CLK` | bus | no | | 10 | `SPI1.MOSI` | bus | no | | 11 | `SPI1.MISO` | bus | no | #### SPI3 (SPI) - Modes: master, slave | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `SPI3.CLK` | bus | no | | 3 | `SPI3.CS` | other | no | | 6 | `SPI3.MOSI` | bus | no | | 7 | `SPI3.MISO` | bus | no | #### USART2 (USART) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 6 | `USART2.RX` | bus | no | | 8 | `USART2.TX` | bus | no | | 9 | `USART2.RX` | bus | no | | 10 | `USART2.RTS_DE` | bus | no | | 11 | `USART2.CK` | bus | no | | 13 | `USART2.TX` | bus | no | #### SAI1 (SAI) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 3 | `SAI1.D2` | bus | no | | 4 | `SAI1.CK2` | bus | no | | 4 | `SAI1.MCLK_A` | bus | no | | 5 | `SAI1.SCK_A` | bus | no | | 9 | `SAI1.MCLK_B` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `A0` | digital | `SPI3.CLK`, `ADC9`, `TIM1.2N`, `TIM3.3`, `TIM3.ETR`, `LPTIM1.IN1`, `G2.IO2` | | 3 | `A5` | digital | `SPI3.CS`, `ADC4`, `TIM2.1`, `LPTIM2.ETR`, `SAI1.D2`, `G1.IO4` | | 4 | `A6` | digital | `I2C3.CLK`, `ADC3`, `TIM2.4`, `SAI1.CK2`, `SAI1.MCLK_A`, `G1.IO3` | | 5 | `A7` | digital | `I2C3.DAT`, `ADC2`, `TIM2.3`, `SAI1.SCK_A`, `G1.IO2` | | 6 | `B8` | digital | `SPI3.MOSI`, `TIM1.1`, `TIM3.ETR`, `TIM16.1N`, `LPTIM1.ETR`, `USART2.RX`, `G2.IO4` | | 7 | `B9` | digital | `SPI3.MISO`, `ADC10`, `TIM1.3N`, `TIM3.4`, `TIM16.1`, `LPTIM2.IN1`, `G2.IO3`, `IR_OUT` | | 8 | `A12` | digital | `SPI1.CS`, `TIM1.2`, `USART2.TX`, `G3.IO4` | | 9 | `B4` | digital | `SPI1.CLK`, `TIM1.3`, `TIM17.1`, `LPTIM2.IN2`, `USART2.RX`, `SAI1.MCLK_B`, `G3.IO1` | | 10 | `A15` | digital | `I2C1.CLK`, `SPI1.MOSI`, `TIM1.ETR`, `LPTIM1.2`, `USART2.RTS_DE`, `TIM17.BKIN` | | 11 | `B3` | digital | `I2C1.DAT`, `SPI1.MISO`, `TIM1.4`, `TIM17.1N`, `LPTIM1.IN2`, `USART2.CK`, `G3.IO2` | | 12 | `A13` | digital | `SWDIO`, `TIM17.BKIN`, `G3.IO3` | | 13 | `A14` | digital | `SWCLK`, `USART2.TX` | | 14 | `V+` | power | — | | 15 | `BOOT0` | system | `H3` | | 16 | `NRST` | system | — | ### Application notes #### Single-Wire Debug & Bootloading The single-wire debug port is available on pads 13 (SWCLK) and 14 (SWDIO). While not absolutely required, it is often helpful to have the ability to hold pads 22 (NRST) low when connecting to the debugger. You can likely also use BOOT0 to help the debugger connect. #### Antenna Clearance For the best wireless performance, the antenna-end of the Tile (the end without any pads) should not have any copper traces or planes above or below it. #### LED The onboard LED is connected to PB12 in an active-high configuration. --- Generated 2026-08-12 from the canonical tile definition (version 0.39). Canonical HTML: https://bergsonne.io/catalog/core/stw5 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Core-ST-W5-b-0-39.pdf ============================================================================== ## Display.RGBW > Smart RGBW LED | | | | --- | --- | | Tile | `Display.RGBW` | | Revision | `a` | | Family | Display | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.3` | | Driver reference | https://bergsonne.io/docs/tiles/display/rgbw | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Display-RGBW-a-0-3.pdf | | Canonical page | https://bergsonne.io/catalog/display/rgbw | ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Texas Instruments | `LP5811` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/58/Texas_Instruments-LP5811.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 5.5 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Addresses: `0x50` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `` | other | — | | 3 | `` | other | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `` | other | — | | 7 | `` | other | — | | 8 | `EN` | digital | — | | 9 | `` | other | — | | 10 | `V+` | power | — | --- Generated 2026-08-12 from the canonical tile definition (version 0.3). Canonical HTML: https://bergsonne.io/catalog/display/rgbw · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Display-RGBW-a-0-3.pdf ============================================================================== ## Drive.A.2 > I2C/SPI-input dual 3W Class-D amplifier | | | | --- | --- | | Tile | `Drive.A.2` | | Revision | `a` | | Family | Drive | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.11` | | Driver reference | https://bergsonne.io/docs/tiles/drive/a2 | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-A-2-a-0-11.pdf | | Canonical page | https://bergsonne.io/catalog/drive/a2 | ### Overview The Drive.A.2 is a dual-channel audio output tile combining a DAC63202W 12-bit smart DAC with two TPA2028D1 Class-D audio power amplifiers. It provides two independent audio channels, each capable of delivering up to 3 W into 4 Ω or 880 mW into 8 Ω speakers. The DAC63202W generates analog waveforms from digital data with 12-bit resolution and supports built-in waveform generation (sine, cosine, triangular, sawtooth) for processor-less tone generation. The DAC connects to a host processor via either I2C (up to 1 Mbps) or SPI (up to 50 MHz), auto-detected at power-on. Each TPA2028D1 amplifier features I2C-programmable gain control (−28 dB to +30 dB in 1 dB steps), automatic gain control (AGC), and dynamic range compression (DRC) to prevent speaker overdrive and enhance perceived loudness. The amplifiers operate filter-free in Class-D mode for high efficiency and include thermal and short-circuit protection. The tile operates from a 2.5 V to 5.5 V supply. All three ICs are controlled via a shared I2C bus, while the DAC can alternatively be driven over SPI for higher-bandwidth waveform updates. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Texas Instruments | `DAC63202W` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/13/Texas_Instruments-DAC63202W.pdf | | Texas Instruments | `TPA2028D1` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/13/Texas_Instruments-TPA2028D1.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 2.5 | 5.5 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 1MHz - Addresses: `0x49` (default), `0x48`, `0x4A`, `0x4B` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | #### SPI (SPI) - Modes: slave - Max clock: 50MHz | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `SPI.MISO` | bus | yes | | 3 | `SPI.MOSI` | bus | yes | | 5 | `SPI.CLK` | bus | yes | | 4 | `SPI.CS` | other | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `GPIO` | digital | `SPI.MISO` | | 3 | `A0` | digital | `SPI.MOSI` | | 4 | `I2C.CLK` | digital | `SPI.CS` | | 5 | `I2C.DAT` | digital | `SPI.CLK` | | 6 | `OUT.0-` | drive | — | | 7 | `OUT.0+` | drive | — | | 8 | `OUT.1-` | drive | — | | 9 | `OUT.1+` | drive | — | | 10 | `V+` | power | — | ### Application notes #### Automatic Bus Detection The DAC in the Drive.A.2 tile automatically detects whether it is connected via I2C of SPI. #### I2C Addresses The Drive.A.2 can be configured for one of four I2C addresses by manipulating the connection of pad 3 (A0). Leaving the pad floating (or connecting to V+) uses the default address of 0x49. Connecting pad 3 to GND will change to address 0x48, while connecting to pad 3 to pad 4 (I2C.CLK) or pad 5 (I2C.DAT) will set the address to 0x4B or 0x4A, respectively. --- Generated 2026-08-12 from the canonical tile definition (version 0.11). Canonical HTML: https://bergsonne.io/catalog/drive/a2 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-A-2-a-0-11.pdf ============================================================================== ## Drive.DC.H > 11V/2A H-bridge DC-motor driver | | | | --- | --- | | Tile | `Drive.DC.H` | | Revision | `a` | | Family | Drive | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.16` | | Driver reference | https://bergsonne.io/docs/tiles/drive/dch | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-DC-H-a-0-16.pdf | | Canonical page | https://bergsonne.io/catalog/drive/dch | ### Overview The Drive.DC.I is a single-channel H-bridge motor driver built around the DRV8214. It can deliver up to 4 A peak (2 A RMS continuous) and operates from a 1.65 V to 11 V supply, making it suitable for a wide range of small brushed DC motors in robotics, haptics, and actuation applications. The DRV8214 features sensorless ripple counting, which tracks motor commutation ripples in the current waveform to determine relative position and speed — eliminating the need for external encoders. Integrated speed regulation maintains constant motor speed across varying supply voltages, while voltage regulation allows driving the motor at a programmable lower terminal voltage to save energy. Motor speed can be controlled via either PWM at frequencies up to 200 kHz or via the I2C interface, which is also used for register-based configuration and diagnostics, including soft-start/stop, stall detection, and various limits. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Texas Instruments | `DRV8214` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/14/Texas_Instruments-DRV8214.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 5 | | +10, GND 1 | yes | | drive | | input | 1.8 | 11 | | +9, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Addresses: `0x34` (default), `0x30`, `0x31`, `0x33` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `EN` | digital | `IN1` | | 3 | `PH` | digital | `IN2` | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `NPROP` | analog | — | | 7 | `OUT2` | drive | — | | 8 | `OUT1` | drive | — | | 9 | `VM` | power | — | | 10 | `V+` | power | — | ### Application notes #### Integrated Current Sensing Integrated current sensing via an internal current mirror provides an analog output (NPROP) proportional to load current, with selectable gain for accurate sensing down to 10 mA. Current regulation limits motor current autonomously during stall or high-load events. #### I2C address variants Four different order-time variants exist for this tile, representing the four possible I2C addresses (0x30, 0x31, 0x33, 0x34), allowing up to four motors to be independently controlled on the same bus. --- Generated 2026-08-12 from the canonical tile definition (version 0.16). Canonical HTML: https://bergsonne.io/catalog/drive/dch · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-DC-H-a-0-16.pdf ============================================================================== ## Drive.H > haptic ERM/LRA driver | | | | --- | --- | | Tile | `Drive.H` | | Revision | `a` | | Family | Drive | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.5` | | Driver reference | https://bergsonne.io/docs/tiles/drive/h | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-H-a-0-5.pdf | | Canonical page | https://bergsonne.io/catalog/drive/h | ### Overview The Drive.H tile is designed for driving linear-resonant actuators (LRAs) or eccentric rotating-mass motors (ERMs) using the Texas Instruments DRV2605 IC.  Haptic signals can be generated either from an internal effects library, streamed in real time over I2C, or input as a high-speed pulse-width modulation (PWM) signal.  Additionally, an optional hardware trigger can be used to play back pre-configured effects.  Separate logic and motor supply connections allows for digital interfacing with logic down to 1.8V. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Texas Instruments | `DRV2605` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/4/Texas_Instruments-DRV2605.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 5 | | +10, GND 1 | yes | | drive | | input | 2.5 | 5.5 | | +9, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 400kHz - Addresses: `0x5A` (default) - Data bits: 8 | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `TRIG` | digital | — | | 3 | `EN` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `—` | — | — | | 7 | `OUT+` | drive | — | | 8 | `OUT-` | drive | — | | 9 | `V_MOTOR` | power | — | | 10 | `V+` | power | — | --- Generated 2026-08-12 from the canonical tile definition (version 0.5). Canonical HTML: https://bergsonne.io/catalog/drive/h · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-H-a-0-5.pdf ============================================================================== ## Drive.P > piezoelectric sensor/actuator driver | | | | --- | --- | | Tile | `Drive.P` | | Revision | `a` | | Family | Drive | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm × 1.8 mm | | Definition version | `0.8` | | Driver reference | https://bergsonne.io/docs/tiles/drive/p | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-P-a-0-8.pdf | | Canonical page | https://bergsonne.io/catalog/drive/p | ### Overview Built around the Boréas Technologies BOS1921 driver, the advanced DRIVE.P tile provides an ultra-compact, fully-integrated solution for interfacing with piezoelectric sensors and actuators. With a maximum peak-to-peak differential output amplitude of 190V, the ability to drive one or two capacitive loads up to a total of 820nF, a built-in waveform synthesizer and high-speed 1024-sample continuous-playback FIFO, as well as a sensing resolution of just 7.6mV, this tile provides an unparalleled level of integration for next-generation haptics and other piezoactuator solutions. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Boréas Technologies | `BOS1921` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/5/Bor_as_Technologies-BOS1921.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 5.5 | | +10, GND 1 | yes | | drive | | input | 3 | 5.5 | | +9, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 1MHz - Addresses: `0x44` (default) - Data bits: 8 | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | #### I3C (I3C) - Modes: slave | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I3C.CLK` | bus | yes | | 5 | `I3C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `—` | — | — | | 3 | `GPIO` | digital | — | | 4 | `I2C.CLK` | digital | `I3C.CLK` | | 5 | `I2C.DAT` | digital | `I3C.DAT` | | 6 | `—` | — | — | | 7 | `OUT+` | drive | — | | 8 | `OUT-` | drive | — | | 9 | `V_DRIVE` | power | — | | 10 | `V+` | power | — | ### Application notes #### Single Piezo A single piezo connected across the OUT terminals can be used for both sensing and output, including bipolar output #### Dual Piezo Two piezos can be driven by in a unipolar configuration by connecting the positive lead of each piezo to each of the OUT pads, and the negative terminals to GND. Sensing is not possible in this configuration. --- Generated 2026-08-12 from the canonical tile definition (version 0.8). Canonical HTML: https://bergsonne.io/catalog/drive/p · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Drive-P-a-0-8.pdf ============================================================================== ## Power.BB > multi-output buck-boost regulator | | | | --- | --- | | Tile | `Power.BB` | | Revision | `a` | | Family | Power | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.3` | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Power-BB-a-0-3.pdf | | Canonical page | https://bergsonne.io/catalog/power/bb | ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Texas Instruments | `TPS63901` | — | | Texas Instruments | `TPS7A20` | — | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 5.5 | | +3, GND 1 | yes | | system | | output | 1.8 | 1.8 | | +10, GND 1 | no | | system | | output | 3.3 | 3.3 | | +9, GND 1 | no | | system | | output | 5 | 5 | | +8, GND 1 | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `` | | — | | 3 | `VIN` | power | — | | 4 | `I2C.CLK` | interface | — | | 5 | `I2C.DAT` | interface | — | | 6 | `` | | — | | 7 | `` | | — | | 8 | `5V0` | power | — | | 9 | `3V3` | power | — | | 10 | `1V8` | power | — | --- Generated 2026-08-12 from the canonical tile definition (version 0.3). Canonical HTML: https://bergsonne.io/catalog/power/bb · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Power-BB-a-0-3.pdf ============================================================================== ## Power.L.1N > 1C Li-Ion charge manager w/ 1.8 & 3.3V out | | | | --- | --- | | Tile | `Power.L.1N` | | Revision | `a` | | Family | Power | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.12` | | Driver reference | https://bergsonne.io/docs/tiles/power/l1n | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Power-L-1N-a-0-12.pdf | | Canonical page | https://bergsonne.io/catalog/power/l1n | ### Overview Single-cell Li-ion/Li-poly/LiFePO₄ battery charging and power management tile built around the Nordic nPM1300 PMIC. Provides both 1.8V and 3.3V regulated outputs via high-efficiency buck converters rated to 200mA each. An unregulated VSYS rail is also available for loads that require direct battery voltage or higher current. USB-C power input with CC pins exposed for connection detection. Supports charging currents from 32 to 800 mA for batteries up to 1000 mAh, and has three on-board LEDs for state observation. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Nordic Semiconductor | `NPM1300` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/10/Nordic_Semiconductor-NPM1300.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | LDO | output | 1.8 | 1.8 | 200mA | +10, GND 1 | no | | system | LDO | output | 3.3 | 3.3 | 200mA | +9, GND 1 | no | | rechargeable battery | BATT | input | 2.3 | 4.45 | 800mA (charge) | +7, GND 1 | yes | | charge | CHG | input | 3.4 | 5.5 | 1500mA | +6, GND 1 | no | | system | VSYS | output | 2.3 | 5.5 | 1340mA | +8, GND 1 | no | ### Interfaces #### I2C (I2C) - Modes: slave - Addresses: `0x6B` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | no | | 5 | `I2C.DAT` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `CC1` | other | — | | 3 | `CC2` | other | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `CHG` | power | — | | 7 | `BATT` | power | — | | 8 | `VSYS` | power | — | | 9 | `3V3` | power | — | | 10 | `1V8` | power | — | ### Application notes #### Battery charging Battery charging, if desired, must be enabled via firmware. #### LEDs Three onboard LEDs (red, yellow, green) driven by the nPM1300's LED outputs provide visual indication of charging and system status. Red is connected to LED0 (default = error reporting), yellow to LED1 (default = charge indication), and green to LED2 (default = host control). The LED outputs must be enabled via firmware. #### CC pads for USB C The two CC pads can be connected to the CC lines of a standard USB C input to detect the source power capability. They have internal 5.1k pull-down resistors to conform to the standard for a sink device. Upon connection and detection, the source capability is reported in the USBCDETECTSTATUS register. #### Charging Current The default charging current limit is set to 100mA, and can be adjusted via firmware. --- Generated 2026-08-12 from the canonical tile definition (version 0.12). Canonical HTML: https://bergsonne.io/catalog/power/l1n · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Power-L-1N-a-0-12.pdf ============================================================================== ## Power.L.1T > single-cell Li-Ion charge controller with adjustable LDO output | | | | --- | --- | | Tile | `Power.L.1T` | | Revision | `b` | | Family | Power | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.12` | | Driver reference | https://bergsonne.io/docs/tiles/power/l1t | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Power-L-1T-b-0-12.pdf | | Canonical page | https://bergsonne.io/catalog/power/l1t | ### Overview Perfect for wearables and compact battery-powered devices, the Power.L1.T tile integrates a single-cell Lithium-Ion battery charger with a max charge current of 500mA with a programmable LDO output and a 12-bit ADC for battery and system monitoring. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Texas Instruments | `BQ25150` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/6/Texas_Instruments-BQ25150.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | output | 0.6 | 3.7 | | +10, GND 1 | yes | | charge | | input | 3.4 | 5.5 | 500mA | +8, GND 9 | no | | rechargeable battery | single-cell Li-Ion battery | input | 3.6 | 4.6 | 500mA (charge) | +7, GND 6 | no | ### Interfaces #### I2C (I2C) - Modes: slave - Addresses: `0x6B` (default) - Data bits: 8 | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `LP` | digital | — | | 3 | `SW` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `BATT-` | power | — | | 7 | `BATT+` | power | — | | 8 | `SUPPLY+` | power | — | | 9 | `SUPPLY-` | power | — | | 10 | `V+` | power | — | ### Application notes #### Existing Ship Mode When a battery is first connected, the IC will enter the ultra-low-current ship mode, which it will exit when voltage is first applied to the SUPPLY pads. Ship mode can be re-entered either programmatically or by disconnecting/reconnecting the battery. #### Normally-On Operation When the SW pad is floating, the LDO output will be connected. Connect SW to GND/SUPPLY- to turn off the output. #### Low-Power Mode In the default low-power mode, the I2C interface is disabled. To exit low-power mode, drive the LP to greater than 1.35V. #### adjustable LDO output The LDO output can supply up to 150mA in a range from 0.6-3.7V in 0.1V steps. The power-on default output is set to 1.8V, and can be adjusted by writing to the LDOCTRL register. --- Generated 2026-08-12 from the canonical tile definition (version 0.12). Canonical HTML: https://bergsonne.io/catalog/power/l1t · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Power-L-1T-b-0-12.pdf ============================================================================== ## Sense.ACP > ambient light, RGB color, proximity | | | | --- | --- | | Tile | `Sense.ACP` | | Revision | `b` | | Family | Sense | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.4` | | Driver reference | https://bergsonne.io/docs/tiles/sense/acp | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-ACP-b-0-4.pdf | | Canonical page | https://bergsonne.io/catalog/sense/acp | ### Overview Built around the ams-OSRAM TMD3725, the Sense.ACP provides digital ambient light sensing (ALS), RGBC+IR color measurement with 16-bit data converters, and IR-based proximity detection with an integrated LED and factory-calibrated driver. Color photodiodes include UV and IR blocking filters while ALS gain is adjustable from 1× to 128× for operation across a wide range of lighting conditions. Proximity detection is interrupt-driven with configurable thresholds, offset compensation, and automatic ambient light subtraction. An on-board 3.3V buck converter supplies the IR LED driver from the V+ input. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ams-Osram | `TMD3725` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/24/ams-Osram-TMD3725.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 1.8 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 400kHz - Addresses: `0x39` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `—` | — | — | | 3 | `—` | — | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `—` | — | — | | 7 | `—` | — | — | | 8 | `—` | — | — | | 9 | `INT` | digital | — | | 10 | `V+` | power | — | --- Generated 2026-08-12 from the canonical tile definition (version 0.4). Canonical HTML: https://bergsonne.io/catalog/sense/acp · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-ACP-b-0-4.pdf ============================================================================== ## Sense.I.6P6 > extended-range 6-DOF IMU | | | | --- | --- | | Tile | `Sense.I.6P6` | | Revision | `a` | | Family | Sense | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.15` | | Driver reference | https://bergsonne.io/docs/tiles/sense/i6p6 | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-I-6P6-a-0-15.pdf | | Canonical page | https://bergsonne.io/catalog/sense/i6p6 | ### Overview The Sense.I.6P is a precision 6-DOF inertial measurement unit based on the extended-range TDK Invensense ICM-42686P. The 16-bit accelerometer full-scale range can be set to +/- 2g, 4g, 8g, 16g, or 32g with an output data rate of up between 1.5Hz and 32kHz, while the 16-bit gyro range can be set to +/- 15.6, 31.5, 62.5, 125, 250, 500, 1000, 2000, or 4000 degrees per second with an output data rate of 12.5Hz and 32kHz. The sensor includes a user-programmable filters, on-board motion functions (pedometer, tilt, tap, raise, etc.), and a 2kB FIFO. This IMU is functionally very similar to the Sense.I.6P8, which has lower gyro noise and better temperature stability but with lower max ranges of 16g and 2000 degrees per second. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | TDK Invensense | `ICM-42686P` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/22/TDK_Invensense-ICM-42686P.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.8 | 3.6 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Addresses: `0x69` (default), `0x68` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | | 2 | `I2C.AD0` | strap | no | | 3 | `I2C.EN` | strap | no | #### I3C (I3C) - Modes: slave - Max clock: 12.5MHz (SDR) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I3C.CLK` | bus | yes | | 5 | `I3C.DAT` | bus | yes | | 3 | `I3C.EN` | strap | no | #### SPI (SPI) - Modes: slave - Max clock: 24MHz | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `SPI.MISO` | bus | yes | | 3 | `SPI.CS` | other | yes | | 4 | `SPI.CLK` | bus | yes | | 5 | `SPI.MOSI` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `I2C.AD0` | digital | `SPI.MISO` | | 3 | `I2C.EN` | digital | `SPI.CS` | | 4 | `I2C.CLK` | digital | `SPI.CLK`, `I3C.CLK` | | 5 | `I2C.DAT` | digital | `SPI.MOSI`, `I3C.DAT` | | 6 | `—` | — | — | | 7 | `—` | — | — | | 8 | `INT2` | digital | — | | 9 | `INT1` | digital | — | | 10 | `V+` | power | — | ### Application notes #### Sensor Axes The Z axis of the IMU protrudes through the center of the tile, while the XY plane is approximately 1.0mm from the bottom surface of the tile, with the X axis pointing toward the left side (pads 1-5), as shown. --- Generated 2026-08-12 from the canonical tile definition (version 0.15). Canonical HTML: https://bergsonne.io/catalog/sense/i6p6 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-I-6P6-a-0-15.pdf ============================================================================== ## Sense.I.9 > digital 9-DOF IMU | | | | --- | --- | | Tile | `Sense.I.9` | | Revision | `c` | | Family | Sense | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.11` | | Driver reference | https://bergsonne.io/docs/tiles/sense/i9 | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-I-9-c-0-11.pdf | | Canonical page | https://bergsonne.io/catalog/sense/i9 | ### Overview The Sense.I.9 embeds the TDK Invensense ICM20948 IMU with a co-packaged 6-DOF IMU and a 3-DOF magnetic compass. The triaxial accelerometer has a configurable 16-bit full-scale range from +/-2g to +/-16g with an output data rate up to 4.5 kHz, while the triaxial rate gyroscope has a 16-bit full-scale range from +/-250 to 2000 degrees per second with an output date rate up to 9 kHz. The triaxial magnetic compass has a 16-bit full-scale range of +/-4900µT with an output data rate of up to 100 Hz. The sensor includes configurable filters and an on-board motion processor. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | TDK Invensense | `ICM-20948` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/2/TDK_Invensense-ICM20948.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.71 | 1.95 | 3.11mA | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 400kHz - Addresses: `0x68`, `0x69` (default) - Data bits: 8 | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `I2C.AD0` | strap | no | | 3 | `I2C.EN` | strap | no | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | #### SPI (SPI) - Modes: slave - Max clock: 7MHz | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `SPI.MISO` | bus | yes | | 3 | `SPI.CS` | other | yes | | 4 | `SPI.CLK` | bus | yes | | 5 | `SPI.MOSI` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `I2C.AD0` | digital | `SPI.MISO` | | 3 | `I2C.EN` | digital | `SPI.CS` | | 4 | `I2C.CLK` | digital | `SPI.CLK` | | 5 | `I2C.DAT` | digital | `SPI.MOSI` | | 6 | `—` | — | — | | 7 | `—` | — | — | | 8 | `—` | — | — | | 9 | `INT` | digital | — | | 10 | `V+` | power | — | ### Application notes #### Sensor Axes The IMU and compass origins are colocated, with the axes aligned at the center of the IC, which is at (-0.2, -0.2) mm relative to the center of the tile. The Z axis (blue head) of the sensor extends from the top surface of the tile, while the X axis (red head) points up between pads 1 and 10. --- Generated 2026-08-12 from the canonical tile definition (version 0.11). Canonical HTML: https://bergsonne.io/catalog/sense/i9 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-I-9-c-0-11.pdf ============================================================================== ## Sense.MIC > I2C-output MEMS microphone | | | | --- | --- | | Tile | `Sense.MIC` | | Revision | `a` | | Family | Sense | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.6` | | Driver reference | https://bergsonne.io/docs/tiles/sense/mic | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-MIC-a-0-6.pdf | | Canonical page | https://bergsonne.io/catalog/sense/mic | ### Overview The Sense.MIC combines a PUI Audio AMM-2742-T-R omnidirectional MEMS microphone with the MAX11645 12-bit ADC. The microphone covers 20 Hz to 20 kHz with 59 dB SNR and 123 dB acoustic overload point. The ADC samples at up to 94.4 ksps and can operate a I2C speeds up to 1.7MHz, thereby fully covering the microphone's bandwidth. Suited for voice detection, sound-level monitoring, and audio recording applications. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Maxim | `MAX11645` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/19/unknown-MAX11645.pdf | | Same Sky | `CMM-2718AT-42316-TR` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/19/Same_Sky-CMM-2718AT-42316-TR.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 2.7 | 3.6 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 1.7MHz - Addresses: `0x36` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | yes | | 5 | `I2C.DAT` | bus | yes | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `—` | — | — | | 3 | `—` | — | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `AOUT` | analog | — | | 7 | `—` | — | — | | 8 | `—` | — | — | | 9 | `—` | — | — | | 10 | `V+` | power | — | --- Generated 2026-08-12 from the canonical tile definition (version 0.6). Canonical HTML: https://bergsonne.io/catalog/sense/mic · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-MIC-a-0-6.pdf ============================================================================== ## Sense.T.C > capacitive touch | | | | --- | --- | | Tile | `Sense.T.C` | | Revision | `a` | | Family | Sense | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.7` | | Driver reference | https://bergsonne.io/docs/tiles/sense/tc | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-T-C-a-0-7.pdf | | Canonical page | https://bergsonne.io/catalog/sense/tc | ### Overview Built around the Azoteq IQS323 ProxFusion controller, the Sense.T.C tile's entire top surface functions as a capacitive touch sensor, while a second input channel is available via one of the pads. The IQS323 provides both proximity and touch detection with all signal processing on-chip, exposing processed results over I2C. Each channel reports 16-bit filtered counts, a 16-bit long-term average baseline, and binary proximity/touch states with configurable thresholds, debounce, and hysteresis. When configured as a slider across multiple tiles, the controller provides a 16-bit position output with on-chip gesture recognition including tap, swipe, flick, and hold. Report rates are configurable per power mode, from every 16ms in normal self-capacitive mode (125µA average current consumption) up to every 160ms in 4 µA ultra-low-power mode (160 ms). In addition, event-driven reporting can leave the host free between changes in state. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Azoteq | `IQS323` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/18/Azoteq-IQ323.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.71 | 3.5 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 1MHz - Addresses: `0x44` | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | no | | 5 | `I2C.DAT` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `—` | — | — | | 3 | `RDY` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `—` | — | — | | 7 | `—` | — | — | | 8 | `C0` | analog | — | | 9 | `—` | — | — | | 10 | `V+` | power | — | ### Application notes #### Sensing Surface The entire top surface of the tile connected to the C1 sensor input with a 470-ohm series resistor. #### Secondary Input The C0 input on the IQS323 is directly connected to pad 8 of the tile, allowing for user configuration of a secondary capacitive touch input. --- Generated 2026-08-12 from the canonical tile definition (version 0.7). Canonical HTML: https://bergsonne.io/catalog/sense/tc · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-T-C-a-0-7.pdf ============================================================================== ## Sense.TOF > time-of-flight distance | | | | --- | --- | | Tile | `Sense.TOF` | | Revision | `a` | | Family | Sense | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.11` | | Driver reference | https://bergsonne.io/docs/tiles/sense/tof | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-TOF-a-0-11.pdf | | Canonical page | https://bergsonne.io/catalog/sense/tof | ### Overview The Sense.TOF is a single-zone time-of-flight distance sensor based on the ams-Osram TMF8806, which uses a Class-1 eye-safe 940 nm VCSEL laser emitter with SPAD detection. It provides 1-mm sensing resolution over a range from 1 centimeter to 5 meters (extendable to 10 meters) with a max output data rate of 30Hz. The sensor features on-chip histogram processing, a 24-degree field of view, and pre-installed firmware suitable for obstacle detection, proximity sensing, collision avoidance, and autofocus applications. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | ams-Osram | `TMF8806` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/17/ams-Osram-TMF8806.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 2.7 | 3.3 | | +10, GND 1 | yes | ### Interfaces #### I2C (I2C) - Modes: slave - Max clock: 1MHz - Addresses: `0x41` (default) | Pad | Function | Role | Required | | --- | --- | --- | --- | | 4 | `I2C.CLK` | bus | no | | 5 | `I2C.DAT` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `—` | — | — | | 3 | `EN` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `—` | — | — | | 7 | `—` | — | — | | 8 | `—` | — | — | | 9 | `INT` | digital | — | | 10 | `V+` | power | — | --- Generated 2026-08-12 from the canonical tile definition (version 0.11). Canonical HTML: https://bergsonne.io/catalog/sense/tof · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Sense-TOF-a-0-11.pdf ============================================================================== ## Store.O.128 > 128Mbit NOR flash | | | | --- | --- | | Tile | `Store.O.128` | | Revision | `a` | | Family | Store | | Package | T44 · 10 pads | | Dimensions | 4 mm × 4 mm | | Definition version | `0.10` | | Driver reference | https://bergsonne.io/docs/tiles/store/o128 | | Datasheet (PDF) | https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Store-O-128-a-0-10.pdf | | Canonical page | https://bergsonne.io/catalog/store/o128 | ### Overview The Store.M contains 128Mbits of NOR flash memory for non-volatile code and data storage with support for single, dual, or quad SPI communications at bus-clock rates of up to 104MHz. ### Onboard components | Manufacturer | Part | Datasheet | | --- | --- | --- | | Renesas | `AT25QL128A` | https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/15/Renesas-AT25QL128A.pdf | ### Power | Rail | Function | Direction | Min | Max | Max current | Pads | Required | | --- | --- | --- | --- | --- | --- | --- | --- | | system | | input | 1.71 | 2 | | +10, GND 1 | yes | ### Interfaces #### SPI (SPI) - Modes: slave - Max clock: 104MHz | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `SPI.MOSI` | bus | yes | | 3 | `SPI.CS` | other | yes | | 4 | `SPI.CLK` | bus | yes | | 5 | `SPI.MISO` | bus | yes | #### QSPI (QSPI) - Modes: slave - Max clock: 104MHz | Pad | Function | Role | Required | | --- | --- | --- | --- | | 2 | `QSPI.IO0` | bus | no | | 3 | `QSPI.CS` | other | no | | 4 | `QSPI.CLK` | bus | no | | 5 | `QSPI.IO1` | bus | no | | 6 | `QSPI.IO2` | bus | no | | 9 | `QSPI.IO3` | bus | no | ### Pad map | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `SPI.MOSI` | digital | `QSPI.IO0` | | 3 | `SPI.CS` | digital | `QSPI.CS` | | 4 | `SPI.CLK` | digital | `QSPI.CLK` | | 5 | `SPI.MISO` | digital | `QSPI.IO1` | | 6 | `WP` | digital | `QSPI.IO2` | | 7 | `—` | — | — | | 8 | `—` | — | — | | 9 | `HOLD` | digital | `QSPI.IO3` | | 10 | `V+` | power | — | ### Application notes #### --- Generated 2026-08-12 from the canonical tile definition (version 0.10). Canonical HTML: https://bergsonne.io/catalog/store/o128 · PDF: https://mosaic-datasheets.s3.eu-north-1.amazonaws.com/Store-O-128-a-0-10.pdf ============================================================================== ## Display.RGBW driver > RGBW LED driver for the Display.RGBW tile (LP5811). | | | | --- | --- | | Tile | `Display.RGBW` | | Driver version | `2.3.0` | | Header | `tile_display_rgbw.h` | | Generated from | `tiles@842d43e` | | Canonical page | https://bergsonne.io/docs/tiles/display/rgbw | ### Overview @version 2.1.0 4-channel LED driver with independent PWM + current control. Channels: R (LED0), G (LED2), B (LED1), W (LED3). Version history: v2.3.0 — init() ramps the boost 3.0 -> 4.5 V in 0.1 V committed steps instead of one slam. The single-step commit's inrush can brown-out a marginal supply (long leads, loaded USB rail) and reset the host MCU. v2.1.0 — Tier-2 idiomatic helpers (set_color, pulse, breathe, flash, is_faulted) + section= tagging for Coverage Table. v2.0.0 — Initial tier-1 surface (set, off, current, faults). ### Examples #### Quick start ```c tile_t led; tile_display_rgbw_init(&hal, 0, &led, NULL); tile_display_rgbw_set_color(&led, 255, 0, 0); // red tile_display_rgbw_pulse(&led, 0, 255, 0, 200); // 200ms green flash tile_display_rgbw_off(&led); // all off ``` ### API 31 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_display_rgbw_sleep` — Studio ```c void tile_display_rgbw_sleep(tile_t *tile) ``` Enter sleep (disable chip). ##### `tile_display_rgbw_wake` — Studio ```c void tile_display_rgbw_wake(tile_t *tile) ``` Wake (re-enable chip, LEDs retain previous state). ##### `tile_display_rgbw_reset` — Studio ```c void tile_display_rgbw_reset(tile_t *tile) ``` Software reset. Must call init() again after. #### Runtime ##### `tile_display_rgbw_set` — Studio ```c void tile_display_rgbw_set(tile_t *tile, uint8_t r, uint8_t g, uint8_t b, uint8_t w) ``` Set RGBW output levels. - `r` — [0..255] Red PWM. - `g` — [0..255] Green PWM. - `b` — [0..255] Blue PWM. - `w` — [0..255] White PWM. ##### `tile_display_rgbw_off` — Studio ```c void tile_display_rgbw_off(tile_t *tile) ``` Turn all LEDs off (PWM = 0). ##### `tile_display_rgbw_set_current` — Studio ```c void tile_display_rgbw_set_current(tile_t *tile, uint8_t r, uint8_t g, uint8_t b, uint8_t w) ``` Set per-channel current limit. - `r` — [0..255] Red current (fraction of full-scale max). - `g` — [0..255] Green current. - `b` — [0..255] Blue current. - `w` — [0..255] White current. ##### `tile_display_rgbw_read_faults` — Studio ```c void tile_display_rgbw_read_faults(tile_t *tile, disp_rgbw_faults_t *out) ``` Read the per-channel open / short / thermal fault state. Pulls TSD_Config_Status, LOD_Status_0, and LSD_Status_0 from the chip's page-3 register space (the LP5811 multiplexes register pages onto two extra address bits — the driver handles this transparently). Faults latch in the chip until cleared via `clear_faults()`. Open-circuit threshold (VLOD_TH) is fixed by the chip at ~70 mV (25.5 mA mode) or ~180 mV (51 mA mode). Short-circuit threshold (VLSD_TH) is configurable via `set_short_threshold()`. - `tile` — Initialised tile handle - `out` — Caller-allocated fault snapshot (zeroed on entry) ##### `tile_display_rgbw_clear_faults` — Studio ```c void tile_display_rgbw_clear_faults(tile_t *tile) ``` Clear all latched LED open / short / TSD fault flags. Writes 0x07 to Fault_Clear (W1C — write 1 to clear). After this call, `read_faults()` reflects only currently-active faults. - `tile` — Initialised tile handle ##### `tile_display_rgbw_set_color` — Studio ```c void tile_display_rgbw_set_color(tile_t *tile, uint8_t r, uint8_t g, uint8_t b) ``` Set a solid RGB colour (W channel forced off). Convenience wrapper around @ref tile_display_rgbw_set for the common case where only the RGB channels matter. White is held at zero — drop to `set()` directly if you want to mix white in. - `tile` — Initialised tile handle - `r` — Red PWM [0..255] - `g` — Green PWM [0..255] - `b` — Blue PWM [0..255] ##### `tile_display_rgbw_pulse` — Studio ```c void tile_display_rgbw_pulse(tile_t *tile, uint8_t r, uint8_t g, uint8_t b, uint16_t ms) ``` Show a colour for `ms` milliseconds, then turn off. Simple alert / acknowledgement idiom. Blocking — the call returns after `ms` has elapsed and the LEDs have been turned back off. White is forced off (RGB-only); use @ref tile_display_rgbw_set + manual delay if you need full RGBW pulse control. - `tile` — Initialised tile handle - `r` — Red PWM [0..255] - `g` — Green PWM [0..255] - `b` — Blue PWM [0..255] - `ms` — Duration in milliseconds ##### `tile_display_rgbw_breathe` — Studio ```c void tile_display_rgbw_breathe(tile_t *tile, uint8_t r, uint8_t g, uint8_t b, uint16_t period_ms) ``` Continuous fade in/out (one breath cycle). The "thinking" indicator. Plays a single up-and-down PWM ramp over `period_ms` (i.e., dark → peak → dark = one full breath). Loop in the caller for sustained breathing. Implementation is a software loop — the LP5811 has on-chip animation engines (AEU) that could run this autonomously, but the AEU bytecode/timing semantics aren't fully documented in the public datasheet (see @ref tile_display_rgbw.h "unsupported AEU" annotation). The software loop is fine for indicator-grade breathing at v2.1; revisit when AEU lands. - `tile` — Initialised tile handle - `r` — Peak red PWM [0..255] - `g` — Peak green PWM [0..255] - `b` — Peak blue PWM [0..255] - `period_ms` — One full breath duration in milliseconds (>= 64 recommended) **Note:** Blocking. Spends `period_ms` in `delay_ms()`. Call from a dedicated task or accept the stall — the function does not yield to other peripherals. **Note:** White is forced off (RGB-only). Step granularity is fixed at 32 PWM levels per half-cycle; with `period_ms < 64` the delay-per-step rounds to 1 ms and the breath becomes choppy. ##### `tile_display_rgbw_flash` — Studio ```c void tile_display_rgbw_flash(tile_t *tile, uint8_t r, uint8_t g, uint8_t b, uint8_t count) ``` N quick on/off blinks at the given colour. Notification idiom — alarm, error indication, "got it" ack. Each blink is 100 ms on, 100 ms off (200 ms per cycle). White is forced off (RGB-only). Leaves the LEDs off after the final blink. - `tile` — Initialised tile handle - `r` — Red PWM [0..255] - `g` — Green PWM [0..255] - `b` — Blue PWM [0..255] - `count` — Number of on/off cycles (1..255) **Note:** Blocking. Total runtime is approximately `count * 200` ms. Call from a dedicated task or accept the stall. ##### `tile_display_rgbw_is_faulted` — Studio ```c uint8_t tile_display_rgbw_is_faulted(tile_t *tile) ``` Quick yes/no on whether any LP5811 fault is currently latched. Wraps @ref tile_display_rgbw_read_faults and returns 1 if any fault bit is set — open-circuit (LOD), short-circuit (LSD), thermal shutdown (TSD), or configuration error. For per-channel detail (which LED is open / shorted), use `read_faults()` directly. Faults stay latched until cleared via @ref tile_display_rgbw_clear_faults. - `tile` — Initialised tile handle **Returns:** 1 if any fault bit is set, 0 if healthy ##### `tile_display_rgbw_update` — Studio ```c void tile_display_rgbw_update(tile_t *tile) ``` Latch pending config/animation register writes (CMD_Update = 0x55). The LP5811 only acts on Dev_Config / animation writes once this is issued. Call after set_aeu / set_animation / set_autonomous and before animate_start. ##### `tile_display_rgbw_animate_start` — Studio ```c void tile_display_rgbw_animate_start(tile_t *tile) ``` Start (or restart) autonomous animation on all enabled channels. ##### `tile_display_rgbw_animate_stop` — Studio ```c void tile_display_rgbw_animate_stop(tile_t *tile) ``` Stop autonomous animation and return to the INITIAL state. ##### `tile_display_rgbw_animate_pause` — Studio ```c void tile_display_rgbw_animate_pause(tile_t *tile) ``` Pause autonomous animation, holding the current output. ##### `tile_display_rgbw_animate_continue` — Studio ```c void tile_display_rgbw_animate_continue(tile_t *tile) ``` Resume autonomous animation after a pause. ##### `tile_display_rgbw_breathe_auto` — Studio ```c void tile_display_rgbw_breathe_auto(tile_t *tile, uint8_t channel, uint8_t peak, uint16_t period_ms, uint8_t repeats) ``` Configure + start an autonomous breathe on one channel. Builds a single-AEU symmetric ramp (0 → peak → 0) on the channel, enables autonomous mode, latches, and starts. Convenience wrapper over the AEU API for the common case. - `channel` — 0-3. - `peak` — Peak brightness 0-255. - `period_ms` — Full breathe period (up+down) in ms. - `repeats` — Whole-pattern repeat: 0-14, 15 = infinite. #### Configuration ##### `tile_display_rgbw_set_max_current` — Studio ```c void tile_display_rgbw_set_max_current(tile_t *tile, disp_rgbw_max_current_t mode) ``` Set the global maximum-current range (MC bit). Selects 25.5 mA or 51 mA full-scale per channel. After changing, the 8-bit per-channel DC codes (set via `set_current()`) re-scale automatically — DC=255 always means full-scale current. Useful when wiring lower-rated LEDs (drop to 25.5 mA to keep DC resolution fine) or when running cooler / saving power. The driver writes `Dev_Config_0` and re-issues the `CMD_Update` latch (0x55) the chip requires for config-register writes to actually take effect. - `tile` — Initialised tile handle - `mode` — 25.5 mA (0) or 51 mA (1) ##### `tile_display_rgbw_set_short_threshold` — Studio ```c void tile_display_rgbw_set_short_threshold(tile_t *tile, disp_rgbw_lsd_threshold_t threshold) ``` Set the short-circuit detection threshold (fraction of VOUT). Lower thresholds catch milder partial-shorts; higher thresholds tolerate more LED forward-voltage variation without false alarms. Driver default (init) is 0.65 × VOUT — most permissive, least likely to mis-fire on cold LEDs whose Vf hasn't settled. - `tile` — Initialised tile handle - `threshold` — One of DISP_RGBW_LSD_TH_* ##### `tile_display_rgbw_set_short_shutdown` — Studio ```c void tile_display_rgbw_set_short_shutdown(tile_t *tile, uint8_t enabled) ``` Configure whether a short-circuit fault auto-disables outputs. When enabled, an LSD fault sends the chip into OFAF (one-fail-all- fail) state — every channel turns off until LSD_Clear is written. When disabled (driver default), the chip flags the fault but keeps driving — firmware decides what to do. - `tile` — Initialised tile handle - `enabled` — 1 = chip auto-shuts-down on LSD, 0 = report only ##### `tile_display_rgbw_set_open_shutdown` — Studio ```c void tile_display_rgbw_set_open_shutdown(tile_t *tile, uint8_t enabled) ``` Configure whether an open-circuit fault auto-disables a sink. When enabled (chip default), a per-channel LOD fault turns off that single current sink. When disabled, the chip flags the fault via `read_faults()` but keeps driving the channel — useful when the open is intermittent (e.g., a flexing wire) and firmware wants to retry rather than relying on the chip to recover. - `tile` — Initialised tile handle - `enabled` — 1 = chip auto-shuts-down a single sink, 0 = report only ##### `tile_display_rgbw_ms_to_slope` — Studio ```c uint8_t tile_display_rgbw_ms_to_slope(uint16_t ms) ``` Convert a duration in milliseconds to the nearest AEU slope/pause code. - `ms` — Duration in milliseconds (0-8050). **Returns:** 4-bit time code (0-15) for an AEU T-field or Auto_Pause field. ##### `tile_display_rgbw_set_autonomous` — Studio ```c void tile_display_rgbw_set_autonomous(tile_t *tile, uint8_t channel, uint8_t enabled) ``` Put a channel into (or out of) autonomous animation mode. In autonomous mode the on-chip engine runs the channel's AEU program with no MCU intervention; in manual mode the channel follows the PWM registers (set / set_color). Call update() after configuring. - `channel` — 0-3 (R, B, G, W). - `enabled` — 1 = autonomous, 0 = manual. ##### `tile_display_rgbw_set_aeu` — Studio ```c void tile_display_rgbw_set_aeu(tile_t *tile, uint8_t channel, uint8_t aeu, const display_rgbw_aeu_t *prog) ``` Program one AEU sub-engine of a channel. - `channel` — 0-3. - `aeu` — Sub-engine index 1-3. - `prog` — 5-keyframe ramp + slope times + repeat. ##### `tile_display_rgbw_set_animation` — Studio ```c void tile_display_rgbw_set_animation(tile_t *tile, uint8_t channel, uint8_t num_aeu, uint8_t pause_start, uint8_t pause_end, uint8_t repeats) ``` Set a channel's overall autonomous playback. - `channel` — 0-3. - `num_aeu` — How many AEUs to chain, 1-3. - `pause_start` — Start-of-pattern pause code (0-15, see ms_to_slope). - `pause_end` — End-of-pattern pause code (0-15). - `repeats` — Whole-pattern repeat: 0-14, 15 = infinite. ##### `tile_display_rgbw_set_exp_dimming` — Studio ```c void tile_display_rgbw_set_exp_dimming(tile_t *tile, uint8_t channel, uint8_t enabled) ``` Enable/disable exponential PWM dimming for a channel (Dev_Config_5). - `channel` — 0-3. - `enabled` — 1 = exponential curve, 0 = linear. ##### `tile_display_rgbw_set_phase_align` — Studio ```c void tile_display_rgbw_set_phase_align(tile_t *tile, uint8_t channel, uint8_t mode) ``` Set a channel's PWM phase-align method (Dev_Config_7). - `channel` — 0-3. - `mode` — 0/1 = forward, 2 = middle, 3 = backward align. #### Other ##### `tile_display_rgbw_find` ```c uint8_t tile_display_rgbw_find(tiles_pal_t *hal, uint8_t instance) ``` Check if a Disp.RGBW is present on the bus. ##### `tile_display_rgbw_init` ```c void tile_display_rgbw_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const disp_rgbw_cfg_t *cfg) ``` Initialize the LP5811 LED driver. Enables the chip, ramps the boost to 4.5 V in 0.1 V committed steps (~1.6 s — avoids the single-step inrush that can brown-out a marginal supply; see version history v2.3.0), sets max current to 51mA, enables all 4 LED channels, and sets current limits to 50%. LSD action is left at "no shutdown" (driver-level choice) so a transient short doesn't latch the device into OFAF state without firmware seeing it. Pass cfg=NULL for defaults. - `hal` — Platform abstraction handle - `instance` — Device instance (0 = default address 0x50) - `tile` — Tile handle to populate - `cfg` — Optional config, or NULL for defaults ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Multi-address support (0x50–0x53) (niche) Chip-gated. The four LP5811 addresses 0x50/0x51/0x52/0x53 are selected by Bit4/Bit3 of the chip-address byte, but those bits are fixed by the factory material variant (LP5811A/B/C/D, see datasheet §4 Device Comparison). They are not pin-strapped or register-configurable. The Display.RGBW (rev a) tile ships only the A variant. Adding the other three addresses requires a tile hardware revision that places the alternate part numbers on the PCB — not something the driver can close on its own. ### Enums #### `disp_rgbw_max_current_t` Per-channel maximum-current selector (LP5811 MC bit). | Member | Value | Description | | --- | --- | --- | | `DISP_RGBW_MAX_CURRENT_25_5_MA` | `0` | 25.5 mA full scale per channel | | `DISP_RGBW_MAX_CURRENT_51_MA` | `1` | 51 mA full scale per channel | #### `disp_rgbw_lsd_threshold_t` Short-circuit detection threshold (fraction of VOUT). | Member | Value | Description | | --- | --- | --- | | `DISP_RGBW_LSD_TH_0_35` | `0` | 0.35 × VOUT (most sensitive) | | `DISP_RGBW_LSD_TH_0_45` | `1` | 0.45 × VOUT | | `DISP_RGBW_LSD_TH_0_55` | `2` | 0.55 × VOUT | | `DISP_RGBW_LSD_TH_0_65` | `3` | 0.65 × VOUT (least sensitive — driver default) | ### Structs #### `disp_rgbw_faults_t` LED open / short fault snapshot. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `open_mask` | Bits 3:0 — channels with open-circuit fault. | | `uint8_t` | `short_mask` | Bits 3:0 — channels with short-circuit fault. | | `uint8_t` | `thermal_shutdown` | 1 = chip in thermal shutdown (TSD). | | `uint8_t` | `config_error` | 1 = configuration error reported by chip. | #### `disp_rgbw_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `reserved` | Placeholder — no options yet. | #### `display_rgbw_aeu_t` One animation sub-engine (AEU) program for a channel. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `repeats` | AEU pattern repeat: 0-2, 3 = infinite. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_DISP_RGBW_VERSION_MAJOR` | `2` | | | `TILE_DISP_RGBW_VERSION_MINOR` | `3` | | | `TILE_DISP_RGBW_VERSION_PATCH` | `0` | | | `LP5811_I2C_ADDR_DEFAULT` | `0x50` | | | `LP5811_CONFIG_2_DEFAULT` | `0xE4` | | | `LP5811_BOOST_VOUT_CODE_4V5` | `15` | | | `LP5811_CONFIG_0_MC_51MA` | `0x01` | | | `LP5811_BOOST_RAMP_STEP_MS` | `100` | | --- Generated 2026-08-12 from the driver header at tiles@842d43e. Canonical HTML: https://bergsonne.io/docs/tiles/display/rgbw ============================================================================== ## Drive.A.2 driver > Dual-channel audio output driver for the Drive.A.2 tile (DAC63202W smart DAC + 2x TPA2028D1 Class-D amplifiers). Supports I2C and SPI bus access via tiles_pal_t. | | | | --- | --- | | Tile | `Drive.A.2` | | Driver version | `3.1.0` | | Header | `tile_drive_a_2.h` | | Generated from | `tiles@faf9db5` | | Canonical page | https://bergsonne.io/docs/tiles/drive/a2 | ### Overview (DAC63202W smart DAC + 2x TPA2028D1 Class-D amplifiers). Supports I2C and SPI bus access via tiles_pal_t. @version 3.1.0 The Drive.A.2 tile provides two independent audio output channels, each consisting of a 12-bit DAC channel feeding a 3W Class-D amplifier. DAC features (DAC63202W): - 12-bit resolution, dual channel - Voltage output with configurable gain (1x, 1.5x, 2x, 3x, 4x) - Internal 1.21V reference or VDD/external reference - Built-in waveform generation: sine, triangle, sawtooth - I2C (up to 1 MHz) or SPI (up to 50 MHz), auto-detected at boot Amplifier features (TPA2028D1, x2): - 3W into 4 ohm, 880 mW into 8 ohm per channel - I2C-programmable gain: -28 dB to +30 dB in 1 dB steps - AGC with configurable compression, attack/release/hold times - Both amplifiers share I2C address 0x58 — writes affect both In SPI mode, only the DAC is controllable; amplifier functions become no-ops since the I2C bus pins are repurposed for SPI. Datasheet (DAC): https://www.ti.com/product/DAC63202W Datasheet (Amp): https://www.ti.com/product/TPA2028D1 ### Examples #### Quick start (I2C — DAC + amplifiers) ```c #include "core_tiles.h" // provides core_tiles_pal() tile_t dac; tiles_pal_t *hal = core_tiles_pal(&core_i2c1); tile_drive_a_2_init(hal, 0, &dac, NULL); if (tile_is_ready(&dac)) { tile_drive_a_2_set(&dac, 0, 2048); // Ch 0 mid-scale tile_drive_a_2_amp_set_gain(&dac, 12); // +12 dB both amps } ``` #### Quick start (SPI — DAC only, no amplifier control) ```c #include "core_tiles.h" tile_t dac; tiles_pal_t *hal = core_tiles_pal(&core_spi1); tile_drive_a_2_init(hal, 0, &dac, NULL); // instance = CS index tile_drive_a_2_set_mv(&dac, 0, 1500); // 1.5 V on channel 0 ``` ### API 34 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_drive_a_2_sleep` — Studio ```c void tile_drive_a_2_sleep(tile_t *tile) ``` Enter low-power sleep. Powers down both DAC VOUT channels (Hi-Z) and puts the amplifiers into software shutdown. ##### `tile_drive_a_2_wake` — Studio ```c void tile_drive_a_2_wake(tile_t *tile) ``` Wake from sleep. Powers up both DAC VOUT channels and re-enables the amplifiers. Restores cached gain settings. #### Runtime ##### `tile_drive_a_2_set` — Studio ```c void tile_drive_a_2_set(tile_t *tile, uint8_t channel, uint16_t value) ``` Set a DAC channel output by raw 12-bit code (0–4095). - `tile` — Tile handle - `channel` — 0 or 1 - `value` — 12-bit DAC code (0 = 0V, 4095 = full-scale) ##### `tile_drive_a_2_set_mv` — Studio ```c void tile_drive_a_2_set_mv(tile_t *tile, uint8_t channel, uint16_t mv) ``` Set a DAC channel output in millivolts. Computes the DAC code from the cached reference voltage and gain. - `tile` — Tile handle - `channel` — 0 or 1 - `mv` — Desired output in millivolts ##### `tile_drive_a_2_get` — Studio ```c uint16_t tile_drive_a_2_get(tile_t *tile, uint8_t channel) ``` Read back the current DAC code for a channel. - `tile` — Tile handle - `channel` — 0 or 1 **Returns:** 12-bit DAC code currently loaded ##### `tile_drive_a_2_set_waveform` — Studio ```c void tile_drive_a_2_set_waveform(tile_t *tile, uint8_t channel, drive_a_2_wave_t wave) ``` Configure the waveform shape for a channel. Sets FUNC-CONFIG-X in DAC-X-FUNC-CONFIG. The waveform oscillates between DAC-X-MARGIN-LOW and DAC-X-MARGIN-HIGH at the configured slew rate. Call start_waveform() to begin output. - `tile` — Tile handle - `channel` — 0 or 1 - `wave` — Waveform shape ##### `tile_drive_a_2_start_waveform` — Studio ```c void tile_drive_a_2_start_waveform(tile_t *tile, uint8_t channel) ``` Start waveform generation on a channel. - `tile` — Tile handle - `channel` — 0 or 1 ##### `tile_drive_a_2_stop_waveform` — Studio ```c void tile_drive_a_2_stop_waveform(tile_t *tile, uint8_t channel) ``` Stop waveform generation on a channel. - `tile` — Tile handle - `channel` — 0 or 1 ##### `tile_drive_a_2_amp_set_gain` — Studio ```c void tile_drive_a_2_amp_set_gain(tile_t *tile, int8_t gain_db) ``` Set the amplifier fixed gain. Both TPA2028D1 amplifiers share I2C address 0x58, so this write affects both channels simultaneously. No-op in SPI mode. - `tile` — Tile handle - `gain_db` — Gain in dB (-28 to +30). Clamped if out of range. ##### `tile_drive_a_2_amp_get_gain` — Studio ```c int8_t tile_drive_a_2_amp_get_gain(tile_t *tile) ``` Read the current amplifier fixed gain. - `tile` — Tile handle **Returns:** Gain in dB (-28 to +30), or 0 if amp not available ##### `tile_drive_a_2_amp_enable` — Studio ```c void tile_drive_a_2_amp_enable(tile_t *tile) ``` Enable the amplifiers (clear software shutdown). No-op in SPI mode. ##### `tile_drive_a_2_amp_disable` — Studio ```c void tile_drive_a_2_amp_disable(tile_t *tile) ``` Disable the amplifiers (enter software shutdown). No-op in SPI mode. ##### `tile_drive_a_2_amp_set_agc` — Studio ```c void tile_drive_a_2_amp_set_agc(tile_t *tile, const drive_a_2_agc_cfg_t *cfg) ``` Configure the full AGC/DRC parameters. Writes all AGC registers (attack, release, hold, fixed gain, limiter, compression, noise gate, max gain). Affects both amps. No-op in SPI mode. - `tile` — Tile handle - `cfg` — AGC configuration ##### `tile_drive_a_2_amp_read_status` — Studio ```c uint8_t tile_drive_a_2_amp_read_status(tile_t *tile) ``` Read the amplifier status register. Check bit 3 (FAULT) for short-circuit and bit 2 (Thermal) for over-temperature. Write 0 to the respective bit to clear. - `tile` — Tile handle **Returns:** Raw register 0x01 value, or 0 if amp not available ##### `tile_drive_a_2_read_status` — Studio ```c uint16_t tile_drive_a_2_read_status(tile_t *tile) ``` Read the DAC GENERAL-STATUS register. Contains DEVICE-ID, VERSION-ID, NVM CRC status, and DAC busy flags. - `tile` — Tile handle **Returns:** 16-bit GENERAL-STATUS value ##### `tile_drive_a_2_play_tone` — Studio ```c void tile_drive_a_2_play_tone(tile_t *tile, drive_a_2_channel_t channel, uint16_t freq_hz, uint16_t ms) ``` Play a sine tone on `channel` for `ms` milliseconds. Configures DAC margins for full-scale swing, sets a sine waveform, and uses the on-chip parametric generator together with a software-tuned slew rate / code step to approximate `freq_hz` (audio range 50–5000 Hz works well; the chip's function generator is rough at the high end). The amplifier is unmuted for the duration of the tone and restored to its prior mute state on return. - `tile` — Initialised tile handle - `channel` — DRIVE_A_2_CH_LEFT, _RIGHT, or _BOTH - `freq_hz` — Tone frequency in Hz (1–20000 useful range) - `ms` — Duration in milliseconds **Note:** Blocks for `ms` milliseconds via `hal->delay_ms`. ##### `tile_drive_a_2_play_silence` — Studio ```c void tile_drive_a_2_play_silence(tile_t *tile, drive_a_2_channel_t channel, uint16_t ms) ``` Hold the channel at silence (DAC mid-scale) for `ms` ms. Stops any active waveform on `channel`, parks the DAC code at mid-scale (= zero differential at the amp input), and blocks for `ms` milliseconds. Useful to insert a precise gap between tones without toggling the amp shutdown — the amp stays alive but draws minimal idle current with no signal. - `tile` — Initialised tile handle - `channel` — DRIVE_A_2_CH_LEFT, _RIGHT, or _BOTH - `ms` — Duration in milliseconds **Note:** Blocks for `ms` milliseconds via `hal->delay_ms`. ##### `tile_drive_a_2_play_chirp` — Studio ```c void tile_drive_a_2_play_chirp(tile_t *tile, drive_a_2_channel_t channel, uint16_t start_hz, uint16_t end_hz, uint16_t ms) ``` Linear frequency sweep on `channel` from `start_hz` to `end_hz`. Software sine-LUT chirp delivered through `set` (raw DAC code) at ~8 kHz update rate. Frequency advances linearly across `ms`. For ascending sweeps pass start < end; for descending, start > end. The amp is unmuted for the duration and restored to its prior mute state on return. - `tile` — Initialised tile handle - `channel` — DRIVE_A_2_CH_LEFT, _RIGHT, or _BOTH - `start_hz` — Initial frequency in Hz - `end_hz` — Final frequency in Hz - `ms` — Sweep duration in milliseconds **Note:** Blocks for `ms` milliseconds via `hal->delay_ms`. ##### `tile_drive_a_2_set_volume_pct` — Studio ```c void tile_drive_a_2_set_volume_pct(tile_t *tile, drive_a_2_channel_t channel, uint8_t pct) ``` Map a 0–100 percent volume to amplifier fixed gain. Linear mapping from `pct` to the TPA2028D1's full -28 dB to +30 dB programmable range: gain_db = -28 + (pct × 58) / 100 0 % → -28 dB (near-mute), 50 % → +1 dB, 100 % → +30 dB. The mapping is intentionally linear-in-dB rather than perceptually weighted; for finer dB control use @ref tile_drive_a_2_amp_set_gain directly. Because both amps share an I²C address, the channel argument is accepted for symmetry with the rest of the tier-2 API but the write lands on both physical amps. - `tile` — Initialised tile handle - `channel` — DRIVE_A_2_CH_LEFT, _RIGHT, or _BOTH (advisory) - `pct` — 0–100 percent (clamped if out of range) ##### `tile_drive_a_2_mute` — Studio ```c void tile_drive_a_2_mute(tile_t *tile, drive_a_2_channel_t channel) ``` Mute `channel` by entering amp software shutdown. Stashes the current fixed-gain register so a later @ref tile_drive_a_2_unmute restores it byte-exact, then puts the amp pair into software shutdown (SWS=1). Both physical amps mute regardless of `channel` (shared I²C address). - `tile` — Initialised tile handle - `channel` — DRIVE_A_2_CH_LEFT, _RIGHT, or _BOTH (advisory) ##### `tile_drive_a_2_unmute` — Studio ```c void tile_drive_a_2_unmute(tile_t *tile, drive_a_2_channel_t channel) ``` Restore audio output after a previous `mute` call. Re-applies the gain that was active at the time of @ref tile_drive_a_2_mute and clears software shutdown (SWS=0). If the channel was never muted by this driver instance the call still wakes the amp using whatever gain is currently in the register. - `tile` — Initialised tile handle - `channel` — DRIVE_A_2_CH_LEFT, _RIGHT, or _BOTH (advisory) #### Configuration ##### `tile_drive_a_2_set_gain` — Studio ```c void tile_drive_a_2_set_gain(tile_t *tile, uint8_t channel, drive_a_2_gain_t gain) ``` Set the voltage output gain for a DAC channel. Automatically enables the internal reference when an internal-reference gain is selected, and updates the cached Vref for set_mv() calculations. - `tile` — Tile handle - `channel` — 0 or 1 - `gain` — One of the drive_a_2_gain_t values ##### `tile_drive_a_2_set_slew_rate` — Studio ```c void tile_drive_a_2_set_slew_rate(tile_t *tile, uint8_t channel, drive_a_2_slew_t slew) ``` Set the slew rate (time per code step) for a DAC channel. Programs SLEW-RATE-X bits[3:0] in DAC-X-FUNC-CONFIG. Affects both slewed direct-output updates and the on-chip function generator's frequency. The default is no-slew — outputs settle as fast as the analog stage allows. Use a slower slew to soften zero-crossings for capacitive / inductive loads, or to dial in a target waveform frequency together with set_code_step() and set_margins(). - `tile` — Tile handle - `channel` — 0 or 1 - `slew` — Slew-rate code (drive_a_2_slew_t value 0–15) ##### `tile_drive_a_2_set_code_step` — Studio ```c void tile_drive_a_2_set_code_step(tile_t *tile, uint8_t channel, drive_a_2_step_t step) ``` Set the code step (LSBs per slew tick) for a DAC channel. Programs CODE-STEP-X bits[6:4] in DAC-X-FUNC-CONFIG. Larger steps give faster ramps / higher waveform frequencies at the cost of coarser resolution. - `tile` — Tile handle - `channel` — 0 or 1 - `step` — Code-step code (drive_a_2_step_t value 0–7) ##### `tile_drive_a_2_set_margins` — Studio ```c void tile_drive_a_2_set_margins(tile_t *tile, uint8_t channel, uint16_t low, uint16_t high) ``` Set the upper / lower bounds for waveform & window-comparator modes. Writes DAC-X-MARGIN-HIGH and DAC-X-MARGIN-LOW. The function generator oscillates between these levels, and they also serve as the thresholds for the chip's window / hysteresis comparator modes. Values are 12-bit DAC codes; high must be > low. - `tile` — Tile handle - `channel` — 0 or 1 - `low` — 12-bit DAC code for the lower bound (0–4095) - `high` — 12-bit DAC code for the upper bound (0–4095) ##### `tile_drive_a_2_set_phase` — Studio ```c void tile_drive_a_2_set_phase(tile_t *tile, uint8_t channel, drive_a_2_phase_t phase) ``` Set the phase offset for the function generator. Programs PHASE-SEL-X bits[12:11] in DAC-X-FUNC-CONFIG. Take effect the next time start_waveform() is called. Use phase = 90° on one channel and 0° on the other to drive a quadrature pair. - `tile` — Tile handle - `channel` — 0 or 1 - `phase` — Phase offset (drive_a_2_phase_t) ##### `tile_drive_a_2_set_waveform_params` — Studio ```c void tile_drive_a_2_set_waveform_params(tile_t *tile, uint8_t channel, drive_a_2_wave_t wave, drive_a_2_step_t step, drive_a_2_slew_t slew) ``` Configure the parametric on-chip waveform generator. Convenience helper that sets up the waveform shape, full-scale margins (0 → 4095), code step, and slew rate in one call. The resulting frequency is approximately: f_triangle = 1 / (2 × time_step × ceil((margin_high − margin_low) / code_step)) f_sawtooth = 1 / ( time_step × ceil((margin_high − margin_low) / code_step + 1)) Call start_waveform() to begin output once configured. For fine-grained control, call set_margins() / set_code_step() / set_slew_rate() / set_waveform() / set_phase() individually. - `tile` — Tile handle - `channel` — 0 or 1 - `wave` — Waveform shape (drive_a_2_wave_t) - `step` — Code-step (drive_a_2_step_t) - `slew` — Time per step (drive_a_2_slew_t) ##### `tile_drive_a_2_nvm_save` — Studio ```c void tile_drive_a_2_nvm_save(tile_t *tile) ``` Save the DAC's current register state into shadow NVM. Triggers NVM-PROG in COMMON-TRIGGER. The DAC's user-programmable registers (gain, margins, slew, waveform shape, COMMON-CONFIG, etc. — see datasheet "highlighted gray" rows) are committed to non-volatile storage and become the new power-on defaults. Blocking: holds the bus busy for the NVM write cycle. Limited write endurance — TI specs ~1000 cycles. Use only for one-time factory tuning, not for runtime configuration storage. - `tile` — Tile handle ##### `tile_drive_a_2_nvm_reload` — Studio ```c void tile_drive_a_2_nvm_reload(tile_t *tile) ``` Reload all DAC registers from shadow NVM. Triggers NVM-RELOAD in COMMON-TRIGGER. Restores the saved power-on configuration without a full reset — equivalent to the load that happens automatically on POR. Blocks until the reload completes. Note that this resets the cached gain / vref state in the driver: call set_gain() afterwards if you need set_mv() to work correctly. - `tile` — Tile handle #### Advanced ##### `tile_drive_a_2_read_reg` — Studio ```c uint16_t tile_drive_a_2_read_reg(tile_t *tile, uint8_t reg) ``` Read any 16-bit DAC63202W register. Escape hatch for advanced users wanting to touch registers the driver doesn't expose. Caller is responsible for not bricking the chip — most useful registers have typed setters above. - `tile` — Tile handle - `reg` — Register address (7-bit) **Returns:** 16-bit register value ##### `tile_drive_a_2_write_reg` — Studio ```c void tile_drive_a_2_write_reg(tile_t *tile, uint8_t reg, uint16_t value) ``` Write any 16-bit DAC63202W register. - `tile` — Tile handle - `reg` — Register address (7-bit) - `value` — 16-bit value to write (big-endian on the wire) #### Other ##### `tile_drive_a_2_find` ```c uint8_t tile_drive_a_2_find(tiles_pal_t *hal, uint8_t instance) ``` Check whether a DAC63202W is present on the bus. - `hal` — Platform HAL handle - `instance` — Instance index (see mapping table) **Returns:** 1 if device ACKs (I2C) or responds to WHO_AM_I (SPI), 0 otherwise ##### `tile_drive_a_2_init` ```c void tile_drive_a_2_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const drive_a_2_cfg_t *cfg) ``` Initialize the Drive.A.2 tile. Resets the DAC, verifies DEVICE-ID, powers up both VOUT channels, configures gain, and (in I2C mode) probes and configures the amplifiers. - `hal` — Platform HAL handle (see core_tiles.h). - `instance` — Instance index (I2C: address variant, SPI: CS index) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults (1x VDD, 6 dB amp) ##### `tile_drive_a_2_reset` ```c void tile_drive_a_2_reset(tile_t *tile) ``` Perform a software reset of the DAC. All DAC registers return to defaults. tile->state becomes TILE_STATE_NONE; call init() again to reconfigure. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### DAC PWM-output mode (FBx pins) (advanced) The DAC63202W FB0 / FB1 pins (package balls B2 / C2) can be configured as comparator / PWM outputs for driving external power-stage gates. On the Drive.A.2 tile these pins are tied to the on-board TPA2028D1 amplifier inputs (closed-loop voltage- output feedback) and are NOT routed to any tile-connector pad — so even if the driver enabled PWM mode, no Core GPIO could observe or use the output. Closing this gap requires a tile hardware revision that breaks FBx out to a pad. #### Per-amp independent control (niche) The tile carries 2× TPA2028D1 amps wired to a single shared I²C bus, and the TPA2028D1 has a fixed factory I²C address (0x58) — any write at 0x58 is acknowledged by both amps simultaneously. Independent left / right gain, AGC, or shutdown therefore can't be achieved in software; closing this gap requires either an I²C-mux on the tile or amp ICs with selectable addresses. ### Enums #### `drive_a_2_gain_t` DAC output voltage gain setting. | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_GAIN_1X_EXT` | `0` | 1x, external VREF pin | | `DRIVE_A_2_GAIN_1X_VDD` | `1` | 1x, VDD as reference (default) | | `DRIVE_A_2_GAIN_1P5X_INT` | `2` | 1.5x, internal 1.21V → 0–1.815V | | `DRIVE_A_2_GAIN_2X_INT` | `3` | 2x, internal 1.21V → 0–2.42V | | `DRIVE_A_2_GAIN_3X_INT` | `4` | 3x, internal 1.21V → 0–3.63V | | `DRIVE_A_2_GAIN_4X_INT` | `5` | 4x, internal 1.21V → 0–4.84V | #### `drive_a_2_wave_t` Built-in waveform shapes for the DAC function generator. | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_WAVE_TRIANGLE` | `0` | Triangular wave | | `DRIVE_A_2_WAVE_SAWTOOTH` | `1` | Sawtooth (ramp up) | | `DRIVE_A_2_WAVE_INV_SAW` | `2` | Inverse sawtooth (ramp down) | | `DRIVE_A_2_WAVE_SINE` | `4` | Sine wave | | `DRIVE_A_2_WAVE_OFF` | `7` | Function generation disabled | #### `drive_a_2_slew_t` Slew-rate control (time per code step) for the DAC. | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_SLEW_NONE` | `0x0` | No slew (default) — immediate update | | `DRIVE_A_2_SLEW_4_US` | `0x1` | | | `DRIVE_A_2_SLEW_8_US` | `0x2` | | | `DRIVE_A_2_SLEW_12_US` | `0x3` | | | `DRIVE_A_2_SLEW_18_US` | `0x4` | | | `DRIVE_A_2_SLEW_27_US` | `0x5` | | | `DRIVE_A_2_SLEW_41_US` | `0x6` | | | `DRIVE_A_2_SLEW_61_US` | `0x7` | | | `DRIVE_A_2_SLEW_91_US` | `0x8` | | | `DRIVE_A_2_SLEW_137_US` | `0x9` | | | `DRIVE_A_2_SLEW_239_US` | `0xA` | | | `DRIVE_A_2_SLEW_419_US` | `0xB` | | | `DRIVE_A_2_SLEW_733_US` | `0xC` | | | `DRIVE_A_2_SLEW_1282_US` | `0xD` | | | `DRIVE_A_2_SLEW_2564_US` | `0xE` | | | `DRIVE_A_2_SLEW_5128_US` | `0xF` | | #### `drive_a_2_step_t` Code-step size (LSBs per slewed update tick) for the DAC. | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_STEP_1_LSB` | `0x0` | 1 LSB per step (default — finest) | | `DRIVE_A_2_STEP_2_LSB` | `0x1` | | | `DRIVE_A_2_STEP_3_LSB` | `0x2` | | | `DRIVE_A_2_STEP_4_LSB` | `0x3` | | | `DRIVE_A_2_STEP_6_LSB` | `0x4` | | | `DRIVE_A_2_STEP_8_LSB` | `0x5` | | | `DRIVE_A_2_STEP_16_LSB` | `0x6` | | | `DRIVE_A_2_STEP_32_LSB` | `0x7` | | #### `drive_a_2_phase_t` Phase offset for the function generator (sine / triangle). | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_PHASE_0` | `0` | 0° (default) | | `DRIVE_A_2_PHASE_120` | `1` | 120° | | `DRIVE_A_2_PHASE_240` | `2` | 240° | | `DRIVE_A_2_PHASE_90` | `3` | 90° (quadrature) | #### `drive_a_2_comp_t` Amplifier compression ratio. | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_COMP_1_1` | `0` | 1:1 — compression off | | `DRIVE_A_2_COMP_2_1` | `1` | 2:1 | | `DRIVE_A_2_COMP_4_1` | `2` | 4:1 (default) | | `DRIVE_A_2_COMP_8_1` | `3` | 8:1 | #### `drive_a_2_channel_t` Logical channel selector for the tier-2 runtime helpers (`play_tone`, `set_volume_pct`, `mute`, …). | Member | Value | Description | | --- | --- | --- | | `DRIVE_A_2_CH_LEFT` | `0` | DAC0 / left amp logically | | `DRIVE_A_2_CH_RIGHT` | `1` | DAC1 / right amp logically | | `DRIVE_A_2_CH_BOTH` | `2` | Both DAC channels (amp writes still hit both) | ### Structs #### `drive_a_2_agc_cfg_t` Full AGC/DRC configuration for the TPA2028D1 amplifiers. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `compression` | drive_a_2_comp_t. 0 = 1:1 (off). | | `int8_t` | `fixed_gain_db` | -28 to +30 dB. 0 = 0 dB. | | `uint8_t` | `max_gain_db` | 18 to 30 dB. 0 = 18 dB (use 30 for default). | | `uint8_t` | `limiter_level` | 0–31 (0.5 dB steps from -6.5 dBV). 0 = -6.5 dBV. | | `uint8_t` | `attack` | 0–63 (0.1067 ms per step). 0 = fastest. | | `uint8_t` | `release` | 0–63 (0.0137 s per step). 0 = fastest. | | `uint8_t` | `hold` | 0–63 (0.0137 s per step). 0 = disabled. | | `uint8_t` | `noise_gate` | 0–3 (0=1mV, 1=4mV, 2=10mV, 3=20mV rms). | #### `drive_a_2_cfg_t` Optional configuration for tile_drive_a_2_init(). | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `gain` | DAC gain (drive_a_2_gain_t). Default: DRIVE_A_2_GAIN_1X_VDD. | | `int8_t` | `amp_gain_db` | Amplifier fixed gain in dB (-28 to +30). Default: 6 dB. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_DRIVE_A_2_VERSION_MAJOR` | `3` | | | `TILE_DRIVE_A_2_VERSION_MINOR` | `1` | | | `TILE_DRIVE_A_2_VERSION_PATCH` | `0` | | | `DAC63202W_I2C_ADDR_DEFAULT` | `0x49` | A0 → VDD (default) | | `DAC63202W_I2C_ADDR_GND` | `0x48` | A0 → GND | | `DAC63202W_I2C_ADDR_SDA` | `0x4A` | A0 → SDA | | `DAC63202W_I2C_ADDR_SCL` | `0x4B` | A0 → SCL | | `TPA2028D1_I2C_ADDR` | `0x58` | Fixed (both amps) | --- Generated 2026-08-12 from the driver header at tiles@faf9db5. Canonical HTML: https://bergsonne.io/docs/tiles/drive/a2 ============================================================================== ## Drive.DC.H driver > H-bridge DC motor driver for the Drive.DC.H tile (rev a). | | | | --- | --- | | Tile | `Drive.DC.H` | | Driver version | `4.2.0` | | Header | `tile_drive_dc_h.h` | | Generated from | `tiles@7738ede` | | Canonical page | https://bergsonne.io/docs/tiles/drive/dch | ### Overview Embeds the TI DRV8214, a full H-bridge motor driver with integrated current sensing, stall detection, and voltage/speed regulation. Key specifications: - Output: full H-bridge, 4 A peak / 2 A RMS continuous - Supply: 1.65-11 V motor supply (VM), 1.65-5.5 V logic (VCC) - R_DS(on): 240 mOhm (high + low side total) - Regulation: voltage or speed, via internal PI loop - Current sense: internal current mirror (IPROPI), 3 gain settings - Stall detect: hardware, configurable via IPROPI/VREF threshold - Ripple count: sensorless position/speed via commutation ripples - Protection: UVLO, OCP, OVP, thermal shutdown The tile also exposes EN/PH (or IN1/IN2) control pads (pads 2, 3) for direct GPIO/PWM-based motor control without I²C, and an analog current-proportional output on pad 6 (NPROP/IPROPI) that mirrors the motor winding current scaled by the CS_GAIN_SEL setting. Switch the bridge control source at runtime with set_control_mode(). The DRV8214 integrates a sensorless ripple counting algorithm that counts commutation ripples in the motor current waveform to estimate rotor position and speed without external encoders or Hall sensors. In speed regulation mode (DRIVE_DC_H_MODE_SPEED), the chip's internal PI loop maintains constant motor speed across varying supply voltages and loads. The ripple count and estimated speed can be read at any time via get_ripple_count() / get_speed(). Accurate ripple counting requires motor-specific tuning of the INV_R, KMC, and filter parameters (see DRV8214 datasheet section 9.2.3.1). Datasheet: https://www.bergsonne.io/tiles/drive/dc-h IC datasheet: https://www.ti.com/lit/ds/symlink/drv8214.pdf ### Examples #### Quick start ```c #include "core_tiles.h" tile_t motor; tile_drive_dc_h_init(core_tiles_pal(&core_i2c3), 0, &motor, NULL); if (tile_is_ready(&motor)) { tile_drive_dc_h_forward(&motor); // drive forward core_delay_ms(2000); tile_drive_dc_h_brake(&motor); // slow-decay brake } ``` ### API 32 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_drive_dc_h_sleep` — Studio ```c void tile_drive_dc_h_sleep(tile_t* tile) ``` Disable the output stage (all FETs Hi-Z). Clears EN_OUT in CONFIG0. The device remains on the I2C bus and registers are accessible, but no current flows through the motor. OVP protection remains active in sleep. - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_wake` — Studio ```c void tile_drive_dc_h_wake(tile_t* tile) ``` Re-enable the output stage. Sets EN_OUT in CONFIG0. The bridge returns to the last commanded state (coast/brake/forward/reverse). - `tile` — Pointer to tile handle #### Runtime ##### `tile_drive_dc_h_forward` — Studio ```c void tile_drive_dc_h_forward(tile_t* tile) ``` Drive motor forward (OUT1=H, OUT2=L). - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_reverse` — Studio ```c void tile_drive_dc_h_reverse(tile_t* tile) ``` Drive motor in reverse (OUT1=L, OUT2=H). - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_brake` — Studio ```c void tile_drive_dc_h_brake(tile_t* tile) ``` Active brake (slow-decay, both low-side FETs on). Motor is actively held. Current recirculates through the low-side FETs, providing strong braking force. - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_coast` — Studio ```c void tile_drive_dc_h_coast(tile_t* tile) ``` Coast (Hi-Z, all FETs off). Motor freewheels. No braking force is applied. This is the initial state after init(). - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_set_target` — Studio ```c void tile_drive_dc_h_set_target(tile_t* tile, uint8_t value) ``` Set the regulation target (WSET_VSET). In voltage mode: sets target motor terminal voltage. With VM_GAIN_SEL=0: value * 15700/255 mV (61.6 mV/bit) With VM_GAIN_SEL=1: value * 3920/255 mV (15.4 mV/bit) In speed mode: sets target ripple speed. Target speed = value * W_SCALE rad/s. - `tile` — Pointer to tile handle - `value` — Target setpoint (0-255) ##### `tile_drive_dc_h_get_fault` — Studio ```c uint8_t tile_drive_dc_h_get_fault(tile_t* tile) ``` Read the raw FAULT register (0x00). Contains FAULT[7], STALL[5], OCP[4], OVP[3], TSD[2], NPOR[1], CNT_DONE[0]. Use the DRV8214_FAULT_* masks to decode individual bits. - `tile` — Pointer to tile handle **Returns:** Raw fault byte (0 = no faults) ##### `tile_drive_dc_h_clear_fault` — Studio ```c void tile_drive_dc_h_clear_fault(tile_t* tile) ``` Clear all latched faults. Sets CLR_FLT in CONFIG0. Clears FAULT, OCP, OVP, TSD, and NPOR bits. The CLR_FLT bit is self-clearing. - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_is_stalled` — Studio ```c uint8_t tile_drive_dc_h_is_stalled(tile_t* tile) ``` Check if a motor stall condition is active. - `tile` — Pointer to tile handle **Returns:** 1 if STALL bit is set, 0 otherwise ##### `tile_drive_dc_h_get_voltage_mv` — Studio ```c uint16_t tile_drive_dc_h_get_voltage_mv(tile_t* tile) ``` Read motor terminal voltage in millivolts. Reads the VMTR register. The reading is proportional to the voltage across OUT1-OUT2 terminals. Valid while driving. - `tile` — Pointer to tile handle **Returns:** Motor voltage in mV (e.g. 3300 = 3.3 V) ##### `tile_drive_dc_h_get_current_ma` — Studio ```c uint16_t tile_drive_dc_h_get_current_ma(tile_t* tile) ``` Read motor current in milliamps. Reads the IMTR register and converts using the current CS_GAIN_SEL setting. The reading reflects the current flowing through the low-side FETs during drive or brake. - `tile` — Pointer to tile handle **Returns:** Motor current in mA ##### `tile_drive_dc_h_get_speed` — Studio ```c uint8_t tile_drive_dc_h_get_speed(tile_t* tile) ``` Read the ripple speed estimate. Returns the raw SPEED register from the ripple counting algorithm. Value is proportional to motor speed but requires motor-specific calibration for RPM conversion. - `tile` — Pointer to tile handle **Returns:** Raw speed estimate (0-255) ##### `tile_drive_dc_h_get_speed_rpm` — Studio ```c uint32_t tile_drive_dc_h_get_speed_rpm(tile_t* tile) ``` Read the estimated motor speed in RPM. Converts the chip's ripple-speed estimate to shaft RPM using the live W_SCALE setting and the configured ripples-per-rev, making it the exact inverse of the set_speed_rpm() target conversion. Accuracy depends on the motor profile (see set_motor_params()); without INV_R/KMC calibration the estimate is approximate. - `tile` — Pointer to tile handle **Returns:** Estimated shaft speed in revolutions per minute ##### `tile_drive_dc_h_get_ripple_count` — Studio ```c uint16_t tile_drive_dc_h_get_ripple_count(tile_t* tile) ``` Read the 16-bit ripple count. Returns the total number of commutation ripples counted since the last clear. Proportional to rotor position. - `tile` — Pointer to tile handle **Returns:** Ripple count (0-65535) ##### `tile_drive_dc_h_clear_ripple_count` — Studio ```c void tile_drive_dc_h_clear_ripple_count(tile_t* tile) ``` Reset the ripple counter to zero. Sets CLR_CNT in CONFIG0. Also clears CNT_DONE flag. The CLR_CNT bit is self-clearing. - `tile` — Pointer to tile handle ##### `tile_drive_dc_h_set_speed_rpm` — Studio ```c void tile_drive_dc_h_set_speed_rpm(tile_t* tile, uint32_t rpm, drive_dc_h_direction_t direction) ``` Drive at a target speed (RPM) with closed-loop regulation. Switches the chip into speed-regulation mode (REG_CTRL=10b), writes WSET_VSET so the internal PI loop targets `rpm` for the given motor, and engages the bridge in `direction`. Returns immediately — the chip ramps the motor to the target speed autonomously and is_running() / get_speed() report progress. - `tile` — Initialised tile handle - `rpm` — Target shaft speed in revolutions per minute - `direction` — DRIVE_DC_H_DIR_FORWARD or DRIVE_DC_H_DIR_REVERSE **Note:** The driver picks the finest W_SCALE (24/40/64/128) whose 8-bit WSET range still reaches the requested speed, so low targets get the best granularity the chip offers: the RPM step is `60 × W_SCALE / ripples_per_rev`, e.g. 120 RPM at ripples_per_rev=12 for targets below ~30 600 RPM. WSET is clamped at 0xFF for targets beyond the coarsest scale, and a nonzero request never rounds down to a stop. **Note:** The conversion depends on the ripples-per-rev value from the init config (or set_motor_params()), and closed-loop accuracy depends on the ripple-counter calibration (INV_R, KMC) from `motor_mohm` / `kv_uv_per_rpm`. If those aren't set, speed regulation falls back to chip defaults and the RPM target is approximate. Provide a full motor profile — at init or via set_motor_params() — for accurate low-RPM control. ##### `tile_drive_dc_h_move_distance` — Studio ```c void tile_drive_dc_h_move_distance(tile_t* tile, uint16_t ripples, drive_dc_h_direction_t direction) ``` Drive `ripples` commutation ripples in `direction`, then brake. Closed-loop step: programs RC_THR for the requested ripple count, clears the ripple counter, engages the bridge in `direction`, then polls until either CNT_DONE fires or the motor stalls. On completion the bridge is put into slow-decay brake — the motor is actively held at the new position, not coasting. - `tile` — Initialised tile handle - `ripples` — Number of ripples to advance (0–65472) - `direction` — DRIVE_DC_H_DIR_FORWARD or DRIVE_DC_H_DIR_REVERSE **Note:** This call BLOCKS until the move completes or the motor stalls. Worst-case wall-time depends on motor speed + load; budget accordingly. There is no built-in timeout beyond the chip's own stall detector — use a separate watchdog or @ref tile_drive_dc_h_wait_for_stop with a timeout if you need a hard upper bound. **Note:** On completion the bridge is left in BRAKE (slow decay, both low-side FETs on). Call @ref tile_drive_dc_h_coast afterwards if you'd rather let the motor freewheel. **Note:** Ripple counts are coarse (integer commutation ticks, not encoder counts). Resolution = 1/ripples_per_rev of a revolution — typically 1/12 turn. Don't expect sub-degree positioning. ##### `tile_drive_dc_h_is_running` — Studio ```c uint8_t tile_drive_dc_h_is_running(tile_t* tile) ``` Check whether the bridge is actively driving the motor. Returns 1 when the bridge is in forward or reverse drive (any non-zero, non-brake combination of IN1/IN2 in I²C bridge mode). Returns 0 when braking, coasting, sleeping, in pad-control mode, or not yet ready. - `tile` — Initialised tile handle **Returns:** 1 if driving, 0 otherwise ##### `tile_drive_dc_h_wait_for_stop` — Studio ```c uint8_t tile_drive_dc_h_wait_for_stop(tile_t* tile, uint32_t timeout_ms) ``` Block until the ripple counter stops advancing or `timeout_ms` elapses. Polls the 16-bit ripple counter at ~10 ms cadence. Returns 1 when two successive samples (≥30 ms apart) report the same count — i.e., the rotor is mechanically stationary. Returns 0 if `timeout_ms` elapses first. Useful after @ref tile_drive_dc_h_brake when you want to know the motor has actually settled rather than just "the bridge has been commanded to brake". - `tile` — Initialised tile handle - `timeout_ms` — Maximum time to wait, in milliseconds **Returns:** 1 if the rotor settled before timeout, 0 on timeout #### Configuration ##### `tile_drive_dc_h_set_control_mode` — Studio ```c void tile_drive_dc_h_set_control_mode(tile_t* tile, drive_dc_h_control_mode_t mode) ``` Switch the bridge control source between I²C and the EN/PH or IN1/IN2 tile pads. In I²C mode (default after init), forward/reverse/brake/coast control the bridge. In either pad-control mode, the chip ignores I²C bridge bits and tracks pads 2/3 directly: - PAD_PHEN — pad 2 = EN (PWM), pad 3 = PH (direction) - PAD_IN1IN2 — pad 2 = IN1, pad 3 = IN2 (independent half-bridges) Monitoring (voltage, current, fault, ripple count) keeps working over I²C in all modes. Switching to a pad mode disables I²C bridge calls (forward/reverse/etc) — they'll log an error and no-op. - `tile` — Pointer to tile handle - `mode` — Bridge control source (DRIVE_DC_H_CTRL_*) ##### `tile_drive_dc_h_set_regulation_mode` — Studio ```c void tile_drive_dc_h_set_regulation_mode(tile_t* tile, drive_dc_h_reg_mode_t mode) ``` Select the regulation loop (open-loop / current / voltage / speed). Writes REG_CTRL bits in REG_CTRL0. After switching, set_target() is interpreted in the new loop's units (voltage code, speed code, etc.). Speed mode requires ripple counting to be enabled — the driver automatically sets EN_RC=1 when SPEED is selected. - `tile` — Pointer to tile handle - `mode` — Regulation mode (DRIVE_DC_H_REG_*) ##### `tile_drive_dc_h_set_current_regulation_mode` — Studio ```c void tile_drive_dc_h_set_current_regulation_mode(tile_t* tile, drive_dc_h_imode_t mode) ``` Set the current-regulation mode (IMODE in CONFIG3). Selects when the chip's internal current loop folds back to keep motor current under the ITRIP threshold. ITRIP itself is set by the combination of CS_GAIN_SEL (set_current_sense_gain) and the internal 500 mV VREF (INT_VREF=1, fixed by this driver since the external VREF pin isn't routed on the Drive.DC.H tile). - `tile` — Pointer to tile handle - `mode` — Current regulation mode (DRIVE_DC_H_IMODE_*) ##### `tile_drive_dc_h_set_current_sense_gain` — Studio ```c void tile_drive_dc_h_set_current_sense_gain(tile_t* tile, uint8_t code) ``` Set the current-sense gain / max-current range. Programs CS_GAIN_SEL[2:0] in RC_CTRL0. Lower max-current ranges give finer current resolution but lower R_DS(on) headroom; higher ranges support larger motors but coarser get_current_ma() steps. Codes: 0 = 4 A, 1 = 2 A, 2 = 1 A, 3 = 0.5 A, 4 = 0.25 A, 5 = 0.125 A Also affects ITRIP when current regulation is enabled. - `tile` — Pointer to tile handle - `code` — CS_GAIN_SEL code (0-5) ##### `tile_drive_dc_h_set_stall_enabled` — Studio ```c void tile_drive_dc_h_set_stall_enabled(tile_t* tile, uint8_t enabled) ``` Enable or disable hardware stall detection. Toggles EN_STALL in CONFIG0. When disabled, the STALL bit in the fault register won't latch and is_stalled() always returns 0. Useful while tuning the inrush time for a new motor. - `tile` — Pointer to tile handle - `enabled` — 1 = stall detection on, 0 = off ##### `tile_drive_dc_h_set_inrush_time_ms` — Studio ```c void tile_drive_dc_h_set_inrush_time_ms(tile_t* tile, uint16_t ms) ``` Set the inrush blanking time (TINRUSH). Programs CONFIG1/CONFIG2 with a 16-bit count of 102.4 µs ticks, giving up to ~6.7 s. During the blanking window after a drive command, the stall detector ignores motor current — necessary because real motors draw several × steady-state during startup. Tune for the slowest motor you want to drive. - `tile` — Pointer to tile handle - `ms` — Inrush blanking time in milliseconds (0 - 6710) ##### `tile_drive_dc_h_set_stall_recovery` — Studio ```c void tile_drive_dc_h_set_stall_recovery(tile_t* tile, drive_dc_h_stall_recovery_t mode) ``` Set the stall-detection recovery behavior (SMODE in CONFIG3). In LATCH mode, hitting a stall turns the bridge off until either clear_fault() is called or the chip is power-cycled. In REPORT mode the chip flags STALL but keeps driving — useful for haptics or actuators that legitimately stall against an end-stop. - `tile` — Pointer to tile handle - `mode` — DRIVE_DC_H_STALL_LATCH or DRIVE_DC_H_STALL_REPORT ##### `tile_drive_dc_h_set_ripple_threshold` — Studio ```c void tile_drive_dc_h_set_ripple_threshold(tile_t* tile, uint16_t count) ``` Set the ripple-count threshold that fires CNT_DONE. The chip raises CNT_DONE when the running ripple count reaches (count × scale). Useful for "rotate N counts then stop" patterns: watch the CNT_DONE bit in get_fault(), then call coast(). Internal threshold is 10-bit; the driver picks the smallest scale (×2, ×8, ×16, or ×64) that fits `count`. - `tile` — Pointer to tile handle - `count` — Threshold count (0 - 65472, larger = coarser scale) ##### `tile_drive_dc_h_set_ripple_filter_gain` — Studio ```c void tile_drive_dc_h_set_ripple_filter_gain(tile_t* tile, uint8_t code) ``` Set the ripple filter input scaling factor. Programs FLT_GAIN_SEL[1:0] in RC_CTRL0. Scales the magnitude of detected ripples before the counter — increase if get_ripple_count() undercounts, decrease if it spuriously counts noise. Codes: 0 = ×2, 1 = ×4 (default), 2 = ×8, 3 = ×16 - `tile` — Pointer to tile handle - `code` — FLT_GAIN_SEL code (0-3) ##### `tile_drive_dc_h_set_motor_params` — Studio ```c void tile_drive_dc_h_set_motor_params(tile_t* tile, uint16_t motor_mohm, uint16_t ripples_per_rev, uint16_t kv_uv_per_rpm) ``` Program the motor profile for ripple counting and speed regulation. Writes the DRV8214's ripple-counter calibration registers (INV_R, KMC and their scale fields) from physical motor parameters, and caches ripples-per-rev for the set_speed_rpm()/get_speed_rpm() conversions. Equivalent to supplying `motor_mohm`, `ripples_per_rev` and `kv_uv_per_rpm` in the init config — use this when the driver was initialised with defaults (e.g. from Studio) and you want accurate closed-loop speed control. Example (RS PRO 834-7644, direct drive): motor_mohm=6000, ripples_per_rev=12, kv_uv_per_rpm=187. chip defaults (no-op for INV_R/KMC). (poles × brush pairs; common: 3, 5, 6, 7, 12). 0 = default (12); clamped to 255. motor: 100-2000). 0 = skip KMC tuning. - `tile` — Initialised tile handle - `motor_mohm` — Winding resistance in milliohms. 0 = leave - `ripples_per_rev` — Commutation ripples per shaft revolution - `kv_uv_per_rpm` — Back-EMF constant in µV/RPM (typical small #### Other ##### `tile_drive_dc_h_find` ```c uint8_t tile_drive_dc_h_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether a DRV8214 is present on the I2C bus. - `hal` — Platform HAL handle - `instance` — Instance index (0-8, see mapping table) **Returns:** 1 if device ACKs, 0 otherwise ##### `tile_drive_dc_h_init` ```c void tile_drive_dc_h_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const drive_dc_h_cfg_t *cfg) ``` Initialize the DRV8214 motor driver. Verifies device presence, configures I2C bridge control in PWM mode, sets regulation parameters, and enables the output stage in coast state (motor not spinning). Call forward() or reverse() to start the motor. - `hal` — Platform HAL handle - `instance` — Instance index (0-8, see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### nSLEEP pin (hardware-gated) (advanced) Chip's nSLEEP pin (~100 nA quiescent in sleep, ~1.3 mA active) is not routed to any tile pad in the Drive.DC.H rev-a layout (verify in kiln/definitions/Drive-DC-H-a.json — pads 1–10 are GND, EN/IN1, PH/IN2, I²C.CLK, I²C.DAT, NPROP, OUT1, OUT2, VM, V+; nSLEEP is strapped active on the PCB). sleep()/wake() only toggle the I²C-side EN_OUT bit; the chip itself can't reach its true quiescent floor. Closing this gap requires a tile hardware revision that routes nSLEEP to a connector pad. #### Address selection (factory variants) (niche) The DRV8214 derives its 7-bit I²C address from tri-level pins A0/A1, but the Drive.DC.H tile straps these to fixed values per factory variant: A30 (0x30), A31 (0x31), A33 (0x33), A34 (0x34 default). Address is chip-gated at order time, not runtime- settable. The 9-entry instance table in this driver covers the full chip address space, but only those four straps are sold — pick a tile variant per Drive.DC.H tile sharing a bus. ### Enums #### `drive_dc_h_direction_t` Drive direction for the tier-2 helpers. | Member | Value | Description | | --- | --- | --- | | `DRIVE_DC_H_DIR_FORWARD` | `0` | OUT1=H, OUT2=L (matches forward()). | | `DRIVE_DC_H_DIR_REVERSE` | `1` | OUT1=L, OUT2=H (matches reverse()). | ### Structs #### `drive_dc_h_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `mode` | | | `uint8_t` | `vm_gain` | | | `uint8_t` | `cs_gain` | | | `uint8_t` | `target` | | | `uint16_t` | `motor_mohm` | | | `uint8_t` | `ripples_per_rev` | | | `uint16_t` | `kv_uv_per_rpm` | | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_DRIVE_DC_H_VERSION_MAJOR` | `4` | | | `TILE_DRIVE_DC_H_VERSION_MINOR` | `2` | | | `TILE_DRIVE_DC_H_VERSION_PATCH` | `0` | | | `DRV8214_I2C_ADDR_DEFAULT` | `0x34` | | | `DRV8214_CONFIG3_DEFAULT` | `0x63` | Expected CONFIG3 register value at power-on. | | `DRV8214_FAULT_FAULT` | `0x80` | Bit 7 — any fault active | | `DRV8214_FAULT_STALL` | `0x20` | Bit 5 — motor stall | | `DRV8214_FAULT_OCP` | `0x10` | Bit 4 — overcurrent | | `DRV8214_FAULT_OVP` | `0x08` | Bit 3 — overvoltage | | `DRV8214_FAULT_TSD` | `0x04` | Bit 2 — thermal shutdown | | `DRV8214_FAULT_NPOR` | `0x02` | Bit 1 — power-on reset | | `DRV8214_FAULT_CNT_DONE` | `0x01` | Bit 0 — ripple count done | | `DRIVE_DC_H_MODE_VOLTAGE` | `0` | | | `DRIVE_DC_H_MODE_SPEED` | `1` | | | `DRIVE_DC_H_MODE_RIPPLE_COUNT` | `2` | | | `DRIVE_DC_H_MODE_PAD_PHEN` | `3` | | | `DRIVE_DC_H_MODE_PAD_IN1IN2` | `4` | | --- Generated 2026-08-12 from the driver header at tiles@7738ede. Canonical HTML: https://bergsonne.io/docs/tiles/drive/dch ============================================================================== ## Drive.H driver > LRA/ERM haptic driver for the Drive.H tile (rev a). | | | | --- | --- | | Tile | `Drive.H` | | Driver version | `4.2.0` | | Header | `tile_drive_h.h` | | Generated from | `tiles@bc9bb0f` | | Canonical page | https://bergsonne.io/docs/tiles/drive/h | ### Overview Embeds the TI DRV2605 (DEVICE_ID 3 — the non-L part), a haptic driver for LRA (Linear Resonant Actuator) and ERM actuators with a built-in waveform library of 123 effects. The actuator is external, connected via the tile's OUT+ / OUT- pads; V_MOTOR accepts 2.5-5.5 V. The chip regulates output amplitude itself: the supply rail only sets headroom. Full-scale drive level comes from RATED_VOLTAGE / OD_CLAMP (see tile_drive_h_set_actuator_voltage()), NOT from the voltage applied to V_MOTOR. Key specifications: - Output: full-bridge, amplitude regulated per config - Waveform lib: 123 haptic effects (6 libraries) - Smart-loop: automatic resonance tracking for LRA (closed loop) - Modes: Internal trigger, RTP, PWM, audio-to-vibe Datasheet: https://www.bergsonne.io/tiles/drive/h IC datasheet: https://www.ti.com/lit/ds/symlink/drv2605.pdf ### Examples #### Quick start ```c #include "core_tiles.h" tile_t haptic; tile_drive_h_init(core_tiles_pal(&core_i2c1), 0, &haptic, NULL); if (tile_is_ready(&haptic)) { tile_drive_h_play(&haptic, 1, 1); // play effect #1 once } ``` ### API 40 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Runtime ##### `tile_drive_h_play` — Studio ```c void tile_drive_h_play(tile_t* tile, uint8_t index, uint8_t repeats) ``` Play a waveform effect from the built-in library. Loads the effect index into sequence slot 0, terminates the sequence, and triggers playback. For repeats > 1, re-triggers with a 200ms gap between plays. - `index` — [1..123] Library effect index (see datasheet). - `repeats` — [1..20] Number of times to play (1 = once). ##### `tile_drive_h_play_sequence` — Studio ```c void tile_drive_h_play_sequence(tile_t* tile, const uint8_t *effects, uint8_t count) ``` Play a sequence of up to 8 waveform effects. Loads effects into the waveform sequencer registers (0x04-0x0B), terminates the sequence, and triggers playback. Returns immediately — use tile_drive_h_is_playing() to poll for completion. DSL pads short sequences with 0 (DRV2605 effect 0 is "stop"), so a 3-effect sequence written as `[5, 10, 15, 0, 0, 0, 0, 0]` plays effects 5/10/15 then halts. - `tile` — Pointer to tile handle - `effects` — Array of effect indices (1-123, 0 = stop) - `count` — Number of effects (1-8) ##### `tile_drive_h_load_sequence` — Studio ```c void tile_drive_h_load_sequence(tile_t* tile, const uint8_t *effects, uint8_t count) ``` Load effects into the waveform sequencer without triggering. Identical to tile_drive_h_play_sequence() but does NOT assert the GO bit. Use this to pre-load effects before switching to external trigger mode (edge or level). - `tile` — Pointer to tile handle - `effects` — Array of effect indices (1-123, 0 = stop) - `count` — Number of effects (1-8) ##### `tile_drive_h_set_trigger` — Studio ```c void tile_drive_h_set_trigger(tile_t* tile, uint8_t mode) ``` Set the trigger mode. Selects how the waveform sequencer is triggered: - DRIVE_H_TRIG_INTERNAL (default): I2C GO bit, used by play() and play_sequence(). - DRIVE_H_TRIG_EDGE: a rising edge on the IN/TRIG pin sets GO. A second rising edge while playing cancels. Pre-load effects with tile_drive_h_load_sequence() before entering this mode. - DRIVE_H_TRIG_LEVEL: GO follows the IN/TRIG pin level. High = playing, low = idle. Falling edge cancels. DRIVE_H_TRIG_LEVEL - `tile` — Pointer to tile handle - `mode` — One of DRIVE_H_TRIG_INTERNAL, DRIVE_H_TRIG_EDGE, ##### `tile_drive_h_is_playing` — Studio ```c uint8_t tile_drive_h_is_playing(tile_t* tile) ``` Check whether an effect or sequence is still playing. Reads the GO bit in register 0x0C. The GO bit remains high until playback completes. **Returns:** 1 if playing, 0 if idle ##### `tile_drive_h_stop` — Studio ```c void tile_drive_h_stop(tile_t* tile) ``` Stop any currently playing effect. ##### `tile_drive_h_rtp_start` — Studio ```c void tile_drive_h_rtp_start(tile_t* tile) ``` Enter RTP (Real-Time Playback) mode. Sets DRV2605L MODE register to 0x05 (RTP). The chip drives the LRA at its resonant frequency with amplitude controlled by tile_drive_h_rtp_write(). Call tile_drive_h_rtp_stop() to return to internal trigger mode. ##### `tile_drive_h_rtp_write` — Studio ```c void tile_drive_h_rtp_write(tile_t* tile, uint8_t amplitude) ``` Write an amplitude value in RTP mode. - `amplitude` — [0..127] Amplitude (0 = off, 127 = max). ##### `tile_drive_h_rtp_stop` — Studio ```c void tile_drive_h_rtp_stop(tile_t* tile) ``` Exit RTP mode and return to internal trigger mode. ##### `tile_drive_h_get_status` — Studio ```c uint8_t tile_drive_h_get_status(tile_t* tile) ``` Read the raw STATUS register (0x00). Contains DEVICE_ID[7:5], DIAG_RESULT[3], FB_STS[2], OVER_TEMP[1], OC_DETECT[0]. Status bits clear on read. - `tile` — Pointer to tile handle **Returns:** Raw status byte ##### `tile_drive_h_diagnose` — Studio ```c uint8_t tile_drive_h_diagnose(tile_t* tile) ``` Run actuator diagnostics. Enters diagnostic mode (MODE=6), triggers GO, and polls for completion. The DRV2605L checks whether the actuator is present, open, or shorted. **Returns:** 1 if actuator passed diagnostics, 0 if fault detected ##### `tile_drive_h_calibrate` — Studio ```c uint8_t tile_drive_h_calibrate(tile_t* tile) ``` Run auto-calibration for the connected actuator. Enters calibration mode (MODE=7) with datasheet-recommended parameters, triggers GO, and polls for completion. On success, the DRV2605L stores optimized A_CAL_COMP and A_CAL_BEMF values that improve playback fidelity. **Returns:** 1 if calibration passed, 0 if it failed to converge ##### `tile_drive_h_get_vbat_mv` — Studio ```c uint16_t tile_drive_h_get_vbat_mv(tile_t* tile) ``` Read supply voltage in millivolts. Reads the VBAT register (0x21). The reading is only valid while the device is actively driving a waveform (RTP, library playback, etc.). **Returns:** Battery voltage in mV (e.g. 3300 = 3.3 V), 0 if idle ##### `tile_drive_h_get_resonance_hz` — Studio ```c uint16_t tile_drive_h_get_resonance_hz(tile_t* tile) ``` Read LRA resonant frequency in Hz. Reads the LRA_PERIOD register (0x22). The reading is only valid while the device is actively driving a waveform and must not be polled during braking. **Returns:** Resonant frequency in Hz (e.g. 235), 0 if unavailable ##### `tile_drive_h_standby` — Studio ```c void tile_drive_h_standby(tile_t* tile) ``` Enter low-power standby. Sets the STANDBY bit in the MODE register. The device retains register values and can be woken quickly with tile_drive_h_wake(). ##### `tile_drive_h_wake` — Studio ```c void tile_drive_h_wake(tile_t* tile) ``` Wake from standby. Clears the STANDBY bit in the MODE register. The device returns to the active state, ready for playback. ##### `tile_drive_h_play_click` — Studio ```c void tile_drive_h_play_click(tile_t* tile) ``` Fire a single sharp tactile click. Plays ROM library effect 1 ("Strong Click — 100%") once. Returns immediately; the chip drives the click after the call returns. Use @ref tile_drive_h_is_playing to poll for completion. - `tile` — Initialised tile handle ##### `tile_drive_h_play_double_tap` — Studio ```c void tile_drive_h_play_double_tap(tile_t* tile) ``` Fire a double tap. Loads the sequencer with two strong-click effects (ROM library effect 10 — "Double Click — 100%") and triggers playback. The chip handles the inter-tap timing internally. Returns immediately; use @ref tile_drive_h_is_playing to poll. - `tile` — Initialised tile handle ##### `tile_drive_h_play_alert` — Studio ```c void tile_drive_h_play_alert(tile_t* tile) ``` Play an alert pattern. Sequences three ROM-library effects to produce a sharp-buzz-sharp notification: effect 14 ("Strong Buzz — 100%" tick) → effect 56 ("Long Buzz for Programmatic Stopping — 100%") → effect 14. Returns immediately; use @ref tile_drive_h_is_playing to poll. - `tile` — Initialised tile handle ##### `tile_drive_h_play_buzz` — Studio ```c void tile_drive_h_play_buzz(tile_t* tile, uint16_t ms) ``` Play a sustained vibration for `ms` milliseconds. Switches into RTP (Real-Time Playback) mode, drives a constant full-amplitude signal for `ms` milliseconds, then stops the RTP stream and returns to internal-trigger mode. / @ref tile_drive_h_rtp_stop. - `tile` — Initialised tile handle - `ms` — Duration in milliseconds **Note:** Blocking call — does not return until the buzz completes. For longer non-blocking patterns, drive RTP directly via ##### `tile_drive_h_is_calibrated` — Studio ```c uint8_t tile_drive_h_is_calibrated(tile_t* tile) ``` Check whether smart-loop calibration converged. Reads the STATUS register's DIAG_RESULT bit, which the auto- calibration engine populates on completion: 0 = converged (calibration produced valid A_CAL_COMP / A_CAL_BEMF values), 1 = failed. Only meaningful after a prior call to @ref tile_drive_h_calibrate. - `tile` — Initialised tile handle **Returns:** 1 if calibration converged, 0 otherwise **Note:** STATUS bits clear on read, so the result reflects the most recent calibration / diagnostic run. #### Configuration ##### `tile_drive_h_set_loop_mode` — Studio ```c void tile_drive_h_set_loop_mode(tile_t* tile, uint8_t closed) ``` Switch between open-loop and closed-loop drive at runtime. Closed loop (recommended for LRAs) enables smart-loop back-EMF feedback: automatic resonance tracking, overdrive, and braking, with full-scale amplitude referenced to RATED_VOLTAGE. Open loop drives blind: amplitude is referenced to OD_CLAMP, RATED_VOLTAGE is ignored, and LRA playback does NOT resonance-track on this part — the commutation frequency comes from DRIVE_TIME (see tile_drive_h_set_resonance_hz()). Applies to whichever actuator type is currently selected (set_library() / FEEDBACK_CTRL N_ERM_LRA). - `tile` — Pointer to tile handle - `closed` — 1 = closed-loop (smart-loop), 0 = open-loop ##### `tile_drive_h_set_actuator_voltage` — Studio ```c void tile_drive_h_set_actuator_voltage(tile_t* tile, uint16_t rated_mv, uint16_t overdrive_mv) ``` Set the actuator drive levels in millivolts. The friendly-units version of tile_drive_h_set_actuator_params(): converts voltages to the RATED_VOLTAGE (0x16) and OD_CLAMP (0x17) register values using the DRV2605 datasheet equations (section 7.5.2, Eq. 2-5), honouring the currently selected actuator type (ERM vs LRA) and, for LRAs, the drive frequency currently programmed via tile_drive_h_set_resonance_hz(). - rated_mv: steady-state full-scale level (RMS for LRA, average for ERM). Closed-loop reference. - overdrive_mv: peak ceiling for overdrive/braking; also the full-scale reference in open-loop mode. Must be >= rated_mv; typically 1.3-1.5x for LRAs. Run tile_drive_h_calibrate() afterwards — the datasheet requires recalibration whenever these references change. - `tile` — Pointer to tile handle - `rated_mv` — [300..3600] Rated drive level in mV - `overdrive_mv` — [300..5000] Overdrive clamp in mV ##### `tile_drive_h_set_resonance_hz` — Studio ```c void tile_drive_h_set_resonance_hz(tile_t* tile, uint16_t hz) ``` Set the LRA resonant frequency the driver targets. Programs CONTROL1 DRIVE_TIME to half the LRA period — the datasheet-optimal value. In closed-loop mode this seeds the auto-resonance tracker (which then follows the real resonance); in open-loop mode it directly sets the commutation frequency. Read the actuator's actual resonance with tile_drive_h_get_resonance_hz() while driving in closed loop. - `tile` — Pointer to tile handle - `hz` — [125..300] LRA resonant frequency in Hz #### Other ##### `tile_drive_h_find` ```c uint8_t tile_drive_h_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether a DRV2605L is present on the I2C bus. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) **Returns:** 1 if device ACKs, 0 otherwise ##### `tile_drive_h_init` ```c void tile_drive_h_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const drive_h_cfg_t *cfg) ``` Initialize the DRV2605L haptic driver. Verifies the device ID, exits standby, and configures the actuator drive mode. Pass cfg=NULL for defaults (LRA closed-loop, library 6, 1.8 Vrms drive levels). - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults **Note:** Blocks for ~500 ms during init. Call once at startup. ##### `tile_drive_h_set_sequence_wait` — Studio ```c void tile_drive_h_set_sequence_wait(tile_t* tile, uint8_t slot, uint8_t delay_steps) ``` Set a per-slot wait delay in the loaded sequence. The 8 sequencer slots can each be marked as either an effect (MSB=0, low 7 bits = waveform 1..123) or a wait time (MSB=1, low 7 bits × 10 ms = pause). This call rewrites slot @p slot with a wait of @p delay_steps × 10 ms (max 1270 ms). Use it after tile_drive_h_load_sequence() to insert pauses between effects, e.g. play 1, wait 200 ms, play 47. Slot is silently ignored if >= 8; delay_steps is clipped to 0x7F. - `tile` — Pointer to tile handle - `slot` — Slot index 0..7 - `delay_steps` — Wait length in 10 ms steps (0 = no wait, 0x7F = 1.27 s) ##### `tile_drive_h_set_library` — Studio ```c void tile_drive_h_set_library(tile_t* tile, uint8_t library) ``` Switch the active waveform library at runtime. The DRV2605L ships with 6 ROM libraries: 1–5 are TS2200 ERM libraries (A–E), 6 is the LRA library. Library 0 is empty (silence). This call also updates the FEEDBACK_CTRL N_ERM_LRA bit so the chip drives the correct actuator type. - `tile` — Pointer to tile handle - `library` — Library index 0..6 (use DRIVE_H_LIB_* constants) ##### `tile_drive_h_set_actuator_params` — Studio ```c void tile_drive_h_set_actuator_params(tile_t* tile, uint8_t rated_voltage, uint8_t od_clamp, uint8_t fb_brake, uint8_t loop_gain) ``` Tune the actuator drive parameters. Writes the four registers that the auto-calibration engine and smart-loop architecture consume: - rated_voltage (0x16): full-scale RMS drive level (closed-loop ref). - od_clamp (0x17): peak overdrive ceiling (also open-loop ref). - fb_brake (0x1A bits 6:4): feedback brake factor (0=1× .. 6=16×, 7=off). - loop_gain (0x1A bits 3:2): closed-loop gain (0=Low .. 3=Very high). Pass 0 for rated_voltage / od_clamp to leave that register untouched. Pass 0xFF for fb_brake / loop_gain to leave them untouched. Run tile_drive_h_calibrate() afterwards to update A_CAL_COMP / A_CAL_BEMF. - `tile` — Pointer to tile handle - `rated_voltage` — RATED_VOLTAGE byte (0 = no change) - `od_clamp` — OD_CLAMP byte (0 = no change) - `fb_brake` — Feedback brake factor 0..7 (0xFF = no change) - `loop_gain` — Loop gain 0..3 (0xFF = no change) ##### `tile_drive_h_set_resonance_params` — Studio ```c void tile_drive_h_set_resonance_params(tile_t* tile, uint8_t sample_time, uint8_t blanking_time, uint8_t idiss_time) ``` Tune the LRA auto-resonance tracker. Writes Control2 (0x1C) bits for sample / blanking / current- dissipation timing. Default values (3 / 1 / 1) work for most coin-type LRAs at 175–235 Hz. Tune only if calibration fails to converge or the actuator runs at the edges of the 125–300 Hz window. Pass 0xFF for any field to leave that register slice untouched. - `tile` — Pointer to tile handle - `sample_time` — Sample time 0..3 (0=150µs, 3=300µs; 0xFF = no change) - `blanking_time` — Blanking time 0..3 (0xFF = no change) - `idiss_time` — Current-dissipation time 0..3 (0xFF = no change) ##### `tile_drive_h_set_waveform_timing` — Studio ```c void tile_drive_h_set_waveform_timing(tile_t* tile, int8_t overdrive, int8_t sustain_pos, int8_t sustain_neg, int8_t brake) ``` Tune the open-loop library waveform timing. Adds signed offsets (in 5 ms steps) to the overdrive, sustain, and brake portions of every library effect. Offsets are 8-bit two's complement, so range is -640..+635 ms per knob. These offsets are only honoured in open-loop mode — closed-loop mode generates them automatically from back-EMF feedback. - `tile` — Pointer to tile handle - `overdrive` — Overdrive Time Offset (0x0D, signed × 5 ms) - `sustain_pos` — Sustain-Time Positive Offset (0x0E, signed × 5 ms) - `sustain_neg` — Sustain-Time Negative Offset (0x0F, signed × 5 ms) - `brake` — Brake Time Offset (0x10, signed × 5 ms) ##### `tile_drive_h_set_rtp_format` — Studio ```c void tile_drive_h_set_rtp_format(tile_t* tile, uint8_t unsigned_, uint8_t bidir) ``` Set the RTP data format. Selects how RTP_INPUT bytes are interpreted: - signed (default for closed-loop bidirectional): 0x80 = full reverse, 0x00 = mid-scale (no drive), 0x7F = full forward. - unsigned (recommended for closed-loop unidirectional and open-loop): 0x00 = no drive, 0xFF = full drive. Also exposes the BIDIR_INPUT bit (CONTROL2[7]) which selects unidirectional vs bidirectional input interpretation; tie this to the same convention as the format flag. - `tile` — Pointer to tile handle - `unsigned_` — 1 = unsigned data format, 0 = signed (default) - `bidir` — 1 = bidirectional input (default), 0 = unidirectional ##### `tile_drive_h_pwm_input_start` — Studio ```c void tile_drive_h_pwm_input_start(tile_t* tile) ``` Drive the actuator from a PWM signal on pad 2 (TRIG). Sets MODE=3 with N_PWM_ANALOG=0. The chip accepts a 10–250 kHz PWM signal: duty cycle directly modulates output amplitude (50 % = no drive in bidirectional mode; 0 % = no drive in unidirectional mode). Useful for offloading waveform generation to a hardware timer or audio rendering pipeline. Call tile_drive_h_set_rtp_format() first if you need to change the unidirectional / bidirectional interpretation. ##### `tile_drive_h_analog_input_start` — Studio ```c void tile_drive_h_analog_input_start(tile_t* tile) ``` Drive the actuator from an analog voltage on pad 2 (TRIG). Sets MODE=3 with N_PWM_ANALOG=1. Reference voltage is 1.8 V: 0 V → 0 % drive, 0.9 V → 50 %, 1.8 V → 100 %. Useful for waveform synthesis from a DAC or analog signal source. **Note:** Pad 2 (TRIG) is a digital pad on the tile. The chip pin itself accepts 0–1.8 V analog, but the driving Core must output a clean analog level — Drive.H has no input filter or AC-coupling cap on TRIG. ##### `tile_drive_h_pwm_input_stop` — Studio ```c void tile_drive_h_pwm_input_stop(tile_t* tile) ``` Exit PWM / analog input mode. Returns to internal-trigger mode (MODE=0). Same effect as tile_drive_h_audio_stop(); both modes share the IN/TRIG pin. ##### `tile_drive_h_audio_start` — Studio ```c void tile_drive_h_audio_start(tile_t* tile) ``` Drive the actuator from an AC-coupled audio signal. Sets MODE=4 (audio-to-vibe), N_PWM_ANALOG=1 (analog), and AC_COUPLE=1 (0.9 V common-mode bias on IN/TRIG). The chip envelope-detects the audio and drives haptic vibration at matching intensity. **Note:** The DRV2605L expects an AC-coupled line-level audio source (1.8 Vpp full-scale). Drive.H rev a does not include a series capacitor on pad 2 — users wanting audio-to-vibe must add their own external 1 µF AC-coupling cap between the audio source and pad 2. ##### `tile_drive_h_set_audio_params` — Studio ```c void tile_drive_h_set_audio_params(tile_t* tile, uint8_t peak_time, uint8_t filter, uint8_t min_input, uint8_t max_input, uint8_t min_drive, uint8_t max_drive) ``` Tune the audio-to-vibe envelope detector. Configures the four ATV control registers: - ATV_CTRL (0x11): peak-detect time (0=10ms..3=40ms) and low-pass filter cutoff (0=100Hz..3=200Hz). - ATV_MIN_INPUT (0x12): minimum input level below which the envelope is gated (output silent). raw × 1.8/255 V. - ATV_MAX_INPUT (0x13): full-scale input level. raw × 1.8/255 V. - ATV_MIN_DRIVE (0x14): minimum output drive once unmuted. raw / 255 × 100 %. - ATV_MAX_DRIVE (0x15): maximum output drive at full input. raw / 255 × 100 %. Pass 0xFF for any field to leave it untouched. - `tile` — Pointer to tile handle - `peak_time` — ATH_PEAK_TIME 0..3 (10/20/30/40 ms; 0xFF = no change) - `filter` — ATH_FILTER 0..3 (100/125/150/200 Hz; 0xFF = no change) - `min_input` — Minimum input gate (0xFF = no change) - `max_input` — Full-scale input level (0xFF = no change) - `min_drive` — Minimum output drive once active (0xFF = no change) - `max_drive` — Full-scale output drive (0xFF = no change) ##### `tile_drive_h_audio_stop` — Studio ```c void tile_drive_h_audio_stop(tile_t* tile) ``` Exit audio-to-vibe mode. Returns to internal-trigger mode (MODE=0) and clears the AC_COUPLE bit. Same physical effect as tile_drive_h_pwm_input_stop(). ##### `tile_drive_h_program_otp` ```c uint8_t tile_drive_h_program_otp(tile_t* tile) ``` Burn the current calibration into on-chip OTP. Writes the current contents of registers 0x16–0x1A (RATED_VOLTAGE, OD_CLAMP, A_CAL_COMP, A_CAL_BEMF, FEEDBACK_CTRL) to the DRV2605L's nonvolatile OTP cells. After this, those values become the power-on defaults — the chip skips run-time calibration on subsequent boots. once per device. A bad programming run permanently mistunes the chip. NOT exposed to Studio by design. Pre-conditions: - Run tile_drive_h_calibrate() first and confirm pass. - VDD must be 4.0–4.4 V at the moment of programming (datasheet section 7.5.7). Lower VDD silently corrupts the cells. Returns 1 if the OTP_STATUS bit reads 1 after programming, 0 if the burn failed or OTP was already programmed. - `tile` — Pointer to tile handle **Returns:** 1 on success, 0 on failure / already-programmed ##### `tile_drive_h_get_otp_status` — Studio ```c uint8_t tile_drive_h_get_otp_status(tile_t* tile) ``` Read the OTP_STATUS bit. Returns 1 if the chip's OTP cells have already been programmed (registers 0x16–0x1A boot from OTP rather than chip defaults). **Returns:** 1 if OTP is programmed, 0 if unprogrammed ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### OTP waveform burning (niche) The DRV2605L can burn custom waveforms into one-time-programmable memory. This is implemented in firmware (tile_drive_h_program_otp()) but intentionally NOT exposed to Studio: it permanently and irreversibly modifies the chip. Driver-deferred by policy, not a hardware gap — call it from C if you really need it. ### Structs #### `drive_h_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `library` | | | `uint8_t` | `closed_loop` | | | `uint8_t` | `rated_voltage` | | | `uint8_t` | `od_clamp` | | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_DRIVE_H_VERSION_MAJOR` | `4` | | | `TILE_DRIVE_H_VERSION_MINOR` | `2` | | | `TILE_DRIVE_H_VERSION_PATCH` | `0` | | | `DRV2605L_I2C_ADDR_DEFAULT` | `0x5A` | | | `DRV2605L_STATUS_DEFAULT` | `0x60` | Expected default STATUS register value (DEVICE_ID = 3). | | `DRV2605L_STATUS_DEVICE_ID` | `0xE0` | Bits 7:5 — device ID | | `DRV2605L_STATUS_DIAG_RESULT` | `0x08` | Bit 3 — diagnostic result | | `DRV2605L_STATUS_OVER_TEMP` | `0x02` | Bit 1 — over-temperature | | `DRV2605L_STATUS_OC_DETECT` | `0x01` | Bit 0 — overcurrent detect | | `DRV2605L_MODE_INTERNAL_TRIG` | `0x00` | Internal trigger (I2C GO) | | `DRV2605L_MODE_EXT_EDGE` | `0x01` | External trigger, edge mode | | `DRV2605L_MODE_EXT_LEVEL` | `0x02` | External trigger, level mode | | `DRV2605L_MODE_PWM_ANALOG` | `0x03` | PWM or analog input on TRIG | | `DRV2605L_MODE_AUDIO` | `0x04` | Audio-to-vibe on TRIG | | `DRV2605L_MODE_RTP` | `0x05` | Real-time playback | | `DRV2605L_MODE_DIAGNOSTICS` | `0x06` | Actuator diagnostics | | `DRV2605L_MODE_CALIBRATION` | `0x07` | Auto-calibration | | `DRV2605L_MODE_STANDBY` | `0x40` | STANDBY bit (bit 6) | | `DRIVE_H_TRIG_INTERNAL` | `0` | | | `DRIVE_H_TRIG_EDGE` | `1` | | | `DRIVE_H_TRIG_LEVEL` | `2` | | | `DRV2605L_SEQ_MAX` | `8` | | | `DRIVE_H_LIB_EMPTY` | `0` | Empty library (silence). | | `DRIVE_H_LIB_ERM_A` | `1` | TS2200 Library A (ERM, fixed open-loop overdrive). | | `DRIVE_H_LIB_ERM_B` | `2` | TS2200 Library B (ERM, no overdrive). | | `DRIVE_H_LIB_ERM_C` | `3` | TS2200 Library C (ERM, no overdrive). | | `DRIVE_H_LIB_ERM_D` | `4` | TS2200 Library D (ERM, no overdrive). | | `DRIVE_H_LIB_ERM_E` | `5` | TS2200 Library E (ERM, no overdrive). | | `DRIVE_H_LIB_LRA` | `6` | LRA Library (auto-resonance). | --- Generated 2026-08-12 from the driver header at tiles@bc9bb0f. Canonical HTML: https://bergsonne.io/docs/tiles/drive/h ============================================================================== ## Drive.P driver > Piezoelectric haptic driver for the Drive.P tile (rev a). | | | | --- | --- | | Tile | `Drive.P` | | Driver version | `3.3.0` | | Header | `tile_drive_p.h` | | Generated from | `tiles@9897b3e` | | Canonical page | https://bergsonne.io/docs/tiles/drive/p | ### Overview Embeds the Boréas Technologies BOS1921, a piezoelectric driver with integrated high-voltage boost (190 Vpp differential), waveform synthesizer, 1024-sample FIFO, and piezo sensing. Key specifications: - Output: 190 Vpp differential, up to 820 nF capacitive load - Sensing: 7.6 mV resolution (fine), 54.5 mV (coarse) - Play modes: Direct, FIFO, RAM, RAM Synth - Sample rate: 8 ksps – 1024 ksps (configurable) Datasheet: https://www.bergsonne.io/tiles/drive/p IC datasheet: https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/5/Bor_as_Technologies-BOS1921.pdf ### Examples #### Quick start ```c #include "core_tiles.h" tile_t piezo; tile_drive_p_init(core_tiles_pal(&core_i2c1), 0, &piezo, NULL); if (tile_is_ready(&piezo)) { tile_drive_p_set_mode(&piezo, DRIVE_P_MODE_PLAY_FIFO); tile_drive_p_write_fifo(&piezo, 0x7FFF); } ``` ### API 27 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_drive_p_sleep` — Studio ```c void tile_drive_p_sleep(tile_t* tile) ``` Enter low-power sleep mode. - `tile` — Pointer to tile handle #### Runtime ##### `tile_drive_p_set_mode` — Studio ```c void tile_drive_p_set_mode(tile_t* tile, drive_p_mode_t mode) ``` Set the operating mode. - `tile` — Pointer to tile handle - `mode` — One of the drive_p_mode_t values ##### `tile_drive_p_read` — Studio ```c uint16_t tile_drive_p_read(tile_t* tile) ``` Read the current return register value. - `tile` — Pointer to tile handle **Returns:** 16-bit value from the currently selected return register ##### `tile_drive_p_read_sense` — Studio ```c int16_t tile_drive_p_read_sense(tile_t* tile) ``` Read the sensed piezo voltage. Must be in SENSE_FINE or SENSE_COARSE mode. - `tile` — Pointer to tile handle **Returns:** Signed 16-bit sense value (−2048 to +2047) ##### `tile_drive_p_read_status` — Studio ```c uint16_t tile_drive_p_read_status(tile_t* tile) ``` Read the IC status register. - `tile` — Pointer to tile handle **Returns:** 16-bit IC_STATUS value ##### `tile_drive_p_play_click` — Studio ```c void tile_drive_p_play_click(tile_t* tile, uint8_t intensity_pct) ``` Fire a single sharp tactile click. Streams a half-sine pulse through the FIFO at 8 ksps. Intensity scales the peak output amplitude in the configured voltage range (default ±95 V; see @ref tile_drive_p_set_output_range to switch to ±13.25 V for low-voltage piezos). Returns when the FIFO has been written; the chip continues playing the click after the call returns. - `tile` — Initialised tile handle - `intensity_pct` — 0–100 percent of full-scale output ##### `tile_drive_p_play_sine` — Studio ```c void tile_drive_p_play_sine(tile_t* tile, uint16_t freq_hz, uint8_t intensity_pct, uint16_t ms) ``` Play a continuous sine wave for `ms` milliseconds. Generates and streams sine samples at 8 ksps. Frequency is software-quantised to the sample rate (max useful ~3 kHz). The call blocks until the FIFO is filled; for streams longer than the 1024-sample FIFO depth (~128 ms at 8 ksps) the call refills as the chip drains. - `tile` — Initialised tile handle - `freq_hz` — Sine frequency in Hz (50–3000 useful range) - `intensity_pct` — 0–100 percent of full-scale output - `ms` — Duration in milliseconds ##### `tile_drive_p_play_buzz` — Studio ```c void tile_drive_p_play_buzz(tile_t* tile, uint8_t intensity_pct, uint16_t ms) ``` Play a buzz (sustained mid-frequency vibration). Convenience wrapper for @ref tile_drive_p_play_sine at 150 Hz — a frequency typical small-form-factor piezo actuators feel strongly at. Use `play_sine` directly if you need a specific frequency. - `tile` — Initialised tile handle - `intensity_pct` — 0–100 percent of full-scale output - `ms` — Duration in milliseconds ##### `tile_drive_p_play_pulse_train` — Studio ```c void tile_drive_p_play_pulse_train(tile_t* tile, uint8_t intensity_pct, uint8_t count, uint16_t gap_ms) ``` Play N clicks separated by gaps. The classic "tick-tick-tick" pattern. Composes @ref tile_drive_p_play_click with `core_delay_ms` between clicks. - `tile` — Initialised tile handle - `intensity_pct` — 0–100 percent of full-scale output - `count` — Number of clicks (1–255) - `gap_ms` — Milliseconds between successive clicks ##### `tile_drive_p_is_touched` — Studio ```c uint8_t tile_drive_p_is_touched(tile_t* tile, uint16_t threshold_mv) ``` Check whether the piezo is being touched / pressed. Switches into sense mode (fine resolution), reads one sense sample, compares the absolute value against `threshold_mv`, and returns the boolean result. Leaves the chip in sense mode after the call — call `tile_drive_p_set_mode(tile, DRIVE_P_MODE_IDLE)` (or any play mode) to return to driving the actuator. - `tile` — Initialised tile handle - `threshold_mv` — Absolute sense voltage threshold in mV **Returns:** 1 if sense > threshold (touched), 0 otherwise ##### `tile_drive_p_play_on_touch` — Studio ```c uint8_t tile_drive_p_play_on_touch(tile_t* tile, uint8_t intensity_pct, uint16_t threshold_mv, uint32_t timeout_ms) ``` Block until touch detected, then fire a click. Polls the sense channel until `threshold_mv` is exceeded, then switches into FIFO mode and plays a click via @ref tile_drive_p_play_click. The classic closed-loop tactile-feedback idiom — press the piezo, feel the click. Polling polls every ~1 ms; returns 0 if `timeout_ms` elapses without detection. - `tile` — Initialised tile handle - `intensity_pct` — 0–100 percent of full-scale output - `threshold_mv` — Touch threshold in mV - `timeout_ms` — Maximum time to wait **Returns:** 1 if a touch fired the click, 0 on timeout ##### `tile_drive_p_play_samples` — Studio ```c void tile_drive_p_play_samples(tile_t* tile, const int16_t* samples, uint16_t count) ``` Stream a buffer of pre-computed samples through the FIFO. Switches into FIFO play mode (if not already there) and writes `count` samples. Samples are signed 12-bit (range −2048..+2047) left-justified into the chip's 16-bit FIFO words; values outside that range are clamped. Use this for arbitrary waveforms that don't fit the click / sine / buzz / pulse-train idioms — e.g., recorded waveforms or DSP-generated patterns. - `tile` — Initialised tile handle - `samples` — Pointer to buffer of int16_t samples - `count` — Number of samples to write ##### `tile_drive_p_read_sense_samples` — Studio ```c void tile_drive_p_read_sense_samples(tile_t* tile, int16_t* buf, uint16_t count) ``` Read a buffer of sense samples. Switches into fine-resolution sense mode (if not already there) and reads `count` consecutive samples into the caller's buffer. Each sample takes ~125 µs to acquire (8 ksps native). Use for impedance characterisation, multi-touch pattern detection, or piezo-as-mic experiments beyond the simple `is_touched` API. - `tile` — Initialised tile handle - `buf` — Output buffer for int16_t sense samples - `count` — Number of samples to read #### Configuration ##### `tile_drive_p_set_output_range` — Studio ```c void tile_drive_p_set_output_range(tile_t* tile, drive_p_output_range_t range) ``` Select the output voltage range (CONFIG.GAIND). High-V (±95 V) is the BOS1921 default and suits most piezo actuators. Low-V (±13.28 V) is for low-voltage piezos where the full ±95 V swing would be wasteful or destructive. Use this from IDLE before setting a play mode — the change takes effect on the next OE-enable. - `tile` — Pointer to tile handle - `range` — DRIVE_P_OUTPUT_HIGH_V or DRIVE_P_OUTPUT_LOW_V **Note:** Changing range invalidates the PARCAP / TI_RISE tuning set at init. If accurate output behaviour matters, recompute and rewrite those registers for the new FBratio (see datasheet §7.5). ##### `tile_drive_p_set_sense_gain` — Studio ```c void tile_drive_p_set_sense_gain(tile_t* tile, drive_p_sense_gain_t gain) ``` Select the sense-channel resolution (CONFIG.GAINS). Fine gain (7.6 mV LSB) is the BOS1921 default and gives the highest sensing resolution. Coarse gain (54.5 mV LSB) widens the input range — useful when sensing high-amplitude press events that would otherwise saturate at fine gain. Use from IDLE before entering a sense mode. - `tile` — Pointer to tile handle - `gain` — DRIVE_P_SENSE_FINE_GAIN or DRIVE_P_SENSE_COARSE_GAIN ##### `tile_drive_p_set_sleep_retention` — Studio ```c void tile_drive_p_set_sleep_retention(tile_t* tile, uint8_t retain) ``` Configure register and RAM retention during SLEEP (CONFIG.RET). Default is retain (~2.4 µA quiescent) so that RAM contents and register configuration survive a sleep cycle. Disabling retention (~0.6 µA) is useful for ultra-low-power applications that re-init on every wake anyway. Set this before calling sleep(). - `tile` — Pointer to tile handle - `retain` — 1 = retain (default), 0 = clear on sleep ##### `tile_drive_p_set_auto_sleep` — Studio ```c void tile_drive_p_set_auto_sleep(tile_t* tile, uint8_t enabled) ``` Enable or disable the auto-sleep timeout (COMM.TOUT). When enabled, the device drops into SLEEP after 4 ms of bus inactivity during Direct or FIFO playback. Useful for unattended one-shot waveforms; harmful for long streaming playback where a host gap would unexpectedly stop the output. - `tile` — Pointer to tile handle - `enabled` — 1 = auto-sleep on idle, 0 = stay awake **Note:** After a timeout-triggered sleep, PLAY_SRATE is reset to 0x7 (8 ksps) — re-set the sample rate before the next playback if you were using a faster rate. ##### `tile_drive_p_set_upi` — Studio ```c void tile_drive_p_set_upi(tile_t* tile, uint8_t enabled) ``` Enable or disable the Unidirectional Power Input (PARCAP.UPI). UPI forces the BOS1921 into sink-only operation: energy recovered from piezo discharge is dumped instead of pushed back into the supply. Useful for battery-powered designs where the supply rail can't safely absorb returned energy. - `tile` — Pointer to tile handle - `enabled` — 1 = sink-only (UPI on), 0 = energy recovery (default) #### Advanced ##### `tile_drive_p_write_fifo` — Studio ```c void tile_drive_p_write_fifo(tile_t* tile, int16_t sample) ``` Write a sample to the FIFO. - `tile` — Pointer to tile handle - `sample` — Signed 16-bit waveform sample ##### `tile_drive_p_wfs_write` — Studio ```c void tile_drive_p_wfs_write(tile_t* tile, const uint16_t* words, uint16_t count) ``` Write a multi-word WFS command to the BOS1921. - `tile` — Pointer to tile handle - `words` — Array of 16-bit words (big-endian on wire) - `count` — Number of words (max 8) ##### `tile_drive_p_check_and_recover` — Studio ```c uint8_t tile_drive_p_check_and_recover(tile_t* tile, drive_p_mode_t restore_mode) ``` Check status and recover from error/fault states. - `tile` — Pointer to tile handle - `restore_mode` — Mode to re-enter after recovery **Returns:** 1 if recovery was performed, 0 if device was healthy #### Other ##### `tile_drive_p_find` ```c uint8_t tile_drive_p_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether a BOS1921 is present on the I2C bus. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) **Returns:** 1 if device ACKs, 0 otherwise ##### `tile_drive_p_init` ```c void tile_drive_p_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const drive_p_cfg_t *cfg) ``` Initialize the BOS1921 piezoelectric driver. Wakes the device, performs a software reset, verifies the chip ID, and configures parasitic capacitance and supply parameters for a 260nF piezo on a 3.7V LiPo supply. The SUP_RISE I2C_ADDR nibble is derived from the instance's address, so a reassigned chip (0x45/0x46) keeps its address — a soft reset doesn't revert it, so init may reset such a chip normally. Pass cfg=NULL for defaults. - `hal` — Platform HAL handle - `instance` — Instance index (0=0x44, 1=0x45, 2=0x46; see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults ##### `tile_drive_p_init_at` ```c void tile_drive_p_init_at(tiles_pal_t* hal, uint8_t addr, tile_t* tile, const drive_p_cfg_t *cfg) ``` Initialize a BOS1921 at an explicit I2C address. The address-explicit sibling of tile_drive_p_init(): identical bring-up, but you name the operating address directly instead of an instance index. This decouples a chip from the fixed instance→address table, which a topology- driven bringup needs — e.g. the v1 Ring runs two Drive.P on separate buses, BOTH at 0x44 (no readdress), which the instance map can't express. Shadow state is keyed per-tile, so two chips at the same address on different buses don't collide. Pass cfg=NULL for defaults. - `hal` — Platform HAL handle (the bus this chip lives on) - `addr` — Operating I2C address (e.g. 0x44 / 0x45 / 0x46) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults ##### `tile_drive_p_reassign_address` ```c uint8_t tile_drive_p_reassign_address(tiles_pal_t* hal, uint8_t cur_addr, uint8_t new_addr) ``` Reassign one BOS1921's I2C address (GPIO-gated, datasheet §6.3.3). For two BOS1921 sharing a bus (both at 0x44), move each to a unique address. The call wakes the chip(s) still at cur_addr, sets COMM.GPIODIR=1 (GPIO becomes a write-gate), writes the new address into SUP_RISE.I2C_ADDR, and verifies CHIP_ID at new_addr. Only the chip whose GPIO the caller holds low latches the change; the others are untouched. The driver performs the I2C register sequence only; the **caller owns the GPIO** (a board-specific Core pad, not reachable through the tile PAL). Move BOTH chips of a pair off 0x44 (→ 0x45 and 0x46); each call gates a different chip: // chip A → 0x45 : hold A's GPIO low, B's high core_pad_write(GPIO_A, 0); core_pad_write(GPIO_B, 1); tile_drive_p_reassign_address(hal, 0x44, 0x45); // chip B → 0x46 : hold B's GPIO low, A's high (A has left 0x44) core_pad_write(GPIO_A, 1); core_pad_write(GPIO_B, 0); tile_drive_p_reassign_address(hal, 0x44, 0x46); core_pad_input(GPIO_A); core_pad_input(GPIO_B); // release // then init instance 1 (0x45) and 2 (0x46) so it must be in 0x40..0x4F (BOS1921_I2C_ADDR_SECOND/THIRD) - `hal` — Platform HAL handle - `cur_addr` — the chip's current 7-bit address (0x44 at power-up) - `new_addr` — desired 7-bit address; only the low nibble is settable, **Returns:** 1 if the chip answers at new_addr with the correct CHIP_ID, else 0 **Note:** The new address persists until power-on reset; a soft reset will not revert it. Re-running this from 0x44 after a reflash (no power cycle) is a no-op — the chips already left 0x44. ##### `tile_drive_p_reset` ```c void tile_drive_p_reset(tile_t* tile) ``` Perform a software reset. - `tile` — Pointer to tile handle ##### `tile_drive_p_write_reg` ```c void tile_drive_p_write_reg(tile_t* tile, uint8_t reg, uint16_t value) ``` Write a raw 16-bit value to any BOS1921 register. - `tile` — Pointer to tile handle - `reg` — 8-bit register address - `value` — 16-bit value (sent big-endian on the wire) ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Multi-device SYNC pin (advanced) SYNC pin coordinates phase between cascaded BOS1921s (< 2 µs delay). The Drive.P tile has 10 pads (I2C/I3C, OUT±, GPIO, V+, V_DRIVE, GND); the SYNC pin on the IC is not routed to a pad, so multi-tile cascading is hardware-gated to a future tile rev. #### I3C alternate bus mode (advanced) Pads 4/5 are bus-shared between I²C (default) and I3C SDR (≤12.5 Mbps with in-band interrupts). The cores tile-driver framework only ships an I²C PAL; I3C support is an ecosystem-wide gap, not BOS1921-specific. ### Enums #### `drive_p_mode_t` Drive.P operating mode. | Member | Value | Description | | --- | --- | --- | | `DRIVE_P_MODE_IDLE` | `0` | Output disabled, status readback | | `DRIVE_P_MODE_SENSE_FINE` | `1` | Piezo sensing, 7.6 mV/LSB resolution | | `DRIVE_P_MODE_SENSE_COARSE` | `2` | Piezo sensing, 54.5 mV/LSB resolution | | `DRIVE_P_MODE_PLAY_DIRECT` | `3` | Direct waveform output | | `DRIVE_P_MODE_PLAY_FIFO` | `4` | FIFO-buffered playback, 8 ksps | | `DRIVE_P_MODE_PLAY_RAM_SYNTH` | `5` | RAM Synthesis waveform playback | #### `drive_p_output_range_t` Output voltage range (CONFIG.GAIND). | Member | Value | Description | | --- | --- | --- | | `DRIVE_P_OUTPUT_HIGH_V` | `0` | ±95 V range, FBratio 31 (default) | | `DRIVE_P_OUTPUT_LOW_V` | `1` | ±13.28 V range, FBratio 4.33 | #### `drive_p_sense_gain_t` Sense-channel resolution (CONFIG.GAINS). | Member | Value | Description | | --- | --- | --- | | `DRIVE_P_SENSE_COARSE_GAIN` | `0` | 54.5 mV LSB, FBratio 31 | | `DRIVE_P_SENSE_FINE_GAIN` | `1` | 7.6 mV LSB, FBratio 4.33 (default) | ### Structs #### `drive_p_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `reserved` | Placeholder — no options yet. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_DRIVE_P_VERSION_MAJOR` | `3` | | | `TILE_DRIVE_P_VERSION_MINOR` | `3` | | | `TILE_DRIVE_P_VERSION_PATCH` | `0` | | | `BOS1921_I2C_ADDR_DEFAULT` | `0x44` | | | `BOS1921_I2C_ADDR_SECOND` | `0x45` | Instance 1 — first of a pair | | `BOS1921_I2C_ADDR_THIRD` | `0x46` | Instance 2 — second of a pair | | `BOS1921_CHIP_ID_DEFAULT` | `0x0781` | Expected lower 12 bits of CHIP_ID register. | | `BOS_STATUS_STATE_MASK` | `0x0300` | STATE field in IC_STATUS (bits 9:8). | | `BOS_STATUS_STATE_IDLE` | `0x0000` | | | `BOS_STATUS_STATE_CALIB` | `0x0100` | | | `BOS_STATUS_STATE_RUNNING` | `0x0200` | | | `BOS_STATUS_STATE_ERROR` | `0x0300` | | | `BOS_STATUS_FAULT_MASK` | `0x00FC` | Fault bits in IC_STATUS (bits 7:2, excluding FULL and PLAYST). | | `BOS_SUP_RISE_I2C_ADDR_POS` | `12` | | | `BOS_SUP_RISE_TIMING_DEFAULT` | `0x0967` | default with I2C_ADDR nibble cleared | --- Generated 2026-08-12 from the driver header at tiles@9897b3e. Canonical HTML: https://bergsonne.io/docs/tiles/drive/p ============================================================================== ## Power.L.1N driver > Power.L.1N tile driver — Nordic nPM1300 PMIC. | | | | --- | --- | | Tile | `Power.L.1N` | | Driver version | `1.0.1` | | Header | `tile_power_l_1n.h` | | Generated from | `tiles@555a591` | | Canonical page | https://bergsonne.io/docs/tiles/power/l1n | ### Overview Battery charger + 2 buck regulators + system supply (VSYS) + 3 indicator LEDs. Platform-agnostic: all bus access goes through tiles_pal_t. The bucks come up at fixed boot voltages chosen by the tile's VSET pulldown resistors (no I2C needed): VSET1 = 47 kΩ → VOUT1 = 1.8 V, VSET2 = 330 kΩ → VOUT2 = 3.3 V. buck_set_mv() switches a buck to software control to override its VSET default at runtime. Tile LEDs. Three discrete 0201 parts (Power-L1-N-a BOM), not an RGB package: U3 = APG015SURKKC-TT 631 nm red U4 = APG015SEKKC-TT 605 nm ORANGE (previously documented as "yellow") U5 = APG015CGKKC-TT 571 nm green Driver mode assignment made by init(): LED0 → HOST mode (firmware "ready"/status) LED1 → CHARGING mode (auto, on while charging) LED2 → ERROR mode (auto, on for charger faults) @warning The LED index → colour mapping is taken from the schematic and is NOT verified on hardware. All three LEDDRV channels are confirmed working (bench, 2026-08-10), but the only tile available was misstuffed with three identical red parts, so colour could not be checked. The BOM refdes order (U3 red, U4 orange, U5 green) runs opposite to the LED0/1/2 order assumed here. This matters: init() puts ERROR on LED2, so if LED2 is in fact the green part, a charger fault lights GREEN. Verify against a correctly stuffed tile before relying on colour to convey meaning. @note Prefer conveying state by blink PATTERN rather than by colour. A misstuffed or single-colour tile still communicates correctly that way. Datasheet: Nordic nPM1300 Product Specification v1.1 (4490_483). ### Examples #### Quick start ```c #include "core_tiles.h" tile_t pmic; tile_power_l_1n_init(core_tiles_pal(&core_i2c1), 0, &pmic, NULL); if (tile_is_ready(&pmic)) { tile_power_l_1n_set_charge_current_ma(&pmic, 200); tile_power_l_1n_charger_enable(&pmic, 1); uint16_t vbat = tile_power_l_1n_get_vbat_mv(&pmic); (void)vbat; } ``` ### API 18 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Runtime ##### `tile_power_l_1n_get_charge_status` — Studio ```c uint8_t tile_power_l_1n_get_charge_status(tile_t* tile) ``` Read the raw charge-status register. **Returns:** BCHGCHARGESTATUS bits (NPM1300_CHG_* mask). ##### `tile_power_l_1n_get_charge_error` — Studio ```c uint8_t tile_power_l_1n_get_charge_error(tile_t* tile) ``` Read the charger error-reason register. **Returns:** BCHGERRREASON bits (0 = no error). ##### `tile_power_l_1n_is_charging` — Studio ```c uint8_t tile_power_l_1n_is_charging(tile_t* tile) ``` Whether the charger is actively charging (trickle / CC / CV). **Returns:** 1 if charging, 0 otherwise. ##### `tile_power_l_1n_is_charge_complete` — Studio ```c uint8_t tile_power_l_1n_is_charge_complete(tile_t* tile) ``` Whether charging has completed (battery full). **Returns:** 1 if charge complete, 0 otherwise. ##### `tile_power_l_1n_battery_present` — Studio ```c uint8_t tile_power_l_1n_battery_present(tile_t* tile) ``` Whether a battery is detected. BATTERYDETECTED in BCHGCHARGESTATUS, a charger-domain bit: the datasheet states "CHARGER waits until a battery is detected before charging", i.e. detection runs as part of the charge cycle. With the charger disabled the bit reads 0 whether or not a cell is fitted — measured on the bench with a battery physically attached (0 before enable, 1 after). Do NOT gate a charge-enable decision on this; that deadlocks. Enable charging first, poll this for a second or two, and disable again if nothing appears. Note also that get_vbat_mv() cannot substitute: an open battery terminal reads a plausible ~3 V once charger current has pushed the VBAT decoupling cap up. **Returns:** 1 if a battery is present, 0 otherwise. ##### `tile_power_l_1n_get_vbat_mv` — Studio ```c uint16_t tile_power_l_1n_get_vbat_mv(tile_t* tile) ``` Measure the battery voltage. **Returns:** VBAT in millivolts (0-5000). ##### `tile_power_l_1n_get_vsys_mv` — Studio ```c uint16_t tile_power_l_1n_get_vsys_mv(tile_t* tile) ``` Measure the system (VSYS) voltage. **Returns:** VSYS in millivolts (0-6375). ##### `tile_power_l_1n_get_die_temp_c` — Studio ```c int16_t tile_power_l_1n_get_die_temp_c(tile_t* tile) ``` Measure the PMIC die temperature. **Returns:** Die temperature in degrees Celsius. ##### `tile_power_l_1n_led_set` — Studio ```c void tile_power_l_1n_led_set(tile_t* tile, uint8_t led, uint8_t on) ``` Turn a host-controlled LED on or off. Only effective when the LED is in HOST mode (see led_set_mode). - `led` — 0, 1 or 2. - `on` — 1 = on, 0 = off. #### Configuration ##### `tile_power_l_1n_charger_enable` — Studio ```c void tile_power_l_1n_charger_enable(tile_t* tile, uint8_t on) ``` Enable or disable battery charging. - `on` — 1 = enable charging, 0 = disable. ##### `tile_power_l_1n_set_charge_current_ma` — Studio ```c void tile_power_l_1n_set_charge_current_ma(tile_t* tile, uint16_t ma) ``` Set the constant-current charge level. Programmable 32-800 mA in 2 mA steps; out-of-range values clamp. - `ma` — [32..800] Charge current in milliamps. ##### `tile_power_l_1n_set_term_mv` — Studio ```c void tile_power_l_1n_set_term_mv(tile_t* tile, uint16_t mv) ``` Set the charge-termination (full) voltage. Selectable 3.50-3.65 V or 4.00-4.45 V in 50 mV steps; the 3.70-3.95 V gap is not supported and clamps to 3.65 V. - `mv` — Termination voltage in millivolts (e.g. 4200). ##### `tile_power_l_1n_buck_enable` — Studio ```c void tile_power_l_1n_buck_enable(tile_t* tile, uint8_t buck, uint8_t on) ``` Enable or disable a buck regulator. - `buck` — 1 (VOUT1) or 2 (VOUT2). - `on` — 1 = enable, 0 = disable. ##### `tile_power_l_1n_buck_set_mv` — Studio ```c void tile_power_l_1n_buck_set_mv(tile_t* tile, uint8_t buck, uint16_t mv) ``` Set a buck regulator's output voltage. Selects software voltage control for that buck (overriding its VSET resistor) and applies the new target. - `buck` — 1 (VOUT1) or 2 (VOUT2). - `mv` — [1000..3300] Output voltage in mV, 100 mV steps. ##### `tile_power_l_1n_led_set_mode` — Studio ```c void tile_power_l_1n_led_set_mode(tile_t* tile, uint8_t led, power_l_1n_led_mode_t mode) ``` Set an indicator LED's drive mode. LED0 is green, LED1 yellow, LED2 red. In auto modes (ERROR/CHARGING) the charger drives the LED directly; HOST mode hands control to led_set(). - `led` — 0, 1 or 2. - `mode` — power_l_1n_led_mode_t. #### Advanced ##### `tile_power_l_1n_get_reset_cause` — Studio ```c uint8_t tile_power_l_1n_get_reset_cause(tile_t* tile) ``` Read the reset-cause register (why the PMIC last reset). **Returns:** RSTCAUSE bits. #### Other ##### `tile_power_l_1n_find` ```c uint8_t tile_power_l_1n_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether an nPM1300 is present on the I2C bus. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) **Returns:** 1 if the device ACKs, 0 otherwise ##### `tile_power_l_1n_init` ```c void tile_power_l_1n_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const power_l_1n_cfg_t* cfg) ``` Initialize the nPM1300 PMIC. Sets the indicator LED modes (LED0=HOST, LED1=CHARGING, LED2=ERROR), disables NTC monitoring (no thermistor on this tile), and applies the charger settings. The buck regulators are left at their boot voltages (1.8 V / 3.3 V, fixed by the VSET resistors) — they are already up. Pass cfg=NULL for defaults (100 mA, 4.20 V, charging enabled). - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### NTC battery thermistor (advanced) Hardware-gated (not a driver gap). The nPM1300 can sense battery temperature via an NTC thermistor for JEITA charge regulation. NTC is tied to GND on Power-L-1N-a (no thermistor fitted), so the driver disables NTC monitoring (ADCNTCRSEL=0, DISABLENTC). Closing requires a tile hardware revision. #### Load switches / LDOs (LSIN/LSOUT) (niche) Hardware-gated (not a driver gap). The nPM1300's two load-switch/LDO outputs are tied off (LSIN1/2, LSOUT1/2 grounded) on this tile, so there's nothing to control. #### GPIO0-3 (niche) Hardware-gated (not a driver gap). The nPM1300 GPIOs aren't routed to tile pads on Power-L-1N-a, so there's no externally useful GPIO function. #### Ship / hibernate mode (advanced) Driver-deferred. The chip supports ultra-low-power ship/hibernate modes (battery disconnect for storage). Not yet exposed — the ship-mode task register address needs datasheet confirmation before wiring. #### POF warning + buck retention / forced-PWM (niche) Driver-deferred. Power-fail early-warning (via GPIO) and buck retention-voltage / forced-PWM modes aren't exposed yet. #### VBUS input current limit (ILIM) (advanced) Driver-deferred. The nPM1300 boots with a 100 mA VBUS input limit, and VBUSINILIM0 (VBUSIN base 0x0200, offset 0x1) plus the TASKUPDATEILIMSW commit are not exposed, so it cannot be raised for a 500 mA USB or a 1500 mA USB-C supply. This is not cosmetic: measured on the bench, enabling a 100 mA charge collapsed VSYS from 5.15 V to ~3.65 V as input current hit the limit (datasheet Fig. 6). Both bucks stayed in regulation — VSYSMIN is 2.7 V — but headroom on a 3.3 V VOUT2 drops to ~350 mV, and a load with a startup surge can brown the rail out entirely (see the Sense.CAM.P bring-up). ### Events - `charge_complete` - `charging` ### Enums #### `power_l_1n_led_mode_t` LED indicator mode (LEDDRVxMODESEL). | Member | Value | Description | | --- | --- | --- | | `NPM1300_LED_ERROR` | `0` | auto: on for charger error | | `NPM1300_LED_CHARGING` | `1` | auto: on while charging | | `NPM1300_LED_HOST` | `2` | software-controlled via led_set() | | `NPM1300_LED_NOTUSED` | `3` | disabled | ### Structs #### `power_l_1n_cfg_t` Optional init config (pass NULL for defaults: 100 mA, 4.20 V, charging on). | Type | Field | Description | | --- | --- | --- | | `uint16_t` | `charge_current_ma` | 32-800 mA. 0 = default (100 mA). | | `uint16_t` | `term_mv` | 3500-4450 mV. 0 = default (4200 mV). | | `uint8_t` | `enable_charging` | 0 = leave charging off, 1 = enable. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_POWER_L_1N_VERSION_MAJOR` | `1` | | | `TILE_POWER_L_1N_VERSION_MINOR` | `0` | | | `TILE_POWER_L_1N_VERSION_PATCH` | `1` | | | `NPM1300_I2C_ADDR` | `0x6B` | | --- Generated 2026-08-12 from the driver header at tiles@555a591. Canonical HTML: https://bergsonne.io/docs/tiles/power/l1n ============================================================================== ## Power.L.1T driver > Li-Ion charge controller driver for the Power.L.1T tile (rev a). | | | | --- | --- | | Tile | `Power.L.1T` | | Driver version | `3.2.1` | | Header | `tile_power_l_1t.h` | | Generated from | `tiles@5f180cb` | | Canonical page | https://bergsonne.io/docs/tiles/power/l1t | ### Overview Embeds the Texas Instruments BQ25150, a single-cell Li-Ion charge controller with programmable 1.8 V LDO output and 12-bit ADC for battery and system monitoring. Key specifications: - Charge input: 3.4–5.5 V (up to 500 mA) - Battery voltage: 3.6–4.6 V (programmable in 10 mV steps) - LDO output: 1.8 V on this tile (chip is programmable) - ADC: 12-bit, 6 input channels - Charge current: 1.25–500 mA programmable - JEITA-style NTC: cold / cool / warm / hot thresholds Datasheet: https://www.bergsonne.io/tiles/power/l1t IC datasheet: https://www.ti.com/lit/ds/symlink/bq25150.pdf ### Examples #### Quick start ```c #include "core_tiles.h" tile_t battery; tile_power_l_1t_init(core_tiles_pal(&core_i2c1), 0, &battery, NULL); if (tile_is_ready(&battery)) { tile_power_l_1t_set_charge_current_ma(&battery, 100); tile_power_l_1t_set_charge_voltage_mv(&battery, 4200); uint16_t vbat = tile_power_l_1t_get_vbat_mv(&battery); if (tile_power_l_1t_is_battery_low(&battery, 10)) { // go to sleep, save the cell } } ``` ### API 38 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Runtime ##### `tile_power_l_1t_get_vbat_mv` — Studio ```c uint16_t tile_power_l_1t_get_vbat_mv(tile_t* tile) ``` Read battery voltage from the ADC. Replaces the deprecated raw-counts API; performs a 2-byte burst read to avoid torn samples. - `tile` — Initialised tile handle **Returns:** Battery voltage in mV (0–6000) ##### `tile_power_l_1t_get_vin_mv` — Studio ```c uint16_t tile_power_l_1t_get_vin_mv(tile_t* tile) ``` Read input (VIN) voltage from the ADC. - `tile` — Initialised tile handle **Returns:** VIN in mV (0–6000) ##### `tile_power_l_1t_get_pmid_mv` — Studio ```c uint16_t tile_power_l_1t_get_pmid_mv(tile_t* tile) ``` Read PMID (system rail) voltage from the ADC. - `tile` — Initialised tile handle **Returns:** PMID in mV (0–6000) ##### `tile_power_l_1t_get_charge_current_ma` — Studio ```c uint16_t tile_power_l_1t_get_charge_current_ma(tile_t* tile) ``` Read charge current (into the battery) from the ADC. Scaled relative to the configured ICHG fast-charge limit; returns actual mA flowing into the cell. - `tile` — Initialised tile handle **Returns:** Charge current in mA (0–500) ##### `tile_power_l_1t_get_input_current_ma` — Studio ```c uint16_t tile_power_l_1t_get_input_current_ma(tile_t* tile) ``` Read input current (from VIN) from the ADC. Range scales with ILIMCTRL: ≤150 mA range gives 0–375 mA full scale; >150 mA range gives 0–750 mA full scale. - `tile` — Initialised tile handle **Returns:** Input current in mA (0–750) ##### `tile_power_l_1t_get_ts_mv` — Studio ```c uint16_t tile_power_l_1t_get_ts_mv(tile_t* tile) ``` Read raw TS pin voltage from the ADC. Returns the millivolt reading on the TS pin (0–1200 mV). Convert to °C using the NTC's resistance/temperature curve in firmware — the chip doesn't expose temperature directly. - `tile` — Initialised tile handle **Returns:** TS voltage in mV (0–1200) ##### `tile_power_l_1t_get_adcin_mv` — Studio ```c uint16_t tile_power_l_1t_get_adcin_mv(tile_t* tile) ``` Read raw ADCIN pin voltage from the ADC. Optional auxiliary input (0–1.2 V range). Useful for monitoring an external sensor (e.g., separate NTC, voltage divider). - `tile` — Initialised tile handle **Returns:** ADCIN voltage in mV (0–1200) ##### `tile_power_l_1t_get_percent` — Studio ```c uint8_t tile_power_l_1t_get_percent(tile_t* tile) ``` Read battery state-of-charge as a percentage. Derived from VBAT using a linear curve (3000 mV = 0%, 4200 mV = 100%). Coarse — a real fuel gauge would integrate Coulombs. - `tile` — Initialised tile handle **Returns:** Battery percentage (0–100) ##### `tile_power_l_1t_get_charge_status` — Studio ```c void tile_power_l_1t_get_charge_status(tile_t* tile, power_l_1t_status_t *out) ``` Snapshot the chip's current state + latched faults. Reads STAT0/STAT1 (live state) and FLAG0/FLAG1/FLAG3 (event flags which clear on read) into the supplied struct. Call once per polling cycle — multiple reads will lose flag transitions. - `tile` — Initialised tile handle - `out` — Caller-allocated status struct (zeroed on entry) ##### `tile_power_l_1t_is_charging` — Studio ```c uint8_t tile_power_l_1t_is_charging(tile_t* tile) ``` Is the cell currently being charged? Convenience over @ref tile_power_l_1t_get_charge_status — returns the `charging` field as a bool. Note this reads (and clears) FLAG3 as a side effect; if you also poll the full status struct, alternate with this rather than calling both per cycle. - `tile` — Initialised tile handle **Returns:** 1 if charging, 0 otherwise ##### `tile_power_l_1t_is_charge_done` — Studio ```c uint8_t tile_power_l_1t_is_charge_done(tile_t* tile) ``` Has charge termination been reached? Returns the `charge_done` bit from STAT0 — set when the cell reaches VBATREG and current tapers below the termination threshold. - `tile` — Initialised tile handle **Returns:** 1 if charge cycle has finished, 0 otherwise ##### `tile_power_l_1t_is_battery_low` — Studio ```c uint8_t tile_power_l_1t_is_battery_low(tile_t* tile, uint8_t threshold_pct) ``` Is the battery below `threshold_pct`? The classic "go to sleep" trigger. Equivalent to `get_percent() < threshold_pct`. Quick: one ADC read. - `tile` — Initialised tile handle - `threshold_pct` — Threshold in percent (0–100) **Returns:** 1 if battery percent is strictly below threshold, 0 otherwise ##### `tile_power_l_1t_is_powered` — Studio ```c uint8_t tile_power_l_1t_is_powered(tile_t* tile) ``` Is external power present (running off VIN)? Returns the `vin_pgood` bit — true when VIN is in the valid operating range (3.4–5.5 V). Use to branch behaviour between "plugged in" and "battery only" modes. - `tile` — Initialised tile handle **Returns:** 1 if VIN is good, 0 if running off battery only ##### `tile_power_l_1t_wait_for_charge_done` — Studio ```c uint8_t tile_power_l_1t_wait_for_charge_done(tile_t* tile, uint32_t timeout_ms) ``` Block until charge_done is observed, or timeout. Polls the charge_done bit at a 1 s cadence (charge state changes on the order of minutes for a typical cell — faster polling wastes MCU cycles and bus bandwidth without finer-grained answers). Returns 1 immediately if the cycle is already finished. - `tile` — Initialised tile handle - `timeout_ms` — Maximum time to wait, in milliseconds **Returns:** 1 if charge_done was observed, 0 on timeout ##### `tile_power_l_1t_get_adc_comparators` — Studio ```c uint8_t tile_power_l_1t_get_adc_comparators(tile_t* tile) ``` Read (and clear) the ADC comparator alarm flags. **Returns:** BQ25150_FLAG2_COMPn_ALARM bits that have tripped since last read. #### Configuration ##### `tile_power_l_1t_set_charge_current_ma` — Studio ```c void tile_power_l_1t_set_charge_current_ma(tile_t* tile, uint16_t ma) ``` Set the fast-charge current. Programs ICHG_CTRL with the appropriate ICHARGE_RANGE bit so the resolution scales: ≤318 mA uses 1.25 mA steps, >318 mA uses 2.5 mA steps. Values clamp to 1.25–500 mA. - `tile` — Initialised tile handle - `ma` — Target charge current in mA (1.25–500) ##### `tile_power_l_1t_set_charge_voltage_mv` — Studio ```c void tile_power_l_1t_set_charge_voltage_mv(tile_t* tile, uint16_t mv) ``` Set the battery regulation voltage. VBATREG = 3600 + code × 10 mV. Values outside 3600–4600 mV clamp to that range. Use 4200 for typical Li-Ion, 3650 for LFP, 4350+ for high-voltage NMC variants. - `tile` — Initialised tile handle - `mv` — Target battery voltage in mV (3600–4600) ##### `tile_power_l_1t_set_pre_charge_ma` — Studio ```c void tile_power_l_1t_set_pre_charge_ma(tile_t* tile, uint8_t ma) ``` Set the pre-charge current (used when VBAT < VLOW). Pre-charge applies to deeply-discharged cells; usually 10–20 % of the fast-charge rate. Values clamp to 1.25–77.5 mA. - `tile` — Initialised tile handle - `ma` — Target pre-charge current in mA (1.25–77.5) ##### `tile_power_l_1t_set_termination_percent` — Studio ```c void tile_power_l_1t_set_termination_percent(tile_t* tile, uint8_t pct) ``` Set the termination current as a percentage of ICHG. Charging ends when the cell's current draw drops below this threshold during the CV phase. 0 disables termination (charges continuously until VBATREG is reached). Values clamp to 1–31 %. - `tile` — Initialised tile handle - `pct` — Termination current as % of ICHG (0 = disabled, 1–31) ##### `tile_power_l_1t_set_input_current_limit_ma` — Studio ```c void tile_power_l_1t_set_input_current_limit_ma(tile_t* tile, uint16_t ma) ``` Set the input current limit (DPM threshold). When the adapter can't supply more than this, charge current folds back to keep VIN above the DPM threshold. Values clamp to 5–500 mA in 5 mA steps. - `tile` — Initialised tile handle - `ma` — Input current limit in mA (5–500) ##### `tile_power_l_1t_set_ts_cold` — Studio ```c void tile_power_l_1t_set_ts_cold(tile_t* tile, uint8_t code) ``` Set the cold-temperature TS threshold (raw register value). The TS register codes are bit-positional (1, 2, 4, 8, 16, ... encode multiples of 4.688 mV up to 600 mV). Datasheet section 8.5.1.49–52 describes the exact mapping; users tuning JEITA thresholds should consult the chip's NTC profile guide. - `tile` — Initialised tile handle - `code` — Raw 8-bit TS_COLD register value ##### `tile_power_l_1t_set_ts_cool` — Studio ```c void tile_power_l_1t_set_ts_cool(tile_t* tile, uint8_t code) ``` Set the cool-temperature TS threshold (raw register value). - `tile` — Initialised tile handle - `code` — Raw 8-bit TS_COOL register value ##### `tile_power_l_1t_set_ts_warm` — Studio ```c void tile_power_l_1t_set_ts_warm(tile_t* tile, uint8_t code) ``` Set the warm-temperature TS threshold (raw register value). - `tile` — Initialised tile handle - `code` — Raw 8-bit TS_WARM register value ##### `tile_power_l_1t_set_ts_hot` — Studio ```c void tile_power_l_1t_set_ts_hot(tile_t* tile, uint8_t code) ``` Set the hot-temperature TS threshold (raw register value). - `tile` — Initialised tile handle - `code` — Raw 8-bit TS_HOT register value ##### `tile_power_l_1t_set_ts_enabled` — Studio ```c void tile_power_l_1t_set_ts_enabled(tile_t* tile, uint8_t enabled) ``` Enable or disable TS-based thermal protection. When disabled, the chip ignores the NTC entirely (charging proceeds regardless of battery temperature). Default at init: enabled. - `tile` — Initialised tile handle - `enabled` — 1 = TS thermal protection on, 0 = off ##### `tile_power_l_1t_set_ldo_voltage_mv` — Studio ```c void tile_power_l_1t_set_ldo_voltage_mv(tile_t* tile, uint16_t mv) ``` Set the LDO output voltage. VLDO = 600 + code × 100 mV. Values clamp to 600–3700 mV. Tile design gates this rail to a fixed 1.8 V at the connector — changing the LDO voltage breaks downstream peripherals expecting 1.8 V on pad 10. Useful for non-default tile variants or load-switch-mode pass-through use. - `tile` — Initialised tile handle - `mv` — Target LDO voltage in mV (600–3700) ##### `tile_power_l_1t_set_ldo_mode` — Studio ```c void tile_power_l_1t_set_ldo_mode(tile_t* tile, power_l_1t_ldo_mode_t mode) ``` Set the LS/LDO output mode (regulated LDO vs load switch). In LDO mode the chip regulates pad 10 to the configured voltage (10 mA max). In load-switch mode it pass-through-connects PMID via a FET (up to 150 mA, but VINLS must be tied to the desired supply on the tile PCB). - `tile` — Initialised tile handle - `mode` — LDO mode (POWER_L_1T_LDO_MODE_LDO or _LOAD_SWITCH) ##### `tile_power_l_1t_set_ldo_enabled` — Studio ```c void tile_power_l_1t_set_ldo_enabled(tile_t* tile, uint8_t enabled) ``` Enable or disable the LS/LDO output. When disabled, pad 10 (V+) goes high-impedance — downstream rails expecting 1.8 V will drop. Default at init: enabled (chip default). - `tile` — Initialised tile handle - `enabled` — 1 = output on, 0 = output off (high-Z) ##### `tile_power_l_1t_charger_enable` — Studio ```c void tile_power_l_1t_charger_enable(tile_t* tile, uint8_t on) ``` Enable or disable battery charging over I²C. Sets/clears ICCTRL2.CHARGER_DISABLE. Note the hardware /CE pin still gates charging: when /CE is high, charging is off regardless of this bit; this only takes effect when /CE is held low (the tile's default). - `on` — 1 = allow charging, 0 = disable charging. ##### `tile_power_l_1t_set_battery_uvlo_mv` — Studio ```c void tile_power_l_1t_set_battery_uvlo_mv(tile_t* tile, uint16_t mv) ``` Set the battery under-voltage lockout (discharge cutoff) threshold. BUVLO[2:0]: 3.0 / 2.8 / 2.6 / 2.4 / 2.2 V (nearest below `mv` is chosen); deeper cutoff trades battery life for runtime. Other BUVLO fields (precharge threshold, OCP limit) are preserved. - `mv` — Desired UVLO in mV (2200-3000). ##### `tile_power_l_1t_set_safety_timer` — Studio ```c void tile_power_l_1t_set_safety_timer(tile_t* tile, power_l_1t_safety_timer_t mode) ``` Set the charge safety-timer limit. - `mode` — power_l_1t_safety_timer_t (3h / 6h / 12h / off). ##### `tile_power_l_1t_set_pmid_mode` — Studio ```c void tile_power_l_1t_set_pmid_mode(tile_t* tile, power_l_1t_pmid_mode_t mode) ``` Set the PMID power-path mode (ICCTRL1[1:0]). - `mode` — power_l_1t_pmid_mode_t. ##### `tile_power_l_1t_set_adc_comparator` — Studio ```c void tile_power_l_1t_set_adc_comparator(tile_t* tile, uint8_t comp, power_l_1t_adc_channel_t channel, uint16_t threshold) ``` Configure a programmable ADC comparator (threshold alarm). Routes ADC channel `channel` to comparator `comp` (1-3) and sets its 16-bit threshold (left-justified, same scale as the channel's ADC result). When the measurement crosses the threshold, the matching COMPn_ALARM bit latches in FLAG2 (read via get_adc_comparators()). Pass channel = POWER_L_1T_ADC_CH_DISABLED to turn a comparator off. Note: the chip's above/below polarity bit (ADCALARM_ABOVE) is not exposed by this driver yet — the comparator uses its default sense. - `comp` — Comparator index 1-3. - `channel` — power_l_1t_adc_channel_t to monitor. - `threshold` — 16-bit raw ADC threshold. #### Advanced ##### `tile_power_l_1t_enter_ship_mode` — Studio ```c void tile_power_l_1t_enter_ship_mode(tile_t* tile) ``` Enter ship mode (~10 nA quiescent). Disconnects the battery internally; only an MR press or VIN insertion will wake the chip. Charge parameters reset to defaults on exit. Use only for long-term storage / end-of-line packaging. re-insertion) to recover. Marked `section=advanced` for the same posture as other one-way / hardware-gated operations. - `tile` — Initialised tile handle ##### `tile_power_l_1t_read_status` — Studio ```c uint8_t tile_power_l_1t_read_status(tile_t* tile, uint8_t reg) ``` Read any 8-bit BQ25150 register. - `tile` — Initialised tile handle - `reg` — Register address **Returns:** 8-bit register value ##### `tile_power_l_1t_write_reg` — Studio ```c void tile_power_l_1t_write_reg(tile_t* tile, uint8_t reg, uint8_t value) ``` Write any 8-bit BQ25150 register. Escape hatch for advanced users wanting to touch registers the driver doesn't expose. Caller is responsible for not bricking the chip — most useful registers have typed setters above. - `tile` — Initialised tile handle - `reg` — Register address - `value` — Value to write #### Other ##### `tile_power_l_1t_find` ```c uint8_t tile_power_l_1t_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether a BQ25150 is present on the I2C bus. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) **Returns:** 1 if device ACKs, 0 otherwise ##### `tile_power_l_1t_init` ```c void tile_power_l_1t_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const power_l_1t_cfg_t *cfg) ``` Initialize the BQ25150 charge controller. Verifies the device ID, disables the I²C watchdog (the chip otherwise resets all charge parameters every 50 s), enables all 6 ADC channels, configures sane charge defaults, and exits ship mode. Pass cfg=NULL for defaults. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### MR button + INT pin handling (advanced) The chip's MR (push-button) and INT (interrupt) pins aren't routed to tile pads on the current revision — nothing for a Core GPIO to attach to. Closing this gap requires a tile hardware revision that routes at least INT to a connector pad. Until then, MASK0–3 / MRCTRL register writes have no external effect. ### Enums #### `power_l_1t_safety_timer_t` Charge safety-timer limit (CHARGERCTRL0[2:1]). | Member | Value | Description | | --- | --- | --- | | `POWER_L_1T_SAFETY_3H` | `0` | 3-hour fast-charge timer | | `POWER_L_1T_SAFETY_6H` | `1` | 6-hour (default) | | `POWER_L_1T_SAFETY_12H` | `2` | 12-hour | | `POWER_L_1T_SAFETY_OFF` | `3` | disabled | #### `power_l_1t_pmid_mode_t` PMID power-path mode (ICCTRL1[1:0]). | Member | Value | Description | | --- | --- | --- | | `POWER_L_1T_PMID_AUTO` | `0` | powered from BAT or VIN (default) | | `POWER_L_1T_PMID_BAT_ONLY` | `1` | forced from BAT even when VIN present | | `POWER_L_1T_PMID_FLOAT` | `2` | disconnected, floating | | `POWER_L_1T_PMID_PULLDOWN` | `3` | disconnected, pulled down | #### `power_l_1t_adc_channel_t` ADC channel selector for the programmable comparators (ADC_COMPn). | Member | Value | Description | | --- | --- | --- | | `POWER_L_1T_ADC_CH_DISABLED` | `0` | | | `POWER_L_1T_ADC_CH_ADCIN` | `1` | | | `POWER_L_1T_ADC_CH_TS` | `2` | | | `POWER_L_1T_ADC_CH_VBAT` | `3` | | | `POWER_L_1T_ADC_CH_ICHARGE` | `4` | | | `POWER_L_1T_ADC_CH_VIN` | `5` | | | `POWER_L_1T_ADC_CH_PMID` | `6` | | | `POWER_L_1T_ADC_CH_IIN` | `7` | | #### `power_l_1t_ldo_mode_t` LDO output mode — regulated voltage vs pass-through load switch. | Member | Value | Description | | --- | --- | --- | | `POWER_L_1T_LDO_MODE_LDO` | `0` | Regulated LDO output | | `POWER_L_1T_LDO_MODE_LOAD_SWITCH` | `1` | Pass-through load switch | ### Structs #### `power_l_1t_status_t` Snapshot of the BQ25150's current state and latched faults. | Type | Field | Description | | --- | --- | --- | #### `power_l_1t_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `reserved` | Placeholder — no options yet. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_POWER_L_1T_VERSION_MAJOR` | `3` | | | `TILE_POWER_L_1T_VERSION_MINOR` | `2` | | | `TILE_POWER_L_1T_VERSION_PATCH` | `1` | | | `BQ25150_I2C_ADDR_DEFAULT` | `0x6B` | | | `BQ25150_FLAG2_ADC_READY` | `0x80` | FLAG2 bit7: ADC conversion completed (clear-on-read) | | `BQ25150_ADCCTRL0_RATE_1S` | `0x80` | ADC_READ_RATE = every 1 s (battery mode) | | `BQ25150_ADCCTRL0_RATE_MANUAL` | `0x00` | ADC_READ_RATE = manual (convert on CONV_START) | | `BQ25150_ADCCTRL0_CONV_START` | `0x20` | bit5: trigger one ADC conversion | | `BQ25150_ADCCTRL0_COMP1_DEFAULT` | `0x02` | ADC_COMP1[2:0] reset value | | `BQ25150_FLAG2_COMP1_ALARM` | `0x40` | Comparator 1 threshold crossed | | `BQ25150_FLAG2_COMP2_ALARM` | `0x20` | Comparator 2 threshold crossed | | `BQ25150_FLAG2_COMP3_ALARM` | `0x10` | Comparator 3 threshold crossed | | `BQ25150_DEVICE_ID_DEFAULT` | `0x20` | Expected DEVICE_ID register value. | --- Generated 2026-08-12 from the driver header at tiles@5f180cb. Canonical HTML: https://bergsonne.io/docs/tiles/power/l1t ============================================================================== ## Sense.ACP driver > TMD3725 ambient-light, RGB colour & proximity sensor. | | | | --- | --- | | Tile | `Sense.ACP` | | Driver version | `1.0.0` | | Header | `tile_sense_acp.h` | | Generated from | `tiles@18abd82` | | Canonical page | https://bergsonne.io/docs/tiles/sense/acp | ### Overview Platform-agnostic driver for the ams/OSRAM TMD3725 ALS/Color/Proximity module on Sense.ACP. Provides RGBC ambient-light sensing (four 16-bit data converters — Clear/Red/Green/Blue) plus an integrated IR-LED proximity engine with an 8-bit result. Key specifications: - RGBC ambient light: 16-bit Clear/Red/Green/Blue channels - Programmable ALS gain (1/4/16/64x) and integration time (2.8-719 ms) - Proximity: integrated, factory-trimmed IR LED, 8-bit output - Programmable proximity gain (1/2/4/8x) and LED drive (6-192 mA) - 1.8 V supply and 1.8 V I2C (up to 400 kHz), fixed address 0x39 Board note: on Sense.ACP the proximity IR LED anode (LEDA) is fed from an on-tile TPS61099 boost converter, so the LED drive current is drawn from a boosted rail rather than the 1.8 V input directly. The boost is always-on hardware; this driver only sets the LED drive strength via PLDRIVE. Datasheet: ams TMD3725, "ALS, Color and Proximity Sensor Module" (v2-00, 2023-Mar-21). ### Examples #### Quick start ```c #include "core.h" #include "core_tiles.h" #include "tile_sense_acp.h" tile_t acp; tile_sense_acp_init(core_tiles_pal(&core_i2c1), 0, &acp, NULL); uint16_t clear = tile_sense_acp_get_clear(&acp); uint8_t prox = tile_sense_acp_get_proximity(&acp); ``` ### API 14 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_acp_sleep` — Studio ```c void tile_sense_acp_sleep(tile_t *tile) ``` Power down the sensor (clears PON/AEN/PEN). - `tile` — Initialised tile handle. ##### `tile_sense_acp_wake` — Studio ```c void tile_sense_acp_wake(tile_t *tile) ``` Resume from sleep, restoring the engines enabled at init. - `tile` — Sleeping tile handle. #### Runtime ##### `tile_sense_acp_get_clear` — Studio ```c uint16_t tile_sense_acp_get_clear(tile_t *tile) ``` Read the Clear (unfiltered) channel — overall ambient light. - `tile` — Initialised tile handle. **Returns:** 16-bit Clear channel count. ##### `tile_sense_acp_get_red` — Studio ```c uint16_t tile_sense_acp_get_red(tile_t *tile) ``` Read the Red channel count. - `tile` — Initialised tile handle. **Returns:** 16-bit Red channel count. ##### `tile_sense_acp_get_green` — Studio ```c uint16_t tile_sense_acp_get_green(tile_t *tile) ``` Read the Green channel count. - `tile` — Initialised tile handle. **Returns:** 16-bit Green channel count. ##### `tile_sense_acp_get_blue` — Studio ```c uint16_t tile_sense_acp_get_blue(tile_t *tile) ``` Read the Blue channel count. - `tile` — Initialised tile handle. **Returns:** 16-bit Blue channel count. ##### `tile_sense_acp_get_proximity` — Studio ```c uint8_t tile_sense_acp_get_proximity(tile_t *tile) ``` Read the proximity result (larger = closer object). - `tile` — Initialised tile handle. **Returns:** 8-bit proximity count. ##### `tile_sense_acp_get_status` — Studio ```c uint8_t tile_sense_acp_get_status(tile_t *tile) ``` Read the STATUS register. Use the TMD3725_ST_* masks — e.g. ASAT (ALS saturation), PSAT (proximity saturation), AINT/PINT (threshold interrupts pending). - `tile` — Initialised tile handle. **Returns:** Raw STATUS byte. #### Configuration ##### `tile_sense_acp_set_als_gain` — Studio ```c void tile_sense_acp_set_als_gain(tile_t *tile, sense_acp_als_gain_t gain) ``` Set the ALS/Color gain. - `tile` — Initialised tile handle. - `gain` — ALS gain selection (sense_acp_als_gain_t). ##### `tile_sense_acp_set_integration_time` — Studio ```c void tile_sense_acp_set_integration_time(tile_t *tile, uint8_t atime) ``` Set the ALS integration time. Integration time is (atime + 1) x 2.78 ms; the maximum ALS count scales with it (1024 per 2.78 ms step, saturating at 65535). For example 0x00 = 2.8 ms, 0x2F = ~133 ms (default), 0xFF = ~719 ms. - `tile` — Initialised tile handle. - `atime` — Raw ATIME register value (0-255). ##### `tile_sense_acp_set_prox_gain` — Studio ```c void tile_sense_acp_set_prox_gain(tile_t *tile, sense_acp_prox_gain_t gain) ``` Set the proximity IR-sensor gain. - `tile` — Initialised tile handle. - `gain` — Proximity gain selection (sense_acp_prox_gain_t). ##### `tile_sense_acp_set_prox_drive_ma` — Studio ```c void tile_sense_acp_set_prox_drive_ma(tile_t *tile, uint8_t ma) ``` Set the proximity IR-LED drive current. The LED current is programmed in ~6 mA steps: i_LED = 6 x (PLDRIVE + 1), so the request is clamped to 6-192 mA and rounded down to the nearest step. Actual current is factory-trimmed to normalise IR intensity, so treat the value as approximate. - `tile` — Initialised tile handle. - `ma` — Desired LED drive current in milliamps (6-192). #### Other ##### `tile_sense_acp_find` ```c uint8_t tile_sense_acp_find(tiles_pal_t *hal, uint8_t instance) ``` Check if a Sense.ACP is present on the bus. - `hal` — Platform HAL handle. - `instance` — Address selector — only 0 is valid (fixed address 0x39). **Returns:** 1 if the device ACKs and its ID register reads 0xE4, else 0. ##### `tile_sense_acp_init` ```c void tile_sense_acp_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_acp_cfg_t *cfg) ``` Initialise a Sense.ACP tile. Probes the device, verifies the ID register, programs ALS gain + integration time and proximity gain + LED drive, then powers on the requested engines (PON is asserted together with AEN so the chip's auto-zero runs before the first ALS measurement). Sets tile state to READY on success, ERROR otherwise. - `hal` — Platform HAL handle. - `instance` — Address selector — only 0 is valid (fixed address 0x39). - `tile` — Tile handle to initialise. - `cfg` — Configuration (NULL for defaults). ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Threshold interrupts (ALS / proximity INT pin) (common) Driver-deferred AND definition-gated. The TMD3725 asserts its INT pin (open-drain, active-low) when the Clear channel or the proximity result crosses the AILT/AIHT or PILT/PIHT thresholds past the PERS persistence count. The tile routes INT to pad 7, but Sense-ACP-b.json does not declare that pad, so Studio cannot wire it as an event source. Polled reads (get_clear / get_proximity) cover the same ground for v1 — a DSL comparison yields "near/far" without the INT path. Closing this needs the tile definition updated to declare pad 7 = INT, then a follow-up pass to expose the threshold/PERS/INTENAB registers and a tile event. #### Illuminance (lux) and correlated colour temperature (advanced) Out of scope for an integer driver. Converting RGBC counts to lux or CCT requires the device-specific calibration coefficients from the ams application note plus floating-point maths. This driver exposes the raw per-channel counts (clear/red/green/blue); compute lux/CCT on the host where floats and calibration data are available. #### Wait state, proximity offset calibration, IR-channel mux (niche) Deferred, not gated. The wait feature (WEN/WTIME/WLONG idle timing), the proximity offset-cancellation engine (POFFSET + CALIB autozero), and the IR_TO_GREEN mux (route the IR photodiode into the green data converter) are left at sane power-on defaults and not surfaced. A later pass can expose them if a use-case needs them. ### Enums #### `sense_acp_als_gain_t` ALS/Color gain (CFG1 AGAIN[1:0]). | Member | Value | Description | | --- | --- | --- | | `SENSE_ACP_ALS_GAIN_1X` | `0x00` | 1x. | | `SENSE_ACP_ALS_GAIN_4X` | `0x01` | 4x. | | `SENSE_ACP_ALS_GAIN_16X` | `0x02` | 16x (default). | | `SENSE_ACP_ALS_GAIN_64X` | `0x03` | 64x. | #### `sense_acp_prox_gain_t` Proximity IR-sensor gain (PCFG1 PGAIN[7:6]). | Member | Value | Description | | --- | --- | --- | | `SENSE_ACP_PROX_GAIN_1X` | `0x00` | 1x. | | `SENSE_ACP_PROX_GAIN_2X` | `0x01` | 2x. | | `SENSE_ACP_PROX_GAIN_4X` | `0x02` | 4x (default). | | `SENSE_ACP_PROX_GAIN_8X` | `0x03` | 8x. | ### Structs #### `sense_acp_cfg_t` Optional init-time configuration for Sense.ACP. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `als_gain` | ALS gain (sense_acp_als_gain_t). Default 16x. | | `uint8_t` | `atime` | ALS integration: (atime+1) x 2.78 ms. Default 0x2F. | | `uint8_t` | `prox_gain` | Proximity gain (sense_acp_prox_gain_t). Default 4x. | | `uint8_t` | `prox_drive_ma` | IR LED drive current in mA (6-192). Default 12. | | `uint8_t` | `enable_als` | 1 = enable ALS at init (default), 0 = leave off. | | `uint8_t` | `enable_prox` | 1 = enable proximity at init (default), 0 = off. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_ACP_VERSION_MAJOR` | `1` | | | `TILE_SENSE_ACP_VERSION_MINOR` | `0` | | | `TILE_SENSE_ACP_VERSION_PATCH` | `0` | | | `TMD3725_I2C_ADDR` | `0x39` | Fixed 7-bit I2C address (only value). | | `TMD3725_ID_VALUE` | `0xE4` | ID register (0x92) reset/expected value. | --- Generated 2026-08-12 from the driver header at tiles@18abd82. Canonical HTML: https://bergsonne.io/docs/tiles/sense/acp ============================================================================== ## Sense.BP driver > ILPS22QS barometric pressure & temperature sensor. | | | | --- | --- | | Tile | `Sense.BP` | | Driver version | `1.2.0` | | Header | `tile_sense_bp.h` | | Generated from | `tiles@faf9db5` | | Canonical page | https://bergsonne.io/docs/tiles/sense/bp | ### Overview Platform-agnostic driver for the ST ILPS22QS dual full-scale absolute pressure sensor (260–1260 hPa or 260–4060 hPa) with embedded temperature sensor, 128-sample FIFO, low-pass filter, and Qvar electrostatic sensing. Key specifications: - 24-bit pressure output, 0.5 hPa absolute accuracy (mode 1) - 16-bit temperature output, 100 LSB/°C - Output data rates from 1 Hz to 200 Hz - Configurable averaging (4 to 512 samples) - 128-slot pressure FIFO with watermark interrupt - One-shot mode for ultra-low-power operation - Pressure threshold interrupts with autozero/autorefp - One-point calibration via hardware offset registers Datasheet: https://www.st.com/resource/en/datasheet/ilps22qs.pdf ### Examples #### Quick start ```c #include "core.h" #include "core_tiles.h" #include "tile_sense_bp.h" tile_t baro; sense_bp_cfg_t cfg = { .odr = SENSE_BP_ODR_25HZ }; tile_sense_bp_init(core_tiles_pal(&core_i2c3), 0, &baro, &cfg); int32_t pressure_mhpa = tile_sense_bp_get_pressure_mhpa(&baro); int32_t temp_cdeg = tile_sense_bp_get_temp_cdeg(&baro); ``` ### API 35 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_bp_sleep` — Studio ```c void tile_sense_bp_sleep(tile_t *tile) ``` Enter power-down mode (ODR = 0). - `tile` — Initialised tile handle. ##### `tile_sense_bp_wake` — Studio ```c void tile_sense_bp_wake(tile_t *tile) ``` Resume from power-down using cached ODR/AVG settings. - `tile` — Sleeping tile handle. ##### `tile_sense_bp_reset` — Studio ```c void tile_sense_bp_reset(tile_t *tile) ``` Software-reset the device. All registers return to defaults. Call init() again after reset. - `tile` — Tile handle. #### Runtime ##### `tile_sense_bp_get_pressure_raw` — Studio ```c int32_t tile_sense_bp_get_pressure_raw(tile_t *tile) ``` Read the raw 24-bit pressure output (two's complement). - `tile` — Initialised tile handle. **Returns:** Raw 24-bit signed value, sign-extended to int32_t. ##### `tile_sense_bp_get_pressure_mhpa` — Studio ```c int32_t tile_sense_bp_get_pressure_mhpa(tile_t *tile) ``` Read pressure in milli-hectopascals (integer, no float). Returns pressure * 1000 in mhPa units. For example, 1013250 = 1013.250 hPa. Accounts for the current full-scale mode setting. - `tile` — Initialised tile handle. **Returns:** Pressure in milli-hPa (mhPa). ##### `tile_sense_bp_get_temp_raw` — Studio ```c int16_t tile_sense_bp_get_temp_raw(tile_t *tile) ``` Read the raw 16-bit temperature output (two's complement). - `tile` — Initialised tile handle. **Returns:** Raw 16-bit signed value, sign-extended to int16_t. ##### `tile_sense_bp_get_temp_cdeg` — Studio ```c int32_t tile_sense_bp_get_temp_cdeg(tile_t *tile) ``` Read temperature in centi-degrees Celsius (integer, no float). Returns temperature * 100. For example, 2534 = 25.34 °C. Sensor sensitivity is 100 LSB/°C, so this is (raw * 100) / 100 = raw. - `tile` — Initialised tile handle. **Returns:** Temperature in centi-°C. ##### `tile_sense_bp_oneshot` — Studio ```c void tile_sense_bp_oneshot(tile_t *tile) ``` Trigger a single measurement in power-down mode. ODR must be POWERDOWN. Sets the ONESHOT bit in CTRL_REG2. The bit self-clears when the measurement is complete. - `tile` — Tile handle in power-down mode. ##### `tile_sense_bp_get_status` — Studio ```c uint8_t tile_sense_bp_get_status(tile_t *tile) ``` Read the STATUS register. - `tile` — Initialised tile handle. **Returns:** Raw STATUS byte (use ILPS22QS_STATUS_* masks). ##### `tile_sense_bp_pressure_ready` — Studio ```c uint8_t tile_sense_bp_pressure_ready(tile_t *tile) ``` Check if new pressure data is available. - `tile` — Initialised tile handle. **Returns:** 1 if P_DA is set, 0 otherwise. ##### `tile_sense_bp_temp_ready` — Studio ```c uint8_t tile_sense_bp_temp_ready(tile_t *tile) ``` Check if new temperature data is available. - `tile` — Initialised tile handle. **Returns:** 1 if T_DA is set, 0 otherwise. ##### `tile_sense_bp_is_boot_complete` — Studio ```c uint8_t tile_sense_bp_is_boot_complete(tile_t *tile) ``` Check whether the chip's power-on boot sequence is complete. After VDD ramps, the ILPS22QS reloads its trim parameters from NVM. The BOOT_ON bit in INT_SOURCE reads 1 during this phase and 0 once the chip is ready to be configured. Reads of this getter are non-clearing (the BOOT_ON bit is preserved across the read; only the threshold/IA flags clear on read). Useful when interrogating the chip immediately after VDD comes up, or after software reset. A typical pattern: tile_sense_bp_reset(&baro); while (!tile_sense_bp_is_boot_complete(&baro)) core_delay_ms(1); // chip is now ready for re-configuration - `tile` — Initialised tile handle. **Returns:** 1 if boot complete (BOOT_ON cleared), 0 if still booting. ##### `tile_sense_bp_read_altitude_mm` — Studio ```c int32_t tile_sense_bp_read_altitude_mm(tile_t *tile, uint32_t sea_level_pa) ``` Estimate altitude (mm) above the supplied sea-level pressure. Pure-integer linear approximation around the reference pressure: h_mm = 8430 * (P0_pa - P_pa) / 100 which corresponds to ~8.43 mm of altitude per pascal of pressure decrease — the standard-atmosphere lapse-rate slope near sea level. Accuracy regime: this is a first-order linearisation valid for roughly ±1500 m around the supplied reference (i.e. the kind of range you care about for indoor floor detection, ascent-rate estimation, or short-baseline relative-altitude tracking). Error grows with the cube of altitude — at ±3000 m the underlying exponential law diverges from the linear fit by tens of metres, and outside the ILPS22QS's pressure range (260–1260 hPa or 260–4060 hPa depending on FS mode) the result is meaningless. For absolute geodetic altitude over wide ranges, use a full barometric-formula library on a host with floats. The reference is supplied in pascals (not hPa) for the convenience of callers that already have a recent QNH/QFE figure handy. Pass `101325` for the ICAO standard sea level reference. the reference. Negative when the measured pressure exceeds the reference (deeper than the reference altitude). - `tile` — Initialised tile handle. - `sea_level_pa` — Reference pressure in pascals (e.g. 101325). **Returns:** Altitude in millimetres above (positive) or below (negative) ##### `tile_sense_bp_wait_for_pressure_change` — Studio ```c uint8_t tile_sense_bp_wait_for_pressure_change(tile_t *tile, uint16_t threshold_hpa, uint32_t timeout_ms) ``` Block until measured pressure deviates from a captured baseline. Captures the current pressure at call time, then polls at the chip's configured ODR cadence until either: - |pressure - baseline| >= threshold_hpa, or - timeout_ms elapses with no qualifying change. This is a blocking helper. The polling cadence is derived from the cached CTRL_REG1 ODR field — the function sleeps for one ODR period between samples (or 10 ms as a fallback if ODR is power-down or unrecognised). For one-shot users, configure a non-power-down ODR before calling. Useful for "tap-to-wake" altitude triggers, ascent/descent detection, or pressure-step-driven UI events — without committing to the full threshold-interrupt configuration in CTRL_REG3 / INTERRUPT_CFG. - `tile` — Initialised tile handle. - `threshold_hpa` — Absolute deviation that triggers return (hPa). - `timeout_ms` — Maximum wait in milliseconds. **Returns:** 1 if the threshold was crossed, 0 if the call timed out. #### Configuration ##### `tile_sense_bp_set_odr` — Studio ```c void tile_sense_bp_set_odr(tile_t *tile, sense_bp_odr_t odr) ``` Set the output data rate. - `tile` — Initialised tile handle. - `odr` — Desired output data rate. ##### `tile_sense_bp_set_avg` — Studio ```c void tile_sense_bp_set_avg(tile_t *tile, sense_bp_avg_t avg) ``` Set the averaging filter depth. - `tile` — Initialised tile handle. - `avg` — Desired averaging. ##### `tile_sense_bp_set_fullscale` — Studio ```c void tile_sense_bp_set_fullscale(tile_t *tile, sense_bp_fs_t fs) ``` Set the full-scale mode. - `tile` — Initialised tile handle. - `fs` — Full-scale selection. ##### `tile_sense_bp_set_lpf` — Studio ```c void tile_sense_bp_set_lpf(tile_t *tile, uint8_t enable, sense_bp_lpf_bw_t bw) ``` Enable or disable the low-pass filter. - `tile` — Initialised tile handle. - `enable` — 1 = enable, 0 = disable. - `bw` — Bandwidth selection (only used if enable = 1). ##### `tile_sense_bp_set_threshold_hpa` — Studio ```c void tile_sense_bp_set_threshold_hpa(tile_t *tile, uint16_t ths_hpa) ``` Set the pressure interrupt threshold in hPa. The threshold is applied to the differential pressure (P_DIFF_IN). Enable PHE/PLE bits in INTERRUPT_CFG to generate interrupts. - `tile` — Initialised tile handle. - `ths_hpa` — Threshold in hPa (unsigned, applied symmetrically). ##### `tile_sense_bp_set_interrupt_cfg` — Studio ```c void tile_sense_bp_set_interrupt_cfg(tile_t *tile, uint8_t cfg) ``` Configure the interrupt source register. - `tile` — Initialised tile handle. - `cfg` — Raw INTERRUPT_CFG byte (use ILPS22QS_INTCFG_* masks). ##### `tile_sense_bp_get_int_source` — Studio ```c uint8_t tile_sense_bp_get_int_source(tile_t *tile) ``` Read the interrupt source register (clears latched flags). - `tile` — Initialised tile handle. **Returns:** Raw INT_SOURCE byte (use ILPS22QS_INT_SRC_* masks). ##### `tile_sense_bp_set_autozero` — Studio ```c void tile_sense_bp_set_autozero(tile_t *tile) ``` Enable autozero mode. Captures current pressure as REF_P. Output registers then show the difference from reference. Reset with reset_autozero(). - `tile` — Initialised tile handle. ##### `tile_sense_bp_reset_autozero` — Studio ```c void tile_sense_bp_reset_autozero(tile_t *tile) ``` Reset autozero mode to normal operation. - `tile` — Initialised tile handle. ##### `tile_sense_bp_set_autorefp` — Studio ```c void tile_sense_bp_set_autorefp(tile_t *tile) ``` Enable autorefp mode. Captures current pressure as REF_P for interrupt threshold comparison. Output registers are not affected. Reset with reset_autorefp(). - `tile` — Initialised tile handle. ##### `tile_sense_bp_reset_autorefp` — Studio ```c void tile_sense_bp_reset_autorefp(tile_t *tile) ``` Reset autorefp mode to normal operation. - `tile` — Initialised tile handle. ##### `tile_sense_bp_set_pressure_offset` — Studio ```c void tile_sense_bp_set_pressure_offset(tile_t *tile, int16_t offset) ``` Set pressure offset for one-point calibration (RPDS registers). The offset is in raw LSB units (signed 16-bit) and is subtracted from the measured pressure before output. - `tile` — Initialised tile handle. - `offset` — Raw offset value (two's complement). ##### `tile_sense_bp_get_ref_pressure` — Studio ```c int16_t tile_sense_bp_get_ref_pressure(tile_t *tile) ``` Read the reference pressure registers (REF_P). - `tile` — Initialised tile handle. **Returns:** 16-bit signed reference pressure value. #### FIFO ##### `tile_sense_bp_set_fifo_mode` — Studio ```c void tile_sense_bp_set_fifo_mode(tile_t *tile, sense_bp_fifo_mode_t mode) ``` Configure the FIFO mode. - `tile` — Initialised tile handle. - `mode` — FIFO mode selection. ##### `tile_sense_bp_set_fifo_watermark` — Studio ```c void tile_sense_bp_set_fifo_watermark(tile_t *tile, uint8_t watermark) ``` Set the FIFO watermark threshold (0–127). - `tile` — Initialised tile handle. - `watermark` — Threshold level (0–127). ##### `tile_sense_bp_get_fifo_level` — Studio ```c uint8_t tile_sense_bp_get_fifo_level(tile_t *tile) ``` Read the number of unread FIFO samples. - `tile` — Initialised tile handle. **Returns:** Number of unread samples (0–128). ##### `tile_sense_bp_get_fifo_status` — Studio ```c uint8_t tile_sense_bp_get_fifo_status(tile_t *tile) ``` Read FIFO status flags. - `tile` — Initialised tile handle. **Returns:** Raw FIFO_STATUS2 byte (use ILPS22QS_FIFO_* masks). ##### `tile_sense_bp_read_fifo_raw` — Studio ```c int32_t tile_sense_bp_read_fifo_raw(tile_t *tile) ``` Read one raw 24-bit pressure sample from the FIFO. - `tile` — Initialised tile handle. **Returns:** Raw 24-bit signed pressure value, sign-extended to int32_t. ##### `tile_sense_bp_read_fifo_batch` — Studio ```c uint8_t tile_sense_bp_read_fifo_batch(tile_t *tile, int32_t *buf, uint8_t count) ``` Read multiple raw pressure samples from the FIFO. 24-bit signed values (sign-extended to int32_t). many samples and returns the actual count. - `tile` — Initialised tile handle. - `buf` — Caller-allocated buffer the driver fills with raw - `count` — Capacity of `buf` — the driver fills up to this **Returns:** Number of samples actually read. #### Other ##### `tile_sense_bp_find` ```c uint8_t tile_sense_bp_find(tiles_pal_t *hal, uint8_t instance) ``` Check if a Sense.BP is present on the bus. - `hal` — Platform HAL handle. - `instance` — 0 = default address (0x5D), 1 = alternate (0x5C). **Returns:** 1 if device ACKs and WHO_AM_I matches, 0 otherwise. ##### `tile_sense_bp_init` ```c void tile_sense_bp_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_bp_cfg_t *cfg) ``` Initialise a Sense.BP tile. Probes the device, verifies WHO_AM_I, configures ODR/AVG/FS/LPF/BDU, disables AH/Qvar for lower power, and sets tile state to READY. - `hal` — Platform HAL handle. - `instance` — 0 = default (0x5D), 1 = alternate (0x5C). - `tile` — Tile handle to initialise. - `cfg` — Configuration (NULL for defaults). ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Analog Hub / Qvar charge-variation sensing (advanced) Hardware-gated on Sense.BP rev a. The ILPS22QS reads charge variation on dedicated input pins (chip pins 5 = AH2/QVAR2 and 7 = AH1/QVAR1) that need an external electrode wired in. The current tile rev leaves tile pads 6/7/8 unconnected (per Sense-BP-a.json) — those chip pins are tied to GND on the PCB per the datasheet's "if not used" recommendation. Even with AH_QVAR_EN=1, no signal can reach the chip. Closing this gap requires a tile hardware revision that routes one or both AH pins to a tile pad with electrode access. #### Alternate bus modes (SPI / I3C) (advanced) Ecosystem-gated. ILPS22QS supports 3-wire and 4-wire SPI (CS = pad 3 strap) and I3C SDR. The driver framework currently uses tiles_pal I²C calls exclusively; adding SPI requires plumbing hal->spi_* through the driver, and I3C requires a new bus abstraction in Studio that doesn't exist yet. Defer to a future multi-bus driver framework pass. ### Enums #### `sense_bp_odr_t` Output data rate selection (CTRL_REG1 ODR[3:0], bits 6:3). | Member | Value | Description | | --- | --- | --- | | `SENSE_BP_ODR_POWERDOWN` | `0x00` | Power-down / one-shot | | `SENSE_BP_ODR_1HZ` | `0x01` | 1 Hz | | `SENSE_BP_ODR_4HZ` | `0x02` | 4 Hz | | `SENSE_BP_ODR_10HZ` | `0x03` | 10 Hz | | `SENSE_BP_ODR_25HZ` | `0x04` | 25 Hz | | `SENSE_BP_ODR_50HZ` | `0x05` | 50 Hz | | `SENSE_BP_ODR_75HZ` | `0x06` | 75 Hz | | `SENSE_BP_ODR_100HZ` | `0x07` | 100 Hz | | `SENSE_BP_ODR_200HZ` | `0x08` | 200 Hz | #### `sense_bp_avg_t` Averaging selection (CTRL_REG1 AVG[2:0], bits 2:0). | Member | Value | Description | | --- | --- | --- | | `SENSE_BP_AVG_4` | `0x00` | 4 samples | | `SENSE_BP_AVG_8` | `0x01` | 8 samples | | `SENSE_BP_AVG_16` | `0x02` | 16 samples | | `SENSE_BP_AVG_32` | `0x03` | 32 samples | | `SENSE_BP_AVG_64` | `0x04` | 64 samples | | `SENSE_BP_AVG_128` | `0x05` | 128 samples | | `SENSE_BP_AVG_512` | `0x07` | 512 samples | #### `sense_bp_fs_t` Full-scale mode selection (CTRL_REG2 FS_MODE, bit 6). | Member | Value | Description | | --- | --- | --- | | `SENSE_BP_FS_1260HPA` | `0` | Mode 1: 260–1260 hPa, 4096 LSB/hPa | | `SENSE_BP_FS_4060HPA` | `1` | Mode 2: 260–4060 hPa, 2048 LSB/hPa | #### `sense_bp_lpf_bw_t` Low-pass filter bandwidth (CTRL_REG2 LFPF_CFG, bit 5). | Member | Value | Description | | --- | --- | --- | | `SENSE_BP_LPF_ODR_4` | `0` | Bandwidth = ODR/4 | | `SENSE_BP_LPF_ODR_9` | `1` | Bandwidth = ODR/9 | #### `sense_bp_fifo_mode_t` FIFO mode selection (FIFO_CTRL F_MODE[1:0] + TRIG_MODES). | Member | Value | Description | | --- | --- | --- | | `SENSE_BP_FIFO_BYPASS` | `0x00` | FIFO disabled | | `SENSE_BP_FIFO_FIFO` | `0x01` | Stop when full | | `SENSE_BP_FIFO_CONTINUOUS` | `0x02` | Dynamic stream, overwrites oldest | | `SENSE_BP_FIFO_BYP2FIFO` | `0x05` | Bypass-to-FIFO on trigger | | `SENSE_BP_FIFO_BYP2CONT` | `0x06` | Bypass-to-continuous on trigger | | `SENSE_BP_FIFO_CONT2FIFO` | `0x07` | Continuous-to-FIFO on trigger | ### Structs #### `sense_bp_cfg_t` Optional init-time configuration for Sense.BP. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `odr` | Output data rate (sense_bp_odr_t). Default: SENSE_BP_ODR_25HZ. | | `uint8_t` | `avg` | Averaging depth (sense_bp_avg_t). Default: SENSE_BP_AVG_4. | | `uint8_t` | `fs` | Full-scale mode (sense_bp_fs_t). Default: SENSE_BP_FS_1260HPA. | | `uint8_t` | `lpf` | Low-pass filter: 1 = enabled (default), 0 = disabled. | | `uint8_t` | `lpf_bw` | LPF bandwidth (sense_bp_lpf_bw_t). Default: SENSE_BP_LPF_ODR_4. | | `uint8_t` | `bdu` | Block data update: 1 = enabled (default), 0 = continuous. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_BP_VERSION_MAJOR` | `1` | | | `TILE_SENSE_BP_VERSION_MINOR` | `2` | | | `TILE_SENSE_BP_VERSION_PATCH` | `0` | | | `ILPS22QS_I2C_ADDR_DEFAULT` | `0x5D` | AD0 float/high (default) | | `ILPS22QS_I2C_ADDR_ALT` | `0x5C` | AD0 to GND | | `ILPS22QS_WHO_AM_I_VALUE` | `0xB4` | | --- Generated 2026-08-12 from the driver header at tiles@faf9db5. Canonical HTML: https://bergsonne.io/docs/tiles/sense/bp ============================================================================== ## Sense.CAM.P driver > Ultra-low-power global-shutter camera driver for Sense.CAM.P (rev a). | | | | --- | --- | | Tile | `Sense.CAM.P` | | Driver version | `0.1.0` | | Header | `tile_sense_cam_p.h` | | Generated from | `tiles@e3a9c64` | | Canonical page | https://bergsonne.io/docs/tiles/sense/camp | ### Overview Embeds the PixArt PAG7920J3, a 320 x 240 monochrome global-shutter camera module with an integrated lens in a 2.94 x 2.44 x 2.11 mm cube. Registers are configured over I2C; image data comes out over SPI. The tile does not break out the 8-bit parallel bus, so SPI is the only image path. Key specifications: - Array: 320 x 240 monochrome, 3.0 um pixels, global shutter - Frame rate: up to 180 fps, 8-bit RAW - Optics: 71.34 deg H x 53.72 deg V, 64 dB dynamic range - Power: 0.76 mW @ QVGA 15 fps, 8.54 mW @ 180 fps, <100 uW motion detect - Supply: VDDMA 3.14-3.45 V (<=50 mV ripple), VDDIO 1.14-3.45 V Datasheet: https://www.bergsonne.io/tiles/sense/cam-p IC datasheet: PixArt PAG7920J3 (V0.8, L1033EN) ### Examples #### Quick start ```c #include "core_tiles.h" static uint8_t frame[160 * 120]; tile_t cam; sense_cam_p_cfg_t cfg = { .spi_cs = 9, .resolution = SENSE_CAM_P_RES_160x120 }; tile_sense_cam_p_init(core_tiles_pal(&core_i2c1), 0, &cam, &cfg); if (tile_is_ready(&cam)) tile_sense_cam_p_capture(&cam, frame, sizeof(frame)); ``` ### API 9 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_cam_p_part_id` — Studio ```c uint16_t tile_sense_cam_p_part_id(tile_t* tile) ``` Read the 16-bit part identifier over I2C. **Returns:** 0x7920 for a healthy PAG7920J3, 0 on a bus error. ##### `tile_sense_cam_p_spi_id` — Studio ```c uint16_t tile_sense_cam_p_spi_id(tile_t* tile) ``` Read the SPI-side identifier, confirming the SPI link is usable. **Returns:** 0xA55A when the SPI link is up, 0 otherwise. **Note:** Returns 0x0000 whenever the sensor is stopped — the SPI register interface is only alive while R_TG_En = 1. ##### `tile_sense_cam_p_reset` — Studio ```c void tile_sense_cam_p_reset(tile_t* tile) ``` Trigger a software reset (section 5.6.2) and return to defaults. Preferred over a power cycle for repeatable tests: the tile carries ~15.7 uF of bulk and at the camera's microamp idle draw that rail can take seconds to fall below the power-on-reset threshold, so an unplug/replug may not reset the part at all. **Note:** Leaves the sensor unconfigured; call init again afterwards. #### Runtime ##### `tile_sense_cam_p_capture` — Studio ```c uint8_t tile_sense_cam_p_capture(tile_t* tile, uint8_t* dst, uint32_t len) ``` Capture one frame into a caller-supplied buffer. Frame-locks on Frame_Start, then reads the frame in 4800-byte buffers. The caller owns the memory: 19200 bytes at 160x120, 76800 at 320x240. Each buffer must be serviced within roughly 3.4 ms at default timing, or FB_Ovf trips and the frame is void. Anything slow between buffers — a per-byte print, a slow link — will tear the image across several exposures. - `dst` — Destination buffer. - `len` — Size of dst; must be >= tile_sense_cam_p_frame_bytes(). **Returns:** 1 on a complete, non-overflowed frame; 0 otherwise. #### Configuration ##### `tile_sense_cam_p_width` — Studio ```c uint16_t tile_sense_cam_p_width(tile_t* tile) ``` Frame width in pixels for the configured resolution. **Returns:** 160 or 320. ##### `tile_sense_cam_p_height` — Studio ```c uint16_t tile_sense_cam_p_height(tile_t* tile) ``` Frame height in pixels for the configured resolution. **Returns:** 120 or 240. ##### `tile_sense_cam_p_frame_bytes` — Studio ```c uint32_t tile_sense_cam_p_frame_bytes(tile_t* tile) ``` Number of bytes one frame occupies (width x height, 8-bit RAW). **Returns:** 19200 or 76800. #### Other ##### `tile_sense_cam_p_find` ```c uint8_t tile_sense_cam_p_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether a PAG7920J3 is present on the I2C bus. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) **Returns:** 1 if the device ACKs, 0 otherwise ##### `tile_sense_cam_p_init` ```c void tile_sense_cam_p_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const sense_cam_p_cfg_t* cfg) ``` Initialize the camera for SPI image output. Verifies PartID, applies the vendor init tables verbatim for the requested resolution, then confirms the SPI link by reading CheckID. Pass cfg=NULL for defaults (spi_cs 0, 160x120). - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults **Note:** Blocks for ~250 ms. Requires both an I2C bus and an SPI bus on the PAL. Single-instance: the SPI CS and resolution are driver-global. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Motion detection (common) The PAG7920J3 has a proprietary on-chip motion-detection mode running at 80x60 under 100 uW, which is the tile's headline always-on feature. Section 8.1.3.8 gives its init table. Driver-deferred, not a hardware gap, and the highest-value thing to add next. #### Manual exposure and gain (common) Auto-exposure and auto-gain run by default and hold mean brightness constant. Section 9.3 defines manual control, which is needed for any machine-vision use wanting repeatable radiometry. Driver-deferred. #### Trigger mode (advanced) Section 8.4 allows one-frame-on-demand capture, with frame rate set by the host's trigger interval. Note it does NOT relax the per-buffer readout deadline — the frame is still emitted in one output cycle at the sensor's own cadence. Driver-deferred. #### Windowing, mirror and flip (advanced) Section 10 covers WOI, mirror/flip, skip and average. Only the fixed 320x240 and 160x120 (2x skip) modes are exposed. Driver-deferred. #### LED strobe control (niche) The sensor can drive an external LED in sync with exposure via GPIO1 (R_expo_LED_* registers). The tile routes GPIO1 to a pad, so this is driver-deferred rather than hardware-gated. #### Multi-sensor frame sync (niche) Section 8.7 supports synchronising several sensors to a shared frame clock. Driver-deferred. #### External clock input (niche) Section 5.5 allows clocking the sensor from GPIO1 instead of its internal oscillator (1/6/12/24/30 MHz). Driver-deferred; the internal clock is used unconditionally. #### 8-bit parallel image output (niche) HARDWARE-GATED. The parallel bus needs PXD0-PXD7 plus PXCLK/HSYNC/VSYNC, and the tile's 10-pad T44 package breaks out only the four SPI signals. Not reachable on this tile at any driver revision. ### Enums #### `sense_cam_p_res_t` @brief Capture resolution. | Member | Value | Description | | --- | --- | --- | | `SENSE_CAM_P_RES_160x120` | `0` | 19200 B/frame, analog 2x skip. | | `SENSE_CAM_P_RES_320x240` | `1` | 76800 B/frame, full array. | ### Structs #### `sense_cam_p_cfg_t` Driver configuration. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `spi_cs` | CS identifier for the PAL SPI calls. | | `sense_cam_p_res_t` | `resolution` | Capture resolution. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_CAM_P_VERSION_MAJOR` | `0` | | | `TILE_SENSE_CAM_P_VERSION_MINOR` | `1` | | | `TILE_SENSE_CAM_P_VERSION_PATCH` | `0` | | | `PAG7920_I2C_ADDR_FLOAT` | `0x35` | GPIO2 floating (tile default). | | `PAG7920_I2C_ADDR_HIGH` | `0x25` | GPIO2 tied to VDDIO. | | `PAG7920_I2C_ADDR_GND` | `0x40` | GPIO2 tied to GND. | | `PAG7920_I2C_ADDR_BROADCAST` | `0x60` | Always live, strap-independent. | | `PAG7920_PART_ID` | `0x7920` | Expected PartID. | | `PAG7920_SPI_RD_BIT` | `0x80` | Bit 7 of byte 0: 1 = read. Addr is 7-bit. | | `PAG7920_SPI_IMG_RD_EN` | `0x02` | 1 = grant access to the output buffer. | | `PAG7920_SPI_INT_STATUS` | `0x03` | Interrupt flags, write-to-clear. | | `PAG7920_SPI_CHECKID_L` | `0x06` | 0x5A | | `PAG7920_SPI_CHECKID_H` | `0x07` | 0xA5 | | `PAG7920_SPI_IMG_DATA` | `0x40` | Image burst source. | | `PAG7920_SPI_CHECK_ID` | `0xA55A` | Expected CheckID; 0 unless running. | | `PAG7920_BUFFER_BYTES` | `4800` | Output buffer size. A frame is delivered in units of this. | --- Generated 2026-08-12 from the driver header at tiles@e3a9c64. Canonical HTML: https://bergsonne.io/docs/tiles/sense/camp ============================================================================== ## Sense.CAP driver > Capacitive trackpad driver for the Sense.CAP tile (IQS7211A). | | | | --- | --- | | Tile | `Sense.CAP` | | Driver version | `0.3.1` | | Header | `tile_sense_cap.h` | | Generated from | `tiles@129eab4` | | Canonical page | https://bergsonne.io/docs/tiles/sense/cap | ### Overview @version 0.3.1 Azoteq IQS7211A mutual-capacitance trackpad controller: up to 2-finger absolute XY tracking, relative XY, per-channel touch status, built-in gesture engine (tap, press-and-hold, four-way swipe), and an Alternate Low-Power (ALP) channel for presence wake-up. **Surface configuration.** The trackpad's electrical geometry (Rx/Tx count and mapping, conversion cycles, XY resolution, ATI target) is a property of the physical electrode surface. init() deliberately does NOT overwrite the chip's memory map — call tile_sense_cap_configure_surface() after init with a sense_cap_surface_t describing the attached surface, which writes the geometry, packs the sensing cycles, sets the ATI target and runs re-ATI. The r0 2x3 surface (2 Rx strips x 3 Tx blocks) ships as the built-in preset sense_cap_surface_2x3; setup_2x3() is the one-call form. Without a surface config the device runs on factory defaults and XY output is not meaningful. Communication windows: the device only serves register data inside a communication window. In its default streaming mode a window opens every report cycle and the part clock-stretches a master that arrives early (datasheet §11.9.2), so plain reads simply work. In event mode, in the low-power modes, or with Comms Request enabled, they do not — the read returns @ref IQS7211A_INVALID_RESPONSE (0xEEEE) instead. The driver therefore retries every access behind an explicit comms request (write 0x00 to 0xFF, wait, retry), the same shape as the IQS323 force-comms dance on Sense.T.C. One window serves one transaction — an I2C STOP closes it again. The 0xEEEE response is the sharp edge on this part: it is valid-looking data with Comms Request, Event Mode, Manual Control and WDT bits all set. A read-modify-write that trusts it will write those bits back and strand the device in request-only mode — recoverable only by power cycling the tile. Every helper here checks for it before writing. Two communication modes: - **Polled mode** (default): call process() from your main loop. No RDY pin wiring required. **This is the required mode on r0 tiles fitted with the IQS7211A** — the board was designed for the IQS7211E, whose A5 ball combines RDY+MCLR; on the A variant that ball is MCLR only, so tile pad 3 is a hardware reset line (100k pull-up on board), and the A's real RDY (ball C3) is unrouted. - **RDY mode**: provide a rdy_pin in config. Usable once IQS7211E parts are fitted (pad 3 then carries the combined RDY/MCLR line); wire it to a Core pad and pass that pad number. An EXTI falling-edge ISR sets a flag; process() only does I2C when set. Datasheet: Azoteq IQS7211A, Rev v1.3, April 2025 ### Examples #### Polling example (Cores SDK) ```c tile_t pad; tile_sense_cap_init(core_tiles_pal(&core_i2c1), 0, &pad, NULL); while (1) { tile_sense_cap_process(&pad); if (tile_sense_cap_get_num_fingers(&pad) > 0) printf("x=%u y=%u\n", tile_sense_cap_get_finger_x(&pad, 0), tile_sense_cap_get_finger_y(&pad, 0)); core_delay_ms(20); } ``` #### Gesture callback example ```c void on_pad(tile_t *t, uint16_t info, void *ctx) { uint16_t g = tile_sense_cap_get_gestures(t); if (g & SENSE_CAP_GESTURE_SINGLE_TAP) tap(); if (g & SENSE_CAP_GESTURE_SWIPE_X_POS) next(); } sense_cap_cfg_t cfg = { .gestures = SENSE_CAP_GESTURE_SINGLE_TAP | SENSE_CAP_GESTURE_SWIPE_X_POS, .on_event = on_pad, }; tile_sense_cap_init(core_tiles_pal(&core_i2c1), 0, &pad, &cfg); ``` ### API 62 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_cap_setup_2x3` — Studio ```c uint8_t tile_sense_cap_setup_2x3(tile_t *tile) ``` Configure the built-in r0 2x3 surface and run ATI. One-call form of configure_surface() with sense_cap_surface_2x3 — the surface shipped with the r0 tile (2 Rx strips x 3 Tx blocks). - `tile` — Initialised tile handle **Returns:** 1 if configuration verified and re-ATI completed, 0 otherwise. ##### `tile_sense_cap_get_version_major` — Studio ```c uint16_t tile_sense_cap_get_version_major(tile_t *tile) ``` Read the device's firmware major version (cached at init). - `tile` — Tile handle **Returns:** Major version from register 0x01 ##### `tile_sense_cap_get_version_minor` — Studio ```c uint16_t tile_sense_cap_get_version_minor(tile_t *tile) ``` Read the device's firmware minor version (cached at init). - `tile` — Tile handle **Returns:** Minor version from register 0x02 ##### `tile_sense_cap_get_settings_version` — Studio ```c uint16_t tile_sense_cap_get_settings_version(tile_t *tile) ``` Read the settings-version label the device was programmed with. Register 0x74: high byte is the settings major version, low byte the minor. A designer stamps this when exporting a configured firmware from the Azoteq GUI, so the host can confirm the part carries the intended surface configuration (datasheet §10.1.1). - `tile` — Tile handle **Returns:** Raw 16-bit settings version, or 0 if the tile is not ready ##### `tile_sense_cap_reset` — Studio ```c void tile_sense_cap_reset(tile_t *tile) ``` Software-reset the device. The reset takes effect once the communication window closes (datasheet §9.3.2). Blocks for the device's boot time, then clears the reset indication. The tile returns to TILE_STATE_READY. - `tile` — Tile handle ##### `tile_sense_cap_ack_reset` — Studio ```c void tile_sense_cap_ack_reset(tile_t *tile) ``` Clear the reset indication (Show Reset) flag. The device sets Show Reset on every boot. Acknowledging it means a later Show Reset tells the host the part rebooted unexpectedly — a brown-out or a device-watchdog reset. - `tile` — Tile handle ##### `tile_sense_cap_sleep` — Studio ```c void tile_sense_cap_sleep(tile_t *tile) ``` Force the device into its lowest-power mode (LP2). Takes manual control of mode switching. The trackpad keeps sensing at the LP2 report rate; call wake() to hand control back to the device's automatic mode machine. Sets TILE_STATE_SLEEPING. - `tile` — Tile handle ##### `tile_sense_cap_wake` — Studio ```c void tile_sense_cap_wake(tile_t *tile) ``` Return to automatic mode switching, starting in Active mode. - `tile` — Tile handle #### Runtime ##### `tile_sense_cap_get_touch_events` — Studio ```c uint16_t tile_sense_cap_get_touch_events(tile_t *tile) ``` Read and clear the accumulated touch-event bits. Every recognizer result since the previous call, as SENSE_CAP_EV_* bits — the polling counterpart to the callbacks, latched so nothing is missed between reads. - `tile` — Tile handle **Returns:** Latched SENSE_CAP_EV_* bits; reading clears them. ##### `tile_sense_cap_was_tapped` — Studio ```c uint8_t tile_sense_cap_was_tapped(tile_t *tile) ``` Did a tap (single or double) complete since the last check? Consumes the tap bits from the same latch as get_touch_events(). - `tile` — Tile handle **Returns:** 1 if a tap completed, 0 otherwise. ##### `tile_sense_cap_get_zone` — Studio ```c int8_t tile_sense_cap_get_zone(tile_t *tile) ``` Which zone is currently touched? Zones are channel numbers (§5.1.1): for the 2x3 surface, column c / row r is zone c*2 + r — top row 0/2/4 left to right, bottom row 1/3/5. Computed from the tracked finger position. - `tile` — Tile handle **Returns:** Zone number, or -1 when nothing is touched. ##### `tile_sense_cap_zone_at` — Studio ```c int8_t tile_sense_cap_zone_at(tile_t *tile, uint16_t x, uint16_t y) ``` Which zone contains a given position? Pure geometry over the configured surface — use it inside tap/touch callbacks, where the finger has already lifted and get_zone() would report none ("which button was tapped?"). - `tile` — Tile handle - `x` — X position in the configured resolution - `y` — Y position in the configured resolution **Returns:** Zone (channel) number, or -1 before configure_surface(). ##### `tile_sense_cap_is_zone_touched` — Studio ```c uint8_t tile_sense_cap_is_zone_touched(tile_t *tile, uint8_t zone) ``` Is a specific zone's channel reporting touch? - `tile` — Tile handle - `zone` — Zone (channel) number, 0-31 **Returns:** 1 if that channel's touch bit is set (live read). ##### `tile_sense_cap_get_position_pct` — Studio ```c void tile_sense_cap_get_position_pct(tile_t *tile, int32_t *x_pct, int32_t *y_pct) ``` Finger position as percentages, resolution-independent. - `tile` — Tile handle - `x_pct` — 0-100 across X (or -1 with no finger); NULL ok - `y_pct` — 0-100 across Y (or -1 with no finger); NULL ok ##### `tile_sense_cap_wait_for_touch` — Studio ```c uint8_t tile_sense_cap_wait_for_touch(tile_t *tile, uint32_t timeout_ms) ``` Block until a finger touches the surface. Runs process() internally while waiting. - `tile` — Tile handle - `timeout_ms` — Maximum wait **Returns:** 1 on touch, 0 on timeout. ##### `tile_sense_cap_process` — Studio ```c void tile_sense_cap_process(tile_t *tile) ``` Read the trackpad data block and update the cached state. Call from your main loop. Reads info flags, gestures, relative XY and both finger slots in one burst, then fires the event callback. In RDY mode returns immediately unless the RDY line has fired. - `tile` — Tile handle ##### `tile_sense_cap_on_event` — Studio ```c void tile_sense_cap_on_event(tile_t *tile, sense_cap_event_cb_t cb, void *ctx) ``` Register or change the event callback. - `tile` — Tile handle - `cb` — Callback function (NULL to disable) - `ctx` — User context passed to the callback ##### `tile_sense_cap_get_info_flags` — Studio ```c uint16_t tile_sense_cap_get_info_flags(tile_t *tile) ``` Read the cached Info Flags word. - `tile` — Tile handle **Returns:** Info Flags (IQS7211A_INFO_* masks) ##### `tile_sense_cap_get_gestures` — Studio ```c uint16_t tile_sense_cap_get_gestures(tile_t *tile) ``` Read the cached gesture flags. Gesture bits are latched by the device for one report cycle, so read them every process() call or they are missed. - `tile` — Tile handle **Returns:** Gesture bits (SENSE_CAP_GESTURE_* masks) ##### `tile_sense_cap_get_num_fingers` — Studio ```c uint8_t tile_sense_cap_get_num_fingers(tile_t *tile) ``` Number of fingers currently on the trackpad (0, 1 or 2). - `tile` — Tile handle **Returns:** Finger count from the cached Info Flags ##### `tile_sense_cap_get_finger_x` — Studio ```c uint16_t tile_sense_cap_get_finger_x(tile_t *tile, uint8_t finger) ``` Absolute X coordinate of a finger. Only meaningful while that slot holds a tracked finger — gate on cycle to the next (datasheet §7.2.6), so slot 0 stays slot 0 while it is down. The raw register contents are returned as-is. An empty slot reads 0xFFFF in practice (observed on hardware, 2026-08-04) but the datasheet does not document that sentinel, so gate on the finger count rather than testing for it. - `tile` — Tile handle - `finger` — 0 or 1 (SENSE_CAP_FINGER_1 / _2) **Returns:** X in trackpad units (0..X resolution) ##### `tile_sense_cap_get_finger_y` — Studio ```c uint16_t tile_sense_cap_get_finger_y(tile_t *tile, uint8_t finger) ``` Absolute Y coordinate of a finger. Same slot caveat as @ref tile_sense_cap_get_finger_x. - `tile` — Tile handle - `finger` — 0 or 1 (SENSE_CAP_FINGER_1 / _2) **Returns:** Y in trackpad units (0..Y resolution) ##### `tile_sense_cap_get_finger_strength` — Studio ```c uint16_t tile_sense_cap_get_finger_strength(tile_t *tile, uint8_t finger) ``` Touch strength of a finger — the sum of its channels' deltas. Scales with the sensitivity setup, so it is a relative measure, not a force reading. Same slot caveat as @ref tile_sense_cap_get_finger_x. - `tile` — Tile handle - `finger` — 0 or 1 **Returns:** Touch strength in device units ##### `tile_sense_cap_get_finger_area` — Studio ```c uint16_t tile_sense_cap_get_finger_area(tile_t *tile, uint8_t finger) ``` Contact area of a finger, in channels. Same slot caveat as @ref tile_sense_cap_get_finger_x. - `tile` — Tile handle - `finger` — 0 or 1 **Returns:** Number of channels associated with the finger ##### `tile_sense_cap_get_relative_x` — Studio ```c int16_t tile_sense_cap_get_relative_x(tile_t *tile) ``` Relative X movement since the previous report. - `tile` — Tile handle **Returns:** Signed X delta in trackpad units ##### `tile_sense_cap_get_relative_y` — Studio ```c int16_t tile_sense_cap_get_relative_y(tile_t *tile) ``` Relative Y movement since the previous report. - `tile` — Tile handle **Returns:** Signed Y delta in trackpad units ##### `tile_sense_cap_get_touch_status` — Studio ```c uint32_t tile_sense_cap_get_touch_status(tile_t *tile) ``` Read the 32-bit per-channel touch status (CH0..CH31). Live read, not cached — the touch-status words sit outside the data block that process() burst-reads. Useful when unused trackpad channels are repurposed as discrete buttons (datasheet §10.4). - `tile` — Tile handle **Returns:** Bit n set = channel n touched ##### `tile_sense_cap_is_touched` — Studio ```c uint8_t tile_sense_cap_is_touched(tile_t *tile, uint8_t channel) ``` Check whether a single trackpad channel reports touch. - `tile` — Tile handle - `channel` — Channel number, 0..31 **Returns:** 1 if touched, 0 if not ##### `tile_sense_cap_get_num_channels` — Studio ```c uint8_t tile_sense_cap_get_num_channels(tile_t *tile) ``` Number of trackpad channels in the configured surface. - `tile` — Tile handle **Returns:** total_rx * total_tx, or 0 before configure_surface(). ##### `tile_sense_cap_get_channel_count` — Studio ```c uint16_t tile_sense_cap_get_channel_count(tile_t *tile, uint8_t channel) ``` Read one channel's raw count from the extended memory map. Live read. Channel numbers follow the datasheet rule: along the Rxs first, then to the next Tx (for the 2x3 surface: Tx column n has channels 2n and 2n+1, top and bottom row). - `tile` — Tile handle - `channel` — Channel number, 0-31 **Returns:** Count value, or 0 for an out-of-range channel. ##### `tile_sense_cap_get_channel_delta` — Studio ```c uint16_t tile_sense_cap_get_channel_delta(tile_t *tile, uint8_t channel) ``` Read one channel's delta (count minus reference). Live read from the extended memory map. The delta is what the touch threshold acts on — the primary signal for surface bring-up. - `tile` — Tile handle - `channel` — Channel number, 0-31 **Returns:** Delta value, or 0 for an out-of-range channel. ##### `tile_sense_cap_is_alp_active` — Studio ```c uint8_t tile_sense_cap_is_alp_active(tile_t *tile) ``` Check whether the ALP channel detects presence. - `tile` — Tile handle **Returns:** 1 if the ALP channel reports prox/touch in the cached info flags ##### `tile_sense_cap_get_alp_count` — Studio ```c uint16_t tile_sense_cap_get_alp_count(tile_t *tile) ``` Read the raw ALP channel count. - `tile` — Tile handle **Returns:** ALP count value (live read) ##### `tile_sense_cap_get_alp_lta` — Studio ```c uint16_t tile_sense_cap_get_alp_lta(tile_t *tile) ``` Read the ALP channel long-term average. - `tile` — Tile handle **Returns:** ALP LTA value (live read) ##### `tile_sense_cap_get_mode` — Studio ```c uint8_t tile_sense_cap_get_mode(tile_t *tile) ``` Current charging (power) mode of the device. - `tile` — Tile handle **Returns:** sense_cap_mode_t value from the cached info flags ##### `tile_sense_cap_has_ati_error` — Studio ```c uint8_t tile_sense_cap_has_ati_error(tile_t *tile) ``` Report whether the most recent ATI run failed. An ATI error means the auto-tuning could not reach its target — with no sensor surface attached, this is the expected state. - `tile` — Tile handle **Returns:** 1 if either the trackpad or the ALP ATI error flag is set #### Configuration ##### `tile_sense_cap_set_sensitivity` — Studio ```c void tile_sense_cap_set_sensitivity(tile_t *tile, uint8_t level) ``` One-knob touch sensitivity. Maps 1 (least sensitive, firm touches only) to 5 (most sensitive, light touches) onto the touch set/clear multiplier pairs. - `tile` — Tile handle - `level` — 1-5; values outside the range are clamped. ##### `tile_sense_cap_enable_gestures` — Studio ```c void tile_sense_cap_enable_gestures(tile_t *tile, uint16_t mask) ``` Set which gestures the device detects. - `tile` — Tile handle - `mask` — OR of SENSE_CAP_GESTURE_* bits (0 disables all gestures) ##### `tile_sense_cap_set_tap_timing` — Studio ```c void tile_sense_cap_set_tap_timing(tile_t *tile, uint16_t tap_ms, uint16_t hold_ms) ``` Set the tap and press-and-hold timing. A tap must lift within @p tap_ms and move less than the tap distance; a press that stays down past @p hold_ms raises press-and-hold (datasheet §8.1). - `tile` — Tile handle - `tap_ms` — Maximum tap duration in ms - `hold_ms` — Press-and-hold duration in ms ##### `tile_sense_cap_set_swipe_timing` — Studio ```c void tile_sense_cap_set_swipe_timing(tile_t *tile, uint16_t swipe_ms, uint16_t x_dist, uint16_t y_dist) ``` Set the swipe timing and distance thresholds. A swipe must cover @p x_dist or @p y_dist trackpad units within - `tile` — Tile handle - `swipe_ms` — Maximum swipe duration in ms - `x_dist` — X distance threshold in trackpad units - `y_dist` — Y distance threshold in trackpad units ##### `tile_sense_cap_set_report_rate` — Studio ```c void tile_sense_cap_set_report_rate(tile_t *tile, uint8_t mode, uint16_t ms) ``` Set the report rate for one charging mode. - `tile` — Tile handle - `mode` — Which mode's rate to set (sense_cap_mode_t) - `ms` — Report period in milliseconds ##### `tile_sense_cap_set_mode_timeout` — Studio ```c void tile_sense_cap_set_mode_timeout(tile_t *tile, uint8_t mode, uint16_t seconds) ``` Set the inactivity timeout that drops the device to the next mode. - `tile` — Tile handle - `mode` — Mode to set the timeout for (Active, Idle-Touch, Idle or LP1) - `seconds` — Timeout in seconds (0 = never time out of that mode) ##### `tile_sense_cap_set_max_touches` — Studio ```c void tile_sense_cap_set_max_touches(tile_t *tile, uint8_t fingers) ``` Set the maximum number of simultaneous fingers tracked (1 or 2). - `tile` — Tile handle - `fingers` — 1 or 2 ##### `tile_sense_cap_set_resolution` — Studio ```c void tile_sense_cap_set_resolution(tile_t *tile, uint16_t x_res, uint16_t y_res) ``` Set the reported XY resolution of the trackpad. Absolute finger coordinates are scaled to 0..x_res and 0..y_res (datasheet §7.4). Meaningful only once a real surface exists. - `tile` — Tile handle - `x_res` — X resolution in units - `y_res` — Y resolution in units ##### `tile_sense_cap_set_touch_multipliers` — Studio ```c void tile_sense_cap_set_touch_multipliers(tile_t *tile, uint8_t set_mult, uint8_t clear_mult) ``` Set the touch set/clear threshold multipliers. A channel reports touch when its count rises above Reference x (1 + multiplier/128) — §5.5.1. Smaller multiplier = more sensitive. Distinct set and clear values give hysteresis. - `tile` — Tile handle - `set_mult` — Touch-set multiplier, 0-255 - `clear_mult` — Touch-clear multiplier, 0-255 ##### `tile_sense_cap_set_alp_threshold` — Studio ```c void tile_sense_cap_set_alp_threshold(tile_t *tile, uint16_t threshold) ``` Set the ALP wake-channel output threshold. The ALP output asserts when the ALP count deviates from its LTA by more than this delta (§5.5.2). Lower = wakes on lighter proximity. - `tile` — Tile handle - `threshold` — Count-delta threshold ##### `tile_sense_cap_set_event_mode` — Studio ```c void tile_sense_cap_set_event_mode(tile_t *tile, uint8_t enable, uint16_t events) ``` Enable or disable event-mode communication. In event mode the device only opens a communication window when an enabled event occurs, instead of every cycle. Pair it with a RDY pin for interrupt-driven operation; in polled mode the driver reads through the device's clock stretching either way. Pass 0 to leave the current event sources alone. - `tile` — Tile handle - `enable` — 1 to enable event mode, 0 for streaming - `events` — Event sources to enable (IQS7211A_CFG_*_EVENT bits). ##### `tile_sense_cap_set_watchdog` — Studio ```c void tile_sense_cap_set_watchdog(tile_t *tile, uint8_t enable) ``` Enable or disable the device's internal watchdog timer. The device watchdog triggers a full cold boot if its main loop stalls (datasheet §10.2). Independent of the Core's own watchdog. - `tile` — Tile handle - `enable` — 1 to enable, 0 to disable ##### `tile_sense_cap_re_ati` — Studio ```c uint8_t tile_sense_cap_re_ati(tile_t *tile) ``` Queue a re-ATI (auto-tuning) run and wait for it to finish. Re-runs auto-tuning on both the trackpad and the ALP channel against whatever settings the device currently holds, then reports whether it converged. Run this after the sensing environment changes. - `tile` — Tile handle **Returns:** 1 if ATI completed without an error flag, 0 on error or timeout ##### `tile_sense_cap_reseed` — Studio ```c void tile_sense_cap_reseed(tile_t *tile) ``` Reseed the trackpad and ALP reference values. Snaps the references to the present counts, discarding any drift the long-term average has accumulated (datasheet §5.4.3). - `tile` — Tile handle #### Advanced ##### `tile_sense_cap_read_reg` — Studio ```c uint16_t tile_sense_cap_read_reg(tile_t *tile, uint8_t reg) ``` Read a raw 16-bit register. The escape hatch for everything in the `@studio unsupported` list — notably the geometry and ATI registers that a surface bring-up needs before this driver grows a typed API for them. - `tile` — Tile handle - `reg` — Register address (0x00..0xFF) **Returns:** Register value, little-endian, 0 if the tile is not ready ##### `tile_sense_cap_write_reg` — Studio ```c void tile_sense_cap_write_reg(tile_t *tile, uint8_t reg, uint16_t value) ``` Write a raw 16-bit register. - `tile` — Tile handle - `reg` — Register address (0x00..0xFF) - `value` — Value to write, little-endian on the wire #### Other ##### `tile_sense_cap_find` ```c uint8_t tile_sense_cap_find(tiles_pal_t *hal, uint8_t instance) ``` Probe the bus for an IQS7211A trackpad controller. - `hal` — Tiles HAL handle (I2C bus) - `instance` — Device instance (only 0 exists — the address is fixed) **Returns:** 1 if a device ACKs at 0x56, 0 if not ##### `tile_sense_cap_init` ```c void tile_sense_cap_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_cap_cfg_t *cfg) ``` Initialize the trackpad controller. Probes the bus, verifies the product number (763), caches the firmware version, and acknowledges the power-on reset flag. Applies only the optional settings supplied in @p cfg — the chip's own configuration is otherwise left untouched. Follow with configure_surface() (or setup_2x3()) to program the electrode geometry. - `hal` — Tiles HAL handle (I2C bus) - `instance` — Device instance (only 0 exists) - `tile` — Tile handle to initialize - `cfg` — Optional config. NULL for defaults. ##### `tile_sense_cap_configure_surface` ```c uint8_t tile_sense_cap_configure_surface(tile_t *tile, const sense_cap_surface_t *surf) ``` Program the electrode-surface geometry into the device. Writes the Rx/Tx totals and pin mapping, packs and writes the sensing- cycle allocation table (unused cycles cleared), sets XY resolution, axis orientation and the recommended XY filters, sets the ATI target, and finishes with a re-ATI against the new geometry. Every write is window-managed and verified. Call after init, before trusting any XY or touch output. The verified geometry survives in the device until reset, so calling once per boot is sufficient. - `tile` — Initialised tile handle - `surf` — Surface description (see sense_cap_surface_2x3) **Returns:** 1 if every register verified and re-ATI completed, 0 otherwise. ##### `tile_sense_cap_on_touch` ```c void tile_sense_cap_on_touch(tile_t *tile, sense_cap_touch_cb_t cb, void *ctx) ``` Register the raw touch-event stream callback. Fires from process() for every DOWN / MOVED / UP transition, in main-loop context. Pass NULL to disable. - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context passed to the callback ##### `tile_sense_cap_next_touch_event` ```c uint8_t tile_sense_cap_next_touch_event(tile_t *tile, sense_cap_touch_t *ev) ``` Pop the oldest queued touch event. Events queue in an 8-deep ring so a polling loop cannot miss the transitions that happened between process() calls. Oldest events are dropped first on overflow. - `tile` — Tile handle - `ev` — Filled with the event when one was available **Returns:** 1 if an event was returned, 0 if the queue is empty. ##### `tile_sense_cap_on_tap` ```c void tile_sense_cap_on_tap(tile_t *tile, sense_cap_tap_cb_t cb, void *ctx) ``` Register the tap recognizer (single and double). - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context ##### `tile_sense_cap_on_long_press` ```c void tile_sense_cap_on_long_press(tile_t *tile, sense_cap_hold_cb_t cb, void *ctx) ``` Register the long-press recognizer. - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context ##### `tile_sense_cap_on_swipe` ```c void tile_sense_cap_on_swipe(tile_t *tile, sense_cap_swipe_cb_t cb, void *ctx) ``` Register the swipe recognizer. Uses the chip's four-way swipe engine (enable the swipe gestures in the init config), enriched with the tracked velocity. - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context ##### `tile_sense_cap_on_drag` ```c void tile_sense_cap_on_drag(tile_t *tile, sense_cap_drag_cb_t cb, void *ctx) ``` Register the drag (pan) recognizer. Fires on every movement once the finger travels beyond the tap radius, with per-event deltas — the Android onScroll / iOS pan. - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context ##### `tile_sense_cap_on_pinch` ```c void tile_sense_cap_on_pinch(tile_t *tile, sense_cap_pinch_cb_t cb, void *ctx) ``` Register the two-finger pinch recognizer. - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### ATI fine tuning (advanced) The ATI *target* is part of the surface config, and re_ati() / reseed() re-run auto-tuning at any time. The finer knobs — trackpad and ALP ATI multipliers/dividers, compensation dividers and drift limits (0x30-0x31, 0x33-0x3B) — are left at chip defaults, to be revisited if re-ATI against the real surface proves insufficient. Reachable through write_reg meanwhile. #### ALP fine tuning (niche) The ALP wake channel is configured by the surface config (electrode selection, mutual sensing, count filter, ATI target) and its threshold is settable at runtime. Still at chip defaults: the set/ clear debounce counts (0x56), the count-filter betas (0x70/0x71), and the LPX auto-prox cycle setting. ALP count, LTA and output status are readable at runtime. #### XY filtering and trim (advanced) Axis switch and X/Y flips are part of the surface config, and the recommended MAV + dynamic-IIR filters are enabled there. The filter *parameters* (0x64-0x66), finger-split factor and stationary-touch threshold (0x67), and X/Y trim (0x68, 0x69) are not exposed — surface-tuning knobs, left at chip defaults until real-finger testing says otherwise. #### Analog hardware settings (niche) Main oscillator selection and adjustment, calibration-capacitor selection (0x52), and trackpad/ALP charge-transfer frequency (0x58/0x59) are not exposed. These are GUI-derived analog settings for a specific electrode stack-up. #### Comms-request mode selection (niche) Config bit "Comms Request EN" (0x51 bit 4) chooses whether the device clock-stretches a early master or requires an explicit window request. The driver speaks both — it retries through a comms request whenever a read comes back invalid — but does not expose a setter for the bit itself, since the automatic fallback covers both settings. #### MCLR hardware reset (niche) Driver-deferred: on r0 tiles with the IQS7211A fitted, tile pad 3 carries the chip's MCLR (active-low reset, 100k pull-up on board) — a Core pad wired there could hard-reset a comms-locked device, the one failure the software reset cannot reach. Not yet exposed as an API; software reset covers normal operation. (There is no definitions/Sense-CAP-*.json yet; when one lands, this note should be re-checked against the real pad table.) #### Tx short test (niche) The Tx short-test configuration (0x50 bit 15) is a production-line electrode-fault test, out of scope for a runtime driver. ### Events - `touch_down` - `touch_up` - `tap` - `double_tap` - `long_press` - `swipe` - `drag` ### Enums #### `sense_cap_mode_t` Charging (power) mode — reported in Info Flags, selectable in manual control. | Member | Value | Description | | --- | --- | --- | | `SENSE_CAP_MODE_ACTIVE` | `0` | Full report rate | | `SENSE_CAP_MODE_IDLE_TOUCH` | `1` | Touch held, reduced rate | | `SENSE_CAP_MODE_IDLE` | `2` | No touch, reduced rate | | `SENSE_CAP_MODE_LP1` | `3` | Low power 1 | | `SENSE_CAP_MODE_LP2` | `4` | Low power 2 (lowest) | #### `sense_cap_finger_t` Finger slot for the absolute-XY getters. | Member | Value | Description | | --- | --- | --- | | `SENSE_CAP_FINGER_1` | `0` | | | `SENSE_CAP_FINGER_2` | `1` | | #### `sense_cap_phase_t` Phase of a touch event, iOS/Android style. | Member | Value | Description | | --- | --- | --- | | `SENSE_CAP_TOUCH_DOWN` | `0` | Finger arrived | | `SENSE_CAP_TOUCH_MOVED` | `1` | Position changed while down | | `SENSE_CAP_TOUCH_UP` | `2` | Finger left | ### Structs #### `sense_cap_touch_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `phase` | sense_cap_phase_t value. | | `uint8_t` | `finger` | Finger slot, 0 or 1. | | `uint16_t` | `x` | Position in the configured X resolution. | | `uint16_t` | `y` | Position in the configured Y resolution. | | `int16_t` | `dx` | X movement since the previous event. | | `int16_t` | `dy` | Y movement since the previous event. | | `int16_t` | `vx` | X velocity, pixels/second. | | `int16_t` | `vy` | Y velocity, pixels/second. | | `uint16_t` | `strength` | Touch strength at this event. | | `uint32_t` | `t_ms` | Timestamp, driver clock. | #### `sense_cap_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `rdy_pin` | Core pad for RDY. 0 = polled mode. | | `sense_cap_event_cb_t` | `on_event` | Callback. NULL = no callback. | | `uint16_t` | `gestures` | Gesture enable mask (SENSE_CAP_GESTURE_*). 0 = leave as-is. | | `uint16_t` | `active_rate_ms` | Active-mode report rate. 0 = leave as-is. | | `uint8_t` | `max_touches` | Max simultaneous fingers, 1 or 2. 0 = leave as-is. | | `uint8_t` | `event_mode` | 1 = event mode (comms only on events), 0 = leave as-is. | #### `sense_cap_surface_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `total_rx` | Rx electrode count, 1-8. | | `uint8_t` | `total_tx` | Tx electrode count, 1-12. | | `uint8_t` | `switch_xy` | 1 = X along Txs, Y along Rxs. | | `uint8_t` | `flip_x` | 1 = invert X output. | | `uint8_t` | `flip_y` | 1 = invert Y output. | | `uint16_t` | `x_res` | X output resolution (pixels). | | `uint16_t` | `y_res` | Y output resolution (pixels). | | `uint16_t` | `ati_target` | Trackpad ATI target (counts). | | `uint8_t` | `alp_enable` | | | `uint16_t` | `alp_ati_target` | | | `uint16_t` | `ati_base` | | | `uint8_t` | `touch_set_mult` | | | `uint8_t` | `touch_clear_mult` | | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_CAP_VERSION_MAJOR` | `0` | | | `TILE_SENSE_CAP_VERSION_MINOR` | `3` | | | `TILE_SENSE_CAP_VERSION_PATCH` | `1` | | | `IQS7211A_I2C_ADDR` | `0x56` | | | `IQS7211A_PRODUCT_NUMBER` | `763` | | | `IQS7211A_INVALID_RESPONSE` | `0xEEEE` | | | `IQS7211A_CYCLE_PROX_BYTE` | `0x05` | Fixed first byte of every cycle record | | `IQS7211A_CHANNEL_NONE` | `0xFF` | "No channel allocated" in a cycle slot | | `IQS7211A_INFO_MODE_SHIFT` | `0` | | | `IQS7211A_INFO_NUM_FINGERS_SHIFT` | `8` | | | `SENSE_CAP_GESTURE_ALL` | `0x3F` | | | `SENSE_CAP_DIR_LEFT` | `0` | Toward -X | | `SENSE_CAP_DIR_RIGHT` | `1` | Toward +X | | `SENSE_CAP_DIR_UP` | `2` | Toward -Y | | `SENSE_CAP_DIR_DOWN` | `3` | Toward +Y | | `IQS7211A_CTRL_MODE_SHIFT` | `0` | | | `SENSE_CAP_NUM_FINGERS` | `2` | | | `SENSE_CAP_MAX_RX` | `8` | | | `SENSE_CAP_MAX_TX` | `12` | | | `SENSE_CAP_MAX_MAP` | `12` | | --- Generated 2026-08-12 from the driver header at tiles@129eab4. Canonical HTML: https://bergsonne.io/docs/tiles/sense/cap ============================================================================== ## Sense.I.6P6 driver > Complete driver for the Sense.I.6P6 tile (ICM-42686-P). Supports both I2C and SPI bus access via tiles_pal_t. | | | | --- | --- | | Tile | `Sense.I.6P6` | | Driver version | `1.2.0` | | Header | `tile_sense_i_6p6.h` | | Generated from | `tiles@9b8f19e` | | Canonical page | https://bergsonne.io/docs/tiles/sense/i6p6 | ### Overview Supports both I2C and SPI bus access via tiles_pal_t. @version 1.2.0 6-axis IMU with extended measurement range: - Accelerometer: 16-bit, ±2/4/8/16/32 G, up to 32 kHz ODR - Gyroscope: 16-bit, ±31.25 to ±4000 DPS, up to 32 kHz ODR - Temperature: on-chip sensor - FIFO: 2 KB with multiple packet formats - APEX: Pedometer, tilt, wake-on-motion, tap detection Platform-agnostic: uses tiles_pal_t for all bus access. Datasheet: TDK InvenSense DS-000639, Rev 1.0 ### Examples #### Quick start — I2C (polling) ```c tile_t imu; tile_sense_i_6p6_init(core_tiles_pal(&core_i2c3), 0, &imu, NULL); int16_t accel[3], gyro[3]; tile_sense_i_6p6_get_raw_accels(&imu, accel); tile_sense_i_6p6_get_raw_gyros(&imu, gyro); ``` #### Quick start — SPI ```c tile_t imu; tile_sense_i_6p6_init(core_tiles_pal(&core_spi1), 0, &imu, NULL); int16_t accel[3]; tile_sense_i_6p6_get_raw_accels(&imu, accel); ``` #### Quick start — interrupt-driven with callback ```c void on_data(tile_t *t, uint8_t events, void *ctx) { int16_t buf[7]; tile_sense_i_6p6_get_raw_all(t, buf); } sense_i_6p6_cfg_t cfg = { .on_event = on_data, .int1_pin = 9, // Core pad connected to INT1 }; tile_sense_i_6p6_init(core_tiles_pal(&core_i2c3), 0, &imu, &cfg); tile_sense_i_6p6_int1_data_ready(&imu, 1); while (1) { tile_sense_i_6p6_process(&imu); } ``` ### API 70 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_i_6p6_sleep` — Studio ```c void tile_sense_i_6p6_sleep(tile_t *tile) ``` Enter sleep mode (accel + gyro off). ~7.5 µA. ##### `tile_sense_i_6p6_wake` — Studio ```c void tile_sense_i_6p6_wake(tile_t *tile) ``` Wake from sleep, restore low-noise mode. Range/ODR preserved. ##### `tile_sense_i_6p6_reset` — Studio ```c void tile_sense_i_6p6_reset(tile_t *tile) ``` Software reset. Blocks ~2 ms. Must call init() again after. #### Runtime ##### `tile_sense_i_6p6_set_accel_range` — Studio ```c void tile_sense_i_6p6_set_accel_range(tile_t *tile, sense_i_6p6_accel_range_t range) ``` Set accelerometer full-scale range. ##### `tile_sense_i_6p6_set_gyro_range` — Studio ```c void tile_sense_i_6p6_set_gyro_range(tile_t *tile, sense_i_6p6_gyro_range_t range) ``` Set gyroscope full-scale range. ##### `tile_sense_i_6p6_set_accel_odr` — Studio ```c void tile_sense_i_6p6_set_accel_odr(tile_t *tile, sense_i_6p6_odr_t odr) ``` Set accelerometer output data rate. ##### `tile_sense_i_6p6_set_gyro_odr` — Studio ```c void tile_sense_i_6p6_set_gyro_odr(tile_t *tile, sense_i_6p6_odr_t odr) ``` Set gyroscope output data rate. ##### `tile_sense_i_6p6_set_power_mode` — Studio ```c void tile_sense_i_6p6_set_power_mode(tile_t *tile, sense_i_6p6_power_mode_t accel, sense_i_6p6_power_mode_t gyro) ``` Set power mode independently for accel and gyro. **Note:** Wait 200 µs after changing mode before accessing other registers. ##### `tile_sense_i_6p6_data_ready` — Studio ```c uint8_t tile_sense_i_6p6_data_ready(tile_t *tile) ``` Check if new sensor data is available. ##### `tile_sense_i_6p6_get_raw_accels` — Studio ```c void tile_sense_i_6p6_get_raw_accels(tile_t *tile, int16_t *buffer) ``` Read raw accelerometer [X, Y, Z]. Convert: g = raw / sensitivity. ##### `tile_sense_i_6p6_get_raw_gyros` — Studio ```c void tile_sense_i_6p6_get_raw_gyros(tile_t *tile, int16_t *buffer) ``` Read raw gyroscope [X, Y, Z]. Convert: dps = raw / sensitivity. ##### `tile_sense_i_6p6_get_raw_6dof` — Studio ```c void tile_sense_i_6p6_get_raw_6dof(tile_t *tile, int16_t *buffer) ``` Burst read accel + gyro [AX, AY, AZ, GX, GY, GZ]. One transaction. ##### `tile_sense_i_6p6_get_temperature` — Studio ```c int16_t tile_sense_i_6p6_get_temperature(tile_t *tile) ``` Read temperature. Convert: degC = raw / 132.48 + 25.0 ##### `tile_sense_i_6p6_get_raw_all` — Studio ```c void tile_sense_i_6p6_get_raw_all(tile_t *tile, int16_t *buffer) ``` Read all 7 channels [Temp, AX, AY, AZ, GX, GY, GZ] in one burst. ##### `tile_sense_i_6p6_is_face_up` — Studio ```c uint8_t tile_sense_i_6p6_is_face_up(tile_t *tile) ``` Detect whether the device is approximately face-up (Z ≈ +1g). Reads the accelerometer once and returns 1 when Z is within ±200 mg of +1 g and X/Y are within ±300 mg of 0. Uses the current ACCEL_CONFIG0 full-scale range to compute thresholds at runtime, so the helper stays correct after `set_accel_range`. Integer math only. **Returns:** 1 if face-up, 0 otherwise. ##### `tile_sense_i_6p6_is_face_down` — Studio ```c uint8_t tile_sense_i_6p6_is_face_down(tile_t *tile) ``` Detect whether the device is approximately face-down (Z ≈ −1g). Mirror of `is_face_up` with an inverted Z target. Same ±200 mg / ±300 mg tolerance bands; same range-aware thresholding. **Returns:** 1 if face-down, 0 otherwise. ##### `tile_sense_i_6p6_is_moving` — Studio ```c uint8_t tile_sense_i_6p6_is_moving(tile_t *tile, uint16_t threshold_mg) ``` Detect motion: |‖a‖ − 1g| > threshold. Reads the accelerometer, computes the squared magnitude in raw counts, and compares against the squared 1-g + threshold band. Squared-form keeps the math integer (no sqrt). Threshold is symmetric around 1 g so the helper triggers on both lift and drop. Range-aware. - `threshold_mg` — Deviation threshold in milli-g (typical: 50–200). **Returns:** 1 if moving, 0 otherwise. ##### `tile_sense_i_6p6_read_tilt_centi_degrees` — Studio ```c uint8_t tile_sense_i_6p6_read_tilt_centi_degrees(tile_t *tile, uint8_t axis, int16_t *out_centi_deg) ``` Tilt angle of one axis vs gravity, in 0.01°. Computes `atan2(axis, sqrt(other_a^2 + other_b^2))` for the requested axis using a small integer atan2 approximation. Output range: −18000..+18000 (i.e. −180.00°..+180.00°). Returns 0 and writes 0 to `*out_centi_deg` if the device is in free-fall or the accelerometer is off. Range-aware. - `axis` — 0 = X, 1 = Y, 2 = Z. - `out_centi_deg` — Output tilt in 0.01° (signed, −18000..+18000). **Returns:** 1 on success, 0 on bad axis or zero vector. #### Events ##### `tile_sense_i_6p6_process` — Studio ```c void tile_sense_i_6p6_process(tile_t *tile) ``` Process pending interrupt events. Call from your main loop. In interrupt mode: returns immediately if no INT1 interrupt fired; reads INT_STATUS and fires callback only when events are pending. In polled mode: reads INT_STATUS every call, fires callback if set. ##### `tile_sense_i_6p6_on_event` — Studio ```c void tile_sense_i_6p6_on_event(tile_t *tile, sense_i_6p6_event_cb_t cb, void *ctx) ``` Register or change the event callback. - `cb` — Callback to fire on interrupt events. NULL to clear. - `ctx` — Opaque user pointer passed to the callback. #### Configuration ##### `tile_sense_i_6p6_set_filter_bw` — Studio ```c void tile_sense_i_6p6_set_filter_bw(tile_t *tile, sense_i_6p6_filter_bw_t accel_bw, sense_i_6p6_filter_bw_t gyro_bw) ``` Set UI filter bandwidth for both accel and gyro. ##### `tile_sense_i_6p6_set_filter_order` — Studio ```c void tile_sense_i_6p6_set_filter_order(tile_t *tile, sense_i_6p6_filter_order_t accel_order, sense_i_6p6_filter_order_t gyro_order) ``` Set UI filter order for accel and gyro (1st, 2nd, or 3rd). ##### `tile_sense_i_6p6_set_temp_filter` — Studio ```c void tile_sense_i_6p6_set_temp_filter(tile_t *tile, sense_i_6p6_temp_filter_t bw) ``` Set temperature sensor filter bandwidth. ##### `tile_sense_i_6p6_set_temp_enabled` — Studio ```c void tile_sense_i_6p6_set_temp_enabled(tile_t *tile, uint8_t enabled) ``` Enable or disable the temperature sensor. #### FIFO ##### `tile_sense_i_6p6_fifo_config` — Studio ```c void tile_sense_i_6p6_fifo_config(tile_t *tile, sense_i_6p6_fifo_mode_t mode, uint8_t accel, uint8_t gyro, uint8_t temp, uint8_t hires) ``` Configure the FIFO. - `mode` — FIFO operating mode (bypass/stream/stop-on-full) - `accel` — Include accel data in FIFO packets - `gyro` — Include gyro data in FIFO packets - `temp` — Include temperature in FIFO packets - `hires` — Enable 20-bit high-resolution mode (forces ±32g/±4000dps) ##### `tile_sense_i_6p6_fifo_set_watermark` — Studio ```c void tile_sense_i_6p6_fifo_set_watermark(tile_t *tile, uint16_t records) ``` Set the FIFO watermark threshold in records (1–4095). ##### `tile_sense_i_6p6_fifo_flush` — Studio ```c void tile_sense_i_6p6_fifo_flush(tile_t *tile) ``` Flush the FIFO (discard all data). ##### `tile_sense_i_6p6_fifo_read_packet_flat` — Studio ```c void tile_sense_i_6p6_fifo_read_packet_flat(tile_t *tile, int32_t *out) ``` DSL-friendly flat-output variant of fifo_read_packet(). Drops the struct in favor of a positional int[8] array — the DSL doesn't have a struct ABI yet, so the per-field outputs come back indexed. Layout: out[0..2] = accel x,y,z (raw int16 widened to int32) out[3..5] = gyro x,y,z (raw int16 widened to int32) out[6] = temp (raw int8 widened to int32) out[7] = timestamp (raw uint16) Drops the success bool — call fifo_count() first to know whether there's data. On an empty FIFO, the slots are left untouched. - `tile` — Initialised tile handle. - `out` — Output buffer (8 int32_t slots). ##### `tile_sense_i_6p6_fifo_read_packets_flat` — Studio ```c uint16_t tile_sense_i_6p6_fifo_read_packets_flat(tile_t *tile, int32_t *out, uint16_t cap_ints) ``` DSL-friendly flat-output variant of fifo_read_packets(). Drains up to N packets from the FIFO into the caller's int32 buffer, 8 ints per packet (same layout as fifo_read_packet_flat). Returns the number of *packets* read (not ints), so callers can iterate with `for i in 0..n { let ax = buf[i*8 + 0]; ... }`. The caller's array length is the int-cap (N packets × 8 ints), not the packet count — `cap_ints / 8` packets are attempted. Mirrors the cap-mode array-OUT convention used by Sense.BP read_fifo_batch. - `tile` — Initialised tile handle. - `out` — Output buffer; must be sized N × 8 ints. - `cap_ints` — Buffer capacity in int slots (= packets × 8). **Returns:** Number of packets actually drained from the FIFO. ##### `tile_sense_i_6p6_fifo_count` — Studio ```c uint16_t tile_sense_i_6p6_fifo_count(tile_t *tile) ``` Read the FIFO record count. ##### `tile_sense_i_6p6_fifo_lost_count` — Studio ```c uint16_t tile_sense_i_6p6_fifo_lost_count(tile_t *tile) ``` Get the number of lost FIFO packets since last check. #### Interrupts ##### `tile_sense_i_6p6_int1_config` — Studio ```c void tile_sense_i_6p6_int1_config(tile_t *tile, uint8_t config) ``` Configure INT1 pin behavior. - `config` — OR'd flags: ACTIVE_HIGH|PUSH_PULL|LATCHED etc. ##### `tile_sense_i_6p6_int1_data_ready` — Studio ```c void tile_sense_i_6p6_int1_data_ready(tile_t *tile, uint8_t enabled) ``` Route data-ready interrupt to INT1. - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_6p6_int1_fifo_ths` — Studio ```c void tile_sense_i_6p6_int1_fifo_ths(tile_t *tile, uint8_t enabled) ``` Route FIFO watermark interrupt to INT1. - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_6p6_int1_wom` — Studio ```c void tile_sense_i_6p6_int1_wom(tile_t *tile, uint8_t enabled) ``` Route WOM (wake-on-motion) interrupt to INT1. - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_6p6_int2_config` — Studio ```c void tile_sense_i_6p6_int2_config(tile_t *tile, uint8_t config) ``` Configure INT2 pin behavior. Mirrors int1_config for the chip's second interrupt pin (tile pad 8). INT_CONFIG bits [5:3] hold INT2's polarity / drive / mode bits. layout as int1_config; the driver shifts the bits into the INT2 positions internally). - `config` — OR'd flags: ACTIVE_HIGH|PUSH_PULL|LATCHED etc. (same ##### `tile_sense_i_6p6_int2_data_ready` — Studio ```c void tile_sense_i_6p6_int2_data_ready(tile_t *tile, uint8_t enabled) ``` Route data-ready interrupt to INT2. - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_6p6_int2_fifo_ths` — Studio ```c void tile_sense_i_6p6_int2_fifo_ths(tile_t *tile, uint8_t enabled) ``` Route FIFO watermark interrupt to INT2. - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_6p6_int2_wom` — Studio ```c void tile_sense_i_6p6_int2_wom(tile_t *tile, uint8_t enabled) ``` Route WOM (wake-on-motion) interrupt to INT2. - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_6p6_set_int_pulse_duration` — Studio ```c void tile_sense_i_6p6_set_int_pulse_duration(tile_t *tile, sense_i_6p6_int_pulse_t pulse) ``` Set the INT pin pulse-width when configured for pulse mode. Applies to both INT1 and INT2 (it's a chip-wide setting in INT_CONFIG1 bit 6). Only affects pulse-mode interrupts; in latched mode the line stays asserted until the status register is read. - `tile` — Initialised tile handle - `pulse` — Pulse-width selector ##### `tile_sense_i_6p6_get_int_status` — Studio ```c uint8_t tile_sense_i_6p6_get_int_status(tile_t *tile) ``` Read and clear INT_STATUS register. ##### `tile_sense_i_6p6_get_int_status2` — Studio ```c uint8_t tile_sense_i_6p6_get_int_status2(tile_t *tile) ``` Read and clear INT_STATUS2 (WOM/SMD flags). ##### `tile_sense_i_6p6_get_int_status3` — Studio ```c uint8_t tile_sense_i_6p6_get_int_status3(tile_t *tile) ``` Read and clear INT_STATUS3 (APEX: step/tilt/tap flags). #### Advanced ##### `tile_sense_i_6p6_subsystem_reset` — Studio ```c void tile_sense_i_6p6_subsystem_reset(tile_t *tile, sense_i_6p6_subsystem_t which) ``` Reset a single chip subsystem without touching the others. Issues the relevant SIGNAL_PATH_RESET bit. Distinct from the driver's full `reset()` (which re-initialises the whole chip) and from `fifo_flush()` (which targets just the FIFO, also a subsystem reset but already exposed). - APEX: clears DMP memory and re-runs the DMP init sequence. Use after reconfiguring pedometer / tilt / tap features. - TEMP: resets the temperature signal path. Useful if temp readings appear stuck after a power glitch. - `tile` — Initialised tile handle - `which` — Subsystem to reset ##### `tile_sense_i_6p6_set_gyro_offset` — Studio ```c void tile_sense_i_6p6_set_gyro_offset(tile_t *tile, int16_t x_dps16, int16_t y_dps16, int16_t z_dps16) ``` Set user-programmable gyro offset. - `x_dps` — X offset in dps × 16 (12-bit signed, ±128 dps max, 0.0625 dps resolution) - `y_dps` — Y offset - `z_dps` — Z offset ##### `tile_sense_i_6p6_set_accel_offset` — Studio ```c void tile_sense_i_6p6_set_accel_offset(tile_t *tile, int16_t x_mg, int16_t y_mg, int16_t z_mg) ``` Set user-programmable accel offset. - `x_mg` — X offset in mg (12-bit signed, ±2000 mg max, 1 mg resolution) - `y_mg` — Y offset - `z_mg` — Z offset ##### `tile_sense_i_6p6_self_test` — Studio ```c uint8_t tile_sense_i_6p6_self_test(tile_t *tile, uint8_t *accel_pass, uint8_t *gyro_pass) ``` Perform self-test. Runs the built-in self-test for both accel and gyro. - `accel_pass` — Output: 1 if accel self-test passed, 0 if failed (per-axis OR'd) - `gyro_pass` — Output: 1 if gyro self-test passed, 0 if failed **Returns:** 1 if both passed, 0 if either failed ##### `tile_sense_i_6p6_read_reg` — Studio ```c uint8_t tile_sense_i_6p6_read_reg(tile_t *tile, uint8_t bank, uint8_t reg) ``` Read a register from any bank. Automatically switches to the specified bank and back to bank 0. For advanced/debug use. - `bank` — Bank index (0, 1, 2, or 4). - `reg` — Register address within the bank. ##### `tile_sense_i_6p6_write_reg` — Studio ```c void tile_sense_i_6p6_write_reg(tile_t *tile, uint8_t bank, uint8_t reg, uint8_t val) ``` Write a register in any bank. Automatically switches to the specified bank and back to bank 0. For advanced/debug use. - `bank` — Bank index (0, 1, 2, or 4). - `reg` — Register address within the bank. - `val` — Byte to write. #### Other ##### `tile_sense_i_6p6_find` ```c uint8_t tile_sense_i_6p6_find(tiles_pal_t *hal, uint8_t instance) ``` Check if a Sense.I.6P6 is present on the bus (address probe only). ##### `tile_sense_i_6p6_init` ```c void tile_sense_i_6p6_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_i_6p6_cfg_t *cfg) ``` Initialize the ICM-42686P. Performs soft reset, verifies WHO_AM_I, configures sensor ranges and ODR. Pass cfg=NULL for defaults (±8g, ±1000dps, 100Hz, polled). **Note:** Blocks for ~2 ms (reset). Call once at startup. ##### `tile_sense_i_6p6_wait_for_tap` — Studio ```c uint8_t tile_sense_i_6p6_wait_for_tap(tile_t *tile, uint32_t timeout_ms) ``` Block until a tap interrupt fires or the timeout expires. Polls INT_STATUS3 at ~1 ms cadence (delay_ms(1)). Tap detection must be enabled first via `tile_sense_i_6p6_tap_enable`. The status read is destructive (clear-on-read), so call `get_tap_result` immediately after a positive return to retrieve count/axis/direction. - `timeout_ms` — Max time to wait, in ms. Use 0 for a single-shot poll. **Returns:** 1 if a tap was detected, 0 on timeout. ##### `tile_sense_i_6p6_wait_for_motion` — Studio ```c uint8_t tile_sense_i_6p6_wait_for_motion(tile_t *tile, uint32_t timeout_ms) ``` Block until a wake-on-motion interrupt fires or the timeout expires. Polls INT_STATUS2 at ~1 ms cadence. WOM must be configured (thresholds + enable) first. Returns on any-axis WOM. The status read clears the latch. - `timeout_ms` — Max time to wait, in ms. Use 0 for a single-shot poll. **Returns:** 1 if motion was detected, 0 on timeout. ##### `tile_sense_i_6p6_fifo_read_packet` ```c uint8_t tile_sense_i_6p6_fifo_read_packet(tile_t *tile, sense_i_6p6_fifo_packet_t *pkt) ``` Read one standard FIFO packet (16 bytes: accel + gyro + temp + timestamp). **Returns:** 1 if valid packet read, 0 if FIFO empty or error. ##### `tile_sense_i_6p6_fifo_read_packets` ```c uint16_t tile_sense_i_6p6_fifo_read_packets(tile_t *tile, sense_i_6p6_fifo_packet_t *packets, uint16_t max_count) ``` Read multiple FIFO packets. - `packets` — Output array - `max_count` — Maximum packets to read **Returns:** Number of packets actually read ##### `tile_sense_i_6p6_wom_config` — Studio ```c void tile_sense_i_6p6_wom_config(tile_t *tile, uint16_t x_mg, uint16_t y_mg, uint16_t z_mg, sense_i_6p6_wom_mode_t mode) ``` Configure Wake-on-Motion thresholds. - `x_mg` — X-axis threshold in mg (0–1000, resolution ~3.9 mg) - `y_mg` — Y-axis threshold in mg - `z_mg` — Z-axis threshold in mg - `mode` — Compare against initial or previous sample **Note:** Requires accel to be running (LN or LP mode). ##### `tile_sense_i_6p6_wom_enable` — Studio ```c void tile_sense_i_6p6_wom_enable(tile_t *tile) ``` Enable WOM. Must configure thresholds first. ##### `tile_sense_i_6p6_wom_disable` — Studio ```c void tile_sense_i_6p6_wom_disable(tile_t *tile) ``` Disable WOM. ##### `tile_sense_i_6p6_smd_config` — Studio ```c void tile_sense_i_6p6_smd_config(tile_t *tile, sense_i_6p6_smd_mode_t mode) ``` Configure and enable SMD. WOM thresholds must be set first. ##### `tile_sense_i_6p6_pedometer_enable` — Studio ```c void tile_sense_i_6p6_pedometer_enable(tile_t *tile, sense_i_6p6_dmp_odr_t dmp_odr) ``` Enable the pedometer (step counter + step detector). Requires accel at ≥25 Hz. Initializes the DMP if not already running. - `dmp_odr` — DMP processing rate (25 or 50 Hz) ##### `tile_sense_i_6p6_pedometer_disable` — Studio ```c void tile_sense_i_6p6_pedometer_disable(tile_t *tile) ``` Disable the pedometer. ##### `tile_sense_i_6p6_get_step_count` — Studio ```c uint16_t tile_sense_i_6p6_get_step_count(tile_t *tile) ``` Read the step count. **Returns:** 16-bit step count (resets on power cycle or DMP reset) ##### `tile_sense_i_6p6_get_step_cadence` — Studio ```c uint8_t tile_sense_i_6p6_get_step_cadence(tile_t *tile) ``` Read the step cadence. **Returns:** Steps per second in u6.2 fixed-point (divide by 4.0 for float) ##### `tile_sense_i_6p6_get_activity` — Studio ```c sense_i_6p6_activity_t tile_sense_i_6p6_get_activity(tile_t *tile) ``` Read the activity classification. **Returns:** 0=unknown, 1=walk, 2=run ##### `tile_sense_i_6p6_tilt_enable` — Studio ```c void tile_sense_i_6p6_tilt_enable(tile_t *tile, uint8_t wait_seconds) ``` Enable tilt detection. Triggers when device tilts >35° for the configured wait time. Requires accel at ≥25 Hz. Initializes DMP if needed. - `wait_seconds` — [0..6] s Time the tilt must be sustained (0, 2, 4, or 6). ##### `tile_sense_i_6p6_tilt_disable` — Studio ```c void tile_sense_i_6p6_tilt_disable(tile_t *tile) ``` Disable tilt detection. ##### `tile_sense_i_6p6_tap_enable` — Studio ```c void tile_sense_i_6p6_tap_enable(tile_t *tile) ``` Enable tap detection (single + double tap). Requires accel in LN mode at 200 Hz, 500 Hz, or 1 kHz. Initializes DMP if needed. ##### `tile_sense_i_6p6_tap_disable` — Studio ```c void tile_sense_i_6p6_tap_disable(tile_t *tile) ``` Disable tap detection. ##### `tile_sense_i_6p6_get_tap_result` ```c void tile_sense_i_6p6_get_tap_result(tile_t *tile, sense_i_6p6_tap_result_t *result) ``` Read the last tap detection result. - `result` — Output tap information ##### `tile_sense_i_6p6_get_tap_result_flat` — Studio ```c void tile_sense_i_6p6_get_tap_result_flat(tile_t *tile, int32_t *count, int32_t *axis, int32_t *direction, int32_t *timing) ``` DSL-friendly flat-output variant of get_tap_result(). Same data, four positional out-scalars instead of a struct. Useful for polling-style flows; event-driven flows already get these values via the `Sense.I.6P6.tap` event payload. - `tile` — Initialised tile handle. - `count` — Output: 0 = none, 1 = single tap, 2 = double. - `axis` — Output: 0 = X, 1 = Y, 2 = Z. - `direction` — Output: 0 = positive, 1 = negative. - `timing` — Output: double-tap timing (chip-specific units). ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### External timing pins (FSYNC / CLKIN) (advanced) Hardware-gated (not a driver gap). The ICM-42686-P can take a 31–50 kHz FSYNC input (TIMESTAMP_FSYNC_EN) for sample time-stamping and a ~32 kHz external CLKIN (RTC) for precise ODR. Neither pin is routed to a tile pad on Sense-I-6P6-a, so the capability simply isn't wired out — closing requires a tile hardware revision, not driver work. #### FIFO 20-bit hi-res mode (niche) Design-gated. FIFO_HIRES_EN switches FIFO packets to 20-bit accel + 20-bit gyro (Packet 4 with 3-byte extensions) at higher sensitivity scale factors. Closing properly requires extending sense_i_6p6_fifo_packet_t to represent 20-bit fields and reworking the read_packet parser; toggling the bit alone breaks the existing 16-bit parsing path. Deferred to a future driver pass. #### I3C support (advanced) Ecosystem-gated. ICM-42686-P supports I3C SDR (up to 12.5 MHz with in-band interrupts and dynamic addressing) and the tile straps I3C on pads 3/4/5. The driver framework currently uses tiles_pal I²C calls only; closing requires a new bus abstraction in Studio. Defer to a multi-bus driver framework pass. #### APEX raise-to-wake / raise-to-sleep (advanced) Driver-deferred (true driver gap — hardware supports it). The APEX engine detects raise-to-wake / raise-to-sleep gestures (RWR), routable to INT via INT_SOURCE6/7. The driver wraps pedometer / tilt / tap / WOM / SMD but not RWR; no hardware or framework blocker — closing is a driver pass. #### UI notch / anti-alias filter tuning (advanced) Driver-deferred (true driver gap). set_filter_bw / set_filter_order expose the UI filter bandwidth and order, but not the notch-filter centre frequency / Q (Bank-1 GYRO_CFG_STATIC registers) for rejecting a specific vibration band. #### Standalone timestamp (TMSTVAL) read (niche) Driver-deferred (true driver gap). FIFO packets carry a 16-bit timestamp, but the chip's free-running timestamp counter (Bank-1 TMSTVAL0-2) isn't exposed as a direct read. ### Events - `data_ready` - `fifo_watermark` - `tilt` - `tap` - `wake_on_motion` ### Enums #### `sense_i_6p6_accel_range_t` Accelerometer full-scale range. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_ACCEL_32G` | `0x00` | +/- 32g | | `SENSE_I_6P6_ACCEL_16G` | `0x01` | +/- 16g | | `SENSE_I_6P6_ACCEL_8G` | `0x02` | +/- 8g | | `SENSE_I_6P6_ACCEL_4G` | `0x03` | +/- 4g | | `SENSE_I_6P6_ACCEL_2G` | `0x04` | +/- 2g | #### `sense_i_6p6_gyro_range_t` Gyroscope full-scale range. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_GYRO_4000DPS` | `0x00` | +/- 4000 deg/s | | `SENSE_I_6P6_GYRO_2000DPS` | `0x01` | +/- 2000 deg/s | | `SENSE_I_6P6_GYRO_1000DPS` | `0x02` | +/- 1000 deg/s | | `SENSE_I_6P6_GYRO_500DPS` | `0x03` | +/- 500 deg/s | | `SENSE_I_6P6_GYRO_250DPS` | `0x04` | +/- 250 deg/s | | `SENSE_I_6P6_GYRO_125DPS` | `0x05` | +/- 125 deg/s | | `SENSE_I_6P6_GYRO_62_5DPS` | `0x06` | +/- 62.5 deg/s | | `SENSE_I_6P6_GYRO_31_25DPS` | `0x07` | +/- 31.25 deg/s | #### `sense_i_6p6_odr_t` Output data rate selection. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_ODR_32KHZ` | `0x01` | 32 kHz | | `SENSE_I_6P6_ODR_16KHZ` | `0x02` | 16 kHz | | `SENSE_I_6P6_ODR_8KHZ` | `0x03` | 8 kHz | | `SENSE_I_6P6_ODR_4KHZ` | `0x04` | 4 kHz | | `SENSE_I_6P6_ODR_2KHZ` | `0x05` | 2 kHz | | `SENSE_I_6P6_ODR_1KHZ` | `0x06` | 1 kHz | | `SENSE_I_6P6_ODR_200HZ` | `0x07` | 200 Hz | | `SENSE_I_6P6_ODR_100HZ` | `0x08` | 100 Hz | | `SENSE_I_6P6_ODR_50HZ` | `0x09` | 50 Hz | | `SENSE_I_6P6_ODR_25HZ` | `0x0A` | 25 Hz | | `SENSE_I_6P6_ODR_12_5HZ` | `0x0B` | 12.5 Hz | | `SENSE_I_6P6_ODR_6_25HZ` | `0x0C` | 6.25 Hz (accel LP only) | | `SENSE_I_6P6_ODR_3_125HZ` | `0x0D` | 3.125 Hz (accel LP only) | | `SENSE_I_6P6_ODR_1_5625HZ` | `0x0E` | 1.5625 Hz (accel LP only) | | `SENSE_I_6P6_ODR_500HZ` | `0x0F` | 500 Hz | #### `sense_i_6p6_power_mode_t` Power mode for accel/gyro independently. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_MODE_OFF` | `0x00` | Powered off | | `SENSE_I_6P6_MODE_STANDBY` | `0x01` | Gyro only: drive on, no output | | `SENSE_I_6P6_MODE_LP` | `0x02` | Accel only: duty-cycled | | `SENSE_I_6P6_MODE_LN` | `0x03` | Low-noise (full performance) | #### `sense_i_6p6_filter_bw_t` UI filter bandwidth selection. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_FILT_BW_ODR_2` | `0x00` | ODR/2 (Nyquist) | | `SENSE_I_6P6_FILT_BW_ODR_4` | `0x01` | max(400,ODR)/4 (default) | | `SENSE_I_6P6_FILT_BW_ODR_5` | `0x02` | ODR/5 | | `SENSE_I_6P6_FILT_BW_ODR_8` | `0x03` | ODR/8 | | `SENSE_I_6P6_FILT_BW_ODR_10` | `0x04` | ODR/10 | | `SENSE_I_6P6_FILT_BW_ODR_16` | `0x05` | ODR/16 | | `SENSE_I_6P6_FILT_BW_ODR_20` | `0x06` | ODR/20 | | `SENSE_I_6P6_FILT_BW_ODR_40` | `0x07` | ODR/40 | | `SENSE_I_6P6_FILT_LP_1X_AVG` | `0x01` | LP mode: 1× averaging (default) | | `SENSE_I_6P6_FILT_LP_16X_AVG` | `0x06` | LP mode: 16× averaging | #### `sense_i_6p6_filter_order_t` UI filter order. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_FILT_ORDER_1ST` | `0x00` | 1st order | | `SENSE_I_6P6_FILT_ORDER_2ND` | `0x01` | 2nd order | | `SENSE_I_6P6_FILT_ORDER_3RD` | `0x02` | 3rd order | #### `sense_i_6p6_temp_filter_t` Temperature filter bandwidth. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_TEMP_FILT_4000HZ` | `0x00` | 4000 Hz cutoff | | `SENSE_I_6P6_TEMP_FILT_170HZ` | `0x01` | 170 Hz cutoff | | `SENSE_I_6P6_TEMP_FILT_82HZ` | `0x02` | 82 Hz cutoff | | `SENSE_I_6P6_TEMP_FILT_40HZ` | `0x03` | 40 Hz cutoff | | `SENSE_I_6P6_TEMP_FILT_20HZ` | `0x04` | 20 Hz cutoff | | `SENSE_I_6P6_TEMP_FILT_10HZ` | `0x05` | 10 Hz cutoff | | `SENSE_I_6P6_TEMP_FILT_5HZ` | `0x06` | 5 Hz cutoff | #### `sense_i_6p6_fifo_mode_t` FIFO mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_FIFO_BYPASS` | `0x00` | FIFO disabled | | `SENSE_I_6P6_FIFO_STREAM` | `0x01` | Stream mode (oldest data replaced) | | `SENSE_I_6P6_FIFO_STOP_ON_FULL` | `0x02` | Stop on full | #### `sense_i_6p6_dmp_odr_t` APEX DMP output data rate. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_DMP_ODR_25HZ` | `0x00` | 25 Hz | | `SENSE_I_6P6_DMP_ODR_50HZ` | `0x02` | 50 Hz | #### `sense_i_6p6_wom_mode_t` WOM (Wake-on-Motion) compare mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_WOM_INITIAL` | `0x00` | Compare against first sample | | `SENSE_I_6P6_WOM_PREVIOUS` | `0x01` | Compare against previous sample | #### `sense_i_6p6_smd_mode_t` SMD (Significant Motion Detection) mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_SMD_DISABLED` | `0x00` | Disabled | | `SENSE_I_6P6_SMD_SHORT` | `0x02` | 1 second WOM window | | `SENSE_I_6P6_SMD_LONG` | `0x03` | 3 second WOM window | #### `sense_i_6p6_int_config_t` Interrupt pin configuration. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_INT_ACTIVE_LOW` | `0x00` | Active low | | `SENSE_I_6P6_INT_ACTIVE_HIGH` | `0x01` | Active high | | `SENSE_I_6P6_INT_OPEN_DRAIN` | `0x00` | Open drain | | `SENSE_I_6P6_INT_PUSH_PULL` | `0x02` | Push-pull | | `SENSE_I_6P6_INT_PULSED` | `0x00` | Pulsed | | `SENSE_I_6P6_INT_LATCHED` | `0x04` | Latched | #### `sense_i_6p6_activity_t` APEX activity classification. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_ACTIVITY_UNKNOWN` | `0x00` | Unknown | | `SENSE_I_6P6_ACTIVITY_WALK` | `0x01` | Walking | | `SENSE_I_6P6_ACTIVITY_RUN` | `0x02` | Running | #### `sense_i_6p6_int_pulse_t` Pulse-width selector for INT pin pulse mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_INT_PULSE_100US` | `0` | 100 µs pulse (default) | | `SENSE_I_6P6_INT_PULSE_8US` | `1` | 8 µs pulse — for fast hosts | #### `sense_i_6p6_subsystem_t` Subsystem selector for scoped resets. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_6P6_RESET_APEX` | `0` | APEX (DMP) memory + state | | `SENSE_I_6P6_RESET_TEMP` | `1` | Temperature signal path | ### Structs #### `sense_i_6p6_fifo_packet_t` FIFO packet structure (16 bytes, accel + gyro + temp + timestamp). | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `header` | Packet header byte (use SENSE_I_6P6_FIFO_HEADER_* masks). | | `int8_t` | `temp` | 8-bit temperature: degC = temp/2.07 + 25 | | `uint16_t` | `timestamp` | 16-bit timestamp | #### `sense_i_6p6_tap_result_t` Tap detection result. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `count` | 0=none, 1=single, 2=double | | `uint8_t` | `axis` | 0=X, 1=Y, 2=Z | | `uint8_t` | `direction` | 0=positive, 1=negative | | `uint8_t` | `timing` | Double-tap timing: seconds = timing * 16 / ODR | #### `sense_i_6p6_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `int1_pin` | Core pad number for INT1. 0 = polled. | | `uint8_t` | `int2_pin` | Core pad number for INT2. 0 = unused. | | `sense_i_6p6_event_cb_t` | `on_event` | Callback. NULL = no callback. | | `uint8_t` | `accel_range` | sense_i_6p6_accel_range_t. 0 = default. | | `uint8_t` | `gyro_range` | sense_i_6p6_gyro_range_t. 0 = default. | | `uint8_t` | `odr` | sense_i_6p6_odr_t. 0 = default (100Hz). | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_I_6P6_VERSION_MAJOR` | `1` | | | `TILE_SENSE_I_6P6_VERSION_MINOR` | `2` | | | `TILE_SENSE_I_6P6_VERSION_PATCH` | `0` | | | `ICM42686P_I2C_ADDR_DEFAULT` | `0x69` | | | `ICM42686P_I2C_ADDR_ALT` | `0x68` | | | `ICM42686P_B1_SENSOR_CONFIG0` | `0x03` | | | `ICM42686P_B1_GYRO_CFG_STATIC2` | `0x0B` | | | `ICM42686P_B1_GYRO_CFG_STATIC3` | `0x0C` | | | `ICM42686P_B1_GYRO_CFG_STATIC4` | `0x0D` | | | `ICM42686P_B1_GYRO_CFG_STATIC5` | `0x0E` | | | `ICM42686P_B1_GYRO_NF_COSWZ_X` | `0x0F` | | | `ICM42686P_B1_GYRO_NF_COSWZ_Y` | `0x10` | | | `ICM42686P_B1_GYRO_NF_COSWZ_Z` | `0x11` | | | `ICM42686P_B1_GYRO_CFG_STATIC9` | `0x12` | | | `ICM42686P_B1_GYRO_CFG_STATIC10` | `0x13` | | | `ICM42686P_B1_XG_ST_DATA` | `0x5F` | | | `ICM42686P_B1_YG_ST_DATA` | `0x60` | | | `ICM42686P_B1_ZG_ST_DATA` | `0x61` | | | `ICM42686P_B1_TMSTVAL0` | `0x62` | | | `ICM42686P_B1_TMSTVAL1` | `0x63` | | | `ICM42686P_B1_TMSTVAL2` | `0x64` | | | `ICM42686P_B1_INTF_CONFIG4` | `0x7A` | | | `ICM42686P_B1_INTF_CONFIG5` | `0x7B` | | | `ICM42686P_B2_ACCEL_CFG_STATIC2` | `0x03` | | | `ICM42686P_B2_ACCEL_CFG_STATIC3` | `0x04` | | | `ICM42686P_B2_ACCEL_CFG_STATIC4` | `0x05` | | | `ICM42686P_B2_XA_ST_DATA` | `0x3B` | | | `ICM42686P_B2_YA_ST_DATA` | `0x3C` | | | `ICM42686P_B2_ZA_ST_DATA` | `0x3D` | | | `ICM42686P_B4_APEX_CONFIG1` | `0x40` | | | `ICM42686P_B4_APEX_CONFIG2` | `0x41` | | | `ICM42686P_B4_APEX_CONFIG3` | `0x42` | | | `ICM42686P_B4_APEX_CONFIG4` | `0x43` | | | `ICM42686P_B4_APEX_CONFIG7` | `0x46` | | | `ICM42686P_B4_APEX_CONFIG8` | `0x47` | | | `ICM42686P_B4_APEX_CONFIG9` | `0x48` | | | `ICM42686P_B4_WOM_X_THR` | `0x4A` | | | `ICM42686P_B4_WOM_Y_THR` | `0x4B` | | | `ICM42686P_B4_WOM_Z_THR` | `0x4C` | | | `ICM42686P_B4_INT_SOURCE6` | `0x4D` | | | `ICM42686P_B4_INT_SOURCE7` | `0x4E` | | | `ICM42686P_B4_INT_SOURCE8` | `0x4F` | | | `ICM42686P_B4_INT_SOURCE9` | `0x50` | | | `ICM42686P_B4_INT_SOURCE10` | `0x51` | | | `ICM42686P_B4_OFFSET_USER0` | `0x77` | | | `ICM42686P_B4_OFFSET_USER1` | `0x78` | | | `ICM42686P_B4_OFFSET_USER2` | `0x79` | | | `ICM42686P_B4_OFFSET_USER3` | `0x7A` | | | `ICM42686P_B4_OFFSET_USER4` | `0x7B` | | | `ICM42686P_B4_OFFSET_USER5` | `0x7C` | | | `ICM42686P_B4_OFFSET_USER6` | `0x7D` | | | `ICM42686P_B4_OFFSET_USER7` | `0x7E` | | | `ICM42686P_B4_OFFSET_USER8` | `0x7F` | | | `ICM42686P_WHOAMI_DEFAULT` | `0x44` | | --- Generated 2026-08-12 from the driver header at tiles@9b8f19e. Canonical HTML: https://bergsonne.io/docs/tiles/sense/i6p6 ============================================================================== ## Sense.I.9 driver > 9-DOF IMU driver for the Sense.I.9 tile (rev c). | | | | --- | --- | | Tile | `Sense.I.9` | | Driver version | `3.1.1` | | Header | `tile_sense_i_9.h` | | Generated from | `tiles@fd223eb` | | Canonical page | https://bergsonne.io/docs/tiles/sense/i9 | ### Overview @version 3.1.1 Embeds the TDK InvenSense ICM-20948: 6-DOF IMU (accel + gyro) with co-packaged AK09916 3-DOF magnetometer (accessed via the ICM's I2C bypass). Sensor specifications: - Accelerometer: 16-bit, ±2/4/8/16 G, up to 4.5 kHz ODR - Gyroscope: 16-bit, ±250/500/1000/2000 DPS, up to 9 kHz ODR - Magnetometer: 16-bit, ±4900 µT, up to 100 Hz ODR (0.15 µT/LSB) - Temperature: on-chip sensor - FIFO: 512-byte buffer, accel + gyro + temp packets - Wake-on-Motion: 4 mg/LSB threshold, INT routable Datasheet: https://www.bergsonne.io/tiles/sense/i9 ### Examples #### Quick start ```c #include "core_tiles.h" tile_t imu; tile_sense_i_9_init(core_tiles_pal(&core_i2c1), 0, &imu, NULL); if (tile_is_ready(&imu)) { int16_t accel[3], gyro[3], mag[3]; tile_sense_i_9_get_raw_accels(&imu, accel); tile_sense_i_9_get_raw_gyros(&imu, gyro); tile_sense_i_9_get_raw_mags(&imu, mag); } ``` #### Two tiles on one bus ```c tile_t imu_a, imu_b; tiles_pal_t *hal = core_tiles_pal(&core_i2c1); tile_sense_i_9_init(hal, 0, &imu_a, NULL); // pad 2 floating (0x69) tile_sense_i_9_init(hal, 1, &imu_b, NULL); // pad 2 grounded (0x68) ``` ### API 47 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_i_9_sleep` — Studio ```c void tile_sense_i_9_sleep(tile_t* tile) ``` Enter low-power sleep mode. Stops all sensor sampling. Current draw drops to ~8 µA. Call tile_sense_i_9_wake() to resume. - `tile` — Pointer to tile handle ##### `tile_sense_i_9_wake` — Studio ```c void tile_sense_i_9_wake(tile_t* tile) ``` Wake from sleep mode and resume sampling. Restores auto clock selection. Previously configured ranges and ODRs are preserved across sleep/wake cycles. - `tile` — Pointer to tile handle ##### `tile_sense_i_9_reset` — Studio ```c void tile_sense_i_9_reset(tile_t* tile) ``` Perform a software reset. Resets all registers to defaults. Blocks for ~50 ms. You must call tile_sense_i_9_init() again after reset. - `tile` — Pointer to tile handle #### Runtime ##### `tile_sense_i_9_data_ready` — Studio ```c uint8_t tile_sense_i_9_data_ready(tile_t* tile) ``` Check if new IMU data is available. Reads the ICM-20948 interrupt status register. - `tile` — Pointer to tile handle **Returns:** 1 if new data is available, 0 otherwise ##### `tile_sense_i_9_set_accel_range` — Studio ```c void tile_sense_i_9_set_accel_range(tile_t* tile, sense_i_9_accel_range_t range) ``` Set the accelerometer full-scale range. - `tile` — Pointer to tile handle - `range` — One of the sense_i_9_accel_range_t values ##### `tile_sense_i_9_set_gyro_range` — Studio ```c void tile_sense_i_9_set_gyro_range(tile_t* tile, sense_i_9_gyro_range_t range) ``` Set the gyroscope full-scale range. - `tile` — Pointer to tile handle - `range` — One of the sense_i_9_gyro_range_t values ##### `tile_sense_i_9_set_mag_mode` — Studio ```c void tile_sense_i_9_set_mag_mode(tile_t* tile, sense_i_9_mag_mode_t mode) ``` Set the magnetometer operating mode. - `tile` — Pointer to tile handle - `mode` — One of the sense_i_9_mag_mode_t values **Note:** Switching modes resets the AK09916 measurement cycle. ##### `tile_sense_i_9_set_accel_odr` — Studio ```c void tile_sense_i_9_set_accel_odr(tile_t* tile, uint16_t divider) ``` Set the accelerometer output data rate. ODR = 1125 / (1 + divider) Hz. Examples: divider = 0 → 1125 Hz divider = 4 → 225 Hz divider = 10 → ~102 Hz divider = 44 → 25 Hz - `tile` — Pointer to tile handle - `divider` — 11-bit sample rate divider (0–4095) ##### `tile_sense_i_9_set_gyro_odr` — Studio ```c void tile_sense_i_9_set_gyro_odr(tile_t* tile, uint8_t divider) ``` Set the gyroscope output data rate. ODR = 1100 / (1 + divider) Hz. - `tile` — Pointer to tile handle - `divider` — 8-bit sample rate divider (0–255) ##### `tile_sense_i_9_get_raw_accels` — Studio ```c void tile_sense_i_9_get_raw_accels(tile_t* tile, int16_t* buffer) ``` Read raw accelerometer data (3-axis). Returns signed 16-bit ADC counts. Convert to milli-g using the sensitivity for the configured range (e.g. ±2 G → 1 LSB ≈ 0.061 mg). - `tile` — Pointer to tile handle - `buffer` — Output array, minimum 3 × int16_t [X, Y, Z] ##### `tile_sense_i_9_get_raw_gyros` — Studio ```c void tile_sense_i_9_get_raw_gyros(tile_t* tile, int16_t* buffer) ``` Read raw gyroscope data (3-axis). Returns signed 16-bit ADC counts. Convert to °/s using the sensitivity for the configured range (e.g. ±250 DPS → 131 LSB/°/s). - `tile` — Pointer to tile handle - `buffer` — Output array, minimum 3 × int16_t [X, Y, Z] ##### `tile_sense_i_9_get_raw_6dof` — Studio ```c void tile_sense_i_9_get_raw_6dof(tile_t* tile, int16_t* buffer) ``` Read raw accelerometer + gyroscope data in a single burst. More efficient than calling get_raw_accels + get_raw_gyros separately (one I2C transaction instead of two). Data is time-coherent. - `tile` — Pointer to tile handle - `buffer` — Output array, minimum 6 × int16_t [AX, AY, AZ, GX, GY, GZ] ##### `tile_sense_i_9_get_raw_mags` — Studio ```c void tile_sense_i_9_get_raw_mags(tile_t* tile, int16_t* buffer) ``` Read raw magnetometer data (3-axis). Returns signed 16-bit ADC counts from the AK09916. Sensitivity: 0.15 µT/LSB on all axes (factory-trimmed; the AK09916 has no per-axis ASA / FUSE ROM correction — see datasheet §11). - `tile` — Pointer to tile handle - `buffer` — Output array, minimum 3 × int16_t [X, Y, Z] **Note:** Automatically reads the ST2 register to release the magnetometer data lock, enabling the next measurement. ##### `tile_sense_i_9_mag_overflowed` — Studio ```c uint8_t tile_sense_i_9_mag_overflowed(tile_t* tile) ``` Check whether the most recent magnetometer reading overflowed. The AK09916 raises HOFL in ST2 when the sum |HX|+|HY|+|HZ| exceeds 4912 µT (the chip's measurement range). Readings during overflow appear valid in the data registers but are not — for compass-grade work, discard samples where this returns 1. Reading this releases the AK09916 data-lock the same way tile_sense_i_9_get_raw_mags() does, so it can be used standalone after a non-locking peek. - `tile` — Pointer to tile handle **Returns:** 1 if HOFL was set after the most recent measurement, 0 otherwise ##### `tile_sense_i_9_get_temperature` — Studio ```c int16_t tile_sense_i_9_get_temperature(tile_t* tile) ``` Read the on-chip temperature sensor. Convert raw value to °C: temp_degC = (raw / 333.87) + 21.0 - `tile` — Pointer to tile handle **Returns:** Raw signed 16-bit temperature value ##### `tile_sense_i_9_is_face_up` — Studio ```c uint8_t tile_sense_i_9_is_face_up(tile_t* tile) ``` Check whether the tile is lying flat, face up. Reads the accelerometer and returns 1 when the Z-axis is close to +1 g (i.e. gravity is pulling down through the back of the tile). The acceptance band is roughly ±30° of true face-up; the X/Y axes must each read below ~0.5 g for the test to pass, so tilted-but-mostly-up orientations also count as face-up. thresholds remain proportional (band is in fractions of full-scale) so behaviour is unaffected. - `tile` — Initialized tile handle **Returns:** 1 if face-up, 0 otherwise **Note:** Assumes the accel range is the default ±2 g configured by ##### `tile_sense_i_9_is_face_down` — Studio ```c uint8_t tile_sense_i_9_is_face_down(tile_t* tile) ``` Check whether the tile is lying flat, face down. Mirror of @ref tile_sense_i_9_is_face_up — Z-axis close to −1 g. - `tile` — Initialized tile handle **Returns:** 1 if face-down, 0 otherwise ##### `tile_sense_i_9_is_moving` — Studio ```c uint8_t tile_sense_i_9_is_moving(tile_t* tile, uint16_t threshold_mg) ``` Check whether the tile is currently moving. Reads the accelerometer and reports whether |‖a‖ − 1 g| exceeds `threshold_mg`. At rest in any orientation the magnitude is ≈ 1 g, so this captures linear acceleration regardless of which face is up. Use ~50–100 mg for "is being handled" and ~300–500 mg for "is being shaken". Implementation uses an integer approximation of the magnitude comparison: it checks the squared deviation against the squared threshold to avoid a square root. - `tile` — Initialized tile handle - `threshold_mg` — Deviation from 1 g, in milli-g **Returns:** 1 if moving, 0 if at rest **Note:** Assumes ±2 g range (init default). Other ranges still work but the noise floor scales with full-scale. ##### `tile_sense_i_9_read_tilt_centi_degrees` — Studio ```c void tile_sense_i_9_read_tilt_centi_degrees(tile_t* tile, uint8_t axis, int16_t* out_centi_deg) ``` Read the tilt of one accel axis vs gravity, in centi-degrees. Returns the angle (in 0.01° units) between the requested axis and the gravity vector. Range is −18000..+18000 centi-degrees (−180.00°..+180.00°). Computed from `atan2(other_components, axis)` using an integer approximation; absolute accuracy is roughly ±1° in the noise-free case. Axis selector: 0 = X axis (pitch around Y, with the chip lying on its back) 1 = Y axis (roll around X) 2 = Z axis (deviation from horizontal — 0 = face-up, 180 = face-down) - `tile` — Initialized tile handle - `axis` — 0/1/2 selecting X, Y, or Z - `out_centi_deg` — Output tilt in 0.01° units (signed) ##### `tile_sense_i_9_read_heading_centi_degrees` — Studio ```c void tile_sense_i_9_read_heading_centi_degrees(tile_t* tile, uint16_t* out_centi_deg) ``` Read a compass heading from the magnetometer, in centi-degrees. Returns the bearing of the +X axis relative to magnetic north in 0.01° units, range 0..35999 (0.00°..359.99°). 0° means +X is pointing along magnetic north; angle increases clockwise viewed from above. Computed as `atan2(−mag_y, mag_x)` from the AK09916 raw counts (no per-axis trim is needed — the AK09916 has no FUSE ROM and is factory-trimmed at 0.15 µT/LSB). accurate when the tile is held roughly level (within a few degrees of horizontal). When tilted, the heading drifts because the horizontal projection of the magnetic field changes with orientation. A tilt-compensated heading needs both accel data (to recover the gravity-aligned frame) and a 3D rotation — that is left to a later tier-3 helper or the Studio DSL. skew the result. For best accuracy, calibrate the sensor in its final enclosure before relying on the heading. - `tile` — Initialized tile handle - `out_centi_deg` — Output heading in 0.01° units (0..35999) ##### `tile_sense_i_9_wait_for_motion` — Studio ```c uint8_t tile_sense_i_9_wait_for_motion(tile_t* tile, uint32_t timeout_ms) ``` Block until a Wake-on-Motion event fires, or timeout. Polls @ref tile_sense_i_9_get_int_status every ~5 ms and returns 1 as soon as the WoM bit is observed. Returns 0 if `timeout_ms` elapses without an event. The poll cadence is chosen as a compromise: low enough to keep wake latency under ~5 ms but slow enough that the I2C bus isn't saturated by status reads. - `tile` — Initialized tile handle - `timeout_ms` — Maximum time to wait, in milliseconds **Returns:** 1 if motion was detected, 0 on timeout **Note:** Wake-on-Motion must be configured (@ref tile_sense_i_9_wom_config) and enabled (@ref tile_sense_i_9_wom_enable) before this call. INT-pin routing (@ref tile_sense_i_9_int_wom) is optional — this function reads the status register directly. **Note:** This call blocks. For non-blocking use, drive @ref tile_sense_i_9_get_int_status from your own loop. #### FIFO ##### `tile_sense_i_9_fifo_config` — Studio ```c void tile_sense_i_9_fifo_config(tile_t* tile, sense_i_9_fifo_mode_t mode, uint8_t accel, uint8_t gyro, uint8_t temp) ``` Configure which sensor streams write into the FIFO. Enables FIFO operation in USER_CTRL and selects the data sources. Always sets FIFO_MODE = stream when enabling. Disables FIFO entirely if accel, gyro and temp are all 0. - `tile` — Initialized tile handle - `mode` — FIFO operating mode (stream or snapshot) - `accel` — 1 to write all 3 accel axes - `gyro` — 1 to write all 3 gyro axes - `temp` — 1 to write the temperature sample ##### `tile_sense_i_9_fifo_flush` — Studio ```c void tile_sense_i_9_fifo_flush(tile_t* tile) ``` Reset the FIFO contents (clears all queued samples). - `tile` — Initialized tile handle ##### `tile_sense_i_9_fifo_count` — Studio ```c uint16_t tile_sense_i_9_fifo_count(tile_t* tile) ``` Read the current FIFO byte count. Note this is bytes, not packets. A standard accel+gyro packet is 12 bytes. Read FIFO_COUNTL first to latch both bytes (handled internally). - `tile` — Initialized tile handle **Returns:** Bytes available in the FIFO (0–512) ##### `tile_sense_i_9_fifo_read_packet_flat` — Studio ```c void tile_sense_i_9_fifo_read_packet_flat(tile_t* tile, int32_t* out) ``` DSL-friendly flat-output variant of fifo_read_packet(). Drops the struct in favor of a positional int[6] array — the DSL doesn't have a struct ABI yet, so the per-field outputs come back indexed. Layout: out[0..2] = accel x,y,z (raw int16 widened to int32) out[3..5] = gyro x,y,z (raw int16 widened to int32) Drops the success bool — call fifo_count() first to know whether there's data. On an empty FIFO, the slots are left untouched. - `tile` — Initialized tile handle. - `out` — Output buffer (6 int32_t slots). #### Interrupts ##### `tile_sense_i_9_int_config` — Studio ```c void tile_sense_i_9_int_config(tile_t* tile, uint8_t flags) ``` Configure the INT pin electrical behaviour. Sets polarity (active low/high), drive (push-pull/open-drain), mode (pulsed/latched), and the auto-clear-on-any-read bit. OR together flags from sense_i_9_int_flags_t. - `tile` — Initialized tile handle - `flags` — OR of SENSE_I_9_INT_* flags **Note:** INT_PIN_CFG bit 1 (BYPASS_EN) is preserved by this call — the driver needs bypass mode to talk to the AK09916. ##### `tile_sense_i_9_int_data_ready` — Studio ```c void tile_sense_i_9_int_data_ready(tile_t* tile, uint8_t enabled) ``` Route the data-ready interrupt to the INT pin. - `tile` — Initialized tile handle - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_9_int_wom` — Studio ```c void tile_sense_i_9_int_wom(tile_t* tile, uint8_t enabled) ``` Route the wake-on-motion interrupt to the INT pin. - `tile` — Initialized tile handle - `enabled` — 1 to enable, 0 to disable ##### `tile_sense_i_9_int_fifo_overflow` — Studio ```c void tile_sense_i_9_int_fifo_overflow(tile_t* tile, uint8_t enabled) ``` Route the FIFO overflow interrupt to the INT pin. - `tile` — Initialized tile handle - `enabled` — 1 to enable for any sensor, 0 to disable all ##### `tile_sense_i_9_int_fifo_watermark` — Studio ```c void tile_sense_i_9_int_fifo_watermark(tile_t* tile, uint8_t enabled) ``` Route the FIFO watermark interrupt to the INT pin. - `tile` — Initialized tile handle - `enabled` — 1 to enable for any sensor, 0 to disable all ##### `tile_sense_i_9_get_int_status` — Studio ```c uint8_t tile_sense_i_9_get_int_status(tile_t* tile) ``` Read INT_STATUS (WoM / DMP / I2C-master / PLL-ready) and clear it. **Returns:** 8-bit raw register value (use ICM20948_INT_WOM etc. masks) ##### `tile_sense_i_9_get_int_status_fifo_overflow` — Studio ```c uint8_t tile_sense_i_9_get_int_status_fifo_overflow(tile_t* tile) ``` Read INT_STATUS_2 (FIFO overflow per sensor) and clear it. **Returns:** 5-bit FIFO_OVERFLOW_INT[4:0]; non-zero means overflow occurred ##### `tile_sense_i_9_get_int_status_fifo_watermark` — Studio ```c uint8_t tile_sense_i_9_get_int_status_fifo_watermark(tile_t* tile) ``` Read INT_STATUS_3 (FIFO watermark per sensor) and clear it. **Returns:** 5-bit FIFO_WM_INT[4:0]; non-zero means watermark crossed #### Advanced ##### `tile_sense_i_9_self_test` — Studio ```c uint8_t tile_sense_i_9_self_test(tile_t* tile, uint8_t* accel_pass, uint8_t* gyro_pass) ``` Run the built-in mechanical self-test for accel and gyro. Drives the accel and gyro through their factory-stored self-excitation routine and compares the response to the stored reference values in SELF_TEST_*_GYRO/ACCEL (Bank 1). Per TDK app-note AN-000150 the pass criterion is that each axis's self-test response is within 50%–150% of the factory reference. Blocks for ~250 ms. The chip is left in a fresh-init state on exit; ranges/ODRs you set before this call may need to be reapplied. - `tile` — Initialized tile handle - `accel_pass` — Output: bit 0/1/2 = X/Y/Z accel pass (1 = pass) - `gyro_pass` — Output: bit 0/1/2 = X/Y/Z gyro pass (1 = pass) **Returns:** 1 if every accel axis and every gyro axis passed, 0 otherwise ##### `tile_sense_i_9_mag_self_test` — Studio ```c uint8_t tile_sense_i_9_mag_self_test(tile_t* tile) ``` Run the AK09916 self-test (mag). Triggers the AK09916's internal magnetic-source self-excitation. The pass criterion is the per-axis range table from the AK09916 datasheet rev 015007392-E-02 §9.4.4.2: −200 ≤ HX ≤ 200, −200 ≤ HY ≤ 200, −1000 ≤ HZ ≤ −200 Blocks for ~10 ms. Leaves the AK09916 in power-down — call tile_sense_i_9_set_mag_mode() afterwards to resume measurements. - `tile` — Initialized tile handle **Returns:** 1 if all three axes were within spec, 0 otherwise ##### `tile_sense_i_9_dmp_start_quat9` — Studio ```c uint8_t tile_sense_i_9_dmp_start_quat9(tile_t* tile, uint16_t output_period_ms) ``` Configure the DMP for 9-axis quaternion output and start it. Wires the chip up to compute a fused (accel+gyro+mag) orientation quaternion at the requested period and stream it to the FIFO. Equivalent to InvenSense's "ROTATION_VECTOR" Android sensor. Requires dmp_load() to have succeeded since the last reset. Side effects: - Switches AK09916 access from bypass to I2C-master mode (see the mag-access-mode rule comment block above this function). - Reconfigures accel + gyro to 56.25 Hz ODR (DMP-recommended) and ±4 g / ±2000 dps full-scale (also DMP-recommended). - Resets and enables the FIFO; old packets are discarded. Internally maps to a divider against the DMP's ~225 Hz base ODR. - `tile` — Initialized tile handle (DMP firmware loaded). - `output_period_ms` — DMP output period, in ms (clamped to 10–1000 ms). **Returns:** 1 on success, 0 if firmware isn't loaded or a bus error occurred. ##### `tile_sense_i_9_dmp_stop` — Studio ```c void tile_sense_i_9_dmp_stop(tile_t* tile) ``` Stop the DMP and revert to direct-bus mag access. Clears DMP_EN, disables the I2C master, drops the data-output feature masks, and re-enables BYPASS_EN. After this call the four mag helpers (get_raw_mags / set_mag_mode / mag_overflowed / mag_self_test) work again — but you'll need to re-issue set_mag_mode() to put the AK09916 back into a continuous mode (DMP leaves it in single-measurement). Idempotent — safe to call when the DMP is already stopped. - `tile` — Initialized tile handle. ##### `tile_sense_i_9_dmp_data_ready` — Studio ```c uint8_t tile_sense_i_9_dmp_data_ready(tile_t* tile) ``` Non-blocking check for a queued DMP quat9 packet. Reads the FIFO byte-count register and returns 1 when at least one full Quat9 packet (header + payload + accuracy = 16 bytes) is available. Cheap (a single 2-byte register read). - `tile` — Initialized tile handle. **Returns:** 1 if a packet can be read, 0 otherwise. ##### `tile_sense_i_9_dmp_read_quat9` — Studio ```c uint8_t tile_sense_i_9_dmp_read_quat9(tile_t* tile, int32_t out_q[4], uint16_t* out_accuracy) ``` Read one 9-axis quaternion packet from the DMP FIFO. Pulls a header + 14-byte Quat9 payload from the FIFO and decodes: out_q[0] = q0 (scalar / w) — recovered as sqrt(1 − q1² − q2² − q3²) out_q[1] = q1 (vector / x) out_q[2] = q2 (vector / y) out_q[3] = q3 (vector / z) Components are Q30 fixed-point — divide by 2^30 (1073741824) to get a unit-quaternion float. The accuracy field is the DMP's heading accuracy estimate (chip-internal scale; treat smaller = better). On a header mismatch (FIFO content isn't a Quat9 packet) the FIFO is reset and the function returns 0 — this can happen briefly after dmp_start_quat9() while the pipeline drains its first sample. - `tile` — Initialized tile handle. - `out_q` — Output array, 4 × int32_t [q0, q1, q2, q3] in Q30. - `out_accuracy` — Output: DMP heading-accuracy estimate. **Returns:** 1 if a packet was decoded, 0 if FIFO didn't have one ready. #### Other ##### `tile_sense_i_9_find` ```c uint8_t tile_sense_i_9_find(tiles_pal_t* hal, uint8_t instance) ``` Check whether a Sense.I.9 is present on the I2C bus. Performs an address-level probe only (no register reads). - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) **Returns:** 1 if device ACKs, 0 otherwise ##### `tile_sense_i_9_init` ```c void tile_sense_i_9_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const sense_i_9_cfg_t *cfg) ``` Initialize the ICM-20948 and AK09916. Performs a soft reset, verifies WHO_AM_I, wakes the device, enables all accel + gyro axes, enables I2C bypass for direct magnetometer access, and starts the magnetometer in continuous 100 Hz mode. Pass cfg=NULL for defaults. - `hal` — Platform HAL handle - `instance` — Instance index (0 = default, see mapping table) - `tile` — Pointer to tile handle (populated by this function) - `cfg` — Optional config, or NULL for defaults **Note:** Blocks for ~50 ms during reset. Call once at startup. ##### `tile_sense_i_9_wom_config` — Studio ```c void tile_sense_i_9_wom_config(tile_t* tile, uint16_t thr_mg, sense_i_9_wom_mode_t mode) ``` Configure Wake-on-Motion threshold and compare mode. The chip-wide threshold is one 8-bit value at 4 mg/LSB (range 0–1020 mg). Compared independently against |X|, |Y|, |Z|; any axis crossing the threshold raises WOM_INT. - `tile` — Initialized tile handle - `thr_mg` — Threshold in mg (clamped to 1020 mg / 0xFF LSB) - `mode` — Compare against initial sample or previous sample **Note:** Requires accel running. Mag and gyro can be off. ##### `tile_sense_i_9_wom_enable` — Studio ```c void tile_sense_i_9_wom_enable(tile_t* tile) ``` Enable Wake-on-Motion logic. Call after wom_config(). Routes to the INT pin only if int_wom() was also enabled. - `tile` — Initialized tile handle ##### `tile_sense_i_9_wom_disable` — Studio ```c void tile_sense_i_9_wom_disable(tile_t* tile) ``` Disable Wake-on-Motion logic. - `tile` — Initialized tile handle ##### `tile_sense_i_9_fifo_read_packet` ```c uint8_t tile_sense_i_9_fifo_read_packet(tile_t* tile, sense_i_9_fifo_packet_t* pkt) ``` Read one accel + gyro packet from the FIFO. - `tile` — Initialized tile handle - `pkt` — Output packet (populated only if return is 1) **Returns:** 1 if a 12-byte packet was read, 0 if FIFO has fewer bytes ##### `tile_sense_i_9_dmp_load` ```c uint8_t tile_sense_i_9_dmp_load(tile_t* tile) ``` Load the DMP3 firmware into the chip's DMP RAM and verify. Performs the full eMD load sequence: chunked write of the 14301-byte blob to DMP RAM (handling the 0x100-byte bank-page crossings), then a chunked read-back compare. Idempotent — repeated calls re-verify but don't reload. Blocks for ~250 ms on a 400 kHz I²C bus (chunk size = 16 bytes, one bank-select write per crossing). The DMP is left in the loaded-but-disabled state. Enabling output (DMP_EN in USER_CTRL + feature configuration) is a separate step, shipped in a follow-up phase. - `tile` — Initialized tile handle. **Returns:** 1 on successful load + verify, 0 on bus error or verify mismatch. ##### `tile_sense_i_9_dmp_is_loaded` ```c uint8_t tile_sense_i_9_dmp_is_loaded(tile_t* tile) ``` Check whether DMP firmware has been loaded since the last reset. Tracks driver-side state set by dmp_load(). A chip reset clears the DMP RAM but doesn't auto-clear this flag — call after reset() to know whether you need to reload. - `tile` — Initialized tile handle. **Returns:** 1 if dmp_load() has succeeded since the driver last cleared its state. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### DMP3 — extended outputs (common) Phase 1 (firmware load + verify) and Phase 2 (9-axis quaternion / ROTATION_VECTOR) are now exposed via dmp_load / dmp_start_quat9 / dmp_read_quat9. Still unimplemented: 6-axis quaternion (no-mag), geomagnetic rotation vector, pedometer + step detector, BAC activity classification, tap / double-tap, significant-motion detection, pickup, tilt, fsync timestamping, and the multi-sensor accumulator that lets several DMP outputs share one stream. Phase 4 plans dmp_read_orientation (yaw/pitch/roll) on top of Quat9. #### AK09916 FUSE ROM sensitivity adjustment (niche) Not applicable to this chip. Earlier AKM parts (e.g. AK8963) shipped per-axis ASA codes in FUSE ROM that required `raw × (ASA + 128) / 256` correction. The AK09916 in the ICM-20948 has no FUSE ROM and no ASA registers (datasheet rev 015007392-E-02 §11) — sensitivity is factory-trimmed to a fixed 0.15 µT/LSB across all axes. Driver correctly returns raw counts at that scale; no per-axis correction is needed or possible. Annotation kept to document the verification. #### Sensor hub for external aux sensors (advanced) Driver uses INT_PIN_CFG.BYPASS_EN by default so the AK09916 is on the host bus directly (simpler + faster than routing through the ICM's internal I2C master). dmp_start_quat9() now drives the master internally — claiming slots 0 + 1 — but slots 2 + 3 remain unused and there is no public API for attaching user aux sensors to them. Closing this gap means promoting the I2C-master configuration into a public surface and reserving DMP's slot use behind it. #### FSYNC external-clock / timestamping (advanced) Hardware-gated. Chip can take a 31–50 kHz FSYNC input and stamp samples against external timing. The Sense.I.9-c tile does not expose the FSYNC pin on any pad (verified in tile JSON: pads 6, 7, and 8 carry no function). Closing this gap requires a tile hardware revision. #### Alternate bus modes (SPI / I3C) (advanced) Ecosystem-gated. Tile JSON straps support I²C (default) or SPI 4-wire on the same pads (AD0 = MISO, EN = CS), and the ICM-20948 is also I3C electrically compliant. The driver framework currently uses tiles_pal I²C calls only; closing requires extending the bus abstraction (see Sense.I.6P6 v1.0+ for the SPI pattern). Defer to a multi-bus driver framework pass. ### Events - `data_ready` - `fifo_watermark` - `fifo_overflow` - `wake_on_motion` ### Enums #### `sense_i_9_accel_range_t` Accelerometer full-scale range. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_9_ACCEL_2G` | `0x00` | +/- 2g | | `SENSE_I_9_ACCEL_4G` | `0x02` | bits [2:1] of ACCEL_CONFIG | | `SENSE_I_9_ACCEL_8G` | `0x04` | +/- 8g | | `SENSE_I_9_ACCEL_16G` | `0x06` | +/- 16g | #### `sense_i_9_gyro_range_t` Gyroscope full-scale range. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_9_GYRO_250DPS` | `0x00` | +/- 250 deg/s | | `SENSE_I_9_GYRO_500DPS` | `0x02` | bits [2:1] of GYRO_CONFIG | | `SENSE_I_9_GYRO_1000DPS` | `0x04` | +/- 1000 deg/s | | `SENSE_I_9_GYRO_2000DPS` | `0x06` | +/- 2000 deg/s | #### `sense_i_9_mag_mode_t` Magnetometer operating mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_9_MAG_POWER_DOWN` | `0x00` | Power-down | | `SENSE_I_9_MAG_SINGLE` | `0x01` | Single measurement | | `SENSE_I_9_MAG_CONTINUOUS_10HZ` | `0x02` | Continuous at 10 Hz | | `SENSE_I_9_MAG_CONTINUOUS_20HZ` | `0x04` | Continuous at 20 Hz | | `SENSE_I_9_MAG_CONTINUOUS_50HZ` | `0x06` | Continuous at 50 Hz | | `SENSE_I_9_MAG_CONTINUOUS_100HZ` | `0x08` | Continuous at 100 Hz | #### `sense_i_9_int_flags_t` INT pin configuration flags. OR together for `int_config`. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_9_INT_ACTIVE_HIGH` | `0x00` | INT pin active high (default) | | `SENSE_I_9_INT_ACTIVE_LOW` | `0x80` | INT pin active low | | `SENSE_I_9_INT_PUSH_PULL` | `0x00` | Push-pull driver | | `SENSE_I_9_INT_OPEN_DRAIN` | `0x40` | Open-drain driver | | `SENSE_I_9_INT_PULSED` | `0x00` | 50 µs pulse (default) | | `SENSE_I_9_INT_LATCHED` | `0x20` | Held until status read | | `SENSE_I_9_INT_ANYRD_CLEAR` | `0x10` | Clear status on any read | #### `sense_i_9_wom_mode_t` Wake-on-Motion compare mode (ACCEL_INTEL_MODE_INT). | Member | Value | Description | | --- | --- | --- | | `SENSE_I_9_WOM_VS_INITIAL` | `0x00` | Compare against first sample | | `SENSE_I_9_WOM_VS_PREVIOUS` | `0x01` | Compare against previous sample | #### `sense_i_9_fifo_mode_t` FIFO operating mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_I_9_FIFO_STREAM` | `0x00` | Overwrite oldest data when full | | `SENSE_I_9_FIFO_SNAPSHOT` | `0x1F` | Stop accepting writes when full | ### Structs #### `sense_i_9_fifo_packet_t` Standard FIFO packet (12 bytes: accel + gyro). | Type | Field | Description | | --- | --- | --- | #### `sense_i_9_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `reserved` | Placeholder — no options yet. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_I_9_VERSION_MAJOR` | `3` | | | `TILE_SENSE_I_9_VERSION_MINOR` | `1` | | | `TILE_SENSE_I_9_VERSION_PATCH` | `1` | | | `ICM20948_I2C_ADDR_DEFAULT` | `0x69` | | | `ICM20948_I2C_ADDR_ALT` | `0x68` | | | `AK09916_I2C_ADDR` | `0x0C` | AK09916 magnetometer address (fixed, accessed via I2C bypass). | | `ICM20948_B1_SELF_TEST_X_GYRO` | `0x02` | | | `ICM20948_B1_SELF_TEST_Y_GYRO` | `0x03` | | | `ICM20948_B1_SELF_TEST_Z_GYRO` | `0x04` | | | `ICM20948_B1_SELF_TEST_X_ACCEL` | `0x0E` | | | `ICM20948_B1_SELF_TEST_Y_ACCEL` | `0x0F` | | | `ICM20948_B1_SELF_TEST_Z_ACCEL` | `0x10` | | | `ICM20948_WHOAMI_DEFAULT` | `0xEA` | | | `AK09916_WHOAMI_DEFAULT` | `0x09` | | | `AK09916_MODE_SELF_TEST` | `0x10` | | --- Generated 2026-08-12 from the driver header at tiles@fd223eb. Canonical HTML: https://bergsonne.io/docs/tiles/sense/i9 ============================================================================== ## Sense.M.3G driver > Triaxial geomagnetic magnetometer driver for Sense.M.3G (BMM350). | | | | --- | --- | | Tile | `Sense.M.3G` | | Driver version | `0.9.0` | | Header | `tile_sense_m_3g.h` | | Generated from | `tiles@0f3be8b` | | Canonical page | https://bergsonne.io/docs/tiles/sense/m3g | ### Overview @version 0.9.0 Bosch Sensortec BMM350: 3-axis magnetometer, +/-2000 uT range, ODR from 1.5625 Hz to 400 Hz, on-chip magnetic reset, data-ready interrupt, and a 24-bit sensor-time counter. Output is integer nanotesla (nT) and milli-degrees Celsius, fully temperature- and trim-compensated. 1000 nT = 1 uT; Earth's field is roughly 25000-65000 nT depending on where you are. ### Where the compensation math comes from The BMM350 datasheet (BST-BMM350-DS001-27, rev 1.27) deliberately does NOT publish the compensation formulas or the OTP trim-word layout — its chapter 7 is a single link to Bosch's SensorAPI, and every accuracy figure in Table 3 is qualified "after API compensation". The data registers hold *uncompensated* counts. The OTP word map and the compensation chain implemented here are therefore ported from Bosch Sensortec's BMM350_SensorAPI: https://github.com/boschsensortec/BMM350_SensorAPI Copyright (c) 2025 Bosch Sensortec GmbH, BSD-3-Clause Ported, not copied verbatim: the reference implementation is floating-point, and this driver is integer-only per house style, so the chain runs in Q16.16 fixed point on int64 intermediates. Every ported constant is marked `[API]` in the implementation, and everything marked `[DS]` is traceable to the datasheet instead. If you are changing the compensation, check it against the upstream API, not the datasheet — the datasheet cannot tell you whether it is right. ### Power modes Suspend (the boot state, lowest power, settings retained), normal (free-running at the configured ODR), and forced (one conversion on request, then back to suspend). Forced mode is reachable only FROM suspend — a normal-to-forced transition is ignored by the device, so set_mode() routes through suspend for you. ### The dummy-byte rule Every BMM350 I2C read returns TWO dummy bytes before the real data (datasheet §9.2.3), so an n-byte read must fetch n+2 and discard the front. Verified on hardware. Miss it and every register is silently shifted by two — plausible-looking wrong values rather than an obvious failure. All bus access in this driver goes through helpers that handle it; if you add a raw read, do the same. Datasheet: Bosch Sensortec BMM350, BST-BMM350-DS001-27 ### Examples #### Polled example (Cores SDK) ```c tile_t mag; tile_sense_m_3g_init(core_tiles_pal(&core_i2c1), 0, &mag, NULL); tile_sense_m_3g_set_mode(&mag, SENSE_M_3G_MODE_NORMAL); while (1) { if (tile_sense_m_3g_data_ready(&mag)) { tile_sense_m_3g_read(&mag); printf("%ld %ld %ld nT\n", tile_sense_m_3g_get_x_nt(&mag), tile_sense_m_3g_get_y_nt(&mag), tile_sense_m_3g_get_z_nt(&mag)); } core_delay_ms(10); } ``` #### Interrupt-driven example (INT wired to a Core pad) ```c sense_m_3g_cfg_t cfg = { .odr = SENSE_M_3G_ODR_50HZ, .averaging = SENSE_M_3G_AVG_4, .int_pin = 9, // Core pad carrying INT .on_data = on_mag_sample, }; tile_sense_m_3g_init(core_tiles_pal(&core_i2c1), 0, &mag, &cfg); tile_sense_m_3g_set_mode(&mag, SENSE_M_3G_MODE_NORMAL); while (1) { tile_sense_m_3g_process(&mag); } ``` ### API 30 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_m_3g_set_mode` — Studio ```c void tile_sense_m_3g_set_mode(tile_t *tile, uint8_t mode) ``` Set the power mode. Forced mode is reachable only from suspend, so a request to enter it from normal mode is routed through suspend first. Each transition blocks for the settling time the device needs, which for suspend to forced depends on the averaging setting. - `tile` — Tile handle - `mode` — Target mode (sense_m_3g_mode_t) ##### `tile_sense_m_3g_trigger_measurement` — Studio ```c uint8_t tile_sense_m_3g_trigger_measurement(tile_t *tile) ``` Trigger a single forced-mode conversion and wait for it. Uses the fast forced mode when the configured ODR is 25 Hz or above and the slow one otherwise, per datasheet §5.1.4. The device returns to suspend on its own once the conversion completes. Does not read the sample — call read() after. - `tile` — Tile handle **Returns:** 1 if a conversion completed, 0 on timeout ##### `tile_sense_m_3g_sleep` — Studio ```c void tile_sense_m_3g_sleep(tile_t *tile) ``` Put the device in suspend mode (lowest power, settings kept). - `tile` — Tile handle ##### `tile_sense_m_3g_wake` — Studio ```c void tile_sense_m_3g_wake(tile_t *tile) ``` Return the device to normal (free-running) mode. - `tile` — Tile handle ##### `tile_sense_m_3g_reset` — Studio ```c void tile_sense_m_3g_reset(tile_t *tile) ``` Soft-reset the device and re-run the full init sequence. Re-downloads the OTP trim data and repeats the magnetic reset, so the compensation coefficients survive. Leaves the device in suspend. - `tile` — Tile handle ##### `tile_sense_m_3g_magnetic_reset` — Studio ```c uint8_t tile_sense_m_3g_magnetic_reset(tile_t *tile) ``` Run a magnetic reset (bit reset, then flip-gain reset). Restores the transducer's magnetic state after exposure to a strong field. The device does this automatically every ODR tick while measuring and once at boot, so it is only needed after the part has sat in suspend through a field event — the sensor cannot detect one while suspended (datasheet §5.1.5). Worth calling after more than a couple of seconds in suspend, which also recharges the CRST capacitor. Must be run from suspend; a device in normal mode is parked, reset, and restored. - `tile` — Tile handle **Returns:** 1 if both reset phases were acknowledged, 0 otherwise #### Runtime ##### `tile_sense_m_3g_read` — Studio ```c uint8_t tile_sense_m_3g_read(tile_t *tile) ``` Read one sample and apply the full compensation chain. Burst-reads magnetic and temperature data in a single transaction — mandatory, because the device freezes the data registers for the duration of a burst and single reads would tear across an update (datasheet §5.2). - `tile` — Tile handle **Returns:** 1 on success, 0 if the bus read failed ##### `tile_sense_m_3g_process` — Studio ```c void tile_sense_m_3g_process(tile_t *tile) ``` Poll for a new sample, then read and dispatch it. Call from your main loop. Checks data-ready (or the INT flag when an INT pin is configured), and on a new sample calls read() and fires the callback. Cheap when nothing is ready. - `tile` — Tile handle ##### `tile_sense_m_3g_on_data` — Studio ```c void tile_sense_m_3g_on_data(tile_t *tile, sense_m_3g_data_cb_t cb, void *ctx) ``` Register or change the new-sample callback. - `tile` — Tile handle - `cb` — Callback (NULL to disable) - `ctx` — User context passed to the callback ##### `tile_sense_m_3g_get_x_nt` — Studio ```c int32_t tile_sense_m_3g_get_x_nt(tile_t *tile) ``` Compensated X field from the last read, in nanotesla. - `tile` — Tile handle **Returns:** X in nT (1000 nT = 1 uT) ##### `tile_sense_m_3g_get_y_nt` — Studio ```c int32_t tile_sense_m_3g_get_y_nt(tile_t *tile) ``` Compensated Y field from the last read, in nanotesla. - `tile` — Tile handle **Returns:** Y in nT ##### `tile_sense_m_3g_get_z_nt` — Studio ```c int32_t tile_sense_m_3g_get_z_nt(tile_t *tile) ``` Compensated Z field from the last read, in nanotesla. - `tile` — Tile handle **Returns:** Z in nT ##### `tile_sense_m_3g_get_temperature_mc` — Studio ```c int32_t tile_sense_m_3g_get_temperature_mc(tile_t *tile) ``` Die temperature from the last read, in milli-degrees Celsius. This is the compensation chain's own temperature, not an ambient reading — the die sits above ambient by whatever the board is doing. - `tile` — Tile handle **Returns:** Temperature in m°C (25000 = 25.000 °C) ##### `tile_sense_m_3g_get_magnitude_nt` — Studio ```c uint32_t tile_sense_m_3g_get_magnitude_nt(tile_t *tile) ``` Field magnitude of the last sample, in nanotesla. sqrt(x² + y² + z²), computed with an integer square root. Useful as a sanity check: a quiet indoor spot should land in the 25000-65000 nT band, and a magnet nearby will swamp it. - `tile` — Tile handle **Returns:** Magnitude in nT ##### `tile_sense_m_3g_data_ready` — Studio ```c uint8_t tile_sense_m_3g_data_ready(tile_t *tile) ``` Check whether the device has a new sample waiting. - `tile` — Tile handle **Returns:** 1 if INT_STATUS reports data ready ##### `tile_sense_m_3g_get_sensortime` — Studio ```c uint32_t tile_sense_m_3g_get_sensortime(tile_t *tile) ``` Read the 24-bit sensor-time counter. Ticks at 40 µs resolution and marks when the last sample was generated, not when you read it. Only advances in normal mode unless sensor-time always-on is enabled. Wraps at 24 bits without saturating. - `tile` — Tile handle **Returns:** Raw 24-bit counter value #### Configuration ##### `tile_sense_m_3g_set_odr_averaging` — Studio ```c void tile_sense_m_3g_set_odr_averaging(tile_t *tile, uint8_t odr, uint8_t averaging) ``` Set output data rate and averaging together. The pair is constrained: 400 Hz forces no averaging, 200 Hz caps at 2x, 100 Hz at 4x (datasheet Table 5). An illegal combination is clamped to the highest averaging the requested rate allows rather than rejected. Both live in one register and need an update command, which this issues. - `tile` — Tile handle - `odr` — Output data rate (sense_m_3g_odr_t) - `averaging` — Averaging (sense_m_3g_avg_t) ##### `tile_sense_m_3g_set_axes` — Studio ```c void tile_sense_m_3g_set_axes(tile_t *tile, uint8_t mask) ``` Enable or disable individual measurement axes. A disabled axis stops converting, saving current, and reads back 0. - `tile` — Tile handle - `mask` — OR of BMM350_EN_X / _Y / _Z ##### `tile_sense_m_3g_configure_interrupt` — Studio ```c void tile_sense_m_3g_configure_interrupt(tile_t *tile, uint8_t enable, uint8_t active_high, uint8_t push_pull, uint8_t latched) ``` Configure the data-ready interrupt on the INT pin. - `tile` — Tile handle - `enable` — 1 to drive the INT pin, 0 to leave it idle - `active_high` — 1 for active-high, 0 for active-low - `push_pull` — 1 for push-pull, 0 for open-drain (needs a pull-up) - `latched` — 1 to latch until INT_STATUS is read, 0 for a pulse ##### `tile_sense_m_3g_set_pad_drive` — Studio ```c void tile_sense_m_3g_set_pad_drive(tile_t *tile, uint8_t drive) ``` Set the INT/SDA/SCL pad drive strength (0-7, weakest to strongest). - `tile` — Tile handle - `drive` — Drive strength, 0-7 ##### `tile_sense_m_3g_set_i2c_watchdog` — Studio ```c void tile_sense_m_3g_set_i2c_watchdog(tile_t *tile, uint8_t enable, uint8_t long_wdt) ``` Enable or disable the device's I2C watchdog. Recovers the bus if the host stalls mid-transaction. Independent of the Core's own watchdog. - `tile` — Tile handle - `enable` — 1 to enable, 0 to disable - `long_wdt` — 1 for the ~40 ms timeout, 0 for ~1.28 ms ##### `tile_sense_m_3g_set_sensortime_always_on` — Studio ```c void tile_sense_m_3g_set_sensortime_always_on(tile_t *tile, uint8_t enable) ``` Keep the sensor-time counter running outside normal mode. - `tile` — Tile handle - `enable` — 1 to keep counting in suspend and forced mode #### Advanced ##### `tile_sense_m_3g_get_error` — Studio ```c uint8_t tile_sense_m_3g_get_error(tile_t *tile) ``` Read the device's error register. - `tile` — Tile handle **Returns:** ERR_REG contents; bit 0 set means the last PMU command was rejected ##### `tile_sense_m_3g_get_pmu_status` — Studio ```c uint8_t tile_sense_m_3g_get_pmu_status(tile_t *tile) ``` Read the PMU command status register. - `tile` — Tile handle **Returns:** PMU_CMD_STATUS_0: [3:0] last command, plus busy / normal-mode flags ##### `tile_sense_m_3g_get_otp_word` — Studio ```c uint16_t tile_sense_m_3g_get_otp_word(tile_t *tile, uint8_t word) ``` Read one of the 32 OTP trim words cached at init. The compensation coefficients are derived from these. Exposed for diagnostics — comparing them against a known-good part is the fastest way to tell a compensation bug from a broken OTP download. - `tile` — Tile handle - `word` — Word index, 0-31 **Returns:** The cached 16-bit OTP word, or 0 if the index is out of range ##### `tile_sense_m_3g_get_raw` — Studio ```c int32_t tile_sense_m_3g_get_raw(tile_t *tile, uint8_t axis) ``` Read the uncompensated 21-bit signed counts for one axis. Straight from the data registers with no compensation applied — the input to the compensation chain. For debugging only; these are not field values and are not in spec. - `tile` — Tile handle - `axis` — 0 = X, 1 = Y, 2 = Z, 3 = temperature **Returns:** Signed raw counts from the last read() ##### `tile_sense_m_3g_read_reg` — Studio ```c uint8_t tile_sense_m_3g_read_reg(tile_t *tile, uint8_t reg) ``` Read a raw 8-bit register, dummy bytes handled. - `tile` — Tile handle - `reg` — Register address **Returns:** Register value, or 0 if the tile is not ready ##### `tile_sense_m_3g_write_reg` — Studio ```c void tile_sense_m_3g_write_reg(tile_t *tile, uint8_t reg, uint8_t value) ``` Write a raw 8-bit register. - `tile` — Tile handle - `reg` — Register address - `value` — Value to write #### Other ##### `tile_sense_m_3g_find` ```c uint8_t tile_sense_m_3g_find(tiles_pal_t *hal, uint8_t instance) ``` Probe the bus for a BMM350. - `hal` — Tiles HAL handle (I2C bus) - `instance` — 0 for address 0x14 (Sense.M.3G), 1 for 0x15 **Returns:** 1 if a device ACKs and reports CHIP_ID 0x33, 0 otherwise ##### `tile_sense_m_3g_init` ```c void tile_sense_m_3g_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_m_3g_cfg_t *cfg) ``` Initialize the magnetometer. Soft-resets the device, verifies CHIP_ID, downloads the 32 OTP trim words and derives the compensation coefficients from them, powers the OTP back down, then runs a full magnetic reset so the transducer starts from a known magnetic state. Leaves the device in suspend — call set_mode() to start measuring. Takes roughly 100 ms, most of it the OTP download and the magnetic reset's bit-reset and flip-gain-reset settling. - `hal` — Tiles HAL handle (I2C bus) - `instance` — 0 for 0x14, 1 for 0x15 - `tile` — Tile handle to initialize - `cfg` — Optional config. NULL for defaults. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Self-test (advanced) The BMM350 can inject a ~130 uT internal field to verify the X and Y channels (datasheet §5.1.6). The datasheet describes the concept and the pass criterion but not the register sequence, which lives in the SensorAPI's self_test_config / TMR_SELFTEST_USER handling. Exposing it means porting that sequence and validating it against a known field. Driver-deferred, and the highest-value thing to add next. #### OTP programming (advanced) OTP write commands (DIR_PRGM, DIR_PRGM_1B, EXT_PRGM) are exposed by the chip but are a factory-provisioning facility — programming OTP is irreversible and would destroy the part's trim data. Read access is used internally for compensation and available through get_otp_word. #### I3C interface and in-band interrupts (niche) Hardware-gated: the BMM350 supports I3C with in-band interrupts (IBI), but Sense.M.3G is wired for I2C and the Core's I3C support is not routed to this tile. INT_CTRL_IBI and the I3C error register are consequently not exposed. #### Cross-axis coefficient override (niche) The cross-axis compensation coefficients are read from OTP and applied automatically. There is no API to override them with values from a system-level calibration; hard-iron and soft-iron correction for a specific enclosure belongs above this driver anyway. ### Enums #### `sense_m_3g_avg_t` | Member | Value | Description | | --- | --- | --- | | `SENSE_M_3G_AVG_NONE` | `0x0` | 1 sample — "low power" | | `SENSE_M_3G_AVG_2` | `0x1` | 2 samples — "regular power" | | `SENSE_M_3G_AVG_4` | `0x2` | 4 samples — "low noise" | | `SENSE_M_3G_AVG_8` | `0x3` | 8 samples — "ultra low noise" | #### `sense_m_3g_odr_t` Output data rate (PMU_CMD_AGGR_SET bits [3:0]). | Member | Value | Description | | --- | --- | --- | | `SENSE_M_3G_ODR_400HZ` | `0x2` | | | `SENSE_M_3G_ODR_200HZ` | `0x3` | | | `SENSE_M_3G_ODR_100HZ` | `0x4` | | | `SENSE_M_3G_ODR_50HZ` | `0x5` | | | `SENSE_M_3G_ODR_25HZ` | `0x6` | | | `SENSE_M_3G_ODR_12_5HZ` | `0x7` | | | `SENSE_M_3G_ODR_6_25HZ` | `0x8` | | | `SENSE_M_3G_ODR_3_125HZ` | `0x9` | | | `SENSE_M_3G_ODR_1_5625HZ` | `0xA` | | #### `sense_m_3g_mode_t` Power mode. | Member | Value | Description | | --- | --- | --- | ### Structs #### `sense_m_3g_cfg_t` | Type | Field | Description | | --- | --- | --- | | `sense_m_3g_odr_t` | `odr` | Output data rate. 0 = default 25 Hz. | | `sense_m_3g_avg_t` | `averaging` | Averaging. 0 = AVG_NONE (this is a real value, not "unset"). | | `uint8_t` | `axes` | Axis mask (BMM350_EN_*). 0 = all three. | | `uint8_t` | `int_pin` | Core pad carrying INT. 0 = polled. | | `sense_m_3g_data_cb_t` | `on_data` | Called by process() on a new sample. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_M_3G_VERSION_MAJOR` | `0` | | | `TILE_SENSE_M_3G_VERSION_MINOR` | `9` | | | `TILE_SENSE_M_3G_VERSION_PATCH` | `0` | | | `BMM350_I2C_ADDR_LOW` | `0x14` | | | `BMM350_I2C_ADDR_HIGH` | `0x15` | | | `BMM350_CHIP_ID` | `0x33` | | | `BMM350_DUMMY_BYTES` | `2` | | | `BMM350_MAG_TEMP_DATA_LEN` | `12` | | | `BMM350_PMU_CMD_SUS` | `0x00` | Suspend mode | | `BMM350_PMU_CMD_NM` | `0x01` | Normal mode | | `BMM350_PMU_CMD_UPD_OAE` | `0x02` | Apply a new ODR / averaging setting | | `BMM350_PMU_CMD_FM` | `0x03` | Forced mode | | `BMM350_PMU_CMD_FM_FAST` | `0x04` | Forced mode, fast (ODR >= 25 Hz) | | `BMM350_PMU_CMD_FGR` | `0x05` | Flip-gain reset (magnetic reset) | | `BMM350_PMU_CMD_FGR_FAST` | `0x06` | | | `BMM350_PMU_CMD_BR` | `0x07` | Bit reset (magnetic reset) | | `BMM350_PMU_CMD_BR_FAST` | `0x08` | | | `BMM350_CMD_SOFTRESET` | `0xB6` | | | `BMM350_EN_XYZ` | `0x07` | | --- Generated 2026-08-12 from the driver header at tiles@0f3be8b. Canonical HTML: https://bergsonne.io/docs/tiles/sense/m3g ============================================================================== ## Sense.MIC driver > Complete driver for the Sense.MIC tile (analog MEMS mic + amp + ADC). I2C-only, command-based protocol via tiles_pal_t raw I2C. | | | | --- | --- | | Tile | `Sense.MIC` | | Driver version | `2.2.0` | | Header | `tile_sense_mic.h` | | Generated from | `tiles@76608af` | | Canonical page | https://bergsonne.io/docs/tiles/sense/mic | ### Overview I2C-only, command-based protocol via tiles_pal_t raw I2C. @version 2.2.0 Analog signal chain: an analog MEMS microphone is AC-coupled into an AD8605 op-amp gain stage, whose output drives the MAX11645 ADC (and is also tapped out to a pad). Components: - Same Sky / CUI CMM-2718AT-42316-TR: omnidirectional ANALOG MEMS mic, −42 dBV/Pa sensitivity, 57 dBA SNR, 130 dB SPL AOP, 300 Ω output. - Analog Devices AD8605: precision RRIO op-amp, non-inverting gain ~48× (R4 47k / R3 1k → 1 + 47). The ADC therefore sees ~48× the bare mic voltage — the mV→SPL conversion divides this back out. - Maxim MAX11645: 12-bit 2-channel ADC, up to 94.4 ksps, I2C up to 1.7 MHz, internal 2.048V reference. The amplified analog output is also brought out to a tile pad (the schematic routes it to pad 8). NOTE: as of this writing the canonical tile JSON is mid-reconciliation — it lists the older PUI AMM-2742 mic and labels pad 6 (not 8) as the analog out, and omits the AD8605. The driver here reflects the real schematic; the product DB still needs the mic part, the AD8605, and the analog-out pad (8) updated to match. The MAX11645 uses a command-based I2C protocol (no register addresses). All bus access goes through tiles_pal_t i2c_write_raw / i2c_read_raw. Platform-agnostic: uses tiles_pal_t for all bus access. Datasheet: Maxim MAX11644/MAX11645, 19-4544; Rev 3, 9/09 ### Examples #### Quick start — polling ```c tile_t mic; tile_sense_mic_init(core_tiles_pal(&core_i2c3), 0, &mic, NULL); uint16_t raw = tile_sense_mic_get_raw(&mic); uint16_t mv = tile_sense_mic_get_raw_mv(&mic); ``` #### Quick start — continuous sampling ```c tile_t mic; tile_sense_mic_init(core_tiles_pal(&core_i2c3), 0, &mic, NULL); uint16_t samples[256]; tile_sense_mic_get_samples(&mic, samples, 256); uint16_t dc = tile_sense_mic_dc_level(samples, 256); uint16_t pp = tile_sense_mic_peak_to_peak(samples, 256); ``` #### Quick start — with internal reference for precise mV ```c sense_mic_cfg_t cfg = { .ref = SENSE_MIC_REF_INTERNAL }; tile_t mic; tile_sense_mic_init(core_tiles_pal(&core_i2c3), 0, &mic, &cfg); // Now get_raw_mv() uses 2048 mV full-scale (0.5 mV/LSB) ``` ### API 25 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_mic_sleep` — Studio ```c void tile_sense_mic_sleep(tile_t *tile) ``` Enter low-power mode (no conversions). ##### `tile_sense_mic_wake` — Studio ```c void tile_sense_mic_wake(tile_t *tile) ``` Wake from sleep, restore previous configuration. ##### `tile_sense_mic_reset` — Studio ```c void tile_sense_mic_reset(tile_t *tile) ``` Reset the config register to power-on defaults. Must call init() again. #### Runtime ##### `tile_sense_mic_get_vref_mv` — Studio ```c uint16_t tile_sense_mic_get_vref_mv(tile_t *tile) ``` Get the currently configured reference voltage in millivolts. Returns the Vref used for millivolt conversions (3300 for VDD, 2048 for internal, or the user-specified value for external). ##### `tile_sense_mic_get_raw` — Studio ```c uint16_t tile_sense_mic_get_raw(tile_t *tile) ``` Read a single 12-bit ADC sample (0–4095). The MAX11645 auto-converts on every read — no trigger needed. Conversion time is ~3.5 µs (internal clock); the I2C transaction itself dominates the timing (~55 µs at 400 kHz). ##### `tile_sense_mic_get_raw_mv` — Studio ```c uint16_t tile_sense_mic_get_raw_mv(tile_t *tile) ``` Read a single sample and convert to millivolts. Conversion: mv = raw * vref_mv / 4096 ##### `tile_sense_mic_get_audio_sample` — Studio ```c int16_t tile_sense_mic_get_audio_sample(tile_t *tile) ``` Read a single AC-coupled audio sample (signed, relative to DC offset). Returns: (raw - dc_offset) where dc_offset is auto-calibrated during init(). Useful for audio waveform capture where DC bias is removed. **Returns:** Signed 16-bit value. 0 = silence. ##### `tile_sense_mic_get_dc_offset` — Studio ```c uint16_t tile_sense_mic_get_dc_offset(tile_t *tile) ``` Get the auto-calibrated DC offset (mic bias point). Measured during init() by averaging 64 samples. Typically 600–900 with VDD reference, depending on supply voltage and PCB layout. ##### `tile_sense_mic_get_samples` — Studio ```c void tile_sense_mic_get_samples(tile_t *tile, uint16_t *buf, uint16_t count) ``` Burst-read N samples into a buffer. Reads are back-to-back; effective sample rate depends on I2C bus speed: - 400 kHz: ~12.5 ksps - 1 MHz: ~16 ksps - `buf` — Output buffer (caller-allocated, min count entries) - `count` — Number of samples to read ##### `tile_sense_mic_dc_level` — Studio ```c uint16_t tile_sense_mic_dc_level(tile_t *tile, const uint16_t *samples, uint16_t count) ``` Compute the DC level (mean) of a sample buffer. Useful for determining the mic bias point. Varies with supply voltage and PCB bias circuit (typically 600–900 with VDD ref). symmetry with the rest of the driver surface). - `tile` — Tile handle (unused — accepted for DSL-binding - `samples` — Sample buffer (typically from get_samples()). - `count` — Number of samples in the buffer. **Returns:** DC offset in raw ADC counts. ##### `tile_sense_mic_peak_to_peak` — Studio ```c uint16_t tile_sense_mic_peak_to_peak(tile_t *tile, const uint16_t *samples, uint16_t count) ``` Compute peak-to-peak amplitude of a sample buffer (raw counts). Returns max - min across all samples. Silence ≈ 5–20 counts (noise floor). - `tile` — Tile handle (unused — accepted for DSL-binding symmetry). - `samples` — Sample buffer. - `count` — Number of samples. **Returns:** Peak-to-peak amplitude in raw ADC counts. ##### `tile_sense_mic_rms` — Studio ```c uint16_t tile_sense_mic_rms(tile_t *tile, const uint16_t *samples, uint16_t count, uint16_t dc_offset) ``` Compute RMS amplitude relative to a DC offset (raw counts). - `tile` — Tile handle (unused — accepted for DSL-binding symmetry). - `samples` — Sample buffer. - `count` — Number of samples. - `dc_offset` — DC bias point (use dc_level() to measure, or pass 2048). **Returns:** RMS of AC component in raw ADC counts. ##### `tile_sense_mic_amplitude_mv` — Studio ```c uint16_t tile_sense_mic_amplitude_mv(tile_t *tile, uint16_t pp_raw) ``` Convert peak-to-peak raw amplitude to millivolts. Uses the configured reference voltage to scale a peak-to-peak raw ADC count into millivolts: mv = pp_raw * vref_mv / 4096. - `pp_raw` — [0..4095] Peak-to-peak amplitude in raw ADC counts. **Returns:** Amplitude in millivolts. ##### `tile_sense_mic_is_loud` — Studio ```c uint8_t tile_sense_mic_is_loud(tile_t *tile, int16_t threshold_db) ``` Quick "is the room loud right now?" check. Captures a short sample buffer (~64 samples / ~5 ms at 12.5 ksps) via @ref tile_sense_mic_get_samples, computes the RMS amplitude relative to the calibrated DC offset, converts it to dB SPL via the CMM-2718AT sensitivity model (with the AD8605 gain divided out), and compares against `threshold_db` (0.1 dB units, e.g. 700 = 70.0 dB). - `tile` — Initialised tile handle. - `threshold_db` — SPL threshold in 0.1 dB units (e.g. 700 = 70 dB). **Returns:** 1 if measured SPL > threshold, 0 otherwise. **Note:** Blocking. Takes ~5 ms while sampling. ##### `tile_sense_mic_read_spl_db` — Studio ```c int16_t tile_sense_mic_read_spl_db(tile_t *tile) ``` Read instantaneous SPL in 0.1 dB units. Captures a short sample buffer (~64 samples), computes the RMS amplitude relative to the calibrated DC offset, scales to mV via the configured Vref, then maps mV→dB SPL using the CMM-2718AT's −42 dBV/Pa typical sensitivity AND the AD8605's ~48× gain (1 Pa ≈ 7.94 mV RMS at the mic → ~381 mV RMS at the ADC input, where 1 Pa SPL = 94 dB). The conversion is integer-only with a 32-entry log10 lookup table (1..32 mV). Returns dB SPL in 0.1 dB units (e.g. 700 = 70.0 dB). the noise floor. Negative values are not produced. - `tile` — Initialised tile handle. **Returns:** SPL in 0.1 dB units. Floor at 300 (~30 dB) when below **Note:** Blocking. Takes ~5 ms while sampling. **Note:** Accuracy regime documented in the section comment above. ##### `tile_sense_mic_wait_for_sound` — Studio ```c uint8_t tile_sense_mic_wait_for_sound(tile_t *tile, int16_t threshold_db, uint32_t timeout_ms) ``` Block until ambient SPL crosses a threshold (or timeout). Polls @ref tile_sense_mic_read_spl_db every ~5 ms until the computed SPL (in 0.1 dB units) exceeds `threshold_db`, or the elapsed time exceeds `timeout_ms`. Useful for "wake on sound" patterns — e.g., wait for a door slam, then fire an action. - `tile` — Initialised tile handle. - `threshold_db` — SPL threshold in 0.1 dB units. - `timeout_ms` — Maximum wait, in milliseconds. **Returns:** 1 if threshold was crossed, 0 on timeout. **Note:** Blocking until threshold or timeout. ##### `tile_sense_mic_detect_clap` — Studio ```c uint8_t tile_sense_mic_detect_clap(tile_t *tile, uint32_t timeout_ms) ``` Detect a clap pattern (two peaks, quiet brackets). Watches the SPL stream looking for the canonical clap signature: 1. Quiet bracket (≥100 ms below ~50 dB SPL) — establishes baseline 2. First peak (>~70 dB SPL spike, <50 ms wide) 3. Quiet gap (50–500 ms below threshold) 4. Second peak (>~70 dB SPL spike, <50 ms wide) 5. Quiet bracket (≥100 ms below threshold) — ensures it's really a clap-clap Thresholds are integer-fixed: peak ≈ 700 (70.0 dB), quiet floor ≈ 500 (50.0 dB). The pattern detector samples SPL every ~5 ms. - `tile` — Initialised tile handle. - `timeout_ms` — Maximum wait, in milliseconds. **Returns:** 1 if a clap pattern was detected, 0 on timeout. **Note:** Blocking until pattern detected or timeout. False positives on door knocks / drawer slams are expected — this is a coarse pattern detector, not a trained classifier. #### Configuration ##### `tile_sense_mic_set_reference` — Studio ```c void tile_sense_mic_set_reference(tile_t *tile, sense_mic_ref_t ref) ``` Change the reference voltage source. - `ref` — One of the sense_mic_ref_t values (VDD, INTERNAL, etc.) **Note:** When switching to internal ref, allow ~10 µs for settling. This function inserts a 1 ms delay automatically. ##### `tile_sense_mic_set_channel` — Studio ```c void tile_sense_mic_set_channel(tile_t *tile, sense_mic_channel_t ch) ``` Change the active ADC channel. - `ch` — SENSE_MIC_CH_AIN0 (mic) or SENSE_MIC_CH_AIN1 ##### `tile_sense_mic_set_scan_mode` — Studio ```c void tile_sense_mic_set_scan_mode(tile_t *tile, sense_mic_scan_t scan) ``` Change the scan mode. - `scan` — SENSE_MIC_SCAN_SINGLE, SENSE_MIC_SCAN_UP, or SENSE_MIC_SCAN_8X ##### `tile_sense_mic_set_clock_mode` — Studio ```c void tile_sense_mic_set_clock_mode(tile_t *tile, sense_mic_clock_t clk) ``` Switch the conversion-clock source. Selects whether the MAX11645 clocks conversions from its internal 2.8 MHz oscillator (default) or from the I2C SCL line. In external-clock mode, conversion timing is locked to the host I2C bus, which makes it possible to synchronise multiple Sense.MIC tiles or to align ADC sampling with another time-base. - `clk` — SENSE_MIC_CLOCK_INTERNAL or SENSE_MIC_CLOCK_EXTERNAL **Note:** External-clock mode only takes effect during the next read transaction; SCL must remain active for the full conversion window or the result will be invalid. ##### `tile_sense_mic_set_polarity` — Studio ```c void tile_sense_mic_set_polarity(tile_t *tile, sense_mic_polarity_t pol) ``` Switch the output coding (unipolar vs bipolar). Unipolar (default) returns 0–4095 straight binary, mid-bias near 2048. Bipolar returns the same sample re-encoded as a signed 12-bit two's-complement value centred on VREF/2. Both modes use the chip's single-ended input on AIN0 — bipolar does NOT enable true differential reads (the tile does not route an opposing analog input to AIN1). It just changes how get_raw() encodes the same physical sample. After switching to bipolar, treat get_raw() output as a signed 12-bit value (sign-extend before use): see get_audio_sample() for an alternative that subtracts the calibrated DC offset. - `pol` — SENSE_MIC_POLARITY_UNIPOLAR or SENSE_MIC_POLARITY_BIPOLAR #### Advanced ##### `tile_sense_mic_calibrate` — Studio ```c void tile_sense_mic_calibrate(tile_t *tile) ``` Re-calibrate the DC offset (e.g., after changing reference). Averages 64 samples to update the stored bias point. **Note:** Call in a quiet environment for best results. #### Other ##### `tile_sense_mic_find` ```c uint8_t tile_sense_mic_find(tiles_pal_t *hal, uint8_t instance) ``` Check if a Sense.MIC is present on the bus (address probe). Lightweight probe — does not configure the device. The MAX11645 has no WHO_AM_I register; this checks for an ACK at 0x36. ##### `tile_sense_mic_init` ```c void tile_sense_mic_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_mic_cfg_t *cfg) ``` Initialize the MAX11645 ADC. Sends setup byte (reference, clock, polarity) and configuration byte (scan mode, channel, single-ended). Verifies the device responds by performing a test read. Pass cfg=NULL for defaults: VDD reference, AIN0 (mic), single-ended, unipolar, internal clock, single-channel scan. **Note:** Blocks for ~5 ms (setup settling + DC offset calibration). Call once at startup. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### External reference voltage on REF pin (advanced) The MAX11645 supports an external reference on its REF/AIN1 pin, but the Sense.MIC tile does not route REF/AIN1 to any pad (pad 6 carries the chip's analog audio output, not the ADC reference). Closing this gap requires a tile hardware revision that breaks REF out to a connector pad. Until then, the SENSE_MIC_REF_EXTERNAL* enum values configure the chip but have no usable external pin. ### Enums #### `sense_mic_ref_t` Reference voltage selection. | Member | Value | Description | | --- | --- | --- | | `SENSE_MIC_REF_VDD` | `0x00` | VDD reference (default) | | `SENSE_MIC_REF_EXTERNAL` | `0x01` | External ref on REF pin | | `SENSE_MIC_REF_INTERNAL` | `0x02` | Internal 2.048V, always on | | `SENSE_MIC_REF_INTERNAL_BUF` | `0x03` | Internal 2.048V, buffered | | `SENSE_MIC_REF_EXTERNAL_BUF` | `0x05` | External ref, buffered | #### `sense_mic_channel_t` ADC channel selection. | Member | Value | Description | | --- | --- | --- | | `SENSE_MIC_CH_AIN0` | `0` | AIN0 — microphone (default) | | `SENSE_MIC_CH_AIN1` | `1` | AIN1 | #### `sense_mic_scan_t` Scan mode. | Member | Value | Description | | --- | --- | --- | | `SENSE_MIC_SCAN_SINGLE` | `0x03` | Single channel conversion (default) | | `SENSE_MIC_SCAN_UP` | `0x00` | Scan from AIN0 up to CS0 | | `SENSE_MIC_SCAN_8X` | `0x01` | Convert CS0 8 times (averaging) | #### `sense_mic_clock_t` Conversion-clock source. | Member | Value | Description | | --- | --- | --- | | `SENSE_MIC_CLOCK_INTERNAL` | `0` | Internal 2.8 MHz oscillator (default) | | `SENSE_MIC_CLOCK_EXTERNAL` | `1` | Host-clocked via SCL | #### `sense_mic_polarity_t` Output coding (unipolar vs bipolar). | Member | Value | Description | | --- | --- | --- | | `SENSE_MIC_POLARITY_UNIPOLAR` | `0` | 0 .. VREF, straight binary (default) | | `SENSE_MIC_POLARITY_BIPOLAR` | `1` | ±VREF/2, two's complement | ### Structs #### `sense_mic_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `ref` | Voltage reference (sense_mic_ref_t). Default: SENSE_MIC_REF_VDD. | | `uint8_t` | `channel` | ADC channel (sense_mic_channel_t). Default: SENSE_MIC_CH_AIN0. | | `uint8_t` | `scan` | Scan mode (sense_mic_scan_t). Default: SENSE_MIC_SCAN_SINGLE. | | `uint8_t` | `clock` | Conversion clock (sense_mic_clock_t). Default: SENSE_MIC_CLOCK_INTERNAL. | | `uint8_t` | `polarity` | Output coding (sense_mic_polarity_t). Default: SENSE_MIC_POLARITY_UNIPOLAR. | | `uint16_t` | `vref_mv` | Reference voltage in mV. 0 = auto (3300 for VDD, 2048 for internal). Set manually only for external ref. | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_MIC_VERSION_MAJOR` | `2` | | | `TILE_SENSE_MIC_VERSION_MINOR` | `2` | | | `TILE_SENSE_MIC_VERSION_PATCH` | `0` | | | `MAX11645_I2C_ADDR` | `0x36` | | | `MAX11645_ADC_BITS` | `12` | | --- Generated 2026-08-12 from the driver header at tiles@76608af. Canonical HTML: https://bergsonne.io/docs/tiles/sense/mic ============================================================================== ## Sense.T.C driver > Capacitive touch/proximity driver for the Sense.T.C tile (IQS323). | | | | --- | --- | | Tile | `Sense.T.C` | | Driver version | `1.3.0` | | Header | `tile_sense_t_c.h` | | Generated from | `tiles@53e6a59` | | Canonical page | https://bergsonne.io/docs/tiles/sense/tc | ### Overview @version 1.2.0 Azoteq IQS323 ProxFusion controller with self-capacitive touch sensing. The tile's entire top surface is a touch electrode (CH1/CRx1), and a second channel (CH0/CRx0) is available via pad 8 for external sensors. Two communication modes: - **Polled mode** (default): call process() from your main loop. No RDY pin wiring required. - **RDY mode**: provide a rdy_pin in config. EXTI falling-edge ISR sets a flag; process() only does I2C when the flag is set. Lower latency, less wasted I2C traffic. Event callback: register an on_event callback to be notified of touch, proximity, slider, and gesture changes without polling individual status functions. Slider note: slider functionality requires multiple channels enabled (e.g. two Sense.T.C tiles chained, or pad 8 connected to an external electrode). A single tile surface alone cannot produce a slider output. Datasheet: Azoteq IQS323, Rev v1.11, August 2025 ### Examples #### Simple polling example (Cores SDK) ```c tile_t touch; tile_sense_t_c_init(core_tiles_pal(&core_i2c3), 0, &touch, NULL); while (1) { tile_sense_t_c_process(&touch); if (tile_sense_t_c_is_touched(&touch, SENSE_T_C_CH_SURFACE)) led_on(); else led_off(); core_delay_ms(30); } ``` #### Callback example (polled) ```c void on_touch(tile_t *t, uint16_t status, void *ctx) { if (status & SENSE_T_C_SURFACE_TOUCH) led_green(); else led_off(); } sense_t_c_cfg_t cfg = { .on_event = on_touch }; tile_sense_t_c_init(core_tiles_pal(&core_i2c3), 0, &touch, &cfg); while (1) { tile_sense_t_c_process(&touch); core_delay_ms(30); } ``` #### Callback example (RDY interrupt) ```c sense_t_c_cfg_t cfg = { .rdy_pin = 3, .on_event = on_touch, }; tile_sense_t_c_init(core_tiles_pal(&core_i2c3), 0, &touch, &cfg); while (1) { tile_sense_t_c_process(&touch); // ... other work — process() returns fast if no RDY event ... } ``` ### API 34 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_t_c_sleep` — Studio ```c void tile_sense_t_c_sleep(tile_t *tile) ``` Enter low-power sleep mode. ##### `tile_sense_t_c_wake` — Studio ```c void tile_sense_t_c_wake(tile_t *tile) ``` Wake from sleep mode. #### Runtime ##### `tile_sense_t_c_get_status` — Studio ```c uint16_t tile_sense_t_c_get_status(tile_t *tile) ``` Read the cached System Status word from the last process() call. ##### `tile_sense_t_c_get_gestures` — Studio ```c uint16_t tile_sense_t_c_get_gestures(tile_t *tile) ``` Read the cached Gesture Status word from the last process() call. ##### `tile_sense_t_c_is_touched` — Studio ```c uint8_t tile_sense_t_c_is_touched(tile_t *tile, uint8_t channel) ``` Check if the given channel is currently touched. ##### `tile_sense_t_c_is_prox` — Studio ```c uint8_t tile_sense_t_c_is_prox(tile_t *tile, uint8_t channel) ``` Check if the given channel currently reports proximity. ##### `tile_sense_t_c_get_counts` — Studio ```c uint16_t tile_sense_t_c_get_counts(tile_t *tile, uint8_t channel) ``` Read the raw counts value for a channel. ##### `tile_sense_t_c_get_lta` — Studio ```c uint16_t tile_sense_t_c_get_lta(tile_t *tile, uint8_t channel) ``` Read the long-term average (LTA) for a channel. ##### `tile_sense_t_c_get_delta` — Studio ```c int16_t tile_sense_t_c_get_delta(tile_t *tile, uint8_t channel) ``` Read the delta (counts - LTA) for a channel. ##### `tile_sense_t_c_get_slider` — Studio ```c uint16_t tile_sense_t_c_get_slider(tile_t *tile) ``` Read the slider position (if a slider is configured). ##### `tile_sense_t_c_set_thresholds` — Studio ```c void tile_sense_t_c_set_thresholds(tile_t *tile, uint8_t channel, uint8_t prox_thresh, uint8_t touch_thresh) ``` Set proximity and touch thresholds for a channel. ##### `tile_sense_t_c_is_touched_any` — Studio ```c uint8_t tile_sense_t_c_is_touched_any(tile_t *tile) ``` Check whether *any* channel is currently touched. Reflects the cached System Status from the most recent process() call — it does not perform an I²C read itself, so it's safe to call from any rate. Returns 1 if any of CH0/CH1/CH2 currently reports touch, 0 otherwise. Pair with @ref tile_sense_t_c_process in your main loop. - `tile` — Initialised tile handle **Returns:** 1 if any channel is touched, 0 otherwise ##### `tile_sense_t_c_wait_for_touch` — Studio ```c uint8_t tile_sense_t_c_wait_for_touch(tile_t *tile, uint32_t timeout_ms) ``` Block until any channel is touched (or timeout elapses). Polls process() at ~5 ms cadence and returns as soon as any channel reports touch. If RDY-mode is configured, process() is a no-op until RDY fires, so the poll is effectively interrupt-paced. In polled mode the cadence determines latency. Returns 0 if `timeout_ms` elapses without a touch event. - `tile` — Initialised tile handle - `timeout_ms` — Maximum time to wait, in milliseconds **Returns:** 1 if a touch was detected, 0 on timeout ##### `tile_sense_t_c_wait_for_gesture` — Studio ```c uint16_t tile_sense_t_c_wait_for_gesture(tile_t *tile, uint32_t timeout_ms) ``` Block until any gesture fires (or timeout elapses). Polls the Gesture Status register at ~5 ms cadence. Returns the raw 16-bit gesture word as soon as any gesture bit is set (IQS323_GESTURE_TAP / SWIPE_POS / SWIPE_NEG / FLICK_POS / FLICK_NEG / HOLD). Returns 0 on timeout. Gestures require the slider/wheel UI to be configured — a single tile surface alone does not produce gesture events. See the datasheet §7.4 for the slider-config requirements. - `tile` — Initialised tile handle - `timeout_ms` — Maximum time to wait, in milliseconds **Returns:** Raw Gesture Status word (non-zero) on detection, 0 on timeout ##### `tile_sense_t_c_read_slider_pct` — Studio ```c uint8_t tile_sense_t_c_read_slider_pct(tile_t *tile, uint8_t *out_pct) ``` Read the slider position scaled to 0–100 percent. The IQS323 slider register reports a 10-bit position in the range 0–1023. This helper integer-scales it to a 0–100 percentage and stores the result through `out_pct`. Returns 0 if the read returned an invalid (out-of-window) response — `out_pct` is untouched in that case so callers can keep the previous value. Like @ref tile_sense_t_c_get_slider, this requires a valid slider configuration (multi-channel — two Sense.T.C tiles chained, or pad 8 wired to a second electrode). A single tile surface alone cannot produce a slider output. - `tile` — Initialised tile handle - `out_pct` — Pointer to receive the percentage (0–100) **Returns:** 1 on success, 0 on read failure (out_pct unmodified) ##### `tile_sense_t_c_reseed` — Studio ```c void tile_sense_t_c_reseed(tile_t *tile) ``` Force the long-term-average baseline to re-snap to current counts. Sets SYSTEM_CONTROL.RESEED (self-clearing). Use after a known environmental change (object placed/removed, cover added) to discard the slow baseline and re-zero the channels immediately, instead of waiting for the LTA filter to drift there. - `tile` — Initialised tile handle. #### Events ##### `tile_sense_t_c_process` — Studio ```c void tile_sense_t_c_process(tile_t *tile) ``` Read pending touch/proximity events and update internal state. Call from your main loop. In polled mode: reads status register, fires callback if events present. In RDY mode: returns immediately if no RDY interrupt; reads + fires callback only when the device has new data. - `tile` — Tile handle ##### `tile_sense_t_c_on_event` — Studio ```c void tile_sense_t_c_on_event(tile_t *tile, sense_t_c_event_cb_t cb, void *ctx) ``` Register or change the event callback for touch/proximity events. - `tile` — Tile handle - `cb` — Callback function (NULL to disable) - `ctx` — User context passed to callback #### Configuration ##### `tile_sense_t_c_set_power_mode` — Studio ```c void tile_sense_t_c_set_power_mode(tile_t *tile, sense_t_c_power_mode_t mode) ``` Set the device power mode. ##### `tile_sense_t_c_enable_events` — Studio ```c void tile_sense_t_c_enable_events(tile_t *tile, uint16_t mask) ``` Set the events-enable mask (which events drive RDY pulses). ##### `tile_sense_t_c_ati` — Studio ```c void tile_sense_t_c_ati(tile_t *tile) ``` Trigger an ATI (auto-tuning) cycle and wait for completion. ##### `tile_sense_t_c_set_ati_setup` — Studio ```c void tile_sense_t_c_set_ati_setup(tile_t *tile, uint8_t channel, uint16_t value) ``` Set the per-channel ATI setup register (0x36 + ch*0x10). Writes the raw 16-bit ATI Setup register (0x36 + ch·0x10) for the channel. Bit layout (datasheet §A.12): - Bits 15-4: ATI Target / Resolution Factor - Bit 3: ATI Band (0 = 1/16, 1 = 1/8) - Bits 2-0: ATI Mode (0 disabled, 1 comp-only, 2 from-comp-divider, 3 partial, 4 full) (The coarse/fine multipliers and dividers live in the separate ATI Multipliers/Dividers register at 0x38 — see IQS323_REG_ATI_MULTI; the compensation divider lives at 0x39 — see set_compensation.) After changing ATI parameters, call @ref tile_sense_t_c_ati to re-run the auto-tuning sequence with the new values. Useful when ambient capacitance shifts (cover-glass thickness, mounting substrate) push the working point off the chip's defaults. - `tile` — Initialised tile handle - `channel` — 0–2 - `value` — Raw 16-bit ATI Setup register value ##### `tile_sense_t_c_set_counts_filter` — Studio ```c void tile_sense_t_c_set_counts_filter(tile_t *tile, uint16_t beta) ``` Set the global counts-filter beta register (0xB0). Datasheet §A.26. Higher beta = more aggressive smoothing on the raw counts (lower noise, slower response). Default is tuned for typical electrode geometries; tune up for noisy environments, tune down for fast-response applications. - `tile` — Initialised tile handle - `beta` — Raw 16-bit Counts Filter Betas register value ##### `tile_sense_t_c_set_conversion_freq` — Studio ```c void tile_sense_t_c_set_conversion_freq(tile_t *tile, uint8_t channel, uint16_t value) ``` Set per-channel conversion frequency (0x31 + ch*0x10). Datasheet §A.4. Controls charge-transfer frequency and dead time. Tune for unusual electrode geometries, large capacitance loads, or when self-capacitance interactions push beyond the chip's default Conversion Frequency Fraction. - `tile` — Initialised tile handle - `channel` — 0–2 - `value` — Raw 16-bit Conversion Frequency register value ##### `tile_sense_t_c_set_channel_mode` — Studio ```c void tile_sense_t_c_set_channel_mode(tile_t *tile, uint8_t channel, sense_t_c_channel_mode_t mode, uint8_t reference_id) ``` Configure a channel's role in the Reference UI. Reference channels measure ambient capacitance (e.g., temperature / humidity drift on the substrate). Follower channels subtract the reference's LTA delta from their own, eliminating common-mode drift. Datasheet §7.3 describes the design pattern. For follower channels, `reference_id` selects which channel to follow (encoded in Channel Setup bits [7:4]). For independent / reference channels, `reference_id` is unused — pass 0. Apply to all participating channels (set the reference channel to REFERENCE first, then set followers). Re-run @ref tile_sense_t_c_ati after changing channel modes. - `tile` — Initialised tile handle - `channel` — 0–2 - `mode` — Channel role - `reference_id` — Which channel to follow (only used for FOLLOWER) ##### `tile_sense_t_c_set_comm_mode` — Studio ```c void tile_sense_t_c_set_comm_mode(tile_t *tile, sense_t_c_comm_mode_t mode) ``` Switch between event-mode and streaming-mode I²C. Event mode (default) waits for the RDY line to assert before communication; lowest power, lowest CPU overhead. Streaming mode reports samples at every report-rate tick regardless of activity; higher current, lower latency. Use streaming for continuous logging or for hosts that can't handle interrupt-driven I²C cleanly. - `tile` — Initialised tile handle - `mode` — Event or streaming ##### `tile_sense_t_c_set_report_rate` — Studio ```c void tile_sense_t_c_set_report_rate(tile_t *tile, sense_t_c_power_mode_t mode, uint16_t ms) ``` Set the report rate (conversion interval) for a power mode. Writes the per-mode report-rate register (NP 0xC1 / LP 0xC2 / ULP 0xC3 / HALT 0xC4) in plain milliseconds (clamped 0-3000). This is the tile's headline power/latency knob — faster rate = lower latency, higher current. Only the four reporting modes have a rate; AUTO/AUTO_NO_ULP are ignored. - `tile` — Initialised tile handle. - `mode` — Which power mode's rate to set (NORMAL/LOW/ULTRA_LOW/HALT). - `ms` — Report interval in milliseconds (0-3000). ##### `tile_sense_t_c_set_power_timeout` — Studio ```c void tile_sense_t_c_set_power_timeout(tile_t *tile, uint16_t ms) ``` Set the auto power-mode step-down timeout. Writes POWER_TIMEOUT (0xC5) in milliseconds. In AUTO / AUTO_NO_ULP the chip steps down a power mode after this much inactivity; 0 disables auto step-down. - `tile` — Initialised tile handle. - `ms` — Inactivity timeout in milliseconds (0-65000, 0 = off). #### Advanced ##### `tile_sense_t_c_read_reg` — Studio ```c uint16_t tile_sense_t_c_read_reg(tile_t *tile, uint8_t reg) ``` Read a raw 16-bit IQS323 register. ##### `tile_sense_t_c_write_reg` — Studio ```c void tile_sense_t_c_write_reg(tile_t *tile, uint8_t reg, uint16_t value) ``` Write a raw 16-bit IQS323 register. ##### `tile_sense_t_c_get_compensation` — Studio ```c uint16_t tile_sense_t_c_get_compensation(tile_t *tile, uint8_t channel) ``` Read a channel's compensation (CAPDAC-equivalent offset). Returns the raw Compensation register (0x39 + ch·0x10). The low 10 bits are the compensation value; bits 15-11 are the divider. Normally set by ATI — read it to capture a tuned working point. - `tile` — Initialised tile handle. - `channel` — 0-2. **Returns:** Raw 16-bit compensation register, or 0 on a bad channel. ##### `tile_sense_t_c_set_compensation` — Studio ```c void tile_sense_t_c_set_compensation(tile_t *tile, uint8_t channel, uint16_t value, uint8_t divider) ``` Override a channel's compensation value + divider. Writes the Compensation register (0x39 + ch·0x10): value (0-1023) in bits 9-0, divider (0-31) in bits 15-11. NOTE: a subsequent full ATI overwrites this — to hold a fixed offset, set the channel's ATI Mode (set_ati_setup) to a non-full mode first. - `tile` — Initialised tile handle. - `channel` — 0-2. - `value` — Compensation value (0-1023). - `divider` — Compensation divider (0-31). #### Other ##### `tile_sense_t_c_find` ```c uint8_t tile_sense_t_c_find(tiles_pal_t *hal, uint8_t instance) ``` Probe the bus for an IQS323 touch controller. - `hal` — Tiles HAL handle (I2C bus) - `instance` — Device instance (0 or 1, selects I2C address) **Returns:** 1 if found, 0 if not ##### `tile_sense_t_c_init` ```c void tile_sense_t_c_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_t_c_cfg_t *cfg) ``` Initialize the touch controller. Soft resets, configures CH1 (surface) for self-capacitive touch, sets proximity/touch thresholds, runs ATI. - `hal` — Tiles HAL handle (I2C bus) - `instance` — Device instance (0 or 1, selects I2C address) - `tile` — Tile handle to initialize - `cfg` — Optional config (thresholds, RDY pin, callback). NULL for defaults. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Per-gesture threshold tuning (advanced) IQS323 gesture timing (tap touch/release time, hold duration, swipe distance, flick velocity) is packed across several GESTURE_* registers with chip-specific encodings. The driver reports gesture events but doesn't surface a typed API for timing / threshold tuning — closing this properly needs a dedicated pass with Azoteq's gesture-config guide. Raw write_reg works for advanced users in the interim. #### Per-channel timeout / OutA event-indicator (niche) Channel-timeout disable bits (auto-reseed on timeout) and the OutA pin's event-indicator output mode aren't exposed. Most applications don't need them; raw write_reg covers the niche cases that do. ### Enums #### `sense_t_c_channel_mode_t` Channel Mode for the Reference UI (datasheet §A.15 bits [3:0]). | Member | Value | Description | | --- | --- | --- | | `SENSE_T_C_CHANNEL_INDEPENDENT` | `0x00` | Stand-alone sensing channel | | `SENSE_T_C_CHANNEL_FOLLOWER` | `0x01` | Subtracts a reference channel's LTA | | `SENSE_T_C_CHANNEL_REFERENCE` | `0x02` | Acts as ambient reference for followers | #### `sense_t_c_comm_mode_t` I²C communication mode (System Control register §A.30 bit 7). | Member | Value | Description | | --- | --- | --- | | `SENSE_T_C_COMM_STREAM` | `0` | Continuous reporting at the configured power-mode rate | | `SENSE_T_C_COMM_EVENT` | `1` | RDY pulses only on enabled events (default) | ### Structs #### `sense_t_c_cfg_t` | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `rdy_pin` | Platform pin for RDY. 0 = polled mode. | | `sense_t_c_event_cb_t` | `on_event` | Callback. NULL = no callback. | | `uint8_t` | `channels` | Channel enable bitmask (bit 0=CH0, 1=CH1, 2=CH2). 0 = factory default (all enabled). | | `uint8_t` | `prox_threshold` | Proximity threshold (factory default ~4). | | `uint8_t` | `touch_threshold` | Touch threshold (factory default ~8). | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_T_C_VERSION_MAJOR` | `1` | | | `TILE_SENSE_T_C_VERSION_MINOR` | `3` | | | `TILE_SENSE_T_C_VERSION_PATCH` | `0` | | | `IQS323_I2C_ADDR_001` | `0x44` | | | `IQS323_I2C_ADDR_002` | `0x58` | | | `SENSE_T_C_CH0` | `0` | CRx0/CTx0 -- pad 8 (external) | | `SENSE_T_C_CH1` | `1` | CRx1/CTx1 -- tile top surface | | `SENSE_T_C_CH2` | `2` | CRx2/CTx2/Bias | | `SENSE_T_C_NUM_CHANNELS` | `3` | | | `IQS323_STATUS_POWER_SHIFT` | `14` | | | `IQS323_INVALID_RESPONSE` | `0xEEEE` | | | `IQS323_CTRL_POWER_SHIFT` | `4` | | | `IQS323_HW_ID_3DD` | `0xF003` | | | `IQS323_HW_ID_3ED` | `0xF004` | | --- Generated 2026-08-12 from the driver header at tiles@53e6a59. Canonical HTML: https://bergsonne.io/docs/tiles/sense/tc ============================================================================== ## Sense.TOF driver > TMF8806 time-of-flight distance sensor. | | | | --- | --- | | Tile | `Sense.TOF` | | Driver version | `1.4.1` | | Header | `tile_sense_tof.h` | | Generated from | `tiles@be0209a` | | Canonical page | https://bergsonne.io/docs/tiles/sense/tof | ### Overview Platform-agnostic driver for the AMS/Sciosense TMF8806 direct time-of-flight (dToF) sensor with integrated VCSEL emitter and SPAD detector array. Key specifications: - Distance measurement up to 5000 mm (short-range, 2.5 m, and 5 m modes) - 16-bit distance output in millimeters - Configurable measurement period (30 ms to 2000 ms, or single-shot) - Configurable iteration count for accuracy vs. speed trade-off - 6-bit reliability indicator (0 = no object, 63 = highest confidence) - On-chip factory calibration with host-side storage and reload - Algorithm state save/restore for ultra-low-power resume - Embedded 8-bit temperature sensor Datasheet: https://ams.com/tmf8806 ### Examples #### Quick start ```c #include "core.h" #include "core_tiles.h" #include "tile_sense_tof.h" tile_t tof; sense_tof_cfg_t cfg = { .mode = SENSE_TOF_RANGE_2500MM }; tile_sense_tof_init(core_tiles_pal(&core_i2c1), 0, &tof, &cfg); tile_sense_tof_start(&tof); // ... poll or wait for interrupt ... uint16_t dist = tile_sense_tof_get_distance_mm(&tof); // Single-shot convenience: sense_tof_result_t res; tile_sense_tof_measure_single(&tof, &res, 500); ``` ### API 37 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_sense_tof_sleep` — Studio ```c void tile_sense_tof_sleep(tile_t *tile) ``` Enter standby mode (ENABLE = 0x00). Stops any active measurement and powers down the sensor. Use tile_sense_tof_wake() to resume without full re-initialisation. - `tile` — Initialised tile handle. ##### `tile_sense_tof_wake` — Studio ```c void tile_sense_tof_wake(tile_t *tile) ``` Resume from standby mode. Re-executes the bootloader wake and App0 request sequence. Does not restart measurements — call tile_sense_tof_start() after waking. - `tile` — Sleeping tile handle. ##### `tile_sense_tof_reset` — Studio ```c void tile_sense_tof_reset(tile_t *tile) ``` Reset the device via the CPU reset bit in ENABLE. Performs a full CPU reset and re-runs the boot sequence. All runtime state including calibration is lost. Call init() again after reset. - `tile` — Tile handle. #### Runtime ##### `tile_sense_tof_start` — Studio ```c void tile_sense_tof_start(tile_t *tile) ``` Start continuous or single-shot measurement. Writes the factory calibration data (if loaded), configures the measurement command payload from the current cfg, and issues the measurement command. Results are signaled via the result interrupt. If period_ms == 0x00 in the config, a single measurement is taken. Otherwise measurements repeat at the configured period. - `tile` — Initialised tile handle. ##### `tile_sense_tof_stop` — Studio ```c void tile_sense_tof_stop(tile_t *tile) ``` Stop an active measurement. Sends the stop command and waits for the sensor to acknowledge. No-op if no measurement is active. - `tile` — Initialised tile handle. ##### `tile_sense_tof_get_distance_mm` — Studio ```c uint16_t tile_sense_tof_get_distance_mm(tile_t *tile) ``` Read the distance from the most recent result. Returns the peak distance in millimeters. Does not check whether new data is available — call tile_sense_tof_result_ready() first or use tile_sense_tof_get_result() for full status. When nothing is in range the sensor reports 0; this SATURATES that to tile_sense_tof_max_range_mm() rather than returning 0, so `if (distance < threshold)` behaves correctly with no special case — "out of range" reads as "very far away", which is what it physically means. Use tile_sense_tof_get_result() when you need to tell "no target" from "target at max range"; that reports the raw value. - `tile` — Initialised tile handle. **Returns:** Distance in millimeters, saturating at the configured max range. ##### `tile_sense_tof_max_range_mm` — Studio ```c uint16_t tile_sense_tof_max_range_mm(tile_t *tile) ``` Maximum measurable distance for the configured range mode. 200 mm (short range), 2500 mm, or 5000 mm. This is the value tile_sense_tof_get_distance_mm() saturates to when no object is detected, so `distance >= max_range_mm()` is the explicit "nothing in range" test. - `tile` — Initialised tile handle. **Returns:** Maximum range in millimeters. ##### `tile_sense_tof_get_result_flat` — Studio ```c void tile_sense_tof_get_result_flat(tile_t *tile, int32_t *out) ``` DSL-friendly flat-output variant of get_result(). Drops the struct in favor of a positional int[5] array — the DSL doesn't have a struct ABI yet, so the per-field outputs come back indexed. Layout: out[0]=distance_mm, out[1]=status, out[2]=reliability, out[3]=temperature_c, out[4]=result_number. The temperature is widened from int8_t to int32_t so negative values sign-extend correctly. - `tile` — Initialised tile handle. - `out` — Output buffer (5 int32_t slots). ##### `tile_sense_tof_measure_single_flat` — Studio ```c uint8_t tile_sense_tof_measure_single_flat(tile_t *tile, int32_t *mm, int32_t *status, int32_t *reliability, int32_t *temp_c, int32_t *seq, uint32_t timeout_ms) ``` DSL-friendly flat-output variant of measure_single(). Combines a single-shot measurement with positional int outputs. Same layout convention as get_result_flat — the per-field values land in five out-scalar slots. Returns the chip's success flag. - `tile` — Initialised tile handle. - `mm` — Output: distance in millimetres. - `status` — Output: result status code. - `reliability` — Output: 0–63 reliability score. - `temp_c` — Output: die temperature in degrees Celsius. - `seq` — Output: monotonic result counter. - `timeout_ms` — Maximum wait for the measurement to complete. **Returns:** 1 on a valid result, 0 on timeout / bus error. ##### `tile_sense_tof_result_ready` — Studio ```c uint8_t tile_sense_tof_result_ready(tile_t *tile) ``` Check if a new measurement result is available. Reads the INT_STATUS register and checks the result interrupt bit. Does not clear the interrupt — that is done by get_result() or get_distance_mm(). - `tile` — Initialised tile handle. **Returns:** 1 if a new result is pending, 0 otherwise. ##### `tile_sense_tof_set_distance_mode` — Studio ```c void tile_sense_tof_set_distance_mode(tile_t *tile, sense_tof_distance_mode_t mode) ``` Change the distance mode on the fly. Stops any active measurement, updates the cached mode, and restarts. If no measurement was running, only updates the config for the next start(). - `tile` — Initialised tile handle. - `mode` — New distance mode. ##### `tile_sense_tof_set_period` — Studio ```c void tile_sense_tof_set_period(tile_t *tile, uint8_t period_ms) ``` Change the measurement repetition period on the fly. Stops any active measurement, updates the cached period, and restarts. If no measurement was running, only updates the config for the next start(). - `tile` — Initialised tile handle. - `period_ms` — New period code (0x00=single, 0x1E=30ms, 0xFE=1s, 0xFF=2s). ##### `tile_sense_tof_set_kilo_iters` — Studio ```c void tile_sense_tof_set_kilo_iters(tile_t *tile, uint16_t kilo_iters) ``` Change the per-measurement iteration count on the fly. Iterations (in thousands) trade power for SNR/range: more iterations give a stronger return and longer reach at higher current draw. Typical range 10-4000 (10k-4M); the ranging default is 900. Stops any active measurement, updates the cached value, and restarts. - `tile` — Initialised tile handle. - `kilo_iters` — Iterations in thousands (e.g. 900 = 900k). ##### `tile_sense_tof_set_threshold` — Studio ```c void tile_sense_tof_set_threshold(tile_t *tile, uint8_t threshold) ``` Change the detection threshold on the fly. Sets cmd_data3[5:0] — the minimum confidence for a reported target. Higher values reject weak/spurious returns; 0 reports everything. Stops any active measurement, updates the cached value, and restarts. - `tile` — Initialised tile handle. - `threshold` — Detection threshold, 0-63. ##### `tile_sense_tof_get_signal_quality_flat` — Studio ```c void tile_sense_tof_get_signal_quality_flat(tile_t *tile, int32_t *reference_hits, int32_t *object_hits, int32_t *crosstalk) ``` Read signal-quality diagnostics into flat out-params (Studio). - `tile` — Initialised tile handle. - `reference_hits` — Reference-channel hit count (or NULL). - `object_hits` — Object-channel hit count (or NULL). - `crosstalk` — Cross-talk count (or NULL). ##### `tile_sense_tof_get_sys_clock_ticks` — Studio ```c uint32_t tile_sense_tof_get_sys_clock_ticks(tile_t *tile) ``` Read the chip's 32-bit system-clock tick counter. The TMF8806's internal oscillator can drift ±5 % over temperature, which biases distance readings if the host's measurement period doesn't compensate. Reading this register set after each measurement lets the host compute the actual elapsed chip-time vs. its own elapsed wall-time and apply a software correction (HostDriverCommunication §10). Reads SYS_CLOCK_0..3 (0x24–0x27) as a single 4-byte burst. measurement yet). - `tile` — Initialised tile handle. **Returns:** 32-bit system-clock tick count (0 if not in App0 / no ##### `tile_sense_tof_is_object_within` — Studio ```c uint8_t tile_sense_tof_is_object_within(tile_t *tile, uint16_t mm) ``` Single-shot measurement; true if an object is within `mm`. Performs one blocking single-shot measurement (up to 200 ms) and returns 1 iff the reported distance is non-zero, less than or equal to `mm`, and the reliability is at least low-confidence hit is treated as "no object". Restores the prior measurement period on the way out, so this mixes safely with continuous-mode use. 0 otherwise (no target, low reliability, or bus timeout). - `tile` — Initialised tile handle. - `mm` — Distance threshold in millimetres (inclusive). **Returns:** 1 if an object is within range with adequate confidence, ##### `tile_sense_tof_wait_for_object` — Studio ```c uint8_t tile_sense_tof_wait_for_object(tile_t *tile, uint16_t mm, uint32_t timeout_ms) ``` Block until an object is detected within `mm`, or timeout. Polls single-shot measurements until one matches the `is_object_within` predicate or `timeout_ms` elapses. Polls every single-shot measurement adds ~30 ms of its own. - `tile` — Initialised tile handle. - `mm` — Distance threshold in millimetres (inclusive). - `timeout_ms` — Maximum wait time in milliseconds. **Returns:** 1 if an object entered range before timeout, 0 otherwise. **Note:** v1 implementation polls — keeps the helper self-contained and avoids the need to wire the chip's INT pin into the tile pad map. A future revision could swap to the chip's threshold-INT (see @ref tile_sense_tof_set_threshold_interrupt) to let a sleeping host stay asleep until proximity wakes it. ##### `tile_sense_tof_read_distance_with_confidence` — Studio ```c uint8_t tile_sense_tof_read_distance_with_confidence(tile_t *tile, uint16_t *mm, uint8_t *confidence_pct) ``` Read distance and confidence in one call. Performs a single-shot measurement and writes the distance (mm) and the confidence remapped from the chip's 0–63 reliability scale to a 0–100 percent value. Confidence is computed as `(reliability * 100) / 63` — integer math, no floats. - `tile` — Initialised tile handle. - `mm` — Output: distance in millimetres (NULL allowed). - `confidence_pct` — Output: 0–100 confidence (NULL allowed). **Returns:** 1 on a successful measurement, 0 on timeout / bus error. #### Configuration ##### `tile_sense_tof_set_threshold_interrupt` — Studio ```c uint8_t tile_sense_tof_set_threshold_interrupt(tile_t *tile, uint8_t persistence, uint16_t low_mm, uint16_t high_mm) ``` Configure threshold-based interrupt suppression. Without this configured, the chip fires INT on every measurement completion. With it: INT only fires when an object is detected in the [low_mm, high_mm] range for `persistence` consecutive measurements. Lets a sleeping host stay asleep until something gets close. Per HostDriverCommunication §8.12 (cmd 0x08 = WR_ADD_CONFIG): - persistence = 0 → interrupt every measurement (default) - persistence = N → require N consecutive in-range hits - low_mm > high_mm → no interrupts (no valid range) - `tile` — Initialised tile handle. - `persistence` — 0–255; 0 = disabled (every-measurement INT). - `low_mm` — Lower bound (inclusive), millimetres. - `high_mm` — Upper bound (inclusive), millimetres. **Returns:** 1 on success, 0 on bus / command-execution timeout. #### Advanced ##### `tile_sense_tof_factory_calibrate` — Studio ```c uint8_t tile_sense_tof_factory_calibrate(tile_t *tile, uint32_t timeout_ms) ``` Run the factory calibration procedure. Performs a calibration measurement using the current mode settings. The sensor must be positioned with a known target or open field per the TMF8806 calibration guidelines. Results are stored internally and can be retrieved with tile_sense_tof_get_calibration(). This is a blocking call that waits for the calibration to complete. - `tile` — Initialised tile handle. - `timeout_ms` — Maximum wait time in milliseconds. **Returns:** 1 if calibration completed successfully, 0 on timeout or error. ##### `tile_sense_tof_set_calibration` — Studio ```c void tile_sense_tof_set_calibration(tile_t *tile, const uint8_t *data) ``` Load factory calibration data into the driver. Stores a 14-byte calibration dataset that will be written to the sensor before each measurement start. Call this after init() to restore calibration from non-volatile storage. - `tile` — Initialised tile handle. - `data` — Pointer to 14-byte calibration data array. ##### `tile_sense_tof_get_calibration` — Studio ```c void tile_sense_tof_get_calibration(tile_t *tile, uint8_t *data) ``` Read the current factory calibration data from the sensor. Reads 14 bytes of calibration data from the sensor's calibration registers. Typically called after tile_sense_tof_factory_calibrate() to save the data for later reloading via set_calibration(). - `tile` — Initialised tile handle. - `data` — Output buffer for 14 bytes of calibration data. ##### `tile_sense_tof_get_app_version_flat` — Studio ```c void tile_sense_tof_get_app_version_flat(tile_t *tile, int32_t *major, int32_t *minor, int32_t *patch) ``` DSL-friendly flat-output variant of get_app_version(). Drops the struct in favor of three positional out-scalars. - `tile` — Initialised tile handle. - `major` — Output: major version number. - `minor` — Output: minor version number. - `patch` — Output: patch version number. ##### `tile_sense_tof_get_serial_number_flat` — Studio ```c void tile_sense_tof_get_serial_number_flat(tile_t *tile, int32_t *out) ``` DSL-friendly flat-output variant of get_serial_number(). Drops the success bool — on bus error, the buffer comes back as all-zeros, which is invalid as a real serial number so callers can detect failure by checking for a zero serial. Bytes widen to int32 for DSL int compatibility. - `tile` — Initialised tile handle. - `out` — Output buffer (4 int32_t slots). ##### `tile_sense_tof_save_state` — Studio ```c void tile_sense_tof_save_state(tile_t *tile, uint8_t *data) ``` Save the algorithm state from the sensor. Reads 11 bytes of algorithm state data from the sensor (registers 0x28-0x32). This state should be saved before entering sleep in ultra-low-power mode, and restored after wake via restore_state(). Preserving algorithm state across power cycles avoids the ~8 ms re-initialisation penalty and maintains measurement accuracy. - `tile` — Initialised tile handle (measurement should be stopped). - `data` — Output buffer for 11 bytes of state data. ##### `tile_sense_tof_restore_state` — Studio ```c void tile_sense_tof_restore_state(tile_t *tile, const uint8_t *data) ``` Restore algorithm state to the sensor. Writes 11 bytes of previously saved algorithm state to the sensor (registers 0x2E-0x38). Call this after wake() and before start() to resume from the saved algorithm state. The calibration data bitmask in the measurement command (cmd_data7) is automatically updated to include algState when state data has been restored. - `tile` — Initialised tile handle (after wake, before start). - `data` — Pointer to 11 bytes of previously saved state data. ##### `tile_sense_tof_get_threshold_interrupt` — Studio ```c uint8_t tile_sense_tof_get_threshold_interrupt(tile_t *tile, uint8_t *persistence, uint16_t *low_mm, uint16_t *high_mm) ``` Read back the current threshold-interrupt configuration. Per HostDriverCommunication §8.12.2 (cmd 0x09 = RD_ADD_CONFIG). - `tile` — Initialised tile handle. - `persistence` — Output (may be NULL). - `low_mm` — Output (may be NULL). - `high_mm` — Output (may be NULL). **Returns:** 1 on success, 0 on timeout. ##### `tile_sense_tof_read_histogram_flat` — Studio ```c void tile_sense_tof_read_histogram_flat(tile_t *tile, uint8_t hist_type, uint32_t timeout_ms, int32_t *out) ``` DSL-friendly flat-output variant of read_histogram(). Drops the success bool — on timeout / bus error the buffer comes back zero-filled. Bytes widen to int32 for DSL int compatibility. Caller passes hist_type and timeout_ms as scalar args; the buffer is the function's output (collapsed into the int[128] return). - `tile` — Initialised tile handle. - `hist_type` — Histogram-type byte (see read_histogram() docs). - `timeout_ms` — Maximum wait for the histogram-ready interrupt. - `out` — Output buffer (128 int32_t slots). #### Other ##### `tile_sense_tof_find` ```c uint8_t tile_sense_tof_find(tiles_pal_t *hal, uint8_t instance) ``` Check if a Sense.TOF tile is present on the bus. Probes the I2C address and reads the ID register. The TMF8806 has a single fixed address (0x41), so only instance 0 is valid. - `hal` — Platform HAL handle. - `instance` — Must be 0 (single-address device). **Returns:** 1 if device ACKs and ID matches (0x09), 0 otherwise. ##### `tile_sense_tof_init` ```c void tile_sense_tof_init(tiles_pal_t *hal, uint8_t instance, tile_t *tile, const sense_tof_cfg_t *cfg) ``` Initialise a Sense.TOF tile. Performs the full TMF8806 boot sequence: 1. Waits for bootloader to enter sleep (ENABLE == 0x00) 2. Wakes the bootloader (PON = 1) 3. Waits for CPU ready (ENABLE == 0x41) 4. Requests App0 measurement application 5. Waits for App0 to start (APPID == 0xC0) 6. Enables result interrupt Does NOT start measurements — call tile_sense_tof_start() after init. Does NOT software-reset to preserve any existing calibration data. - `hal` — Platform HAL handle. - `instance` — Must be 0 (single-address device). - `tile` — Tile handle to initialise. - `cfg` — Configuration (NULL for defaults: 2.5 m, 30 ms, 900k iters). ##### `tile_sense_tof_measure_single` ```c uint8_t tile_sense_tof_measure_single(tile_t *tile, sense_tof_result_t *result, uint32_t timeout_ms) ``` Perform a blocking single-shot measurement. Temporarily overrides the repetition period to single-shot mode, starts a measurement, polls for the result interrupt, reads the result, and restores the original period setting. - `tile` — Initialised tile handle. - `result` — Output: measurement result (may be NULL to discard). - `timeout_ms` — Maximum wait time in milliseconds. **Returns:** 1 if a valid result was obtained, 0 on timeout or error. ##### `tile_sense_tof_get_result` ```c void tile_sense_tof_get_result(tile_t *tile, sense_tof_result_t *result) ``` Read the full measurement result. Reads distance, status, reliability, temperature, and result number from the sensor in a single bus transaction. Clears the result interrupt flag after reading. - `tile` — Initialised tile handle. - `result` — Output struct to populate. ##### `tile_sense_tof_get_app_version` ```c void tile_sense_tof_get_app_version(tile_t *tile, sense_tof_version_t *version) ``` Read the application firmware version. Returns the major, minor, and patch version of the App0 measurement application running on the TMF8806. - `tile` — Initialised tile handle. - `version` — Output struct to populate. ##### `tile_sense_tof_get_serial_number` ```c uint8_t tile_sense_tof_get_serial_number(tile_t *tile, uint8_t *serial) ``` Read the device serial number. Issues the serial number command (0x47) and reads back the response. The serial number is a 4-byte unique device identifier. - `tile` — Initialised tile handle. - `serial` — Output buffer for 4 bytes of serial data. **Returns:** 1 if serial number was read successfully, 0 on error. ##### `tile_sense_tof_get_signal_quality` ```c void tile_sense_tof_get_signal_quality(tile_t *tile, sense_tof_signal_t *sig) ``` Read per-measurement signal-quality diagnostics. Reads the reference/object hit counts and crosstalk from the most recent result block. Useful for assessing return strength and reflectance beyond the reliability byte. Call after a result is ready. - `tile` — Initialised tile handle. - `sig` — Caller-allocated struct, populated on return. ##### `tile_sense_tof_read_histogram` ```c uint8_t tile_sense_tof_read_histogram(tile_t *tile, uint8_t hist_type, uint8_t *buf128, uint32_t timeout_ms) ``` Capture and read a raw histogram from the SPAD/TDC array. Useful for custom multi-target detection, reflectance estimation, or peak-curvature analysis beyond what App0's distance API provides. Per HostDriverCommunication §8.11. Procedure (handled internally): stop any running measurement, configure histogram-readout mode (cmd 0x30), start cyclic measurement, wait for INT_STATUS bit 1, issue 0x80 to start the histogram block read, wait for TID to advance, then read 128 bytes from the histogram register. After this returns, the chip stays in histogram mode. Call `tile_sense_tof_stop()` and re-issue the original measurement config to return to normal distance reads. §8.11 / TMF8806 datasheet for available types; 0x10 = short-range example). - `tile` — Initialised tile handle. - `hist_type` — Histogram type byte (see HostDriverCommunication - `buf128` — Caller-supplied 128-byte buffer. - `timeout_ms` — Maximum wait for the histogram-ready interrupt. **Returns:** 1 on success (buf128 populated), 0 on timeout / bus error. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### 10 m extended-range mode (common) Deferred to a dedicated session. TMF8806 App0 firmware supports up to 5 m range out of ROM; 10 m mode requires downloading a binary RAM patch from AMS via the bootloader's W_RAM + RAMREMAP_RESET protocol — non-trivial firmware-loading flow not in scope for this driver-coverage pass. #### GPIO0 / GPIO1 runtime control (niche) GPIO0 is routed to tile pad 3 but is consumed as a hardware strap: a 100 k on-board pull-up to V+ holds GPIO0 high at startup, selecting the 1.8-3.3 V digital-I/O level required by this tile's 2.7-3.6 V rail (TMF8806 datasheet §6.7). It is NOT an I2C-address strap (the address is fixed at 0x41, changed only via command 0x49). Because the strap fixes GPIO0 high, runtime GPIO0 output modes (object-detect / VCSEL-sync, command 0x0F) are not exposed — driving the pad would fight the pull-up and risk changing the I/O level. GPIO1 is not routed to a pad. ### Enums #### `sense_tof_distance_mode_t` Distance mode selection. | Member | Value | Description | | --- | --- | --- | | `SENSE_TOF_SHORT_RANGE` | `0` | Short range, ~200 mm max, lowest power | | `SENSE_TOF_RANGE_2500MM` | `1` | Medium range up to 2500 mm (default) | | `SENSE_TOF_RANGE_5000MM` | `2` | Long range up to 5000 mm | ### Structs #### `sense_tof_cfg_t` Optional init-time configuration for Sense.TOF. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `mode` | sense_tof_distance_mode_t (default: RANGE_2500MM). | | `uint8_t` | `period_ms` | Repetition period code (default: 0x1E = 30 ms). 0x00 = single shot, 0xFE = 1 s, 0xFF = 2 s. | | `uint16_t` | `kilo_iters` | Iterations in thousands (default: 900). Higher values improve SNR at the cost of power. | | `uint8_t` | `threshold` | Detection threshold, 0-63 (default: 6). Lower values increase sensitivity. | #### `sense_tof_result_t` Measurement result from a single ToF capture. | Type | Field | Description | | --- | --- | --- | | `uint16_t` | `distance_mm` | Peak distance in millimeters. 0 if no target. | | `uint8_t` | `status` | Result status. 0x00-0x0F = valid, 0x10+ = error. | | `uint8_t` | `reliability` | Confidence indicator, 0-63. 0 = no object, 63 = highest confidence. | | `int8_t` | `temperature` | Die temperature in degrees Celsius. | | `uint8_t` | `result_number` | Monotonically incrementing result counter. | #### `sense_tof_version_t` Application firmware version reported by the TMF8806. | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `major` | Major version number. | | `uint8_t` | `minor` | Minor version number. | | `uint8_t` | `patch` | Patch version number. | #### `sense_tof_signal_t` Per-measurement signal-quality diagnostics from the TMF8806. | Type | Field | Description | | --- | --- | --- | | `uint32_t` | `reference_hits` | Reference-channel photon hits (regs 0x34-0x37). | | `uint32_t` | `object_hits` | Object-channel photon hits (regs 0x38-0x3B). | | `uint16_t` | `crosstalk` | Cross-talk count (regs 0x3C-0x3D). | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_SENSE_TOF_VERSION_MAJOR` | `1` | | | `TILE_SENSE_TOF_VERSION_MINOR` | `4` | | | `TILE_SENSE_TOF_VERSION_PATCH` | `1` | | | `TMF8806_I2C_ADDR` | `0x41` | Fixed 7-bit I2C address | | `TMF8806_DEVICE_ID` | `0x09` | Expected ID value (bits 5:0 only) | | `TMF8806_ID_MASK` | `0x3F` | Mask for valid ID bits | | `TMF8806_ENABLE_PON` | `0x01` | Power-on bit | | `TMF8806_ENABLE_CPU_READY` | `0x41` | CPU ready + PON | | `TMF8806_ENABLE_CPU_RESET` | `0x80` | CPU reset bit | | `TMF8806_APPID_BOOTLOADER` | `0x80` | Bootloader is running | | `TMF8806_APPID_APP0` | `0xC0` | Measurement application running | | `TMF8806_CMD_MEASURE` | `0x02` | Start measurement | | `TMF8806_CMD_FACTORY_CAL` | `0x0A` | Run factory calibration | | `TMF8806_CMD_STOP` | `0xFF` | Stop measurement | | `TMF8806_CMD_SERIAL` | `0x47` | Read serial number | | `TMF8806_FACTORY_CAL_KITERS` | `0xA000` | | | `TMF8806_INT_RESULT` | `0x01` | Result interrupt flag | | `TMF8806_INT_HISTOGRAM` | `0x02` | Histogram interrupt flag | | `TMF8806_CONTENTS_RESULT` | `0x55` | Result data available | | `TMF8806_CONTENTS_CALIB` | `0x0A` | Calibration data available | | `TMF8806_CONTENTS_SERIAL` | `0x47` | Serial number available | | `TMF8806_CALIB_DATA_LEN` | `14` | Factory calibration data length | | `TMF8806_STATE_DATA_LEN` | `11` | Algorithm state data length | | `TMF8806_BOOT_TIMEOUT_MS` | `500` | Maximum boot sequence wait | | `TMF8806_CMD_TIMEOUT_MS` | `1000` | Maximum command completion wait | | `TMF8806_POLL_INTERVAL_MS` | `2` | Polling interval during boot | | `SENSE_TOF_PRESENCE_RELIABILITY_MIN` | `32` | | | `SENSE_TOF_WAIT_POLL_INTERVAL_MS` | `10` | | --- Generated 2026-08-12 from the driver header at tiles@be0209a. Canonical HTML: https://bergsonne.io/docs/tiles/sense/tof ============================================================================== ## Store.O.128 driver > Store.O.128 tile driver — Renesas/Adesto AT25QL128A 128 Mbit QSPI NOR flash. | | | | --- | --- | | Tile | `Store.O.128` | | Driver version | `1.0.0` | | Header | `tile_store_o_128.h` | | Generated from | `tiles@84075f1` | | Canonical page | https://bergsonne.io/docs/tiles/store/o128 | ### Overview 16 MB serial NOR flash on SPI / Quad-SPI. Standard SPI-NOR command set: JEDEC ID, status, write-enable, page program (256 B pages), 4 KB sector / 64 KB block / chip erase, read, deep power-down. Platform-agnostic: uses the framework's raw SPI transaction primitive (`tiles_pal_t.spi_transfer`) so command + 24-bit address + data fit one CS assertion. A platform that doesn't implement spi_transfer yet leaves it NULL; every call here guards against that and reports an error rather than faulting. Datasheet: Renesas AT25QL128A. ### Examples #### Quick start ```c #include "core_tiles.h" tile_t flash; uint8_t buf[256]; tile_store_o_128_init(core_tiles_pal(&core_spi1), 0, &flash, NULL); if (tile_is_ready(&flash)) { tile_store_o_128_read(&flash, 0x000000, buf, sizeof buf); } ``` ### API 17 public functions. Functions marked **Studio** are callable from the Studio behaviour DSL. #### Lifecycle ##### `tile_store_o_128_deep_power_down` — Studio ```c void tile_store_o_128_deep_power_down(tile_t* tile) ``` Enter Deep Power-Down (lowest standby current; commands ignored). ##### `tile_store_o_128_release` — Studio ```c void tile_store_o_128_release(tile_t* tile) ``` Release from Deep Power-Down. #### Runtime ##### `tile_store_o_128_read_jedec_id` — Studio ```c uint32_t tile_store_o_128_read_jedec_id(tile_t* tile) ``` Read the 24-bit JEDEC ID (manufacturer << 16 | type << 8 | capacity). **Returns:** Packed JEDEC ID (e.g. 0x1F4218 for the AT25QL128A). ##### `tile_store_o_128_read_status` — Studio ```c uint8_t tile_store_o_128_read_status(tile_t* tile) ``` Read Status Register 1. **Returns:** SR1 byte (AT25QL128A_SR1_* bits). ##### `tile_store_o_128_is_busy` — Studio ```c uint8_t tile_store_o_128_is_busy(tile_t* tile) ``` Whether a program/erase is in progress (WIP bit). **Returns:** 1 if busy, 0 if idle. ##### `tile_store_o_128_get_capacity` — Studio ```c uint32_t tile_store_o_128_get_capacity(tile_t* tile) ``` Total flash capacity in bytes. **Returns:** 16777216 (16 MB). ##### `tile_store_o_128_read` — Studio ```c void tile_store_o_128_read(tile_t* tile, uint32_t addr, uint8_t* buf, uint16_t len) ``` Read `len` bytes starting at `addr` (READ 0x03). - `addr` — Byte address (0 .. capacity-1). - `buf` — Output buffer. - `len` — Number of bytes. ##### `tile_store_o_128_fast_read` — Studio ```c void tile_store_o_128_fast_read(tile_t* tile, uint32_t addr, uint8_t* buf, uint16_t len) ``` Fast-read `len` bytes from `addr` (FAST_READ 0x0B, 1 dummy byte). ##### `tile_store_o_128_page_program` — Studio ```c void tile_store_o_128_page_program(tile_t* tile, uint32_t addr, const uint8_t* buf, uint16_t len) ``` Program up to one page (≤256 B) within a single page boundary. Issues Write-Enable, programs, and waits for completion. The caller must keep the write inside one 256-byte page (addr & 0xFF) + len ≤ 256. - `addr` — Start address. - `buf` — Data to program. - `len` — Byte count (1..256). ##### `tile_store_o_128_write` — Studio ```c void tile_store_o_128_write(tile_t* tile, uint32_t addr, const uint8_t* buf, uint32_t len) ``` Program an arbitrary-length span, splitting across page boundaries. ##### `tile_store_o_128_erase_sector` — Studio ```c void tile_store_o_128_erase_sector(tile_t* tile, uint32_t addr) ``` Erase the 4 KB sector containing `addr` (and wait). ##### `tile_store_o_128_erase_block` — Studio ```c void tile_store_o_128_erase_block(tile_t* tile, uint32_t addr) ``` Erase the 64 KB block containing `addr` (and wait). ##### `tile_store_o_128_erase_chip` — Studio ```c void tile_store_o_128_erase_chip(tile_t* tile) ``` Erase the entire chip (and wait). ##### `tile_store_o_128_wait_ready` — Studio ```c uint8_t tile_store_o_128_wait_ready(tile_t* tile, uint32_t timeout_ms) ``` Block until any in-progress program/erase finishes, or timeout. - `timeout_ms` — Max wait. **Returns:** 1 if ready, 0 if timed out. #### Configuration ##### `tile_store_o_128_write_enable` — Studio ```c void tile_store_o_128_write_enable(tile_t* tile) ``` Issue Write-Enable (sets WEL). Required before any program/erase. #### Other ##### `tile_store_o_128_find` ```c uint8_t tile_store_o_128_find(tiles_pal_t* hal, uint8_t instance) ``` Probe instance `instance`: reads the JEDEC ID, returns 1 if it matches. ##### `tile_store_o_128_init` ```c void tile_store_o_128_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const store_o_128_cfg_t* cfg) ``` Initialize: verify JEDEC identity, wake from deep power-down. ### Driver gaps Chip capabilities this driver does **not** expose. Each is tagged hardware-gated (the tile does not route the pin) or driver-deferred (possible, not yet implemented). Do not assume an API exists for these. #### Quad / QPI I/O modes (niche) The AT25QL128A supports Dual/Quad SPI and QPI (≤4 data lines). This driver drives standard single-lane SPI only; quad modes need a quad-capable SPI transfer in the platform layer (driver-deferred, not chip-gated). ### Structs #### `store_o_128_cfg_t` Optional init config (pass NULL for defaults). | Type | Field | Description | | --- | --- | --- | | `uint8_t` | `reserved` | | ### Defines | Name | Value | Description | | --- | --- | --- | | `TILE_STORE_O_128_VERSION_MAJOR` | `1` | | | `TILE_STORE_O_128_VERSION_MINOR` | `0` | | | `TILE_STORE_O_128_VERSION_PATCH` | `0` | | | `AT25QL128A_CAPACITY` | `0x1000000` | | | `AT25QL128A_PAGE_SIZE` | `256` | | | `AT25QL128A_SECTOR_SIZE` | `4096` | | | `AT25QL128A_BLOCK_SIZE` | `65536` | | | `AT25QL128A_MFR_ID` | `0x1F` | Adesto / Renesas manufacturer | | `AT25QL128A_CAPACITY_ID` | `0x18` | 2^24 bytes density code | | `AT25QL128A_CMD_WREN` | `0x06` | Write Enable | | `AT25QL128A_CMD_WRDI` | `0x04` | Write Disable | | `AT25QL128A_CMD_RDID` | `0x9F` | Read JEDEC ID (3 bytes) | | `AT25QL128A_CMD_RDSR1` | `0x05` | Read Status Register 1 | | `AT25QL128A_CMD_RDSR2` | `0x35` | Read Status Register 2 | | `AT25QL128A_CMD_WRSR` | `0x01` | Write Status Register | | `AT25QL128A_CMD_READ` | `0x03` | Read Data (cmd + 24-bit addr) | | `AT25QL128A_CMD_FAST_READ` | `0x0B` | Fast Read (+1 dummy byte) | | `AT25QL128A_CMD_PP` | `0x02` | Page Program | | `AT25QL128A_CMD_SE` | `0x20` | Sector Erase (4 KB) | | `AT25QL128A_CMD_BE` | `0xD8` | Block Erase (64 KB) | | `AT25QL128A_CMD_CE` | `0xC7` | Chip Erase | | `AT25QL128A_CMD_DP` | `0xB9` | Deep Power-Down | | `AT25QL128A_CMD_RDP` | `0xAB` | Release from Deep Power-Down | | `AT25QL128A_SR1_WIP` | `0x01` | Write In Progress (busy) | | `AT25QL128A_SR1_WEL` | `0x02` | Write Enable Latch | --- Generated 2026-08-12 from the driver header at tiles@84075f1. Canonical HTML: https://bergsonne.io/docs/tiles/store/o128 ============================================================================== ## Core.ADC — SDK reference > Read analog inputs and drive an analog output: ADC raw/millivolt/temperature reads and DAC output — at the core, hal, or ll layer. | | | | --- | --- | | Category | `adc` | | Core header | `core_adc.h` | | HAL header | `hal_adc.h` | | LL header | `ll_adc.h` | | Generated from | `tiles@6bbf0d8` | | Canonical page | https://bergsonne.io/docs/sdk/adc | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_adc_init` ```c core_adc_init(core_adc_t * adc, uint32_t resolution) ``` Initialise the ADC at the given resolution. - `core_adc_t * adc` - `uint32_t resolution` **Returns:** `hal_status_t` #### `core_adc_add` ```c core_adc_add(core_adc_t * adc, uint8_t pad, uint32_t samp) ``` Register a pad as an ADC input with the given sampling speed. - `core_adc_t * adc` - `uint8_t pad` - `uint32_t samp` **Returns:** `hal_status_t` #### `core_adc_read` ```c core_adc_read(core_adc_t * adc, uint8_t pad) ``` Single-shot read — returns raw ADC count. - `core_adc_t * adc` - `uint8_t pad` **Returns:** `uint16_t` #### `core_adc_read_mv` ```c core_adc_read_mv(core_adc_t * adc, uint8_t pad) ``` Single-shot read — returns calibrated millivolts. - `core_adc_t * adc` - `uint8_t pad` **Returns:** `uint32_t` #### `core_adc_read_pad` — Studio ```c core_adc_read_pad(uint8_t pad) ``` Read a pad as raw ADC counts (0–4095 at 12-bit resolution). - `uint8_t pad` — range 1…64 **Returns:** `int` #### `core_adc_read_mv_pad` — Studio ```c core_adc_read_mv_pad(uint8_t pad) ``` Read a pad as calibrated millivolts (uses VREFINT for per-chip accuracy). - `uint8_t pad` — range 1…64 **Returns:** `int` #### `core_adc_temp_decidegc` — Studio ```c core_adc_temp_decidegc(void) ``` Die temperature in tenths of deg C (e.g. 253 = 25.3 C). Dispatches to the default ADC instance. **Returns:** `int` #### `core_adc_vdd_mv` — Studio ```c core_adc_vdd_mv(void) ``` VDD supply voltage in millivolts (via VREFINT). Dispatches to the default ADC instance. **Returns:** `int` #### `core_adc_temp` ```c core_adc_temp(core_adc_t * adc) ``` Die temperature in tenths of deg C (e.g. 253 = 25.3 C). - `core_adc_t * adc` **Returns:** `int32_t` #### `core_adc_vdd` ```c core_adc_vdd(core_adc_t * adc) ``` Actual VDD supply voltage in millivolts via VREFINT. - `core_adc_t * adc` **Returns:** `uint32_t` #### `core_adc_start_dma` ```c core_adc_start_dma(core_adc_t * adc, uint16_t * buf, uint16_t len, hal_callback_t cb, void * ctx) ``` Start continuous conversion with DMA circular buffer. - `core_adc_t * cb` - `uint16_t * ctx` - `uint16_t len` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `core_adc_stop_dma` ```c core_adc_stop_dma(core_adc_t * adc) ``` Stop continuous DMA conversion. - `core_adc_t * adc` **Returns:** `void` #### `core_adc_dma_read` ```c core_adc_dma_read(core_adc_t * adc, uint8_t pad) ``` Read most recent DMA result for a pad. - `core_adc_t * adc` - `uint8_t pad` **Returns:** `uint16_t` ### HAL layer Peripheral-level control with explicit handles. #### `hal_adc_init` ```c hal_adc_init(hal_adc_t * adc, ADC_TypeDef * instance, uint32_t sysclk_hz, hal_adc_res_t res) ``` Initialise an ADC instance. Enables the peripheral clock, exits deep power-down (L4/H5), runs self-calibration, and enables the ADC. Must be called before add_channel or read. ADC input pins must already be configured analog (hal_pad_analog). - `hal_adc_t * adc` - `ADC_TypeDef * instance` - `uint32_t sysclk_hz` - `hal_adc_res_t res` **Returns:** `hal_status_t` #### `hal_adc_add_channel` ```c hal_adc_add_channel(hal_adc_t * adc, uint8_t channel, hal_adc_samp_t samp) ``` Add a channel to the conversion sequence. Channels are converted in the order they are added. Call after hal_adc_init, before hal_adc_read or hal_adc_start_dma. - `hal_adc_t * adc` - `uint8_t channel` - `hal_adc_samp_t samp` **Returns:** `hal_status_t` #### `hal_adc_set_oversample` ```c hal_adc_set_oversample(hal_adc_t * adc, hal_adc_oversample_t ratio) ``` Set hardware oversampling ratio for noise reduction (applies to all channels). The right-shift equals log2(N), so the output word stays at the configured resolution. Can be called after init; takes effect on the next conversion. - `hal_adc_t * adc` - `hal_adc_oversample_t ratio` **Returns:** `hal_status_t` #### `hal_adc_set_oversample_ex` ```c hal_adc_set_oversample_ex(hal_adc_t * adc, hal_adc_oversample_t ratio, uint8_t shift) ``` Set hardware oversampling with explicit right-shift for resolution extension. shift controls how many bits the hardware divides the accumulator by before writing to DR. Valid range: 0 – log2(N) where N is the oversampling ratio. shift = log2(N) → same as hal_adc_set_oversample (noise reduction only) shift < log2(N) → output word is wider; effective_bits increases shift = 0 → raw accumulator in DR; max bit depth Example — 12-bit ADC, 256× oversampling, shift = 4: accumulator = up to 20 bits; DR = accumulator >> 4 = 16-bit result effective_bits = 16; hal_adc_read_mv scales against 65535 automatically. - `hal_adc_t * adc` - `hal_adc_oversample_t ratio` - `uint8_t shift` **Returns:** `hal_status_t` #### `hal_adc_read` ```c hal_adc_read(hal_adc_t * adc, uint8_t channel) ``` Single-shot blocking read of one channel. Returns the raw ADC count (range depends on resolution). The channel must have been added via hal_adc_add_channel. - `hal_adc_t * adc` - `uint8_t channel` **Returns:** `uint16_t` #### `hal_adc_read_mv` ```c hal_adc_read_mv(hal_adc_t * adc, uint8_t channel) ``` Read a channel and convert to millivolts. Uses the VREFINT factory calibration to derive the actual VDDA supply voltage, so the result is correct even when VDD ≠ 3.3 V. The VDDA measurement is cached after the first call. - `hal_adc_t * adc` - `uint8_t channel` **Returns:** `uint32_t` #### `hal_adc_read_temp_decidegc` ```c hal_adc_read_temp_decidegc(hal_adc_t * adc) ``` Read die temperature in tenths of a degree C. Example: returns 253 for 25.3 °C. Uses TS_CAL1 / TS_CAL2 factory calibration values. Automatically adds the internal temperature sensor channel on first call. - `hal_adc_t * adc` **Returns:** `int32_t` #### `hal_adc_read_vdda_mv` ```c hal_adc_read_vdda_mv(hal_adc_t * adc) ``` Read the actual VDD supply voltage in millivolts via VREFINT. Result is accurate regardless of nominal supply. - `hal_adc_t * adc` **Returns:** `uint32_t` #### `hal_adc_read_all` ```c hal_adc_read_all(hal_adc_t * adc, uint16_t * buf) ``` Read all configured channels into buf[]. Buffer order matches the order channels were added via hal_adc_add_channel. buf must be at least adc->n_channels elements. - `hal_adc_t * adc` - `uint16_t * buf` **Returns:** `void` #### `hal_adc_start_dma` ```c hal_adc_start_dma(hal_adc_t * adc, uint16_t * buf, uint16_t len, hal_callback_t callback, void * ctx) ``` Start continuous ADC conversion with DMA circular buffer. callback is invoked at both half-complete and complete events, supporting a double-buffered processing pattern: - At half-complete: process buf[0 .. len/2 - 1] - At complete: process buf[len/2 .. len - 1] Channels must already be configured via hal_adc_add_channel. DMA and ADC peripheral clocks are enabled automatically. - `hal_adc_t * adc` - `uint16_t * buf` - `uint16_t len` - `hal_callback_t callback` - `void * ctx` **Returns:** `hal_status_t` #### `hal_adc_stop_dma` ```c hal_adc_stop_dma(hal_adc_t * adc) ``` Stop DMA conversion and disable the DMA channel. - `hal_adc_t * adc` **Returns:** `void` #### `hal_adc_init_simple` ```c hal_adc_init_simple(hal_adc_t * adc, ADC_TypeDef * instance) ``` - `hal_adc_t * adc` - `ADC_TypeDef * instance` **Returns:** `hal_status_t` #### `hal_adc_read_pad` ```c hal_adc_read_pad(hal_adc_t * adc, uint8_t pad) ``` - `hal_adc_t * adc` - `uint8_t pad` **Returns:** `uint16_t` #### `hal_adc_read_pad_mv` ```c hal_adc_read_pad_mv(hal_adc_t * adc, uint8_t pad) ``` - `hal_adc_t * adc` - `uint8_t pad` **Returns:** `uint32_t` ### LL layer Register-level access. No safety rails. #### `ll_adc_calibrate` ```c ll_adc_calibrate(ADC_TypeDef * adc) ``` Run ADC self-calibration. Must be called before enabling ADC. ADC must be disabled (ADEN=0). - `ADC_TypeDef * adc` **Returns:** `void` #### `ll_adc_init` ```c ll_adc_init(ADC_TypeDef * adc, uint32_t smpr) ``` Initialize ADC for single-shot conversion, 12-bit resolution. adc: ADC instance smpr: sampling time (LL_ADC_SMPR_* — applies to all channels) Prerequisites: - ADC peripheral clock enabled - ADC pins configured as analog via ll_gpio_config_analog() - `ADC_TypeDef * adc` - `uint32_t smpr` **Returns:** `void` #### `ll_adc_read` ```c ll_adc_read(ADC_TypeDef * adc, uint32_t channel) ``` Read a single ADC channel (blocking). adc: ADC instance channel: ADC channel number (0-18, depends on pin mapping) Returns: 12-bit ADC reading (0-4095) - `ADC_TypeDef * adc` - `uint32_t channel` **Returns:** `uint16_t` #### `ll_adc_read_mv` ```c ll_adc_read_mv(ADC_TypeDef * adc, uint32_t channel, uint32_t vref_mv) ``` Read a channel and convert to millivolts. vref_mv: reference voltage in millivolts (typically 3300 for 3.3V) - `ADC_TypeDef * adc` - `uint32_t channel` - `uint32_t vref_mv` **Returns:** `uint32_t` --- Generated 2026-08-12 from the SDK headers at tiles@6bbf0d8. Canonical HTML: https://bergsonne.io/docs/sdk/adc ============================================================================== ## Core.Audio — SDK reference > Capture a digital PDM microphone: the SAI clocks the mic and DMAs its 1-bit bitstream, then a software CIC decimator turns it into PCM — at the core, hal, or ll layer. | | | | --- | --- | | Category | `audio` | | Core header | `core_pdm.h` | | HAL header | `hal_sai.h` | | LL header | `ll_sai.h` | | Generated from | `tiles@1f69849` | | Canonical page | https://bergsonne.io/docs/sdk/audio | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_pdm_init` ```c core_pdm_init(core_pdm_cic_t * st, uint8_t order, uint16_t decimation, uint8_t gain_shift, uint8_t lsb_first) ``` Initialise a CIC decimator. - `core_pdm_cic_t * st` - `uint8_t order` - `uint16_t decimation` - `uint8_t gain_shift` - `uint8_t lsb_first` **Returns:** `void` #### `core_pdm_process` ```c core_pdm_process(core_pdm_cic_t * st, const uint8_t * pdm, uint32_t nbytes, int16_t * out) ``` Decimate a block of packed PDM bits into PCM samples. - `core_pdm_cic_t * st` - `const uint8_t * pdm` - `uint32_t nbytes` - `int16_t * out` **Returns:** `uint32_t` ### HAL layer Peripheral-level control with explicit handles. #### `hal_sai_pdm_init` ```c hal_sai_pdm_init(hal_sai_t * s, SAI_Block_TypeDef * block, const hal_sai_pdm_config_t * cfg) ``` Configure SAI1 block A as a PDM master receiver (block left disabled). - `hal_sai_t * s` - `SAI_Block_TypeDef * block` - `const hal_sai_pdm_config_t * cfg` **Returns:** `hal_status_t` #### `hal_sai_capture_start` ```c hal_sai_capture_start(hal_sai_t * s, GPDMA_Channel_TypeDef * ch, uint8_t * buf, uint32_t len, hal_callback_t callback, void * ctx) ``` Start continuous DMA capture into `buf` (circular). - `hal_sai_t * buf` - `GPDMA_Channel_TypeDef * ch` - `uint8_t * buf` - `uint32_t len` - `hal_callback_t callback` - `void * ctx` **Returns:** `hal_status_t` #### `hal_sai_capture_stop` ```c hal_sai_capture_stop(hal_sai_t * s) ``` Stop capture (SAIEN off, DMA channel disabled). - `hal_sai_t * s` **Returns:** `void` #### `hal_sai_irq_handler` ```c hal_sai_irq_handler(hal_sai_t * s) ``` GPDMA channel ISR body — call from the channel's IRQ handler. - `hal_sai_t * s` **Returns:** `void` ### LL layer Register-level access. No safety rails. #### `ll_sai_block_enable` ```c ll_sai_block_enable(SAI_Block_TypeDef * b) ``` Enable a block — for master RX this immediately starts the bit clock. - `SAI_Block_TypeDef * b` **Returns:** `void` #### `ll_sai_block_disable` ```c ll_sai_block_disable(SAI_Block_TypeDef * b) ``` Disable a block (clear SAIEN) and wait for it to read back 0. - `SAI_Block_TypeDef * b` **Returns:** `void` #### `ll_sai_block_enable_dma` ```c ll_sai_block_enable_dma(SAI_Block_TypeDef * b) ``` Enable peripheral DMA requests (must precede SAIEN). - `SAI_Block_TypeDef * b` **Returns:** `void` #### `ll_sai_flush` ```c ll_sai_flush(SAI_Block_TypeDef * b) ``` Flush the FIFO (one-shot, self-clearing). - `SAI_Block_TypeDef * b` **Returns:** `void` #### `ll_sai_read` ```c ll_sai_read(SAI_Block_TypeDef * b) ``` Read one data word from the FIFO. - `SAI_Block_TypeDef * b` **Returns:** `uint32_t` #### `ll_sai_overrun` ```c ll_sai_overrun(SAI_Block_TypeDef * b) ``` True if an overrun has been latched. - `SAI_Block_TypeDef * b` **Returns:** `int` #### `ll_sai_clear_overrun` ```c ll_sai_clear_overrun(SAI_Block_TypeDef * b) ``` Clear the overrun flag. - `SAI_Block_TypeDef * b` **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@1f69849. Canonical HTML: https://bergsonne.io/docs/sdk/audio ============================================================================== ## Core.BLE — SDK reference > Bluetooth Low Energy on Core.ST.W5: advertise, define a GATT server with read/write/notify characteristics, and handle connections — a Core-layer API over the ST radio stack. | | | | --- | --- | | Category | `ble` | | Core header | `core_ble.h` | | Generated from | `tiles@990ad75` | | Canonical page | https://bergsonne.io/docs/sdk/ble | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_ble_set_services` ```c core_ble_set_services(void) ``` Register a service builder function. Called before core_ble_init(). The builder is invoked during init after the GATT server is ready. **Returns:** `void` #### `core_ble_init` ```c core_ble_init(void) ``` Initialize the BLE stack. Call once after core_init(). **Returns:** `void` #### `core_ble_advertise` ```c core_ble_advertise(const char * name) ``` Start advertising. Call after init + at least one core_ble_process(). - `const char * name` **Returns:** `int` #### `core_ble_stop_advertise` ```c core_ble_stop_advertise(void) ``` Stop advertising. **Returns:** `int` #### `core_ble_process` ```c core_ble_process(void) ``` Process BLE events. Call continuously from main loop. **Returns:** `void` #### `core_ble_add_service` ```c core_ble_add_service(const char * name) ``` 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. - `const char * name` **Returns:** `core_ble_svc_t` #### `core_ble_add_service_id` ```c core_ble_add_service_id(const char * name, uint16_t id) ``` Add a GATT service with an EXPLICIT 16-bit ID. The full UUID is 0000-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. - `const char * name` - `uint16_t id` **Returns:** `core_ble_svc_t` #### `core_ble_add_char` ```c 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) ``` Add a characteristic to a service. - `core_ble_svc_t svc` - `const char * name` - `uint8_t access` - `uint8_t type` - `core_ble_write_cb on_write` - `void * ctx` **Returns:** `core_ble_char_t` #### `core_ble_add_char_id` ```c 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) ``` Add a characteristic with an EXPLICIT 16-bit ID (0000-8E22-…), the order-independent counterpart to core_ble_add_char(). Use for the stable contract shared with client apps. - `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` **Returns:** `core_ble_char_t` #### `core_ble_add_service_sig` ```c core_ble_add_service_sig(const char * name, uint16_t uuid16) ``` 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. - `const char * name` - `uint16_t uuid16` **Returns:** `core_ble_svc_t` #### `core_ble_add_char_sig` ```c 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) ``` Add a SIG-adopted characteristic by its 16-bit Bluetooth UUID (e.g. 0x2A19 Battery Level, 0x2A29 Manufacturer Name) to a service. - `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` **Returns:** `core_ble_char_t` #### `core_ble_set_value` ```c core_ble_set_value(core_ble_char_t ch, const void * data, uint16_t len) ``` Update a characteristic's value. For readable characteristics, this is what the central will read. For notify characteristics, call core_ble_notify() after to push the update. - `core_ble_char_t ch` - `const void * data` - `uint16_t len` **Returns:** `int` #### `core_ble_notify` ```c core_ble_notify(core_ble_char_t ch) ``` Send a notification to the connected central. The central must have enabled notifications (CCCD) for this to work. Sends the current value set by core_ble_set_value(). - `core_ble_char_t ch` **Returns:** `int` #### `core_ble_connected` ```c core_ble_connected(void) ``` Returns 1 if a central is connected. **Returns:** `int` #### `core_ble_on_connect` ```c core_ble_on_connect(void * ctx) ``` Set callback for connection events. - `void * ctx` **Returns:** `void` #### `core_ble_on_disconnect` ```c core_ble_on_disconnect(void * ctx) ``` Set callback for disconnection events. - `void * ctx` **Returns:** `void` #### `core_ble_set_tx_power` ```c core_ble_set_tx_power(uint8_t level) ``` TX power: 0=low(-20dBm), 1=medium(0dBm), 2=high(+10dBm). Default: 1. - `uint8_t level` **Returns:** `void` #### `core_ble_set_adv_interval` ```c core_ble_set_adv_interval(uint16_t min_ms, uint16_t max_ms) ``` Advertising interval in ms (20-10240). Default: 100/150. - `uint16_t min_ms` - `uint16_t max_ms` **Returns:** `void` #### `core_ble_set_conn_params` ```c core_ble_set_conn_params(uint16_t min_ms, uint16_t max_ms, uint16_t latency, uint16_t timeout_ms) ``` 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. - `uint16_t min_ms` - `uint16_t max_ms` - `uint16_t latency` - `uint16_t timeout_ms` **Returns:** `void` #### `core_ble_enable_pairing` ```c core_ble_enable_pairing(void) ``` 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. **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@990ad75. Canonical HTML: https://bergsonne.io/docs/sdk/ble ============================================================================== ## Clocks — SDK reference > How the system clock is set up: core_init() brings it to the Core's rated max, the HSI / HSE / MSI / PLL sources, and how clocking differs across architectures. | | | | --- | --- | | Category | `clocks` | | LL header | `ll_rcc.h` | | Generated from | `tiles@6bbf0d8` | | Canonical page | https://bergsonne.io/docs/sdk/clocks | ### LL layer Register-level access. No safety rails. #### `ll_rcc_hsi16_enable` ```c ll_rcc_hsi16_enable(void) ``` Enable the high-speed internal (HSI16) oscillator. **Returns:** `void` #### `ll_rcc_hse_enable` ```c ll_rcc_hse_enable(void) ``` Enable the high-speed external (HSE) oscillator / crystal. **Returns:** `void` #### `ll_flash_set_latency` ```c ll_flash_set_latency(uint32_t wait_states) ``` Set flash read latency (wait states). Must be set BEFORE increasing SYSCLK, or AFTER decreasing it. Typical values for VDD >= 2.7V: L0: 0 WS ≤ 16MHz, 1 WS ≤ 32MHz L4: 0 WS ≤ 16MHz, 1 WS ≤ 32MHz, 2 WS ≤ 48MHz, 3 WS ≤ 64MHz, 4 WS ≤ 80MHz WBA: 0 WS ≤ 32MHz, 1 WS ≤ 64MHz, 2 WS ≤ 96MHz, 3 WS ≤ 100MHz H5: 0 WS ≤ 32MHz, 1 WS ≤ 64MHz, 2 WS ≤ 96MHz, ... 5 WS ≤ 250MHz - `uint32_t wait_states` **Returns:** `void` #### `ll_flash_latency_for_mhz` ```c ll_flash_latency_for_mhz(uint32_t mhz) ``` Calculate required flash wait states from SYSCLK frequency. - `uint32_t mhz` **Returns:** `uint32_t` #### `ll_rcc_pll_config` ```c ll_rcc_pll_config(uint32_t src, uint32_t m, uint32_t n, uint32_t r) ``` Configure and enable the main PLL. src: PLL source (LL_RCC_PLLSRC_*) m: input divider (1-based) n: VCO multiplier r: output divider for SYSCLK PLL must be disabled before calling this function. - `uint32_t src` - `uint32_t m` - `uint32_t n` - `uint32_t r` **Returns:** `void` #### `ll_rcc_pll_enable` ```c ll_rcc_pll_enable(void) ``` Enable the main PLL **Returns:** `void` #### `ll_rcc_pll_ready` ```c ll_rcc_pll_ready(void) ``` Check if PLL is locked and ready **Returns:** `int` #### `ll_rcc_set_sysclk` ```c ll_rcc_set_sysclk(uint32_t source) ``` Set the SYSCLK source - `uint32_t source` **Returns:** `void` #### `ll_rcc_wait_sysclk` ```c ll_rcc_wait_sysclk(uint32_t source) ``` Wait until the SYSCLK source has switched - `uint32_t source` **Returns:** `void` #### `ll_rcc_set_ahb_div` ```c ll_rcc_set_ahb_div(uint32_t div) ``` Set AHB prescaler. div: 1, 2, 4, 8, 16, 64, 128, 256, 512 - `uint32_t div` **Returns:** `void` #### `ll_rcc_set_apb1_div` ```c ll_rcc_set_apb1_div(uint32_t div) ``` Set APB1 prescaler. div: 1, 2, 4, 8, 16 - `uint32_t div` **Returns:** `void` #### `ll_rcc_set_apb2_div` ```c ll_rcc_set_apb2_div(uint32_t div) ``` Set APB2 prescaler. div: 1, 2, 4, 8, 16 - `uint32_t div` **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@6bbf0d8. Canonical HTML: https://bergsonne.io/docs/sdk/clocks ============================================================================== ## Core.Pad — SDK reference > Drive and read tile pads as digital I/O at the layer you want: core_pad by pad number, hal_pad one tier down, or ll_gpio in raw MCU port + pin. | | | | --- | --- | | Category | `pad` | | Core header | `core_pad.h` | | HAL header | `hal_gpio.h` | | LL header | `ll_gpio.h` | | Generated from | `tiles@777be99` | | Canonical page | https://bergsonne.io/docs/sdk/gpio | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_pad_output` ```c core_pad_output(uint8_t pad) ``` Configure a pad as push-pull output (default). - `uint8_t pad` **Returns:** `void` #### `core_pad_output_od` ```c core_pad_output_od(uint8_t pad, uint32_t pull) ``` 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. - `uint8_t pad` - `uint32_t pull` **Returns:** `void` #### `core_pad_input` ```c core_pad_input(uint8_t pad, uint32_t pull) ``` Configure a pad as input with pull resistor. - `uint8_t pad` - `uint32_t pull` **Returns:** `void` #### `core_pad_write` — Studio ```c core_pad_write(uint8_t pad, int state) ``` Set a pad high (ON) or low (OFF). - `uint8_t pad` — range 1…64 - `int state` — range 0…1 **Returns:** `void` #### `core_pad_read` — Studio ```c core_pad_read(uint8_t pad) ``` Read a pad. Returns 0 or 1. - `uint8_t pad` — range 1…64 **Returns:** `int` #### `core_pad_toggle` — Studio ```c core_pad_toggle(uint8_t pad) ``` Toggle a pad output. - `uint8_t pad` — range 1…64 **Returns:** `void` #### `core_pad_resolve` ```c core_pad_resolve(uint8_t pad) ``` Resolve a pad to a fast handle once, outside the hot loop. - `uint8_t pad` **Returns:** `core_pad_fast_t` #### `core_pad_fast_set` ```c core_pad_fast_set(core_pad_fast_t h) ``` Drive the resolved pad high — single BSRR write. - `core_pad_fast_t h` **Returns:** `void` #### `core_pad_fast_clear` ```c core_pad_fast_clear(core_pad_fast_t h) ``` Drive the resolved pad low — single BSRR write. - `core_pad_fast_t h` **Returns:** `void` #### `core_pad_fast_write` ```c core_pad_fast_write(core_pad_fast_t h, int state) ``` Drive the resolved pad to `state` (0/1) — single BSRR write. - `core_pad_fast_t h` - `int state` **Returns:** `void` #### `core_pad_fast_read` ```c core_pad_fast_read(core_pad_fast_t h) ``` Read the resolved pad — single IDR load. Returns 0 or 1. - `core_pad_fast_t h` **Returns:** `int` #### `core_pad_bulk_write` ```c core_pad_bulk_write(GPIO_TypeDef * port, uint32_t set_mask, uint32_t clear_mask) ``` 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. - `GPIO_TypeDef * port` - `uint32_t set_mask` - `uint32_t clear_mask` **Returns:** `void` #### `core_pad_analog` ```c core_pad_analog(uint8_t pad) ``` Configure a pad as analog (for ADC, DAC, comparator). - `uint8_t pad` **Returns:** `void` #### `core_pad_speed` ```c core_pad_speed(uint8_t pad, uint32_t speed) ``` 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. - `uint8_t pad` - `uint32_t speed` **Returns:** `void` #### `core_pad_on_change` ```c core_pad_on_change(uint8_t pad, uint32_t edge, hal_callback_t cb, void * ctx) ``` 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) - `uint8_t pad` - `uint32_t edge` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `core_pad_on_change_stop` ```c core_pad_on_change_stop(uint8_t pad) ``` Stop the interrupt on a pad. - `uint8_t pad` **Returns:** `void` ### HAL layer Peripheral-level control with explicit handles. #### `hal_pad_lookup` ```c hal_pad_lookup(uint8_t pad) ``` Look up the GPIO port and pin for a tile pad number. Returns {NULL, 0} for pads with no GPIO (power, reset, etc.). - `uint8_t pad` **Returns:** `hal_pad_gpio_t` #### `hal_pad_output` ```c hal_pad_output(uint8_t pad) ``` Configure a pad as push-pull output. Enables the port clock. - `uint8_t pad` **Returns:** `void` #### `hal_pad_output_od` ```c hal_pad_output_od(uint8_t pad, uint32_t pull) ``` Configure a pad as open-drain output with pull selection. Enables the port clock. - `uint8_t pad` - `uint32_t pull` **Returns:** `void` #### `hal_pad_input` ```c hal_pad_input(uint8_t pad, uint32_t pull) ``` Configure a pad as input with pull selection. - `uint8_t pad` - `uint32_t pull` **Returns:** `void` #### `hal_pad_analog` ```c hal_pad_analog(uint8_t pad) ``` Configure a pad as analog (for ADC, DAC, comparator). - `uint8_t pad` **Returns:** `void` #### `hal_pad_af` ```c hal_pad_af(uint8_t pad, uint32_t af, uint32_t otype, uint32_t speed, uint32_t pull) ``` Configure a pad for alternate function. - `uint8_t pad` - `uint32_t af` - `uint32_t otype` - `uint32_t speed` - `uint32_t pull` **Returns:** `void` #### `hal_pad_write` ```c hal_pad_write(uint8_t pad, int state) ``` Write a pad: 0 = low, non-zero = high - `uint8_t pad` - `int state` **Returns:** `void` #### `hal_pad_read` ```c hal_pad_read(uint8_t pad) ``` Read a pad. Returns 0 or 1. - `uint8_t pad` **Returns:** `int` #### `hal_pad_toggle` ```c hal_pad_toggle(uint8_t pad) ``` Toggle a pad. - `uint8_t pad` **Returns:** `void` ### LL layer Register-level access. No safety rails. #### `ll_gpio_set` ```c ll_gpio_set(GPIO_TypeDef * port, uint32_t pin_mask) ``` Set one or more pins high (write 1s to BSRR lower half) - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `void` #### `ll_gpio_clear` ```c ll_gpio_clear(GPIO_TypeDef * port, uint32_t pin_mask) ``` Set one or more pins low (write 1s to BSRR upper half) - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `void` #### `ll_gpio_toggle` ```c ll_gpio_toggle(GPIO_TypeDef * port, uint32_t pin_mask) ``` Toggle one or more pins (XOR with ODR) - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `void` #### `ll_gpio_read` ```c ll_gpio_read(GPIO_TypeDef * port, uint32_t pin_mask) ``` Read the input state of one or more pins - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `uint32_t` #### `ll_gpio_read_output` ```c ll_gpio_read_output(GPIO_TypeDef * port, uint32_t pin_mask) ``` Read the output state of one or more pins - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `uint32_t` #### `ll_gpio_set_mode` ```c ll_gpio_set_mode(GPIO_TypeDef * port, uint32_t pin, uint32_t mode) ``` Set the mode for a single pin. mode: LL_GPIO_MODE_INPUT, _OUTPUT, _AF, or _ANALOG - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t mode` **Returns:** `void` #### `ll_gpio_set_output_type` ```c ll_gpio_set_output_type(GPIO_TypeDef * port, uint32_t pin, uint32_t otype) ``` Set the output type for a single pin. otype: LL_GPIO_OTYPE_PP (push-pull) or LL_GPIO_OTYPE_OD (open-drain) - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t otype` **Returns:** `void` #### `ll_gpio_set_speed` ```c ll_gpio_set_speed(GPIO_TypeDef * port, uint32_t pin, uint32_t speed) ``` Set the output speed for a single pin. speed: LL_GPIO_SPEED_LOW, _MED, _HIGH, or _VHIGH - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t speed` **Returns:** `void` #### `ll_gpio_set_pull` ```c ll_gpio_set_pull(GPIO_TypeDef * port, uint32_t pin, uint32_t pull) ``` Set the pull-up/pull-down for a single pin. pull: LL_GPIO_PULL_NONE, _UP, or _DOWN - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t pull` **Returns:** `void` #### `ll_gpio_set_af` ```c ll_gpio_set_af(GPIO_TypeDef * port, uint32_t pin, uint32_t af) ``` Set the alternate function for a single pin (0-15). Pins 0-7 use AFR[0] (AFRL), pins 8-15 use AFR[1] (AFRH). - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t af` **Returns:** `void` #### `ll_gpio_config_output` ```c ll_gpio_config_output(GPIO_TypeDef * port, uint32_t pin) ``` Configure a pin as general-purpose output (push-pull, no pull, medium speed). - `GPIO_TypeDef * port` - `uint32_t pin` **Returns:** `void` #### `ll_gpio_config_input` ```c ll_gpio_config_input(GPIO_TypeDef * port, uint32_t pin, uint32_t pull) ``` Configure a pin as input with optional pull. - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t pull` **Returns:** `void` #### `ll_gpio_config_af` ```c ll_gpio_config_af(GPIO_TypeDef * port, uint32_t pin, uint32_t af, uint32_t otype, uint32_t speed, uint32_t pull) ``` Configure a pin for an alternate function. af: alternate function number (0-15) otype: LL_GPIO_OTYPE_PP or _OD speed: LL_GPIO_SPEED_LOW/MED/HIGH/VHIGH pull: LL_GPIO_PULL_NONE/UP/DOWN - `GPIO_TypeDef * port` - `uint32_t pin` - `uint32_t af` - `uint32_t otype` - `uint32_t speed` - `uint32_t pull` **Returns:** `void` #### `ll_gpio_config_analog` ```c ll_gpio_config_analog(GPIO_TypeDef * port, uint32_t pin) ``` Configure a pin as analog (disconnects from digital logic). - `GPIO_TypeDef * port` - `uint32_t pin` **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@777be99. Canonical HTML: https://bergsonne.io/docs/sdk/gpio ============================================================================== ## Core.I2C — SDK reference > I2C master on every Core: probe and scan the bus, read and write device registers at 100 kHz / 400 kHz / 1 MHz — at the core_i2c, hal_i2c, or ll_i2c layer. | | | | --- | --- | | Category | `i2c` | | Core header | `core_i2c.h` | | HAL header | `hal_i2c.h` | | LL header | `ll_i2c.h` | | Generated from | `tiles@6bbf0d8` | | Canonical page | https://bergsonne.io/docs/sdk/i2c | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `_core_i2c_timing` ```c _core_i2c_timing(uint32_t speed_hz) ``` 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. - `uint32_t speed_hz` **Returns:** `uint32_t` #### `core_i2c_init` ```c core_i2c_init(core_i2c_t * h, I2C_TypeDef * instance, uint32_t speed_hz) ``` 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_t * h` - `I2C_TypeDef * instance` - `uint32_t speed_hz` **Returns:** `hal_status_t` #### `core_i2c_init_cfg` ```c core_i2c_init_cfg(core_i2c_t * h, I2C_TypeDef * instance, const hal_i2c_config_t * cfg) ``` Initialize I2C with explicit config struct (advanced). For most use cases, prefer core_i2c_init(h, instance, speed_hz). - `core_i2c_t * h` - `I2C_TypeDef * instance` - `const hal_i2c_config_t * cfg` **Returns:** `hal_status_t` #### `core_i2c_write` ```c core_i2c_write(core_i2c_t * h, uint8_t addr, const uint8_t * data, uint32_t len) ``` Write data to a 7-bit address device. - `core_i2c_t * h` - `uint8_t addr` - `const uint8_t * data` - `uint32_t len` **Returns:** `hal_status_t` #### `core_i2c_read` ```c core_i2c_read(core_i2c_t * h, uint8_t addr, uint8_t * buf, uint32_t len) ``` Read data from a 7-bit address device. - `core_i2c_t * h` - `uint8_t addr` - `uint8_t * buf` - `uint32_t len` **Returns:** `hal_status_t` #### `core_i2c_write_reg` ```c core_i2c_write_reg(core_i2c_t * h, uint8_t addr, uint16_t reg, const uint8_t * data, uint32_t len) ``` Write to a device register (8 or 16-bit register address). - `core_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `const uint8_t * data` - `uint32_t len` **Returns:** `hal_status_t` #### `core_i2c_read_reg` ```c core_i2c_read_reg(core_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t * buf, uint32_t len) ``` Read from a device register (repeated START). - `core_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `uint8_t * buf` - `uint32_t len` **Returns:** `hal_status_t` #### `core_i2c_write_byte` ```c core_i2c_write_byte(core_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t value) ``` Write a single byte to a register. - `core_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `uint8_t value` **Returns:** `hal_status_t` #### `core_i2c_read_byte` ```c core_i2c_read_byte(core_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t * value) ``` Read a single byte from a register. - `core_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `uint8_t * value` **Returns:** `hal_status_t` #### `core_i2c_probe` ```c core_i2c_probe(core_i2c_t * h, uint8_t addr) ``` Check if a device responds at addr. Returns I2C_OK or I2C_NACK. - `core_i2c_t * h` - `uint8_t addr` **Returns:** `hal_status_t` #### `core_i2c_scan` ```c core_i2c_scan(core_i2c_t * h, uint8_t * found, uint8_t * count, uint8_t max_count) ``` Scan the I2C bus (0x08–0x77). Fills found[] with responding addresses. - `core_i2c_t * h` - `uint8_t * found` - `uint8_t * count` - `uint8_t max_count` **Returns:** `void` #### `core_i2c_write_byte_bus` — Studio ```c core_i2c_write_byte_bus(uint8_t bus, uint8_t addr, uint16_t reg, uint8_t value) ``` 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. - `uint8_t bus` - `uint8_t addr` - `uint16_t reg` - `uint8_t value` **Returns:** `hal_status_t` #### `core_i2c_read_byte_bus` — Studio ```c core_i2c_read_byte_bus(uint8_t bus, uint8_t addr, uint16_t reg) ``` 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. - `uint8_t bus` - `uint8_t addr` - `uint16_t reg` **Returns:** `int` #### `core_i2c_probe_bus` — Studio ```c core_i2c_probe_bus(uint8_t bus, uint8_t addr) ``` Check if a device responds at `addr` on `bus`. Returns 1 if the device ACKs, 0 on NACK / timeout / undeclared bus. - `uint8_t bus` - `uint8_t addr` **Returns:** `int` ### HAL layer Peripheral-level control with explicit handles. #### `hal_i2c_init` ```c hal_i2c_init(hal_i2c_t * h, I2C_TypeDef * instance, const hal_i2c_config_t * cfg) ``` Initialize I2C in master mode. The peripheral clock is auto-enabled. SDA/SCL pins must be configured as AF open-drain with pull-up (via tile_init or manually). - `hal_i2c_t * h` - `I2C_TypeDef * instance` - `const hal_i2c_config_t * cfg` **Returns:** `hal_status_t` #### `hal_i2c_recover` ```c hal_i2c_recover(hal_i2c_t * h) ``` Attempt bus recovery when I2C is stuck (SDA held low). Disables the peripheral, bit-bangs SCL as GPIO to clock out the stuck slave, generates a STOP condition, then re-initializes I2C. Returns HAL_OK if SDA was released, HAL_ERROR if still stuck. - `hal_i2c_t * h` **Returns:** `hal_status_t` #### `hal_i2c_write` ```c hal_i2c_write(hal_i2c_t * h, uint8_t addr, const uint8_t * data, uint32_t len) ``` Write data to a 7-bit address device - `hal_i2c_t * h` - `uint8_t addr` - `const uint8_t * data` - `uint32_t len` **Returns:** `hal_status_t` #### `hal_i2c_read` ```c hal_i2c_read(hal_i2c_t * h, uint8_t addr, uint8_t * buf, uint32_t len) ``` Read data from a 7-bit address device - `hal_i2c_t * h` - `uint8_t addr` - `uint8_t * buf` - `uint32_t len` **Returns:** `hal_status_t` #### `hal_i2c_write_reg` ```c hal_i2c_write_reg(hal_i2c_t * h, uint8_t addr, uint16_t reg, const uint8_t * data, uint32_t len) ``` Write to a register on an I2C device. reg is 8-bit or 16-bit: if > 0xFF, two address bytes are sent (MSB first). - `hal_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `const uint8_t * data` - `uint32_t len` **Returns:** `hal_status_t` #### `hal_i2c_read_reg` ```c hal_i2c_read_reg(hal_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t * buf, uint32_t len) ``` Read from a register on an I2C device. reg is 8-bit or 16-bit: if > 0xFF, two address bytes are sent (MSB first). - `hal_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `uint8_t * buf` - `uint32_t len` **Returns:** `hal_status_t` #### `hal_i2c_write_byte` ```c hal_i2c_write_byte(hal_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t value) ``` Write a single byte to a register (8-bit or 16-bit reg) - `hal_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `uint8_t value` **Returns:** `hal_status_t` #### `hal_i2c_read_byte` ```c hal_i2c_read_byte(hal_i2c_t * h, uint8_t addr, uint16_t reg, uint8_t * value) ``` Read a single byte from a register (8-bit or 16-bit reg) - `hal_i2c_t * h` - `uint8_t addr` - `uint16_t reg` - `uint8_t * value` **Returns:** `hal_status_t` #### `hal_i2c_probe` ```c hal_i2c_probe(hal_i2c_t * h, uint8_t addr) ``` Check if a device responds at addr. Returns HAL_OK or HAL_NACK. - `hal_i2c_t * h` - `uint8_t addr` **Returns:** `hal_status_t` #### `hal_i2c_scan` ```c hal_i2c_scan(hal_i2c_t * h, uint8_t * found, uint8_t * count, uint8_t max_count) ``` Scan the I2C bus (0x08–0x77). Fills found[] with responding addresses. Returns actual count via *count. - `hal_i2c_t * h` - `uint8_t * found` - `uint8_t * count` - `uint8_t max_count` **Returns:** `void` ### LL layer Register-level access. No safety rails. #### `ll_i2c_init` ```c ll_i2c_init(I2C_TypeDef * i2c, uint32_t timing) ``` Initialize I2C in master mode. i2c: I2C instance timing: TIMINGR value (use LL_I2C_TIMING_* defines) fmp: set non-zero to enable Fast-mode Plus (CR1.FMP bit) Only effective on STM32WBA55 and STM32H523. On L422, FMP is enabled via SYSCFG — see hal_i2c.c. On L011, FMP is not supported. - `I2C_TypeDef * i2c` - `uint32_t timing` **Returns:** `void` #### `ll_i2c_init_fmp` ```c ll_i2c_init_fmp(I2C_TypeDef * i2c, uint32_t timing) ``` Initialize I2C in Fast-mode Plus (1 MHz). Sets the CR1.FMP bit for 20 mA output drive on WBA55 / H523. - `I2C_TypeDef * i2c` - `uint32_t timing` **Returns:** `void` #### `ll_i2c_write` ```c ll_i2c_write(I2C_TypeDef * i2c, uint8_t addr, const uint8_t * data, uint32_t len) ``` Master write to a 7-bit address device. addr: 7-bit slave address (unshifted, e.g. 0x68 for MPU6050) data: pointer to bytes to send len: number of bytes Returns LL_I2C_OK on success, LL_I2C_NACK, LL_I2C_ERROR, or LL_I2C_TIMEOUT on failure. - `I2C_TypeDef * i2c` - `uint8_t addr` - `const uint8_t * data` - `uint32_t len` **Returns:** `int` #### `ll_i2c_read` ```c ll_i2c_read(I2C_TypeDef * i2c, uint8_t addr, uint8_t * buf, uint32_t len) ``` Master read from a 7-bit address device. addr: 7-bit slave address (unshifted) buf: buffer to store received bytes len: number of bytes to read Returns LL_I2C_OK on success, LL_I2C_NACK, LL_I2C_ERROR, or LL_I2C_TIMEOUT on failure. - `I2C_TypeDef * i2c` - `uint8_t addr` - `uint8_t * buf` - `uint32_t len` **Returns:** `int` #### `ll_i2c_timing_100k` ```c ll_i2c_timing_100k(uint32_t kernel_mhz) ``` Select appropriate timing constant based on kernel clock MHz. Returns a 100kHz (standard mode) timing value. Returns 0 if no pre-computed value exists for the given frequency. - `uint32_t kernel_mhz` **Returns:** `uint32_t` #### `ll_i2c_timing_400k` ```c ll_i2c_timing_400k(uint32_t kernel_mhz) ``` Select appropriate timing constant based on kernel clock MHz. Returns a 400kHz (fast mode) timing value. Minimum kernel clock: 4 MHz. Returns 0 if unsupported. - `uint32_t kernel_mhz` **Returns:** `uint32_t` #### `ll_i2c_timing_1m` ```c ll_i2c_timing_1m(uint32_t kernel_mhz) ``` Select appropriate timing constant based on kernel clock MHz. Returns a 1MHz (fast mode plus) timing value. Minimum kernel clock: 16 MHz. Returns 0 if unsupported. - `uint32_t kernel_mhz` **Returns:** `uint32_t` --- Generated 2026-08-12 from the SDK headers at tiles@6bbf0d8. Canonical HTML: https://bergsonne.io/docs/sdk/i2c ============================================================================== ## Core.LED — SDK reference > Drive every Core's onboard status LED at the layer you want — the core_* helpers, or the raw ll_gpio writes underneath. | | | | --- | --- | | Category | `led` | | Core header | `core_led.h` | | LL header | `ll_gpio.h` | | Generated from | `tiles@cf97a14` | | Canonical page | https://bergsonne.io/docs/sdk/led | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_led_init` ```c core_led_init(void) ``` 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. **Returns:** `void` #### `core_led_on` — Studio ```c core_led_on(void) ``` 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. **Returns:** `void` #### `core_led_off` — Studio ```c core_led_off(void) ``` Turn the onboard LED off. **Returns:** `void` #### `core_led_toggle` — Studio ```c core_led_toggle(void) ``` 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). **Returns:** `void` #### `core_led_blink` — Studio ```c core_led_blink(int n, int on_ms, int off_ms) ``` 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. - `int n` — range 1…100 - `int on_ms` (ms) — range 1…5000 - `int off_ms` (ms) — range 1…5000 **Returns:** `void` #### `core_led_sos` ```c core_led_sos(void) ``` Blink the SOS pattern (3 short, 3 long, 3 short) in an infinite loop. Use for unrecoverable errors. This function never returns. **Returns:** `void` #### `core_led_heartbeat` — Studio ```c core_led_heartbeat(int period_ms, int on_ms) ``` 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. Examples: 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. - `int period_ms` (ms) — range 0…60000 - `int on_ms` (ms) — range 0…60000 **Returns:** `void` ### LL layer Register-level access. No safety rails. #### `ll_gpio_config_output` ```c ll_gpio_config_output(GPIO_TypeDef * port, uint32_t pin) ``` Configure a pin as general-purpose output (push-pull, no pull, medium speed). - `GPIO_TypeDef * port` - `uint32_t pin` **Returns:** `void` #### `ll_gpio_set` ```c ll_gpio_set(GPIO_TypeDef * port, uint32_t pin_mask) ``` Set one or more pins high (write 1s to BSRR lower half) - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `void` #### `ll_gpio_clear` ```c ll_gpio_clear(GPIO_TypeDef * port, uint32_t pin_mask) ``` Set one or more pins low (write 1s to BSRR upper half) - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `void` #### `ll_gpio_toggle` ```c ll_gpio_toggle(GPIO_TypeDef * port, uint32_t pin_mask) ``` Toggle one or more pins (XOR with ODR) - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `void` #### `ll_gpio_read` ```c ll_gpio_read(GPIO_TypeDef * port, uint32_t pin_mask) ``` Read the input state of one or more pins - `GPIO_TypeDef * port` - `uint32_t pin_mask` **Returns:** `uint32_t` --- Generated 2026-08-12 from the SDK headers at tiles@cf97a14. Canonical HTML: https://bergsonne.io/docs/sdk/led ============================================================================== ## Core.Power — SDK reference > Low-power modes and wakeup on every Core: sleep, stop, and standby; RTC / pin / EXTI wakeup; and backup registers that survive reset — at the core_power or ll_pwr layer. | | | | --- | --- | | Category | `power` | | Core header | `core_power.h` | | LL header | `ll_pwr.h` | | Generated from | `tiles@2a1a847` | | Canonical page | https://bergsonne.io/docs/sdk/power | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_sleep` — Studio ```c core_sleep(void) ``` Sleep until any interrupt (CPU stopped, peripherals running). **Returns:** `void` #### `core_stop_for` — Studio ```c core_stop_for(uint32_t seconds) ``` Enter Stop mode for a number of seconds, then wake and restore clocks. Uses RTC wakeup timer (LSI). Returns after wake with PLL + SysTick restored. - `uint32_t seconds` — range 1…86400 **Returns:** `void` #### `core_stop_until_on_change` ```c core_stop_until_on_change(uint8_t pad, uint32_t edge) ``` Enter Stop mode until a GPIO edge occurs on the given pad. Configures the pad as input, enables EXTI, enters Stop, and restores clocks on wake. Returns after the edge is detected. - `uint8_t pad` - `uint32_t edge` **Returns:** `void` #### `core_standby_for` ```c core_standby_for(void) ``` Enter Standby mode with RTC wakeup after the given seconds. Does not return — MCU resets on wake. Check core_woke_from_standby() at the top of main() to detect a Standby wake. **Returns:** `void` #### `core_standby_until_on_change` ```c 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. On Core.ST.L4.2: pad 8 (PA0 = WKUP1) and pad 7 (PA2 = WKUP4). - `uint8_t pad` - `uint32_t edge` **Returns:** `void` #### `core_stop` ```c core_stop(void) ``` Enter Stop mode (caller manages wakeup source + clock recovery). **Returns:** `void` #### `core_standby` ```c core_standby(void) ``` Enter Standby (caller manages wakeup source). Does not return. **Returns:** `void` #### `core_woke_from_standby` — Studio ```c core_woke_from_standby(void) ``` Returns 1 if the MCU woke from Standby. **Returns:** `int` #### `core_clear_standby_flag` ```c core_clear_standby_flag(void) ``` Clear the Standby wake flag. Call after core_woke_from_standby() returns 1. **Returns:** `void` #### `core_watchdog_start_seconds` ```c 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. - `uint32_t seconds` **Returns:** `void` ### LL layer Register-level access. No safety rails. #### `ll_pwr_enable_backup_access` ```c ll_pwr_enable_backup_access(void) ``` Enable access to the backup domain (RTC, backup registers). Must be called before writing to RTC or backup registers. **Returns:** `void` #### `ll_pwr_sleep_wfi` ```c ll_pwr_sleep_wfi(void) ``` Enter Sleep mode. CPU clock stopped, all peripherals continue running. Wakes on any enabled interrupt. Use WFI (wait for interrupt) or WFE (wait for event). **Returns:** `void` #### `ll_pwr_stop` ```c ll_pwr_stop(void) ``` Enter Stop mode (lowest power with SRAM retention). All oscillators stopped except LSI/LSE. Wakes via EXTI (GPIO, RTC alarm, etc.). After waking, clock is MSI/HSI — you must reinitialize the PLL if needed. On L4: Stop 0, Stop 1, or Stop 2 available. On WBA/H5: Stop 0 or Stop 1. **Returns:** `void` #### `ll_pwr_standby` ```c ll_pwr_standby(void) ``` Enter Standby mode (lowest power, SRAM lost). Only RTC, IWDG, and wakeup pins remain powered. On wake, the MCU resets (starts from Reset_Handler). WARNING: All SRAM contents are lost. Save state to backup registers or flash before entering standby. **Returns:** `void` #### `ll_pwr_woke_from_standby` ```c ll_pwr_woke_from_standby(void) ``` Check if we woke from standby **Returns:** `int` #### `ll_pwr_clear_standby_flag` ```c ll_pwr_clear_standby_flag(void) ``` Clear the standby flag **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@2a1a847. Canonical HTML: https://bergsonne.io/docs/sdk/power ============================================================================== ## Core.SPI — SDK reference > SPI master on the Cores that have it: full-duplex byte transfers with software chip-select, plus non-blocking DMA — at the core_spi, hal_spi, or ll_spi layer. | | | | --- | --- | | Category | `spi` | | Core header | `core_spi.h` | | HAL header | `hal_spi.h` | | LL header | `ll_spi.h` | | Generated from | `tiles@d3cff63` | | Canonical page | https://bergsonne.io/docs/sdk/spi | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_spi_init` ```c core_spi_init(hal_spi_t * h, SPI_TypeDef * instance, const hal_spi_config_t * cfg) ``` Initialize SPI in master mode. Same signature as hal_spi_init. - `hal_spi_t * h` - `SPI_TypeDef * instance` - `const hal_spi_config_t * cfg` **Returns:** `hal_status_t` #### `core_spi_set_cs` ```c core_spi_set_cs(hal_spi_t * h, uint8_t pad) ``` Assign a CS pin using a tile pad number. Resolves pad to port/pin via hal_pad_lookup, then calls hal_spi_set_cs to configure and deassert the pin. - `hal_spi_t * h` - `uint8_t pad` **Returns:** `void` #### `core_spi_select` ```c core_spi_select(hal_spi_t * h) ``` Assert CS (drive low). - `hal_spi_t * h` **Returns:** `void` #### `core_spi_deselect` ```c core_spi_deselect(hal_spi_t * h) ``` Deassert CS (drive high). - `hal_spi_t * h` **Returns:** `void` #### `core_spi_transfer` ```c core_spi_transfer(hal_spi_t * h, uint8_t tx) ``` Full-duplex single byte transfer. Returns received byte. - `hal_spi_t * h` - `uint8_t tx` **Returns:** `uint8_t` #### `core_spi_write` ```c core_spi_write(hal_spi_t * h, const uint8_t * data, uint32_t len) ``` Write-only (discard received data). - `hal_spi_t * h` - `const uint8_t * data` - `uint32_t len` **Returns:** `void` #### `core_spi_read` ```c core_spi_read(hal_spi_t * h, uint8_t * buf, uint32_t len) ``` Read-only (send zeros). - `hal_spi_t * h` - `uint8_t * buf` - `uint32_t len` **Returns:** `void` #### `core_spi_xfer` ```c core_spi_xfer(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len) ``` Convenience: select + full-duplex transfer + deselect. tx and rx can be the same buffer. Either can be NULL. - `hal_spi_t * h` - `const uint8_t * tx` - `uint8_t * rx` - `uint32_t len` **Returns:** `void` #### `core_spi_xfer_dma` ```c core_spi_xfer_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len, hal_callback_t cb, void * ctx) ``` Start a DMA-based SPI transfer (non-blocking). Full-duplex: tx bytes are sent while rx bytes are received simultaneously. Either tx or rx can be NULL for write-only or read-only transfers. The callback fires from DMA ISR context when the transfer completes. Caller must manage CS: assert before calling, deassert in the callback. The command/address byte should be sent via polling (core_spi_transfer) before starting DMA. - `hal_spi_t * h` - `const uint8_t * tx` - `uint8_t * rx` - `uint32_t len` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `core_spi_busy` ```c core_spi_busy(hal_spi_t * h) ``` Returns 1 if a DMA transfer is in progress. - `hal_spi_t * h` **Returns:** `int` #### `core_spi_xfer_byte_bus` — Studio ```c core_spi_xfer_byte_bus(uint8_t bus, uint8_t cs_pad, uint8_t tx) ``` 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). 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. - `uint8_t bus` - `uint8_t cs_pad` - `uint8_t tx` **Returns:** `int` ### HAL layer Peripheral-level control with explicit handles. #### `hal_spi_init` ```c hal_spi_init(hal_spi_t * h, SPI_TypeDef * instance, const hal_spi_config_t * cfg) ``` Initialize SPI in master mode, 8-bit, MSB-first. The peripheral clock is auto-enabled. SCK/MOSI/MISO pins must be configured for AF (via coregen or manually). - `hal_spi_t * h` - `SPI_TypeDef * instance` - `const hal_spi_config_t * cfg` **Returns:** `hal_status_t` #### `hal_spi_deinit` ```c hal_spi_deinit(hal_spi_t * h) ``` Disable the SPI peripheral and release the handle. - `hal_spi_t * h` **Returns:** `void` #### `hal_spi_set_cs` ```c hal_spi_set_cs(hal_spi_t * h, GPIO_TypeDef * port, uint32_t pin) ``` Assign a CS GPIO pin for automatic chip-select management. The pin is configured as push-pull output and deasserted (high). - `hal_spi_t * h` - `GPIO_TypeDef * port` - `uint32_t pin` **Returns:** `void` #### `hal_spi_select` ```c hal_spi_select(hal_spi_t * h) ``` Assert CS (drive active — low by default). - `hal_spi_t * h` **Returns:** `void` #### `hal_spi_deselect` ```c hal_spi_deselect(hal_spi_t * h) ``` Deassert CS (drive inactive — high by default). - `hal_spi_t * h` **Returns:** `void` #### `hal_spi_transfer` ```c hal_spi_transfer(hal_spi_t * h, uint8_t tx) ``` Full-duplex single byte: send tx, return received byte. - `hal_spi_t * h` - `uint8_t tx` **Returns:** `uint8_t` #### `hal_spi_transfer_buf` ```c hal_spi_transfer_buf(hal_spi_t * h, uint8_t * buf, uint32_t len) ``` Full-duplex buffer transfer (in-place: rx overwrites buf). - `hal_spi_t * h` - `uint8_t * buf` - `uint32_t len` **Returns:** `void` #### `hal_spi_write` ```c hal_spi_write(hal_spi_t * h, const uint8_t * data, uint32_t len) ``` Write-only: send data, discard received bytes. - `hal_spi_t * h` - `const uint8_t * data` - `uint32_t len` **Returns:** `void` #### `hal_spi_read` ```c hal_spi_read(hal_spi_t * h, uint8_t * buf, uint32_t len) ``` Read-only: send zeros, capture received bytes. - `hal_spi_t * h` - `uint8_t * buf` - `uint32_t len` **Returns:** `void` #### `hal_spi_xfer` ```c hal_spi_xfer(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len) ``` Convenience: select → full-duplex transfer → deselect. tx and rx can be the same buffer. Either can be NULL. - `hal_spi_t * h` - `const uint8_t * tx` - `uint8_t * rx` - `uint32_t len` **Returns:** `void` #### `hal_spi_xfer_dma` ```c hal_spi_xfer_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len, hal_callback_t cb, void * ctx) ``` Start a full-duplex DMA transfer. - `hal_spi_t * h` - `const uint8_t * tx` - `uint8_t * rx` - `uint32_t len` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `hal_spi_busy` ```c hal_spi_busy(hal_spi_t * h) ``` Returns 1 if a DMA transfer is in progress. - `hal_spi_t * h` **Returns:** `int` ### LL layer Register-level access. No safety rails. #### `ll_spi_init` ```c ll_spi_init(SPI_TypeDef * spi, uint32_t prescaler, uint32_t cpol, uint32_t cpha) ``` Initialize SPI in master mode, 8-bit, MSB-first. spi: SPI instance prescaler: LL_SPI_PRESCALER_* (clock divider) cpol: LL_SPI_CPOL_LOW or _HIGH cpha: LL_SPI_CPHA_1EDGE or _2EDGE Prerequisites: - Peripheral clock enabled via ll_rcc_apb1/2_clk_enable() - SCK/MOSI/MISO pins configured for AF via ll_gpio_config_af() - CS managed manually via GPIO (not hardware NSS) - `SPI_TypeDef * spi` - `uint32_t prescaler` - `uint32_t cpol` - `uint32_t cpha` **Returns:** `void` #### `ll_spi_transfer` ```c ll_spi_transfer(SPI_TypeDef * spi, uint8_t tx_data) ``` Transfer a single byte (full-duplex): send tx_data, return received byte. - `SPI_TypeDef * spi` - `uint8_t tx_data` **Returns:** `uint8_t` #### `ll_spi_transfer_buf` ```c ll_spi_transfer_buf(SPI_TypeDef * spi, uint8_t * buf, uint32_t len) ``` Transfer a buffer (full-duplex, in-place). tx_buf is sent; received data overwrites tx_buf. Pass NULL for tx_buf to send zeros (read-only). - `SPI_TypeDef * spi` - `uint8_t * buf` - `uint32_t len` **Returns:** `void` #### `ll_spi_write` ```c ll_spi_write(SPI_TypeDef * spi, uint8_t data) ``` Write-only transfer (discard received data). - `SPI_TypeDef * spi` - `uint8_t data` **Returns:** `void` #### `ll_spi_read` ```c ll_spi_read(SPI_TypeDef * spi) ``` Read-only transfer (send zeros, return received byte). - `SPI_TypeDef * spi` **Returns:** `uint8_t` #### `ll_spi_enable_dma_rx` ```c ll_spi_enable_dma_rx(SPI_TypeDef * spi) ``` Enable DMA requests for RX - `SPI_TypeDef * spi` **Returns:** `void` #### `ll_spi_disable_dma_rx` ```c ll_spi_disable_dma_rx(SPI_TypeDef * spi) ``` Disable DMA requests for RX - `SPI_TypeDef * spi` **Returns:** `void` #### `ll_spi_enable_dma_tx` ```c ll_spi_enable_dma_tx(SPI_TypeDef * spi) ``` Enable DMA requests for TX - `SPI_TypeDef * spi` **Returns:** `void` #### `ll_spi_disable_dma_tx` ```c ll_spi_disable_dma_tx(SPI_TypeDef * spi) ``` Disable DMA requests for TX - `SPI_TypeDef * spi` **Returns:** `void` #### `ll_spi_init_halfduplex` ```c ll_spi_init_halfduplex(SPI_TypeDef * spi, uint32_t prescaler, uint32_t cpol, uint32_t cpha) ``` Initialize SPI v2 as a half-duplex (1-line) master, 8-bit, MSB-first, starting in transmit direction. Leaves SPE disabled. Pin/clock prerequisites are the same as ll_spi_init(). - `SPI_TypeDef * spi` - `uint32_t prescaler` - `uint32_t cpol` - `uint32_t cpha` **Returns:** `void` #### `ll_spi_halfduplex_set_dir` ```c ll_spi_halfduplex_set_dir(SPI_TypeDef * spi, int tx) ``` Select half-duplex data direction (1 = transmit, 0 = receive). Must be called with SPE=0 (HDDIR is config-protected while enabled). - `SPI_TypeDef * spi` - `int tx` **Returns:** `void` #### `ll_spi_halfduplex_write` ```c ll_spi_halfduplex_write(SPI_TypeDef * spi, const uint8_t * data, uint32_t len) ``` Polled half-duplex master transmit of `len` bytes. Programs TSIZE (with SPE disabled), enables the peripheral, starts the master clock, feeds the FIFO, and waits for end-of-transfer. Leaves SPE disabled so the next call can reprogram TSIZE. Returns 1 on success, 0 on overrun/timeout. - `SPI_TypeDef * spi` - `const uint8_t * data` - `uint32_t len` **Returns:** `int` #### `ll_spi_dma_stop` ```c ll_spi_dma_stop(SPI_TypeDef * spi) ``` Stop a DMA transfer: disable the peripheral + DMA request generation, clear flags. - `SPI_TypeDef * spi` **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@d3cff63. Canonical HTML: https://bergsonne.io/docs/sdk/spi ============================================================================== ## Core.Timer — SDK reference > Hardware timers on every Core: PWM output, input capture, and periodic callbacks — at the core_timer / core_pwm, hal_timer, or ll_tim layer. | | | | --- | --- | | Category | `timer` | | Core header | `core_timer.h` | | HAL header | `hal_timer.h` | | LL header | `ll_tim.h` | | Generated from | `tiles@6bbf0d8` | | Canonical page | https://bergsonne.io/docs/sdk/timers | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_timer_init_freq` ```c core_timer_init_freq(core_timer_t * h, TIM_TypeDef * instance, uint32_t freq_hz) ``` 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_t * h` - `TIM_TypeDef * instance` - `uint32_t freq_hz` **Returns:** `hal_status_t` #### `core_timer_init_tick` ```c core_timer_init_tick(core_timer_t * h, TIM_TypeDef * instance, uint32_t tick_hz) ``` 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_t * h` - `TIM_TypeDef * instance` - `uint32_t tick_hz` **Returns:** `hal_status_t` #### `core_timer_start` ```c core_timer_start(core_timer_t * h) ``` Start the timer counter. - `core_timer_t * h` **Returns:** `void` #### `core_timer_stop` ```c core_timer_stop(core_timer_t * h) ``` Stop the timer counter. - `core_timer_t * h` **Returns:** `void` #### `core_timer_set_freq` ```c core_timer_set_freq(core_timer_t * h, uint32_t freq_hz) ``` Change the timer frequency (recalculates PSC/ARR). - `core_timer_t * h` - `uint32_t freq_hz` **Returns:** `void` #### `core_timer_pwm_set` ```c core_timer_pwm_set(core_timer_t * h, uint8_t channel, uint16_t duty_permil) ``` Set PWM duty cycle for a channel. channel: 1–4 duty_permil: 0–1000 (0 = off, 500 = 50%, 1000 = always on) - `core_timer_t * h` - `uint8_t channel` - `uint16_t duty_permil` **Returns:** `void` #### `core_timer_capture_init` ```c core_timer_capture_init(core_timer_t * h, uint8_t channel) ``` Configure a channel for input capture (rising edge). The timer must already be initialized with core_timer_init(). - `core_timer_t * h` - `uint8_t channel` **Returns:** `void` #### `core_timer_capture_read` ```c core_timer_capture_read(core_timer_t * h, uint8_t channel) ``` Read the last captured value from a channel. - `core_timer_t * h` - `uint8_t channel` **Returns:** `uint32_t` #### `core_timer_enable_tick` ```c core_timer_enable_tick(core_timer_t * h, core_callback_t cb, void * ctx) ``` 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_t * h` - `core_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `core_timer_disable_tick` ```c core_timer_disable_tick(core_timer_t * h) ``` Disable the tick callback (clears UIE, keeps timer running). - `core_timer_t * h` **Returns:** `void` #### `core_tick_init` ```c core_tick_init(core_timer_t * h, TIM_TypeDef * instance, uint32_t period_us, core_callback_t cb, void * ctx) ``` 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_timer_t * h` - `TIM_TypeDef * instance` - `uint32_t period_us` - `core_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` ### HAL layer Peripheral-level control with explicit handles. #### `hal_timer_pwm_init` ```c hal_timer_pwm_init(hal_timer_t * h, TIM_TypeDef * instance, uint32_t pclk_hz, uint32_t freq_hz) ``` Initialize a timer for PWM output at the given frequency. Auto-calculates PSC and ARR from pclk_hz and freq_hz. For advanced timers (TIM1), also enables MOE. - `hal_timer_t * h` - `TIM_TypeDef * instance` - `uint32_t pclk_hz` - `uint32_t freq_hz` **Returns:** `hal_status_t` #### `hal_timer_pwm_set_duty` ```c hal_timer_pwm_set_duty(hal_timer_t * h, uint8_t channel, uint16_t duty_permil) ``` Set PWM duty cycle for a channel. channel: 1–4 duty_permil: 0–1000 (0 = off, 500 = 50%, 1000 = 100%) - `hal_timer_t * h` - `uint8_t channel` - `uint16_t duty_permil` **Returns:** `void` #### `hal_timer_pwm_set_freq` ```c hal_timer_pwm_set_freq(hal_timer_t * h, uint32_t freq_hz) ``` Change PWM frequency (recalculates PSC/ARR, resets all channel duties) - `hal_timer_t * h` - `uint32_t freq_hz` **Returns:** `void` #### `hal_timer_pwm_start` ```c hal_timer_pwm_start(hal_timer_t * h) ``` Start the PWM timer - `hal_timer_t * h` **Returns:** `void` #### `hal_timer_pwm_stop` ```c hal_timer_pwm_stop(hal_timer_t * h) ``` Stop the PWM timer - `hal_timer_t * h` **Returns:** `void` #### `hal_timer_tick_init` ```c hal_timer_tick_init(hal_timer_t * h, TIM_TypeDef * instance, uint32_t pclk_hz, uint32_t period_us, hal_callback_t cb, void * ctx) ``` Configure a timer to call a 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 - `hal_timer_t * h` - `TIM_TypeDef * instance` - `uint32_t pclk_hz` - `uint32_t period_us` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `hal_timer_tick_enable` ```c hal_timer_tick_enable(hal_timer_t * h, hal_callback_t cb, void * ctx) ``` Enable periodic tick on an already-initialized timer. Does NOT touch PSC/ARR — the timer keeps its existing timebase. Use this to add a tick callback to a timer that's already set up for PWM or capture via hal_timer_pwm_init(). The callback fires on each counter overflow (update event) at the timer's current frequency. - `hal_timer_t * h` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `hal_timer_tick_disable` ```c hal_timer_tick_disable(hal_timer_t * h) ``` Disable the tick callback (clears UIE, keeps timer running). - `hal_timer_t * h` **Returns:** `void` ### LL layer Register-level access. No safety rails. #### `ll_tim_config` ```c ll_tim_config(TIM_TypeDef * tim, uint32_t prescaler, uint32_t period) ``` Configure basic timebase. tim: timer instance prescaler: clock divider (0 = no division, N = divide by N+1) period: auto-reload value (counter counts 0 → period) Timer frequency = pclk / (prescaler + 1) Overflow rate = pclk / (prescaler + 1) / (period + 1) Example: 80MHz pclk, 1kHz overflow: prescaler = 79 (→ 1MHz tick), period = 999 (→ 1kHz) - `TIM_TypeDef * tim` - `uint32_t prescaler` - `uint32_t period` **Returns:** `void` #### `ll_tim_start` ```c ll_tim_start(TIM_TypeDef * tim) ``` Start the timer counter - `TIM_TypeDef * tim` **Returns:** `void` #### `ll_tim_stop` ```c ll_tim_stop(TIM_TypeDef * tim) ``` Stop the timer counter - `TIM_TypeDef * tim` **Returns:** `void` #### `ll_tim_get_counter` ```c ll_tim_get_counter(TIM_TypeDef * tim) ``` Read the current counter value - `TIM_TypeDef * tim` **Returns:** `uint32_t` #### `ll_tim_set_counter` ```c ll_tim_set_counter(TIM_TypeDef * tim, uint32_t value) ``` Set the counter value - `TIM_TypeDef * tim` - `uint32_t value` **Returns:** `void` #### `ll_tim_update_flag` ```c ll_tim_update_flag(TIM_TypeDef * tim) ``` Check and clear the update (overflow) flag - `TIM_TypeDef * tim` **Returns:** `int` #### `ll_tim_pwm_config` ```c ll_tim_pwm_config(TIM_TypeDef * tim, uint32_t channel, uint32_t duty) ``` Configure a channel for PWM output (mode 1: active while CNT < CCR). tim: timer instance channel: 1, 2, 3, or 4 duty: compare value (0 = always off, ARR = always on) For advanced timers (TIM1), you must also call ll_tim_enable_moe(). - `TIM_TypeDef * tim` - `uint32_t channel` - `uint32_t duty` **Returns:** `void` #### `ll_tim_set_duty` ```c ll_tim_set_duty(TIM_TypeDef * tim, uint32_t channel, uint32_t duty) ``` Set the duty cycle (compare value) for a channel - `TIM_TypeDef * tim` - `uint32_t channel` - `uint32_t duty` **Returns:** `void` #### `ll_tim_set_duty_pct` ```c ll_tim_set_duty_pct(TIM_TypeDef * tim, uint32_t channel, uint32_t pct) ``` Set PWM duty as a percentage (0-100) of the current ARR period - `TIM_TypeDef * tim` - `uint32_t channel` - `uint32_t pct` **Returns:** `void` #### `ll_tim_enable_moe` ```c ll_tim_enable_moe(TIM_TypeDef * tim) ``` Enable the main output enable (MOE) bit on advanced timers (TIM1). Required for TIM1 PWM outputs to actually drive the pins. - `TIM_TypeDef * tim` **Returns:** `void` #### `ll_tim_ic_config` ```c ll_tim_ic_config(TIM_TypeDef * tim, uint32_t channel) ``` Configure a channel for input capture on rising edge. tim: timer instance channel: 1, 2, 3, or 4 - `TIM_TypeDef * tim` - `uint32_t channel` **Returns:** `void` #### `ll_tim_ic_read` ```c ll_tim_ic_read(TIM_TypeDef * tim, uint32_t channel) ``` Read the captured value for a channel - `TIM_TypeDef * tim` - `uint32_t channel` **Returns:** `uint32_t` #### `ll_tim_cc_flag` ```c ll_tim_cc_flag(TIM_TypeDef * tim, uint32_t channel) ``` Check and clear the capture/compare flag for a channel - `TIM_TypeDef * tim` - `uint32_t channel` **Returns:** `int` --- Generated 2026-08-12 from the SDK headers at tiles@6bbf0d8. Canonical HTML: https://bergsonne.io/docs/sdk/timers ============================================================================== ## Core.Serial — SDK reference > Serial UART on every Core: blocking TX with printf, polled or ring-buffered RX, and non-blocking DMA transmit — at the core_serial, hal_uart, or ll_uart layer. | | | | --- | --- | | Category | `serial` | | Core header | `core_serial.h` | | HAL header | `hal_uart.h` | | LL header | `ll_uart.h` | | Generated from | `tiles@6bbf0d8` | | Canonical page | https://bergsonne.io/docs/sdk/uart | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_serial_init` ```c core_serial_init(hal_uart_t * h, USART_TypeDef * instance, const hal_uart_config_t * cfg) ``` Initialize a UART instance. Clock is auto-resolved from PCLK1_HZ (core_config.h). - `hal_uart_t * h` - `USART_TypeDef * instance` - `const hal_uart_config_t * cfg` **Returns:** `hal_status_t` #### `core_serial_init_clk` ```c core_serial_init_clk(hal_uart_t * h, USART_TypeDef * instance, uint32_t pclk_hz, const hal_uart_config_t * cfg) ``` - `hal_uart_t * h` - `USART_TypeDef * instance` - `uint32_t pclk_hz` - `const hal_uart_config_t * cfg` **Returns:** `hal_status_t` #### `core_serial_write` ```c core_serial_write(hal_uart_t * h, const uint8_t * data, uint32_t len) ``` Transmit a buffer (blocking). - `hal_uart_t * h` - `const uint8_t * data` - `uint32_t len` **Returns:** `void` #### `core_serial_print` ```c core_serial_print(hal_uart_t * h, const char * str) ``` Transmit a null-terminated string (blocking). - `hal_uart_t * h` - `const char * str` **Returns:** `void` #### `core_serial_putc` ```c core_serial_putc(hal_uart_t * h, uint8_t byte) ``` Transmit a single byte (blocking). - `hal_uart_t * h` - `uint8_t byte` **Returns:** `void` #### `core_serial_available` ```c core_serial_available(hal_uart_t * h) ``` Number of bytes available in the RX buffer. - `hal_uart_t * h` **Returns:** `uint16_t` #### `core_serial_getc` ```c core_serial_getc(hal_uart_t * h) ``` Receive a single byte (blocking -- waits for data). - `hal_uart_t * h` **Returns:** `uint8_t` #### `core_serial_read` ```c core_serial_read(hal_uart_t * h, uint8_t * buf, uint16_t max_len) ``` Read available bytes from the RX ring buffer (non-blocking). Returns number of bytes read. - `hal_uart_t * h` - `uint8_t * buf` - `uint16_t max_len` **Returns:** `uint16_t` #### `core_serial_print_bus` — Studio ```c core_serial_print_bus(uint8_t bus, const char * str) ``` 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"); - `uint8_t bus` - `const char * str` **Returns:** `int` #### `core_serial_putc_bus` — Studio ```c core_serial_putc_bus(uint8_t bus, uint8_t byte) ``` Write a single byte to `bus` (blocking). Returns 0 on success or -1 on undeclared bus. - `uint8_t bus` - `uint8_t byte` **Returns:** `int` ### HAL layer Peripheral-level control with explicit handles. #### `hal_uart_init` ```c hal_uart_init(hal_uart_t * h, USART_TypeDef * instance, uint32_t pclk_hz, const hal_uart_config_t * cfg) ``` Initialize a UART instance. h: handle (caller provides storage) instance: USART peripheral (USART1, USART2, LPUART1) pclk_hz: peripheral clock frequency feeding this USART cfg: configuration (baud rate, interrupt RX enable) The peripheral clock is auto-enabled. TX/RX pins must be configured for AF (via tile_init or manually). - `hal_uart_t * h` - `USART_TypeDef * instance` - `uint32_t pclk_hz` - `const hal_uart_config_t * cfg` **Returns:** `hal_status_t` #### `hal_uart_deinit` ```c hal_uart_deinit(hal_uart_t * h) ``` Disable the UART and its interrupts - `hal_uart_t * h` **Returns:** `void` #### `hal_uart_putc` ```c hal_uart_putc(hal_uart_t * h, uint8_t byte) ``` Transmit a single byte (blocking) - `hal_uart_t * h` - `uint8_t byte` **Returns:** `void` #### `hal_uart_tx` ```c hal_uart_tx(hal_uart_t * h, const uint8_t * data, uint32_t len) ``` Transmit a buffer (blocking) - `hal_uart_t * h` - `const uint8_t * data` - `uint32_t len` **Returns:** `void` #### `hal_uart_tx_str` ```c hal_uart_tx_str(hal_uart_t * h, const char * str) ``` Transmit a null-terminated string (blocking) - `hal_uart_t * h` - `const char * str` **Returns:** `void` #### `hal_uart_printf` ```c hal_uart_printf(hal_uart_t * h, const char * fmt) ``` Printf over UART (blocking, uses vsnprintf from newlib-nano) - `hal_uart_t * h` - `const char * fmt` **Returns:** `int` #### `hal_uart_rx_ready` ```c hal_uart_rx_ready(hal_uart_t * h) ``` Check if data is available (works for both polling and interrupt mode) - `hal_uart_t * h` **Returns:** `int` #### `hal_uart_rx` ```c hal_uart_rx(hal_uart_t * h) ``` Receive a single byte (blocking — waits for data) - `hal_uart_t * h` **Returns:** `uint8_t` #### `hal_uart_rx_try` ```c hal_uart_rx_try(hal_uart_t * h, uint8_t * byte) ``` Try to receive a byte without blocking. Returns 1 if got one. - `hal_uart_t * h` - `uint8_t * byte` **Returns:** `int` #### `hal_uart_read` ```c hal_uart_read(hal_uart_t * h, uint8_t * buf, uint16_t max_len) ``` Read available bytes from the RX ring buffer (interrupt mode). Non-blocking — returns immediately with however many bytes are available. Returns number of bytes read. - `hal_uart_t * h` - `uint8_t * buf` - `uint16_t max_len` **Returns:** `uint16_t` #### `hal_uart_available` ```c hal_uart_available(hal_uart_t * h) ``` Number of bytes available in the RX ring buffer - `hal_uart_t * h` **Returns:** `uint16_t` #### `hal_uart_tx_dma` ```c hal_uart_tx_dma(hal_uart_t * h, const uint8_t * data, uint32_t len, hal_callback_t cb, void * ctx) ``` Transmit a buffer via DMA (non-blocking). The data buffer must remain valid until the callback fires. Returns HAL_BUSY if a DMA TX is already in progress. - `hal_uart_t * h` - `const uint8_t * data` - `uint32_t len` - `hal_callback_t cb` - `void * ctx` **Returns:** `hal_status_t` #### `hal_uart_tx_busy` ```c hal_uart_tx_busy(hal_uart_t * h) ``` Check if a DMA TX is in progress - `hal_uart_t * h` **Returns:** `int` ### LL layer Register-level access. No safety rails. #### `ll_uart_init` ```c ll_uart_init(USART_TypeDef * uart, uint32_t pclk_hz, uint32_t baud) ``` Initialize a USART for 8N1 at the given baud rate. uart: USART instance (USART1, USART2, etc.) pclk_hz: peripheral clock feeding this USART (APB1 or APB2) baud: desired baud rate Prerequisites: - Peripheral clock enabled via ll_rcc_apb1/2_clk_enable() - TX/RX pins configured for AF via ll_gpio_config_af() - `USART_TypeDef * uart` - `uint32_t pclk_hz` - `uint32_t baud` **Returns:** `void` #### `ll_lpuart_init` ```c ll_lpuart_init(USART_TypeDef * uart, uint32_t pclk_hz, uint32_t baud) ``` Initialize an LPUART for 8N1. LPUART BRR calculation differs: BRR = 256 * pclk / baud - `USART_TypeDef * uart` - `uint32_t pclk_hz` - `uint32_t baud` **Returns:** `void` #### `ll_uart_wait_txe` ```c ll_uart_wait_txe(USART_TypeDef * uart) ``` Wait until transmit data register is empty - `USART_TypeDef * uart` **Returns:** `void` #### `ll_uart_wait_tc` ```c ll_uart_wait_tc(USART_TypeDef * uart) ``` Wait until transmission is complete (last byte fully shifted out) - `USART_TypeDef * uart` **Returns:** `void` #### `ll_uart_tx` ```c ll_uart_tx(USART_TypeDef * uart, uint8_t data) ``` Transmit a single byte (blocking) - `USART_TypeDef * uart` - `uint8_t data` **Returns:** `void` #### `ll_uart_tx_buf` ```c ll_uart_tx_buf(USART_TypeDef * uart, const uint8_t * data, uint32_t len) ``` Transmit a buffer (blocking) - `USART_TypeDef * uart` - `const uint8_t * data` - `uint32_t len` **Returns:** `void` #### `ll_uart_tx_str` ```c ll_uart_tx_str(USART_TypeDef * uart, const char * str) ``` Transmit a null-terminated string (blocking) - `USART_TypeDef * uart` - `const char * str` **Returns:** `void` #### `ll_uart_rx_ready` ```c ll_uart_rx_ready(USART_TypeDef * uart) ``` Check if received data is available - `USART_TypeDef * uart` **Returns:** `int` #### `ll_uart_rx` ```c ll_uart_rx(USART_TypeDef * uart) ``` Receive a single byte (blocking — waits until data arrives) - `USART_TypeDef * uart` **Returns:** `uint8_t` #### `ll_uart_rx_try` ```c ll_uart_rx_try(USART_TypeDef * uart, uint8_t * data) ``` Try to receive a byte without blocking. Returns 1 if a byte was read (stored in *data), 0 if nothing available. - `USART_TypeDef * uart` - `uint8_t * data` **Returns:** `int` #### `ll_uart_clear_errors` ```c ll_uart_clear_errors(USART_TypeDef * uart) ``` Clear all error flags (PE, FE, NE, ORE) - `USART_TypeDef * uart` **Returns:** `void` #### `ll_uart_errors` ```c ll_uart_errors(USART_TypeDef * uart) ``` Check for any error flags - `USART_TypeDef * uart` **Returns:** `uint32_t` --- Generated 2026-08-12 from the SDK headers at tiles@6bbf0d8. Canonical HTML: https://bergsonne.io/docs/sdk/uart ============================================================================== ## Core.USB — SDK reference > USB device on the Cores that have it: a CDC virtual serial port with printf and buffered RX, plus vendor HID reports — at the core_usb or hal_usb_cdc layer. | | | | --- | --- | | Category | `usb` | | Core header | `core_usb.h` | | HAL header | `hal_usb_cdc.h` | | Generated from | `tiles@5d70049` | | Canonical page | https://bergsonne.io/docs/sdk/usb | ### Core layer The recommended layer — portable across every Core, default instances, DSL-exposed. #### `core_usb_init` ```c core_usb_init(void) ``` Initialize USB CDC. Device appears as /dev/tty.usbmodem* on the host. **Returns:** `void` #### `core_usb_connected` ```c core_usb_connected(void) ``` Returns 1 if a host terminal is connected (DTR set). **Returns:** `int` #### `core_usb_write` ```c core_usb_write(const uint8_t * buf, uint16_t len) ``` Transmit data (blocking). Returns bytes sent, or -1 if not configured. - `const uint8_t * buf` - `uint16_t len` **Returns:** `int` #### `core_usb_print` — Studio ```c core_usb_print(const char * s) ``` Print a string followed by a newline. Thin wrapper for DSL-style callers. - `const char * text` **Returns:** `void` #### `core_usb_print_int` — Studio ```c core_usb_print_int(int v) ``` Print a signed integer followed by a newline. - `int value` **Returns:** `void` #### `core_usb_print_float` — Studio ```c core_usb_print_float(double v) ``` Print a double with a newline. %g trims trailing zeros for readability. - `double value` **Returns:** `void` #### `core_usb_print_bool` — Studio ```c core_usb_print_bool(int v) ``` Print "true" / "false" followed by a newline. - `int value` **Returns:** `void` #### `core_usb_on_receive` ```c core_usb_on_receive(hal_usb_cdc_rx_cb_t cb, void * ctx) ``` Set a callback for received data. Called from USB ISR. When set, data is NOT buffered for polling reads. - `hal_usb_cdc_rx_cb_t cb` - `void * ctx` **Returns:** `void` #### `core_usb_available` ```c core_usb_available(void) ``` Returns the number of bytes available to read (ring buffer mode). **Returns:** `uint16_t` #### `core_usb_getc` ```c core_usb_getc(void) ``` Read a single byte (blocking — waits for data). **Returns:** `uint8_t` #### `core_usb_read` ```c core_usb_read(uint8_t * buf, uint16_t max) ``` Read available bytes into buf (non-blocking). Returns bytes read. - `uint8_t * buf` - `uint16_t max` **Returns:** `uint16_t` #### `core_usb_try_read` ```c core_usb_try_read(uint8_t * byte) ``` Non-blocking single byte read. Returns 1 if a byte was read, 0 if empty. - `uint8_t * byte` **Returns:** `int` ### HAL layer Peripheral-level control with explicit handles. #### `hal_usb_cdc_init` ```c hal_usb_cdc_init(void) ``` Initialize USB CDC. This function: - Enables HSI48 + CRS (crystal-less 48MHz for USB) - Selects HSI48 as USB clock source - Enables VDDUSB power supply - Configures PA11/PA12 as AF10 - Enables USB peripheral clock - Configures USB device and endpoints - Enables USB interrupt - Connects DP pull-up (host sees device) **Returns:** `void` #### `hal_usb_cdc_connected` ```c hal_usb_cdc_connected(void) ``` Check if the host has opened the virtual COM port. Returns 1 if configured and DTR is set (terminal connected). **Returns:** `int` #### `hal_usb_cdc_write` ```c hal_usb_cdc_write(const uint8_t * buf, uint16_t len) ``` Transmit data over USB CDC (blocking). Waits for the host to consume data if the EP is busy. Returns number of bytes sent, or -1 if not configured. - `const uint8_t * buf` - `uint16_t len` **Returns:** `int` #### `hal_usb_cdc_printf` ```c hal_usb_cdc_printf(const char * fmt) ``` Printf over USB CDC (blocking). Returns number of characters written (from vsnprintf). - `const char * fmt` **Returns:** `int` #### `hal_usb_cdc_set_rx_callback` ```c hal_usb_cdc_set_rx_callback(hal_usb_cdc_rx_cb_t cb, void * ctx) ``` Set a callback for received data. Called from the USB ISR with a pointer to data copied from PMA. If set, data is NOT stored in the ring buffer. If not set, data goes into the ring buffer for polling reads. - `hal_usb_cdc_rx_cb_t cb` - `void * ctx` **Returns:** `void` #### `hal_usb_cdc_rx_ready` ```c hal_usb_cdc_rx_ready(void) ``` Check if data is available to read (ring buffer mode). **Returns:** `int` #### `hal_usb_cdc_getc` ```c hal_usb_cdc_getc(void) ``` Read a single byte (blocking — waits for data). **Returns:** `uint8_t` #### `hal_usb_cdc_rx_try` ```c hal_usb_cdc_rx_try(uint8_t * byte) ``` Try to read a byte without blocking. Returns 1 if a byte was read, 0 if no data available. - `uint8_t * byte` **Returns:** `int` #### `hal_usb_cdc_read` ```c hal_usb_cdc_read(uint8_t * buf, uint16_t max_len) ``` Read available bytes into buffer (non-blocking). Returns number of bytes actually read. - `uint8_t * buf` - `uint16_t max_len` **Returns:** `uint16_t` #### `hal_usb_cdc_available` ```c hal_usb_cdc_available(void) ``` Number of bytes available in the RX buffer. **Returns:** `uint16_t` #### `hal_usb_cdc_poll` ```c hal_usb_cdc_poll(void) ``` Poll USB events (call from main loop). Use this as an alternative to interrupt-driven operation. **Returns:** `void` #### `hal_usb_hid_send_report` ```c hal_usb_hid_send_report(const uint8_t * buf, uint16_t len) ``` Send a HID report (up to 64 bytes) via EP3 interrupt IN. Blocking — waits for previous report to complete. Returns bytes sent, or -1 if not configured. - `const uint8_t * buf` - `uint16_t len` **Returns:** `int` #### `hal_usb_hid_set_rx_callback` ```c hal_usb_hid_set_rx_callback(hal_usb_hid_rx_cb_t cb, void * ctx) ``` Set a callback for HID OUT reports (host -> device). Called from the USB ISR with one report per call, copied from PMA. Reception arrives via the EP3 OUT interrupt endpoint and via the HID SET_REPORT control request. If no callback is set, OUT reports are dropped (the endpoint is still re-armed). - `hal_usb_hid_rx_cb_t cb` - `void * ctx` **Returns:** `void` --- Generated 2026-08-12 from the SDK headers at tiles@5d70049. Canonical HTML: https://bergsonne.io/docs/sdk/usb ============================================================================== ## Bergsonne Tile I²C addresses > Default and alternate 7-bit I²C addresses for every Tile that exposes a fixed address. Core Tiles are omitted — they are programmable, so their addresses are defined per project. Generated from the canonical tile definitions (the same source the hardware is built from), so this tracks the parts rather than being maintained alongside them. Canonical page: https://bergsonne.io/resources/i2c-addresses ### Sense | Tile | Device | Default | Alternates | | --- | --- | --- | --- | | `Sense.ACP` | I2C | `0x39` | — | | `Sense.I.6P6` | I2C | `0x69` | `0x68` | | `Sense.I.9` | I2C | `0x69` | `0x68` | | `Sense.MIC` | I2C | `0x36` | — | | `Sense.T.C` | I2C | `0x44` | — | | `Sense.TOF` | I2C | `0x41` | — | ### Drive | Tile | Device | Default | Alternates | | --- | --- | --- | --- | | `Drive.A.2` | I2C | `0x49` | `0x48`, `0x4A`, `0x4B` | | `Drive.DC.H` | I2C | `0x34` | `0x30`, `0x31`, `0x33` | | `Drive.H` | I2C | `0x5A` | — | | `Drive.P` | I2C | `0x44` | — | ### Power | Tile | Device | Default | Alternates | | --- | --- | --- | --- | | `Power.L.1N` | I2C | `0x6B` | — | | `Power.L.1T` | I2C | `0x6B` | — | ### Display | Tile | Device | Default | Alternates | | --- | --- | --- | --- | | `Display.RGBW` | I2C | `0x50` | — | --- Generated 2026-08-12. Canonical HTML: https://bergsonne.io/resources/i2c-addresses ============================================================================== ## Bergsonne Tile pad assignments > The default function of each pad on every Tile, with its alternates. Core Tiles are omitted — their pads are configured per project. Generated from the canonical tile definitions (the same source the hardware is built from), so this tracks the parts rather than being maintained alongside them. Canonical page: https://bergsonne.io/resources/pad-assignments ### Sense #### Sense.ACP ambient light, RGB color, proximity | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 9 | `INT` | digital | — | | 10 | `V+` | power | — | #### Sense.I.6P6 extended-range 6-DOF IMU | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `I2C.AD0` | digital | `SPI.MISO` (digital) | | 3 | `I2C.EN` | digital | `SPI.CS` (digital) | | 4 | `I2C.CLK` | digital | `SPI.CLK` (digital), `I3C.CLK` (digital) | | 5 | `I2C.DAT` | digital | `SPI.MOSI` (digital), `I3C.DAT` (digital) | | 8 | `INT2` | digital | — | | 9 | `INT1` | digital | — | | 10 | `V+` | power | — | #### Sense.I.9 digital 9-DOF IMU | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `I2C.AD0` | digital | `SPI.MISO` (digital) | | 3 | `I2C.EN` | digital | `SPI.CS` (digital) | | 4 | `I2C.CLK` | digital | `SPI.CLK` (digital) | | 5 | `I2C.DAT` | digital | `SPI.MOSI` (digital) | | 9 | `INT` | digital | — | | 10 | `V+` | power | — | #### Sense.MIC I2C-output MEMS microphone | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `AOUT` | analog | — | | 10 | `V+` | power | — | #### Sense.T.C capacitive touch | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 3 | `RDY` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 8 | `C0` | analog | — | | 10 | `V+` | power | — | #### Sense.TOF time-of-flight distance | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 3 | `EN` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 9 | `INT` | digital | — | | 10 | `V+` | power | — | ### Drive #### Drive.A.2 I2C/SPI-input dual 3W Class-D amplifier | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `GPIO` | digital | `SPI.MISO` (digital) | | 3 | `A0` | digital | `SPI.MOSI` (digital) | | 4 | `I2C.CLK` | digital | `SPI.CS` (digital) | | 5 | `I2C.DAT` | digital | `SPI.CLK` (digital) | | 6 | `OUT.0-` | drive | — | | 7 | `OUT.0+` | drive | — | | 8 | `OUT.1-` | drive | — | | 9 | `OUT.1+` | drive | — | | 10 | `V+` | power | — | #### Drive.DC.H 11V/2A H-bridge DC-motor driver | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `EN` | digital | `IN1` (digital) | | 3 | `PH` | digital | `IN2` (digital) | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `NPROP` | analog | — | | 7 | `OUT2` | drive | — | | 8 | `OUT1` | drive | — | | 9 | `VM` | power | — | | 10 | `V+` | power | — | #### Drive.H haptic ERM/LRA driver | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `TRIG` | digital | — | | 3 | `EN` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 7 | `OUT+` | drive | — | | 8 | `OUT-` | drive | — | | 9 | `V_MOTOR` | power | — | | 10 | `V+` | power | — | #### Drive.P piezoelectric sensor/actuator driver | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 3 | `GPIO` | digital | — | | 4 | `I2C.CLK` | digital | `I3C.CLK` (digital) | | 5 | `I2C.DAT` | digital | `I3C.DAT` (digital) | | 7 | `OUT+` | drive | — | | 8 | `OUT-` | drive | — | | 9 | `V_DRIVE` | power | — | | 10 | `V+` | power | — | ### Power #### Power.BB multi-output buck-boost regulator | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `` | | — | | 3 | `VIN` | power | — | | 4 | `I2C.CLK` | interface | — | | 5 | `I2C.DAT` | interface | — | | 6 | `` | | — | | 7 | `` | | — | | 8 | `5V0` | power | — | | 9 | `3V3` | power | — | | 10 | `1V8` | power | — | #### Power.L.1N 1C Li-Ion charge manager w/ 1.8 & 3.3V out | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `CC1` | other | — | | 3 | `CC2` | other | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `CHG` | power | — | | 7 | `BATT` | power | — | | 8 | `VSYS` | power | — | | 9 | `3V3` | power | — | | 10 | `1V8` | power | — | #### Power.L.1T single-cell Li-Ion charge controller with adjustable LDO output | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `LP` | digital | — | | 3 | `SW` | digital | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `BATT-` | power | — | | 7 | `BATT+` | power | — | | 8 | `SUPPLY+` | power | — | | 9 | `SUPPLY-` | power | — | | 10 | `V+` | power | — | ### Display #### Display.RGBW Smart RGBW LED | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `` | other | — | | 3 | `` | other | — | | 4 | `I2C.CLK` | digital | — | | 5 | `I2C.DAT` | digital | — | | 6 | `` | other | — | | 7 | `` | other | — | | 8 | `EN` | digital | — | | 9 | `` | other | — | | 10 | `V+` | power | — | ### Store #### Store.O.128 128Mbit NOR flash | Pad | Default function | Type | Alternates | | --- | --- | --- | --- | | 1 | `GND` | power | — | | 2 | `SPI.MOSI` | digital | `QSPI.IO0` (digital) | | 3 | `SPI.CS` | digital | `QSPI.CS` (digital) | | 4 | `SPI.CLK` | digital | `QSPI.CLK` (digital) | | 5 | `SPI.MISO` | digital | `QSPI.IO1` (digital) | | 6 | `WP` | digital | `QSPI.IO2` (digital) | | 9 | `HOLD` | digital | `QSPI.IO3` (digital) | | 10 | `V+` | power | — | --- Generated 2026-08-12. Canonical HTML: https://bergsonne.io/resources/pad-assignments ============================================================================== End of corpus — 52 documents, generated 2026-08-12.