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.
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.
| Setting | Tier | Values | Default | Function |
|---|---|---|---|---|
| Output voltage range | basic | ±95V, ±13.28V | ±95V | set_output_range(range) |
| Touch sensing resolution | advanced | Coarse (54.5 mV per step), Fine (7.6 mV per step) | Fine (7.6 mV per step) | set_sense_gain(gain) |
| Keep settings while asleep | advanced | on / off | on | set_sleep_retention(retain) |
| Sleep after 4 ms idle | advanced | on / off | off | set_auto_sleep(enabled) |
| Never return energy to the supply | advanced | on / off | off | set_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
Studiovoid 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
Studiouint8_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
Studiovoid 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
Studiouint16_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
Studioint16_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
Studiouint16_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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiouint8_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
Studiouint8_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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiovoid 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
Studiouint8_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
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.
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_MAJOR | 3 | |
| TILE_DRIVE_P_VERSION_MINOR | 5 | |
| 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. |
| BOS1921_REFERENCE_MAX | 1743 | |
| 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_FIFO_STATE_SPACE_MASK | 0x03FF | |
| BOS1921_FIFO_DEPTH | 1024 | FIFO depth in samples (chip default; the driver never changes it). |
| BOS_SUP_RISE_I2C_ADDR_POS | 12 | |
| BOS_SUP_RISE_TIMING_DEFAULT | 0x0967 | default with I2C_ADDR nibble cleared |

