Sunday, July 2, 2023

The VL53L8 8x8 laser ranging sensor

The sensor

A VL53L8 is a continuation of a family of laser ranging sensors from ST. They use Single Photon Avalanche Diodes in an 8x8 array giving a pyramid of distances projected out from the sensor from ranges of 2-400cms. It can also do 4x4 with SW interpolation.

I ordered a sensor (set) from ST. 


You get two sensors on breakout boards for your $27. This is good because w/o this fancy breakout board, you will be on the hook for generating 1.2 & 1.8v.

Wired up to a modified Bluepill with an STM32L443 as a donor CPU, via some simple Dupont wiring, we can communicate with the sensor via I2C. SPI is also possible but the example code that ST provides that was used to debug the comm was I2C and this made comparing easier.

Ada driver

An Ada driver was crafted for the sensor. This was a translation of ST's API for the VL53L8. It was a job to translate it. There is 90k of FW that needs to get DL'd into the sensor. Numerous blind writes to undocumented proprietary registers. But... the important parts are there. If you follow the sequence and do as they do, the device can be brought up w/o error. Hindsight, perhaps using a hybrid project of C/Ada might work but the data structures between the C impl and the Ada might be a snag. Maybe a wrapper could work though. In the end there is an Ada driver for it but at what cost and what did Ada really bring to the table? If the sensor bringup is just a bunch of blind writes, C is perfectly fine for that. Still, the work is done. We have an Ada driver, lets use it. 

Ada stacksize

I ran into an interesting issue with the sensors FWDL. In Ada's I2C implementation, if you want to write to a 16 bit register on a device it looks as so:

<i2c addr><hi_index><lo_index><data .....>

How Adacores's top level I2C implementation handled that was as so:

Observe, this makes a new array composed of the indices and the data. This is fine for small data writes, which is the norm for most device I2C transactions. The VL53L8 though with its 90+K of FW is a challenge here. ST sends the FW in 32K chunks, so at a minimum, 32K will now go on the stack to produce the new buffer. The solution I came up with was a two array write.

Of course there is a commensurate I2C_Write and Master_Transmit that accept two arrays. The win here is no extra stack usage and my STM32L443 is now using a reasonable stack. During debug, I had less than 32 bytes free I think (all of the 64K was consumed due to the giant stack).

The caller in the driver can then say:

Prev, the index was passed in in this case as 16#0000#.

There are some other cases that are large DL's also. crosstalk, default_config. Those also use the scheme.

I think 16#7fff# is some form of bank select for the part. I wonder if the index of 0 is the offset into that bank. If so, perhaps the FWDL could have been done using the existing Ada code and just send less each time.


That's what were here for. What happens when you issue a ranging command.

So what is in results?

Looking at distance_mm ranging on a white cube that is both angled and tipped

angled l->r (l closer to pin1 arrow)
+ tipped (top further from pin1)
0x40, 0x42, 0x3c, 0x3b, 0x36, 0x38, 0x32, 0x2f,        greater right
0x3e, 0x3b, 0x35, 0x33, 0x33, 0x32, 0x2d, 0x2d,
0x3b, 0x39, 0x35, 0x30, 0x2f, 0x2e, 0x2e, 0x2a, | gt top -> less |
0x39, 0x33, 0x31, 0x2e, 0x2b, 0x28, 0x27, 0x28,
0x35, 0x2f, 0x2f, 0x2d, 0x27, 0x27, 0x26, 0x26,
0x2f, 0x2e, 0x28, 0x2a, 0x27, 0x24, 0x24, 0x23,
0x2d, 0x2b, 0x2a, 0x28, 0x24, 0x23, 0x22, 0x23,
0x29, 0x26, 0x25, 0x22, 0x22, 0x22, 0x21, 0x21          less left

Thus (0) is furthest and (63) closest

The accuracy gel's with ST's data that its +/- 2mm. I found that to be quite accurate over the measured area.

So if you rotated that 2D matrix 90 degrees clockwise it would look like what I see from the physical setup.