Adding the VL53L1X Driver to an STM32Cube Project

Introduction

image

The VL53L1X Time-of-Flight (ToF) module is a long-distance laser-ranging sensor that is ideal for applications like gesture recognition, user/obstacle detection, camera autofocus, etc. While its predecessor (the VL53L0X) boasted an absolute range of up to 2 meters, the pin-to-pin compatible VL53L1X surpasses it with an absolute range of 4 meters and the ability to reduce its field-of-view (FoV). Prototyping with this sensor has been made simple with the Arduino compatible X-NUCLEO-53L1A1 evaluation board. This shield is populated with one VL53L1X sensor, which may be used with the included glass cover window and spacers to simulate the sensor’s behavior in a practical application. Also included are two VL53L1X breakout boards which may be connected directly to the shield or used independently with flying leads. ST has not published any documentation describing the internal registers of the VL53L1X, meaning the VL53L1X driver (also referred to as the VL53L1X API) must be used for the MCU to interface with the sensor. This includes everything from initializing/configuring the sensor to performing ranging measurements.

At the time of writing, ST’s product page for the X-NUCLEO-53L1A1 states that this expansion board is “for the NUCLEO-F401RE and NUCLEO-L476RG development boards”. This is a bit misleading. What they mean is that the example projects that have been provided for the X-NUCLEO-53L1A1 shield will only work with the NUCLEO-F401RE and NUCLEO-L476RG boards. For those wishing to get started as quickly as possible with the VL53L1X module, it is recommended that they use either of these Nucleo boards and modify ST’s example application. They might even consider purchasing the P-NUCLEO-53L1A1 evaluation kit which includes the X-NUCLEO-53L1A1 shield as well as a NUCLEO-F401RE board.

Requirements

The firmware, hardware, and software used in this tutorial are as follows.

  • STSW-IMG007 (Version 2.4.5)
    • This package contains the platform independent VL53L1X driver files (written in C). It can be found here.
  • X-CUBE-53L1A1 (Version 2.1.0)
    • This is a software expansion package for the X-NUCLEO-53L1A1 expansion board, including a Board Support Package (BSP) that utilizes the VL53L1X driver. An example program that may be run on either the NUCLEO-F401RE and NUCLEO-L476RG development boards is provided. It can be found here.
  • A Nucleo board
  • X-NUCLEO-53L1A1
    • An expansion board for evaluating the VL53L1X module. It can be purchased here.
  • STM32CubeMX* (Version 4.25.1)
    • With this tool, an STM32Cube project can easily be created along with any desired C initialization code. It may be downloaded here.
  • Atollic TrueSTUDIO for STM32* (Version 9.0.0)
    • Feel free to use another IDE if you prefer, e.g. Keil, IAR, etc. However, since ST has recently acquired Atollic, it is likely that they will be placing more and more emphasis on TrueSTUDIO as a development tool as time goes on. It can be found here.

*Since writing this tutorial, ST has released STM32CubeIDE, which like TrueSTUDIO, is also based on Eclipse. It also integrates the project initialization and configuration capabilities of STM32CubeMX, meaning the procedure outlined in this tutorial can still be followed using STM32CubeIDE with minimal alteration. If you have any questions about migrating to STM32CubeIDE, feel free to ask them below.

The X-NUCLEO-53L1A1 expansion board featuring the VL53L1X ToF module. Also shown are the two VL53L1X breakout boards with their 10 pin connectors that may be attached directly to the shield, the two different cover windows, and the spacers to simulate air gaps.

Creating an STM32Cube Project

For those who don’t already have an existing STM32Cube project, this section will quickly walk through setting one up using STM32CubeMX. For those looking for more detailed information and examples on using this tool, its user manual is a great resource.

  1. Open STM32CubeMX and select New Project . Using either the MCU selector tab or the Board selector tab, choose the MCU/board being used for the project. The Board selector tab (Figure 1) is the preferred option when using an ST evaluation board because not only are there fewer options to wade through, but the Pinout view will be initialized with the pin assignments for the LEDs, buttons, communication interfaces, etc. Use the filters to narrow down your choices, make a selection, and click Start Project . When it asks you if you would like to initialize all peripherals with their default mode, click No .

TIP: The MCU selector can be used during the initial design phase of a project to choose an MCU or development board based on your desired criteria.


Figure 1: Creating a new STM32Cube project for the NUCLEO-F103RB board.

  1. The X-NUCLEO-53L1A1 expansion board communicates with the MCU via the I2C1 peripheral on pins PB8 ( I2C1_SCL ) and PB9 ( I2C1_SDA ). In the Pinout view, click on each of these pins and select their I2C alternate functions. When this is done, the pins should be highlighted orange because the I2C1 peripheral has not yet been enabled. To enable it, expand I2C1 in the peripheral tree and select I2C from the dropdown list. Pins PB8 and PB9 should change from orange to green (see Figure 2).
  2. To print ranging data from the VL53L1X sensor to a serial terminal, pins PA2 and PA3 need to be configured as USART2_TX and USART2_RX respectively. If the Board selector tab was used to create the project, the alternate functions for these pins should already be configured. If not, configure them in the same way the I2C1 pins were configured in the previous step. Then, enable the USART2 peripheral by expanding USART2 in the peripheral tree and selecting Asynchronous mode from the dropdown list, as shown in Figure 2. Again, the pins will change from orange to green.

    Don’t worry about peripherals in the list with red crosses or yellow exclamation marks next to them. These are not errors, just FYI’s. For more information, move your mouse over the name of the peripheral with a symbol next to it and a tooltip will appear explaining the reason for the symbol.


Figure 2: Configuring the MCU pins and enabling the corresponding peripherals

  1. Open the settings window (Figure 3) by choosing Project > Settings from the main menu bar. Give your project a name and choose where to save it. Also, select your desired Toolchain/IDE from the dropdown list. Everything else should be fine at its default value. Click OK.


Figure 3: Modifying the project settings

  1. Generate the code for your project by selecting Project > Generate Code from the main menu bar. If all goes well, a message will be displayed confirming successful code generation. Click Close .
  2. Open Atollic TrueSTUDIO and navigate to File > Open Projects from File System . If you are using an older version and this option is not available, navigate instead to File > Import , choose Projects from Folder or Archive and click Next . Clicking the Directory button will open a browser from which you can select the directory which contains your project. Make sure that the box next to your project name is checked, as shown in Figure 4, and click Finish .


Figure 4: Importing the project into TrueSTUDIO

The project should now be available in the Project Explorer. If you expand the project directory and then expand the Src directory, you should find the main.c file. Looking through this file, you will see that along with the peripheral configuration functions, there are comments that designate user sections. Any code placed in these sections will be preserved the next time code is generated in STM32CubeMX.

Adding the VL53L1X Driver

Adding the VL53L1X driver to an STM32Cube project is quite simple once you understand the differences between the STSW-IMG007 package and the X-CUBE-53L1A1 package. The STSW-IMG007 package contains the VL53L1X driver along with a few “platform” files that must be modified by the programmer to port the driver to a specific MCU platform. The X-CUBE-53L1A1, on the other hand, is a complete STM32Cube expansion package that bundles the VL53L1X driver with a board support package (BSP) for the X-NUCLEO-53L1A1 expansion kit. In this package, the platform files have been modified to allow the driver to run on top of the STM32Cube HAL (hardware abstraction layer), thereby porting it to the entire STM32 MCU line. These modified platform files will be added along with the STSW-IMG007 files to our STM32Cube project. This way, we don’t have to modify the platform files ourselves to get the driver working on the STM32 platform.

In the steps below, files from both the STSW-IMG007 and X-CUBE-53L1A1 packages will be copied into the STM32Cube project we created in the previous section. This can easily be done by dragging and dropping files into the desired folder in the Project Explorer.

  1. Unzip the STSW-IMG007 package and add the core and platform directories to your project. For simplicities sake, I added them both to the Drivers directory after creating a subdirectory called VL53L1X, as shown in Figure 5. However, you can add them anywhere that makes the most sense to you. Exploring the X-CUBE-53L1A1 package will give you an idea of how ST chooses to organize these files in their projects.

A screenshot of the project explorer in the TrueSTUDIO IDE. The Drivers folder has been expanded to reveal the CMSIS folder, STM32F1xx_HAL_Driver folder, and the VL53L1X folder. The VL53L1X folder has been expanded to reveal the core folder and the platform folder.
Figure 5: The STM32Cube project’s directory structure after adding the VL53L1X driver files

  1. To port the VL53L1X driver to the STM32Cube project, unzip the X-CUBE-53L1A1 package and locate the “SimpleRanging” example project. It doesn’t matter which Nucleo board the project is based on. This tutorial utilizes the project located at en.X-CUBE-53L1A1_v2.1.0\STM32CubeExpansion_53L1A1_V2.1.0\Projects\STM32F401RE-Nucleo\Examples\53L1A1\SimpleRanging.

    a) Copy the Src/vl53l1_platform.c file into the platform/src directory of the project (see Figure 5), replacing the version that is already there.

    b) Copy the Inc/vl53l1_platform_user_data.h file into the ‘platform/inc’ directory of the project (see Figure 5), replacing the version that is already there.

    c) Delete the vl53l1_platform_init.h and vl53l1_platform_init.c files from the platform/inc and platform/src directories, respectively.

  2. The driver files we just added contain many header files that will not be found by the compiler unless we add the directories that contain them to the list of include directories. This can easily be done in Eclipse-based IDEs by right-clicking on the directory in the Project Explorer and selecting Add/remove include path… , as shown in Figure 6a. This will result in the window shown in Figure 6b. Make sure both boxes are checked and click OK . For this example, this needs to be done for both the core/inc directory and the platform/inc directory.


a) Right-click on the folder and select Add/remove include path…


b) If the box is checked, the path will be added. If the box is unchecked, the path will be removed.

Figure 6: Adding a directory to the list of include directories

  • Alternatively, one could navigate to Project > Properties in the menu bar and from the resulting window, navigate to C/C++ General > Paths and Symbols . There, the desired directories can be added manually as shown in Figure 7.


Figure 7: The core/inc and platform/inc directories added to the list of included directories

  1. Open the main.c file in the editor. In the user section for includes, include the file vl53l1_api.h , as shown in Figure 8.

cubeide_includeApi
Figure 8: Including the VL53L1X API in main.c

  1. Open the file vl53l1_platform_user_data.h. Find the line where #include "stm32xxx_hal.h" is commented out. Replace this line with an include statement for the xx_hal.h file for your MCU. In my case, I added #include "stm32f1xx_hal.h", as shown in Figure 9.

cubeide_f1xx
Figure 9: Modifying the vl53l1_platform_user_data.h file

  1. Similarly, open the file vl53l1_platform.c . Replace #include "stm32xxx_hal.h" with the appropriate xx_hal.h file (e.g., #include "stm32f1xx_hal.h").

  2. Finally, build the project to locate an existing error in the vl53l1_wait.c file (Figure 10a). The first argument must be removed from each of the VL53L1_GetTickCount() invocations (Figure 10b).

cubeide_waitBugPresent
a) After building the project, two error are shown in the vl53l1_wait.c file.

cubeide_waitBugAbsent
b) Resolve the error by removing the first argument (“Dev”) from the function calls and rebuild the project.

Figure 10: Revising the vl53l1_wait.c file.

NOTE: If you are using a different version of the VL53L1X driver, this bug may not be present.

The project should now build successfully.

Using the VL53L1X Driver

Once the driver has been added to the project, you may wish to consult the VL53L1X API user manual to understand the different initialization, calibration, and ranging functions. However, while this manual provides plenty of excellent information about the driver and capabilities of the module, it lacks any example code. Luckily, as previously mentioned, the X-CUBE-53L1A1 package contains an example project that runs on two of the Nucleo boards. The main.c file found in en.X-CUBE-53L1A1_v2.1.0\STM32CubeExpansion_53L1A1_V2.1.0\Projects\STM32F401RE-Nucleo\Examples\53L1A1\SimpleRanging\Src\ contains a function called AutonomousLowPowerRangingTest() that demonstrates how the module can be configured and used in either interrupt or polling mode.

For those not using either the NUCLEO-F401RE or the NUCLEO-L476RG, it requires a bit of extra work getting the aforementioned example project to a run on a different board. This is because the BSP for the X-NUCLEO-53L1A1 shield used in the example has not been ported to any of the other Nucleo boards. Rather than port it to my board, however, I chose to re-write the example without the BSP. The only annoyance in doing so is interfacing with the GPIO expanders on the X-NUCLEO-53L1A1 shield. These expanders (ST’s STMPE1600 expanders) are essentially leftover from the previous iteration of this shield which utilized the VL53L0X module and included a 4-digit display. The final result is the main.c file provided below in Listing 1.

Listing 1: main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "vl53l1_api.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
// I2C addresses of GPIO expanders on the X-NUCLEO-53L1A1
#define EXPANDER_1_ADDR 0x84 // 0x42 << 1
#define EXPANDER_2_ADDR 0x86 // 0x43 << 1

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t buff[50];
  VL53L1_RangingMeasurementData_t RangingData;
  VL53L1_Dev_t  vl53l1_c; // center module
  VL53L1_DEV    Dev = &vl53l1_c;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  // initialize vl53l1x communication parameters
  Dev->I2cHandle = &hi2c1;
  Dev->I2cDevAddr = 0x52;

  /*** Initialize GPIO expanders ***/
  // Unused GPIO should be configured as outputs to minimize the power consumption
  buff[0] = 0x14; // GPDR (GPIO set direction register)
  buff[1] = 0xFF; // GPIO_0 - GPIO_7
  buff[2] = 0xFF; // GPIO_8 - GPIO_15
  HAL_I2C_Master_Transmit( &hi2c1, EXPANDER_1_ADDR, buff, 3, 0xFFFF );
  HAL_I2C_Master_Transmit( &hi2c1, EXPANDER_2_ADDR, buff, 3, 0xFFFF );

  // clear XSHUT (disable center module) -> expander 1, GPIO_15
  buff[0] = 0x13; // GPSR + 1 ( GPIO set pin state register)
  HAL_I2C_Master_Transmit( &hi2c1, EXPANDER_1_ADDR, buff, 1, 0xFFFF );
  HAL_I2C_Master_Receive( &hi2c1, EXPANDER_1_ADDR, buff, 1, 0xFFFF );
  buff[1] = buff[0] & ~( 1 << ( 15 - 8 ) ); // clear GPIO_15
  buff[0] = 0x13; // GPSR + 1 ( GPIO set pin state register)
  HAL_I2C_Master_Transmit( &hi2c1, EXPANDER_1_ADDR, buff, 2, 0xFFFF );

  HAL_Delay( 2 ); // 2ms reset time

  // set XSHUT (enable center module) -> expander 1, GPIO_15
  buff[0] = 0x13; // GPSR + 1 ( GPIO set pin state)
  HAL_I2C_Master_Transmit( &hi2c1, EXPANDER_1_ADDR, buff, 1, 0xFFFF );
  HAL_I2C_Master_Receive( &hi2c1, EXPANDER_1_ADDR, buff, 1, 0xFFFF );
  buff[1] = buff[0] | ( 1 << ( 15 - 8 ) ); // set GPIO_15
  buff[0] = 0x13; // GPSR + 1 ( GPIO set pin state register)
  HAL_I2C_Master_Transmit( &hi2c1, EXPANDER_1_ADDR, buff, 2, 0xFFFF );

  HAL_Delay( 2 );

  /*** VL53L1X Initialization ***/
  VL53L1_WaitDeviceBooted( Dev );
  VL53L1_DataInit( Dev );
  VL53L1_StaticInit( Dev );
  VL53L1_SetDistanceMode( Dev, VL53L1_DISTANCEMODE_LONG );
  VL53L1_SetMeasurementTimingBudgetMicroSeconds( Dev, 50000 );
  VL53L1_SetInterMeasurementPeriodMilliSeconds( Dev, 500 );
  VL53L1_StartMeasurement( Dev );

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	VL53L1_WaitMeasurementDataReady( Dev );

	VL53L1_GetRangingMeasurementData( Dev, &RangingData );

	sprintf( (char*)buff, "%d, %d, %.2f, %.2f\n\r", RangingData.RangeStatus, RangingData.RangeMilliMeter,
			 ( RangingData.SignalRateRtnMegaCps / 65536.0 ), RangingData.AmbientRateRtnMegaCps / 65336.0 );
	HAL_UART_Transmit( &huart2, buff, strlen( (char*)buff ), 0xFFFF );

	VL53L1_ClearInterruptAndStartMeasurement( Dev );
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

To run this example on your board, start by follow the steps outlined in the sections above. Once you have an STM32Cube project with the VL53L1X driver added, copy and paste the user code sections from the main.c file above to the main.c file in your project. These sections will be designated by comments of the form /* USER CODE BEGIN <section> */ and /* USER CODE END <section> */. You cannot copy the entire main.c unless you are also using the NUCLEO-F103RB board.

By default, my STM32Cube project was using the newlib-nano library, which decreases the functionality of the printf function. In order to print all of the ranging data to the serial terminal, I had to switch to the newlib standard library. In TrueSTUDIO, this can be done by choosing Project > Properties from the main menu bar, navigating to C/C++ Build > Settings , and selecting the Tool Settings tab. In the General settings, you can select your desired runtime library from the dropdown list.

Conclusion

There are several ways one could go about adding the VL53L1X driver to their project, but I tried to select the simplest. Using both the STSW-IMG007 and X-CUBE-53L1A1 packages seems to require the least amount of file manipulation and reorganization. Hopefully you find the tutorial and example code helpful.

Hi

Thanks for this tutorial, I’ve followed it and it’s a perfect start to me.

However, ST released a newer version of STSW-IMG007 (v2.4.5), and it’s different in core somehow. Can you guide how to import the newer version, too?

The 53L1A1 examples uses an outdated version of the STS@-IMG007.

Thanks

Thank you for pointing this out @HamzaHajeir! I’ve been trying for the last several hours to use these updated drivers without much luck. They are certainly not backwards compatible with the old version. I’ll keep working and once I figure it out, I’ll provide an update.

1 Like

Thanks @Matt_Mielke for your efforts.

I’ve sent an inquiry to ST community, they responded with this guide file. Until I try it out, I doubt it’ll be useful.

Here’s the discussion.

Thanks again.
VL53L1X_Driver_Update_Guide.pdf (304.8 KB)

I’ve updated the article to work with the latest driver versions. Let me know if there are still any issues.

1 Like