The ams OSRAM TMF8829 is a direct time-of-flight ranging sensor with a MIPI I3C interface alongside I2C and SPI. It reports a distance for each zone of a SPAD array rather than one distance for the field of view, up to 1536 zones per measurement, and each zone can report up to four returns at different distances.
We ran one from a Binho Supernova over I3C, on a LIGHTRANGER 14 Click connected on SCL, SDA, 3.3 V and ground. The result worth reporting is that the part transfers complete measurement frames in HDR-DDR, the I3C high data rate mode, which neither its datasheet nor its published driver mentions.
What the documentation describes
Two published sources describe this part's bus interface.
The datasheet, DS001140 v2-00, gives it a section of its own. It states that the device "provides I3C serial communication interface with clock speed up to 12.5 MHz", that transactions "comply with the MIPI I3C specification v1.0", and that "after the Dynamic Address Assignment, SDR Mode is selected for private messaging". It goes on to cover CCCs, in-band interrupts, dynamic address assignment and coexistence with legacy I2C devices.
Across those 101 pages there is no occurrence of HDR, of double data rate, or of high data rate in any form. The sentence about SDR is a correct description of the state a target is in once it has an address, since HDR is entered explicitly rather than by default. It is simply that nothing in the document goes on to describe entering it.
The vendor's published Python driver is the same. Its only I3C-specific code re-assigns a dynamic address after a reset. There is no HDR entry, no DDR transfer and no reference to either.
The part itself says otherwise. Enumerating it and reading the bus characteristics register:
dynamic address 0x08
PID 02 10 53 CD 47 22 device id 0x53CD
BCR 0x23 DCR 0x00
HDR claimed IBI capableBCR 0x23 has bit 5 set, which in I3C means the target supports at least one high data rate mode. Bit 1 is set too, declaring that it can raise an in-band interrupt. Both claims are testable, and the rest of this post is what happened when we tested them.
Getting the part to the point where it can be tested
A dToF imager is not a register-mapped sensor, and three things have to happen before there is a frame to transfer at all.
The ranging firmware is not resident. Register 0x00 reports which application is executing: 0x80 for the bootmonitor, 0x02 for a ROM application that reports a version and rejects measurement commands, and 0x01 for the downloaded RAM application that ranges. The part measured here powers up in the bootmonitor at bootloader version 33.0, and the ranging application is downloaded over the bus into RAM at every power-up.
$ python i3c_tof.py bringup --firmware firmware/tmf8829_application.hex
target at 0x08, APP_ID 0x80 v33.0
downloading 14,444 bytes in 1 segment(s)
14,444 bytes in 0.64 s (22.0 kB/s)
RAM application v2.200 running
8x8 configured, result format 0x01
measuring
frames report layout 0x01: 1 peak(s) per zone
frame 2 8x8 62/64 zones (97%) near 128 mm median 452 mm far 5462 mm 0 multi-object 20 C
the part is measuring; run 'live' to watch itOne detail in that download is not optional. The bootloader has two commands that write firmware: W_FIFO at 0x44 writes to one processor, W_FIFO_BOTH at 0x45 writes to both, and the part has two. Using the single-processor command is not reported as an error anywhere. The download completes, every block is acknowledged, the start command answers OK, and then register 0x00 reads 0xFF with cpu_ready low, because the second processor has no firmware. RAM survives a soft reset, so a part that has run once retains a valid image and the wrong command appears to work until the next cold start.
Results come back through a FIFO rather than a register. A completed measurement sets bit 0 of INT_STATUS, and the frame is read starting at the FIFO status register 0xFA, because the pointer auto-increments and four systick registers sit between there and the FIFO data register at 0xFF. A frame is 16 bytes of header, a body of zone results, and 12 bytes of footer, and the second header byte determines the size of every zone in the body, anywhere from 3 to 24 bytes.
And a measurement is large. At one return per zone with no optional fields:
grid zones bytes per measurement transfers
8x8 64 225 1
16x16 256 801 1
32x32 1024 3138 4
48x32 1536 4674 6That is the reason the high data rate question is worth asking on this part rather than on a sensor that returns six bytes of acceleration.
HDR-DDR
Two things about DDR on this part differ from the SDR path, and both were established by measurement because there is nothing to read.
The DDR command byte is a word address. It is not a register address and not an opaque selector. Sweeping every command from 0x80 to 0xFF walks the register map in steps of two:
register = (command - 0x80) * 2Command 0x80 reads from register 0x00. Command 0xFD reads from register 0xFA, which is where a frame read begins.
Each 16-bit word arrives byte-swapped relative to an SDR read of the same registers. Unswapping is what makes the two paths agree:
SDR read of 0x00..0x07: 01 02 C8 00 00 00 00 00
DDR, raw 02 01 00 C8 00 00 00 00
DDR, byte-swapped 01 02 C8 00 00 00 00 00 <- matches SDRWith both accounted for, a complete measurement frame transfers in HDR-DDR and decodes:
$ python i3c_tof.py frame --mode 16x16 --hdr
frame 2 16x16 256/256 zones (100%) near 129 mm median 302 mm far 2796 mm 0 multi-object 21 CForty frames were read alternating between the two paths. All forty decoded valid on both, returning the same zone results byte for byte, whether the transfer ran at single or double data rate.
One consequence of the word addressing is worth knowing. No DDR command lands on the FIFO data register at 0xFF on its own, so a DDR frame read cannot be chunked the way the SDR one is. Continuing from command 0xFF, which addresses register 0xFE and reaches the FIFO one byte later, was tried and does not reconstruct the frame. That confines DDR to configurations whose whole frame fits in a single transfer, which is 8x8 and 16x16. Above that the chunked SDR read carries the frame, and the two produce identical data.
The other claim
A declared capability is not a used one, and the same BCR makes a second claim that goes the other way.
Bit 1 says the target can raise an in-band interrupt, which would let a completed measurement signal on the data line rather than being polled for. With the controller configured to accept them and the sensor free-running at a 100 ms period, three seconds produced 31 frames and no in-band interrupts. Frame completion is signalled through the INT_STATUS register and the interrupt pin, consistent with the vendor's driver, which polls the register and contains no IBI support.
The BCR describes what the silicon's bus interface can do. It does not indicate whether the firmware above it uses the capability, and on this part the two bits fall on opposite sides of that line: HDR is undocumented and works, IBI is declared and unused. Both are worth checking against the firmware revision a design will ship rather than against the bit.
One more declared number
While testing transfer sizes, the same pattern appeared in a third place. The target declares a maximum read length through GETMRL:
GETMRL -> 01 FF maximum read length 511 bytes
GETMWL -> FF FF maximum write length 65535 bytesIt does not enforce it. An 801-byte read of a complete 16x16 frame succeeds and decodes. Meanwhile the largest single read this adapter performs is 1024 bytes, and at 1025 the read returns nothing at all rather than truncating, with the frame still held in the FIFO. That 1024 boundary belongs to the controller rather than the sensor, so another host should be measured rather than assumed to match it.
Three numbers, none of which can be used on its own: the declared length understates what works, the adapter's limit is what actually bounds a transfer, and the frame's own header carries the length that has to be read.
The application note
AN0007 covers the whole walkthrough: the bootloader entry sequence with the register writes that force it, the frame header and footer field by field, the result format bit by bit, the row interleaving that the two largest configurations use, rendering the results in a terminal, the HDR-DDR results with the command sweep that produced them, and a troubleshooting table of failures that present as something else.
The archive includes the utility, in four parts: frame bytes to zone results and back, zone results to a rendering, the sensor, and the command line. The frame decoder has no hardware dependency and encodes as well as decodes, so a round trip exercises the real layout, and the renderer can be developed against a synthetic scene with no part attached. The ranging firmware is included under the MIT license it is published with.


