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.
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)Perform a software reset.
- tile
- Pointer to tile handle
tile_drive_p_sleep
Studiovoid tile_drive_p_sleep(tile_t* tile)Enter low-power sleep mode.
- tile
- Pointer to tile handle
Runtime
tile_drive_p_set_mode
Studiovoid 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
Studiouint16_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
Studioint16_t tile_drive_p_read_sense(tile_t* tile)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
Studiouint16_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
Studiovoid tile_drive_p_play_click(tile_t* tile, uint8_t intensity_pct)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
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 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
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.
- tile
- Initialised tile handle
- intensity_pct
- 0–100 percent of full-scale output
- 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.
- 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
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.
- 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
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.
- 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
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. 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
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.
- tile
- Initialised tile handle
- buf
- Output buffer for int16_t sense samples
- 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.
- tile
- Pointer to tile handle
- 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.
- tile
- Pointer to tile handle
- 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 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
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.
- tile
- Pointer to tile handle
- 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.
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.
- tile
- Pointer to tile handle
- 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)Write a sample to the FIFO.
- tile
- Pointer to tile handle
- sample
- Signed 16-bit waveform sample
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.
- 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
Studiouint8_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_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
- ±95 V range, FBratio 31 (default)
- DRIVE_P_OUTPUT_LOW_V
- ±13.28 V range, FBratio 4.33
drive_p_sense_gain_t
Sense-channel resolution (CONFIG.GAINS).
- DRIVE_P_SENSE_COARSE_GAIN
- 54.5 mV LSB, FBratio 31
- DRIVE_P_SENSE_FINE_GAIN
- 7.6 mV LSB, FBratio 4.33 (default)
Constants
| 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 |

