TMF8829 Universal Driver 0.1.0
Loading...
Searching...
No Matches
Getting Data Out of the Driver

This page explains how measurement results get from the TMF8829 sensor to your application, why the driver uses callbacks for data delivery, and how to assemble a complete frame when the payload is larger than the driver buffer.

Overview

Once a measurement is complete, the TMF8829 writes the result frame into an internal FIFO. Your application retrieves that frame by calling tmf8829_read_results (or tmf8829_read_histogram for histogram data). During this call the driver reads the FIFO in chunks, clock-corrects distance data, and delivers each chunk to your application through one of the registered callback functions set on the tmf8829_driver_t struct:

Field Callback type When invoked
on_stream_header tmf8829_on_stream_header_fn Once per tmf8829_read_results / tmf8829_read_histogram call, before any payload. Carries the fused pre-header (5 bytes) + frame header (16 bytes) = 21 bytes total.
on_result tmf8829_on_result_fn One or more times per tmf8829_read_results call, once for each FIFO chunk. The final chunk ends with the 12-byte frame footer (including the 2-byte EOF marker).
on_histogram tmf8829_on_histogram_fn One or more times per tmf8829_read_histogram call, once for each FIFO chunk. The final chunk also ends with the 12-byte frame footer.

All callbacks are optional. Set any field to NULL to ignore that data path.

Why Callbacks instead of a Return Buffer

The TMF8829 stores result frames in a sensor-side FIFO (TMF8829_FIFO_SIZE = 8192 bytes). A single measurement frame — especially at high resolutions such as 48×32 — can be several kilobytes in size. Allocating a contiguous host buffer large enough for the worst-case frame would waste RAM on constrained microcontrollers.

Instead the driver re-uses the small, caller-supplied drv->buffer as a staging area and calls the registered callback once for each chunk read from the FIFO. This keeps the required RAM at TMF8829_MIN_BUFFER_SIZE (256 bytes minimum) regardless of the measurement resolution. A larger buffer reduces the number of bus transactions and callback invocations, but is never mandatory.

The on_result Callback Is Called Multiple Times

This is the most important thing to understand about the data path.

The total payload of one result frame equals the number of measurement zones multiplied by the bytes per zone (which depends on the active result format). For large configurations (e.g. 48×32 at full format) this can far exceed drv->buffer_len. The driver therefore loops, reading drv->buffer_len bytes (or fewer for the final chunk) and invoking on_result each iteration:

tmf8829_read_results()
├─ read 21 bytes from FIFO_STATUS
│ └─ on_stream_header(drv, buf, 21) [once]
│ ├─ bytes 0– 4 : pre-header (TMF8829_PRE_HEADER_SIZE = 5)
│ └─ bytes 5–20 : frame header (TMF8829_FRAME_HEADER_SIZE = 16)
├─ loop while payload remaining:
│ ├─ read min(buffer_len, remaining) bytes from FIFO
│ ├─ apply clock correction to distance data (if enabled)
│ └─ on_result(drv, buf, chunk_len) [one or more times]
│ ├─ intermediate chunks : pure measurement data
│ └─ final chunk : remaining measurement data
│ + 12-byte footer (TMF8829_FRAME_FOOTER_SIZE)
│ └─ last 2 bytes: EOF marker 0xE0F7
└─ verify EOF marker in final chunk

Your on_result implementation must therefore accumulate all chunks to reconstruct the full frame. The callback receives a pointer to drv->buffer (overwritten each iteration) and the chunk length. Copy the data out before the function returns.

A buffer of at least the full payload size reduces on_result to a single call per measurement.

Minimal Setup to Trigger a Measurement

The steps below show the minimum code required to have on_result fire. Error handling is omitted for clarity.

1. Implement the Result Callback

static uint8_t my_frame_buf[5120]; /* large buffer */
static uint16_t my_frame_len = 0;
static void on_result(tmf8829_driver_t* drv, const uint8_t* data, uint16_t len) {
(void)drv;
/* Accumulate chunks into a contiguous frame buffer. */
if (my_frame_len + len <= sizeof(my_frame_buf)) {
memcpy(my_frame_buf + my_frame_len, data, len);
my_frame_len += len;
}
}
struct tmf8829_driver tmf8829_driver_t
Definition tmf8829_types.h:33

2. Register the Header Callback (Optional)

If you also want the 21-byte stream header, implement on_stream_header and register it on the driver instance. The callback fires once per tmf8829_read_results call, before any on_result invocations, so you can use it to initialise your accumulation buffer:

static void on_header(tmf8829_driver_t* drv, const uint8_t* data, uint16_t len) {
(void)drv;
/* Copy header to the start of the frame buffer and advance the write position. */
memcpy(my_frame_buf, data, len);
my_frame_len = len;
}

3. Configure the Driver Instance

static uint8_t drv_buf[512]; /* driver staging buffer; larger = fewer bus transactions */
static tmf8829_driver_t sensor = {
.user_ctx = &my_i2c_handle,
.buffer = drv_buf,
.buffer_len = sizeof(drv_buf),
.on_result = on_result, /* register the callback */
.on_stream_header = on_header, /* optional; remove if header is not needed */
};
#define TMF8829_DEFAULT_I2C_ADDR
Definition tmf8829_regs.h:32
@ TMF8829_BUS_I2C
Definition tmf8829_types.h:44

4. Initialise, Enable, Select Interface, and Download Firmware

Warning
After every power cycle you must call the bootloader interface-selection command before any other communication. Omitting this step causes all subsequent calls to silently fail.
tmf8829_init(&sensor, &my_ops);
tmf8829_enable(&sensor);
/* Tell the bootloader which bus to use. Call the opposite of your active bus:
* SPI active → disable I2C: tmf8829_bootloader_i2c_off()
* I2C active → disable SPI: tmf8829_bootloader_spi_off()
* If the application firmware is already running the call returns an error — safe to ignore. */
tmf8829_bootloader_i2c_off(&sensor); /* example: using SPI */
int tmf8829_download_firmware(tmf8829_driver_t *drv, uint32_t image_start_addr, int use_fifo)
Stream firmware through tmf8829_driver_t::fw_image_read and start the RAM application.
int tmf8829_bootloader_i2c_off(tmf8829_driver_t *drv)
Bootloader: TMF8829_BL_CMD_STAT_I2C_OFF.
int tmf8829_enable(tmf8829_driver_t *drv)
Drive the enable pin high and wait for CPU ready.
int tmf8829_init(tmf8829_driver_t *drv, const tmf8829_ops_t *ops)
Validate parameters, bind ops, and reset driver state.
#define TMF8829_FW_IMAGE_LOAD_ADDR_DEFAULT
Definition tmf8829_regs.h:404

5. Load a Configuration Preset and Start Measuring

/* Load a preset that matches the desired resolution. */
tmf8829_command(&sensor, TMF8829_CMD_LOAD_CFG_8X8, /*timeout_ms=*/ 50);
/* Enable the result interrupt and start cyclic measurement. */
int tmf8829_clr_and_enable_interrupts(tmf8829_driver_t *drv, uint8_t mask)
Read-modify-write TMF8829_REG_INT_ENAB to enable mask bits.
int tmf8829_command(tmf8829_driver_t *drv, uint8_t cmd, uint16_t timeout_ms)
Issue a raw application command byte and poll TMF8829_REG_CMD_STAT for acceptance.
int tmf8829_start_measurement(tmf8829_driver_t *drv)
TMF8829_CMD_MEASURE
#define TMF8829_CMD_LOAD_CFG_8X8
Definition tmf8829_regs.h:261
#define TMF8829_INT_RESULTS
Definition tmf8829_regs.h:284

6. Service the Interrupt in Your Main Loop (or ISR)

Poll the interrupt status register and call tmf8829_read_results when the result-ready bit is set:

uint8_t pending = 0;
if (pending & TMF8829_INT_RESULTS) {
my_frame_len = 0; /* reset accumulator before each new measurement */
/* on_result has now been called one or more times; my_frame_buf is complete. */
process_frame(my_frame_buf, my_frame_len);
}
int tmf8829_get_and_clr_interrupts(tmf8829_driver_t *drv, uint8_t mask, uint8_t *out_pending)
Read TMF8829_REG_INT_STATUS and write-1-to-clear bits in mask.
int tmf8829_read_results(tmf8829_driver_t *drv)
Read a result frame from the FIFO (header via TMF8829_REG_FIFO_STATUS, payload via TMF8829_REG_FIFO).

Reset the accumulator (my_frame_len = 0) before calling tmf8829_read_results, not inside on_result, so the first chunk is not accidentally appended to a previous incomplete frame.

Frame Layout Reference

The following table shows the complete on-wire frame structure and which callback delivers each section:

Section Size (bytes) Callback Notes
Pre-header 5 (TMF8829_PRE_HEADER_SIZE) on_stream_header Device tick, FIFO status
Frame header 16 (TMF8829_FRAME_HEADER_SIZE) on_stream_header Frame id, payload length, focal-plane layout
Measurement data pixel_size × num_zones on_result (one or more chunks) Distance + optional signal/noise per zone
Frame footer 12 (TMF8829_FRAME_FOOTER_SIZE) on_result (appended to final chunk) Calibration metadata; last 2 bytes are the EOF marker 0xE0F7

The on_stream_header buffer is therefore always exactly 21 bytes. The total on_result data across all chunks is pixel_size × num_zones + 12 bytes.

Read Previous Read Next
Integration Guide