The VL53L4CD is a TOF (Time of Flight) sensor capable of measure distances from 1mm to 1200mm.
Unfortunately the datasheet doesn't provide any register information. But they provide an API but no good documentation about api functions.
The STM32Cube have a package for this: X-CUBE-TOF1 there are several applications but all uses a lot of abstractions to handle all the devices, so my goal was to have a very simple example for this specific board based on that.
To setup the project, you need to do:
Enable I2C1
Enable X-CUBE-TOF1 in the Middleware & Software section
Select Board Part Ranging, and select VL53L4CD
Configure to use the I2C1 and XShut pin
This will create a project that will contain one file called custom_tof_conf.h
which contains some defines, for example:
#define USE_CUSTOM_RANGING_VL53L4CD (1U)
#define CUSTOM_VL53L4CD_XSHUT_PORT GPIOA
#define CUSTOM_VL53L4CD_XSHUT_PIN GPIO_PIN_1
#define CUSTOM_VL53L4CD_I2C_INIT BSP_I2C1_Init
#define CUSTOM_VL53L4CD_I2C_DEINIT BSP_I2C1_DeInit
#define CUSTOM_VL53L4CD_I2C_WRITEREG BSP_I2C1_Send
#define CUSTOM_VL53L4CD_I2C_READREG BSP_I2C1_Recv
You need to create a file to use the API, I've created two files: tof.c
and tof.h
The code is very simple it will:
- Init the device (check device is reachable through I2C and get capabilities)
- Configure the device (set device settings and set the measure as continuous)
- Get the distance
Is important to note that the XShut pin in the TOF is an enable pin and the API is toggling automatically, so for this example the XShut is connected to VCC (3.3v) to be always enabled, if you have multiple I2C devices, you will need to enable / disable using the CUSTOM_VL53L4CD_XSHUT_PORT
and CUSTOM_VL53L4CD_XSHUT_PIN
before any API call (or add in the tof.c
).
The UM2931 contains more information about how to connect each pin in TOF board and dev board.
The tof.c
code:
#include "tof.h"
#include "custom_tof_conf.h"
#include <stdio.h>
VL53L4CD_Object_t tof_obj;
VL53L4CD_Capabilities_t tof_cap;
VL53L4CD_ProfileConfig_t tof_profile;
static int32_t decimal_part(float_t x)
{
int32_t int_part = (int32_t) x;
return (int32_t)((x - int_part) * 100);
}
static void print_result(VL53L4CD_Result_t *result)
{
uint8_t i;
uint8_t j;
for (i = 0; i < result->NumberOfZones; i++)
{
if (i > 0) {
printf("\r\n");
}
printf("Targets = %lu", (unsigned long)result->ZoneResult[i].NumberOfTargets);
for (j = 0; j < result->ZoneResult[i].NumberOfTargets; j++)
{
printf("\r\n |---> ");
printf("Status = %ld, Distance = %5ld mm ",
(long)result->ZoneResult[i].Status[j],
(long)result->ZoneResult[i].Distance[j]);
if (tof_profile.EnableAmbient)
{
printf(", Ambient = %ld.%02ld kcps/spad",
(long)result->ZoneResult[i].Ambient[j],
(long)decimal_part(result->ZoneResult[i].Ambient[j]));
}
if (tof_profile.EnableSignal)
{
printf(", Signal = %ld.%02ld kcps/spad",
(long)result->ZoneResult[i].Signal[j],
(long)decimal_part(result->ZoneResult[i].Signal[j]));
}
}
}
printf("\r\n");
}
int32_t TOF_Init()
{
int32_t ret;
VL53L4CD_IO_t IOCtx;
uint32_t id;
/* Configure the ranging sensor driver */
IOCtx.Address = VL53L4CD_DEVICE_ADDRESS;
IOCtx.Init = CUSTOM_VL53L4CD_I2C_INIT;
IOCtx.DeInit = CUSTOM_VL53L4CD_I2C_DEINIT;
IOCtx.WriteReg = CUSTOM_VL53L4CD_I2C_WRITEREG;
IOCtx.ReadReg = CUSTOM_VL53L4CD_I2C_READREG;
IOCtx.GetTick = BSP_GetTick;
if (VL53L4CD_RegisterBusIO(&tof_obj, &IOCtx) != VL53L4CD_OK)
{
printf("Cannot register bus\r\n");
ret = BSP_ERROR_COMPONENT_FAILURE;
}
else if ( (ret = VL53L4CD_ReadID(&tof_obj, &id)) != 0)
{
printf("Cannot read ID %ld\r\n", ret);
ret = BSP_ERROR_COMPONENT_FAILURE;
}
else
{
if (id != VL53L4CD_ID)
{
printf("Wrong ID %ld\r\n", id);
ret = BSP_ERROR_UNKNOWN_COMPONENT;
}
else
{
printf("Got device id: %ld\r\n", id);
//init device
if (VL53L4CD_Init(&tof_obj) != VL53L4CD_OK)
{
printf("Cannot init tof\r\n");
ret = BSP_ERROR_COMPONENT_FAILURE;
}
else if (VL53L4CD_GetCapabilities(&tof_obj, &tof_cap) != VL53L4CD_OK)
{
printf("Cannot get capabilities\r\n");
ret = BSP_ERROR_COMPONENT_FAILURE;
}
else
{
printf("TOF Initialized\r\n");
ret = BSP_ERROR_NONE;
}
}
}
return ret;
}
int32_t TOF_Configure()
{
tof_profile.RangingProfile = VL53L4CD_PROFILE_CONTINUOUS;
tof_profile.TimingBudget = 30U;
tof_profile.Frequency = 0; /* Induces intermeasurement period, NOT USED for normal ranging */
tof_profile.EnableAmbient = 1; /* Enable: 1, Disable: 0 */
tof_profile.EnableSignal = 1; /* Enable: 1, Disable: 0 */
/* set the profile if different from default one */
VL53L4CD_ConfigProfile(&tof_obj, &tof_profile);
return VL53L4CD_Start(&tof_obj, VL53L4CD_MODE_BLOCKING_CONTINUOUS);
}
int32_t TOF_GetDistance(VL53L4CD_Result_t *result)
{
int32_t status = VL53L4CD_GetDistance(&tof_obj, result);
if (status == BSP_ERROR_NONE)
{
print_result(result);
}
else
{
printf("Failed to get measure: %ld\r\n", status);
}
return status;
}
tof.h
#ifndef INC_TOF_H_
#define INC_TOF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "vl53l4cd.h"
int32_t TOF_Init();
int32_t TOF_Configure();
int32_t TOF_GetDistance(VL53L4CD_Result_t *result);
#ifdef __cplusplus
}
#endif
#endif /* INC_TOF_H_ */
The use is very simple:
#include "tof.h"
...
int main(void)
{
/* USER CODE BEGIN 2 */
// init TOF device
int32_t tof_status = TOF_Init();
if (tof_status != 0)
{
printf("Failed to init TOF: %ld\r\n", tof_status);
}
else
{
TOF_Configure();
}
/* USER CODE END 2 */
...
/* USER CODE BEGIN WHILE */
VL53L4CD_Result_t result;
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
TOF_GetDistance(&result);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
This will initialize the device and configure it.
After that it will get the distance, the tof.c
will by default print the results like this:
Targets = 1
|---> Status = 0, Distance = 110 mm , Ambient = 0.00 kcps/spad, Signal = 112.00 kcps/spad
Targets = 1
|---> Status = 0, Distance = 112 mm , Ambient = 0.00 kcps/spad, Signal = 113.00 kcps/spad
But can be changed to not print it.
Top comments (0)