BERGSONNE

Drive.P

Piezoelectric haptic driver for the Drive.P tile (rev a).

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.

Datasheet

Vibe Settings

What Studio’s Vibe interface lets you change on this tile, in plain language or from the tile inspector. Each one is an argument of a driver function below, so the same setting is available from Blocks, the DSL and C. Basic settings are shown by default; advanced ones sit behind the inspector’s Advanced toggle.

SettingTierValuesDefaultFunction
Output voltage rangebasic±95V, ±13.28V±95Vset_output_range(range)
Touch sensing resolutionadvancedCoarse (54.5 mV per step), Fine (7.6 mV per step)Fine (7.6 mV per step)set_sense_gain(gain)
Keep settings while asleepadvancedon / offonset_sleep_retention(retain)
Sleep after 4 ms idleadvancedon / offoffset_auto_sleep(enabled)
Never return energy to the supplyadvancedon / offoffset_upi(enabled)

Examples

Quick start

  #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 reference

Initialization

tile_drive_p_find

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

void tile_drive_p_init(tiles_pal_t* hal, uint8_t instance, tile_t* tile, const drive_p_cfg_t *cfg)

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

Lifecycle

tile_drive_p_reset

void tile_drive_p_reset(tile_t* tile)

Follows the datasheet's safe order (§6.2.8): output to 0 V (Direct / FIFO modes) and OE = 0, wait for IC_STATUS.STATE to leave RUN (bounded at 150 ms: a full FIFO keeps playing 128 ms after OE = 0), then RST. The chip and the driver's shadow return to power-on defaults (the I2C address is kept) and the tile goes to NONE: re-run init() afterwards. To reset and keep your configuration, use @ref tile_drive_p_check_and_recover.

tile
Pointer to tile handle

tile_drive_p_sleep

Studio
void tile_drive_p_sleep(tile_t* tile)

Writes CONFIG with DS = 1 and OE = 0 (datasheet §6.2.6): the output stops and the chip drops to SLEEP — ~2.4 µA with retention, ~0.6 µA without (see @ref tile_drive_p_set_sleep_retention). Any I2C traffic wakes the chip, so leave the bus alone until @ref tile_drive_p_wake; set_mode() and the play / sense helpers call wake() for you.

tile_drive_p_wake

Studio
uint8_t tile_drive_p_wake(tile_t* tile)

Follows the datasheet start-up sequence (§7.4.1 steps 3-6, §7.4.2): a dummy write wakes the chip (its data is ignored, §6.2.6), a 1 ms settle covers the 50 µs SLEEP→IDLE time, then CONFIG (DS = 0, OE = 0, GAINS / GAIND / RET), PARCAP (with UPI), SUP_RISE and COMM (TOUT) are rewritten from the driver's shadow, and CHIP_ID is read back. Rewriting everything covers both retention settings: with retention off (RET = 1) the chip loses its registers in SLEEP; its I2C address survives either way (§6.10.8). RAM contents (RAM-playback / synthesis waveforms) are not restored — re-load them after a no-retention sleep. The tile returns to IDLE mode. Also use it after a COMM.TOUT auto-sleep (@ref tile_drive_p_set_auto_sleep), which leaves the tile READY but the chip asleep. Harmless on an awake chip. 0 otherwise (tile ERROR, or the tile was never initialized)

Returns 1 if the chip answered with the expected CHIP_ID (tile READY),

Runtime

tile_drive_p_set_mode

Studio
void tile_drive_p_set_mode(tile_t* tile, drive_p_mode_t mode)

Set the operating mode.

mode
One of the drive_p_mode_t values

tile_drive_p_read

Studio
uint16_t tile_drive_p_read(tile_t* tile)

Read the current return register value.

Returns 16-bit value from the currently selected return register

tile_drive_p_read_sense

Studio
int16_t tile_drive_p_read_sense(tile_t* tile)

Must be in SENSE_FINE or SENSE_COARSE mode.

Returns Signed 16-bit sense value (−2048 to +2047)

tile_drive_p_read_status

Studio
uint16_t tile_drive_p_read_status(tile_t* tile)

Read the IC status register.

Returns 16-bit IC_STATUS value

tile_drive_p_play_click

Studio
void tile_drive_p_play_click(tile_t* tile, uint8_t intensity_pct)

Streams a 2 ms half-sine pulse through the FIFO at 8 ksps in one burst, framed by 0 V samples: two before it (discharges residual piezo charge, §6.2.18) and the pulse itself ends at exactly 0 V, so the output rests at 0 V when the FIFO drains (§6.6). 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.28 V for low-voltage piezos). Returns when the FIFO has been written; the chip continues playing the click after the call returns. The output is switched off (OE = 0) right after the last sample is queued; the chip finishes the FIFO first (§6.6), then idles. The next play / set_mode call turns it back on.

intensity_pct
[0..100] Percent of full-scale output (100 = ±95 V, or ±13.28 V in the low range)

tile_drive_p_play_sine

Studio
void tile_drive_p_play_sine(tile_t* tile, uint16_t freq_hz, uint8_t intensity_pct, uint16_t ms)

Generates and streams sine samples at 8 ksps. Frequency is software-quantised to the sample rate (max useful ~3 kHz). The last 2 ms fade linearly to exactly 0 V, so the output never parks at a DC level (§6.6). Streaming paces on FIFO_STATE.FIFO_SPACE and writes 32-sample bursts: the call returns once the last sample is queued, i.e. it blocks for about (ms − 128) ms on long tones and the chip plays out the final ≤ 128 ms after it returns. Gapless output needs a ≥ 400 kHz bus. Stops early if the chip reports ERROR or stops draining for 20 ms. Like play_click(), turns the output off (OE = 0) after queuing the last sample; the chip idles once the FIFO has drained.

freq_hz
[1..4000] Sine frequency in Hz (50–3000 useful range)
intensity_pct
[0..100] Percent of full-scale output (100 = ±95 V, or ±13.28 V in the low range)
ms
Duration in milliseconds

tile_drive_p_play_buzz

Studio
void tile_drive_p_play_buzz(tile_t* tile, uint8_t intensity_pct, uint16_t ms)

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.

intensity_pct
[0..100] Percent of full-scale output (100 = ±95 V, or ±13.28 V in the low range)
ms
Duration in milliseconds

tile_drive_p_play_pulse_train

Studio
void tile_drive_p_play_pulse_train(tile_t* tile, uint8_t intensity_pct, uint8_t count, uint16_t gap_ms)

The classic "tick-tick-tick" pattern. Composes @ref tile_drive_p_play_click with `core_delay_ms` between clicks. The output stays on between clicks and goes off (OE = 0) once, after the last click is queued.

intensity_pct
[0..100] Percent of full-scale output (100 = ±95 V, or ±13.28 V in the low range)
count
[1..255] Number of clicks
gap_ms
Milliseconds between successive clicks

tile_drive_p_is_touched

Studio
uint8_t tile_drive_p_is_touched(tile_t* tile, uint16_t threshold_mv)

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.

threshold_mv
Absolute sense voltage threshold in mV

Returns 1 if sense > threshold (touched), 0 otherwise

tile_drive_p_play_on_touch

Studio
uint8_t tile_drive_p_play_on_touch(tile_t* tile, uint8_t intensity_pct, uint16_t threshold_mv, uint32_t timeout_ms)

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.

intensity_pct
[0..100] Percent of full-scale output (100 = ±95 V, or ±13.28 V in the low range)
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
void tile_drive_p_play_samples(tile_t* tile, const int16_t* samples, uint16_t count)

Switches into FIFO play mode (if not already there) and writes `count` samples to REFERENCE[11:0] (signed 12-bit, two's complement), 8 ksps. Values beyond ±BOS1921_REFERENCE_MAX (±1743, the ±95 V rated output) are clamped. Start and end the waveform at 0: the last sample stays on the output when the FIFO drains, and the driver does not append one (so back-to-back calls can stream a long waveform in pieces). Paced on FIFO_SPACE with 32-sample burst writes; returns once the last sample is queued (up to 128 ms of it still playing). Stops early if the chip reports ERROR or stops draining for 20 ms. Leaves the output on (OE = 1) so calls chain; call set_mode(DRIVE_P_MODE_IDLE) when done to switch it off. Use this for arbitrary waveforms that don't fit the click / sine / buzz / pulse-train idioms — e.g., recorded waveforms or DSP-generated patterns.

samples
Buffer of signed samples, within ±1743
count
Number of samples to write

tile_drive_p_read_sense_samples

Studio
void tile_drive_p_read_sense_samples(tile_t* tile, int16_t* buf, uint16_t count)

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.

buf
Output buffer for signed 12-bit sense samples (7.6 mV/LSB)
count
Number of samples to read

Config

tile_drive_p_set_output_range

Studio
void tile_drive_p_set_output_range(tile_t* tile, drive_p_output_range_t range)

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.

range
DRIVE_P_OUTPUT_HIGH_V or DRIVE_P_OUTPUT_LOW_V
  • 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
void tile_drive_p_set_sense_gain(tile_t* tile, drive_p_sense_gain_t gain)

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.

gain
DRIVE_P_SENSE_FINE_GAIN or DRIVE_P_SENSE_COARSE_GAIN

tile_drive_p_set_sleep_retention

Studio
void tile_drive_p_set_sleep_retention(tile_t* tile, uint8_t retain)

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; wake() rewrites the driver's register configuration either way, but RAM contents are lost. Set this before calling sleep().

retain
1 = retain (default), 0 = clear on sleep

tile_drive_p_set_auto_sleep

Studio
void tile_drive_p_set_auto_sleep(tile_t* tile, uint8_t enabled)

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.

enabled
1 = auto-sleep on idle, 0 = stay awake
  • 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. The driver's tile state stays READY; the first I2C write after the timeout only wakes the chip and is discarded, so call @ref tile_drive_p_wake before the next command.

tile_drive_p_set_upi

Studio
void tile_drive_p_set_upi(tile_t* tile, uint8_t enabled)

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.

enabled
1 = sink-only (UPI on), 0 = energy recovery (default)

Advanced

tile_drive_p_write_fifo

Studio
void tile_drive_p_write_fifo(tile_t* tile, int16_t sample)

Raw REFERENCE write, no clamping (the same register takes WFS command words in the RAM modes). In Direct / FIFO mode keep samples within ±BOS1921_REFERENCE_MAX (±1743 = ±95 V).

sample
Signed 12-bit waveform sample (REFERENCE[11:0], two's complement)

tile_drive_p_write_reg

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)

tile_drive_p_wfs_write

Studio
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.

words
Array of 16-bit words (big-endian on wire)
count
Number of words (max 8)

tile_drive_p_check_and_recover

Studio
uint8_t tile_drive_p_check_and_recover(tile_t* tile, drive_p_mode_t restore_mode)

If IC_STATUS shows STATE = ERROR or any fault bit, resets the chip in the §6.2.8 order (see @ref tile_drive_p_reset; blocks up to ~150 ms while a FIFO drains), then rewrites the full configuration — init's tuned PARCAP (with UPI), SUP_RISE, output range, sense gain, sleep retention and auto-sleep — and re-enters restore_mode. A sleeping tile is woken first.

restore_mode
Mode to re-enter after recovery

Returns 1 if recovery was performed, 0 if device was healthy

gapMulti-device SYNC pinSYNC 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.

Other

tile_drive_p_init_at

void tile_drive_p_init_at(tiles_pal_t* hal, uint8_t addr, tile_t* tile, const drive_p_cfg_t *cfg)

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

uint8_t tile_drive_p_reassign_address(tiles_pal_t* hal, uint8_t cur_addr, uint8_t new_addr)

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

  • 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.

Driver gaps · 1

Chip capabilities this driver doesn’t expose yet.

advancedI3C alternate bus modePads 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.

DRIVE_P_MODE_IDLE
Output disabled, status readback
DRIVE_P_MODE_SENSE_FINE
Piezo sensing, 7.6 mV/LSB resolution
DRIVE_P_MODE_SENSE_COARSE
Piezo sensing, 54.5 mV/LSB resolution
DRIVE_P_MODE_PLAY_DIRECT
Direct waveform output
DRIVE_P_MODE_PLAY_FIFO
FIFO-buffered playback, 8 ksps
DRIVE_P_MODE_PLAY_RAM_SYNTH
RAM Synthesis waveform playback

drive_p_output_range_t

Output voltage range (CONFIG.GAIND).

DRIVE_P_OUTPUT_HIGH_V
±95V
DRIVE_P_OUTPUT_LOW_V
±13.28V

drive_p_sense_gain_t

Sense-channel resolution (CONFIG.GAINS).

DRIVE_P_SENSE_COARSE_GAIN
Coarse (54.5 mV per step)
DRIVE_P_SENSE_FINE_GAIN
Fine (7.6 mV per step)

Constants

TILE_DRIVE_P_VERSION_MAJOR3
TILE_DRIVE_P_VERSION_MINOR5
TILE_DRIVE_P_VERSION_PATCH0
BOS1921_I2C_ADDR_DEFAULT0x44
BOS1921_I2C_ADDR_SECOND0x45Instance 1 — first of a pair
BOS1921_I2C_ADDR_THIRD0x46Instance 2 — second of a pair
BOS1921_CHIP_ID_DEFAULT0x0781Expected lower 12 bits of CHIP_ID register.
BOS1921_REFERENCE_MAX1743
BOS_STATUS_STATE_MASK0x0300STATE field in IC_STATUS (bits 9:8).
BOS_STATUS_STATE_IDLE0x0000
BOS_STATUS_STATE_CALIB0x0100
BOS_STATUS_STATE_RUNNING0x0200
BOS_STATUS_STATE_ERROR0x0300
BOS_STATUS_FAULT_MASK0x00FCFault bits in IC_STATUS (bits 7:2, excluding FULL and PLAYST).
BOS_FIFO_STATE_SPACE_MASK0x03FF
BOS1921_FIFO_DEPTH1024FIFO depth in samples (chip default; the driver never changes it).
BOS_SUP_RISE_I2C_ADDR_POS12
BOS_SUP_RISE_TIMING_DEFAULT0x0967default with I2C_ADDR nibble cleared