Based on STM32CUBEMX driving TOF module VL53l0x(1)----The best practice of single module distance acquisition

Overview

VL53L0X is a new generation of time-of-flight (ToF) laser ranging module (different from traditional technology). It adopts the smallest package currently on the market and can provide accurate distance measurement regardless of the target reflectivity. It can measure absolute distances up to 2m, setting a new benchmark for ranging performance levels and opening the door to a variety of new applications.
I am currently taking ST courses. If you need samples, you can join the group and apply: 615061293.
Insert image description here

The VL53L0X integrates a leading SPAD array (Single Photon Avalanche Diode) and embeds ST’s second-generation FlightSense™ patented technology.
The VL53L0X's 940nm VCSEL emitter (vertical cavity surface emitting laser) is completely invisible to the human eye. Coupled with the built-in physical infrared filter, it has a longer ranging distance, stronger immunity to ambient light, and is resistant to cover sheets. The optical crosstalk has better stability.

I am currently taking ST courses. If you need samples, you can join the group and apply: 615061293.

Video teaching

https://www.bilibili.com/video/BV1dH4y1D7Px/

Based on STM32CUBEMX driving TOF module VL53l0x(1)----The best practice of single module distance acquisition

sample application

https://www.wjx.top/vm/OhcKxJk.aspx#

Source code download

https://download.csdn.net/download/qq_24312945/88332771

All features

● Fully integrated miniaturized module
○ 940 nm laser VCSEL
○ VCSEL driver
○ Ranging sensor with embedded advanced microcontroller
○ 4.4 x 2.4 x 1.0 mm
● Fast, accurate ranging
○ Measured absolute distance up to 2m
○ Reported distance Independent of target reflectivity
○ Advanced embedded optical crosstalk compensation simplifies cover glass selection
● Eye-safe
○ Class 1 laser device, compliant with the latest standard IEC 60825-1:2014 (3rd edition)
● Easy integration
○ Single reflow Solderable components
○ No additional optical components
○ Single power supply
○ I2C interface for device control and data transfer
○ Xshutdown (reset) and interrupt GPIO
○ Programmable I2C address
Insert image description here

specifications

The power supply requirement of this module is 2.8V, which is suitable for low-voltage application scenarios. It performs host control and data communication through the I2C interface to facilitate integration with other devices. Supports maximum fast mode rate, reaching 400k, ensuring efficient data transmission.
Finally, the VL53L0X module has a device address with a default address of 0x29, making it easy to manage and differentiate between different modules when multiple I2C devices share the same bus.

Measuring range

Insert image description here

interface

The schematic diagram of the VL53L0X module interface is shown below.
Insert image description here

Interface Description

Insert image description here

minimal system diagram

Insert image description here

IIC configuration

In this application, the VL53L0X module communicates with the master controller through the I2C (IIC) interface. Specifically, the I2C pins of the VL53L0X module are connected to the two IO ports PB6 (pin B6) and PB7 (pin B7) of the main controller.
This connection method ensures reliable data transmission and communication between the module and the main controller. PB6 serves as the serial data line (SDA) of the I2C bus and is responsible for data transmission and reception. PB7 acts as the serial clock line (SCL) of the I2C bus and is used to synchronize the timing of data transmission.

Insert image description here

Configure IIC in fast mode with a speed of 400k.
Insert image description here

Serial port redirection

Open the magic wand and check MicroLIB

Insert image description here
In main.c, add the header file. If you do not add it, an identifier "FILE" is undefined error will appear.

/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

Function declaration and serial port redirection:

/* USER CODE BEGIN PFP */
int fputc(int ch, FILE *f){
    
    
	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
/* USER CODE END PFP */

Module chip select

According to the table information provided, we can know that the XSHUT pin of the VL53L0X module is used as the chip select pin (Chip Enable). This is the Xshutdown pin. It is a digital input. When it is in a low level state (Active LOW), Can be used to turn off (i.e. "shutdown") the sensor. This is typically used to reset the sensor or turn it off to save power when sensor measurements are not needed.
Insert image description here

Looking at the manual, we can see that the corresponding IOs are PB2 and PB4.

Insert image description here

The configuration in STM32CUBEMX is as follows.
Insert image description here

module address

The default device address of the VL53L0X module is 0x29. A device address is an identifier used to identify and communicate with a specific device. By setting the device address of the VL53L0X module to 0x29, you can ensure normal communication and control with the module.
If read and write bits are added, the write address is 0x52 and the read address is 0x53.
Insert image description here

For the VL53L0X module, the default 7-bit address is 0x29 (010 1001 in binary), which is 0x52 (0101 0010 in binary) after adding the write bit, and 0x53 (0101 0011 in binary) after adding the read bit.
This means that when the master communicates with the VL53L0X module, it sends 0x52 address byte for a write operation, or 0x53 address byte for a read operation.

extern I2C_HandleTypeDef hi2c1;

void VL53L0X_WriteByte(uint8_t add,uint8_t reg,uint8_t data)
{
    
    
	HAL_I2C_Mem_Write(&hi2c1 ,(add<<1)|0,reg,I2C_MEMADD_SIZE_8BIT,&data,1,0xffff);
	
}
void VL53L0X_WriteByte_16Bit(uint8_t add,uint8_t reg,uint16_t data)
{
    
    
	uint8_t data2[2]={
    
    0,0};
	data2[0]=data>>8;
	data2[1]=data;
	HAL_I2C_Mem_Write(&hi2c1 ,(add<<1)|0,reg,I2C_MEMADD_SIZE_8BIT,data2,2,0xffff);
	
}

void VL53L0X_WriteByte_32Bit(uint8_t add,uint8_t reg,uint32_t data)
{
    
    
	uint8_t data2[4]={
    
    0,0,0,0};
	data2[0]=data>>24;
	data2[1]=data>>16;
	data2[2]=data>>8;
	data2[3]=data;
	HAL_I2C_Mem_Write(&hi2c1 ,(add<<1)|0,reg,I2C_MEMADD_SIZE_8BIT,data2,4,0xffff);
	
}

uint8_t VL53L0X_ReadByte(uint8_t add,uint8_t reg)
{
    
    
	uint8_t data=0;
	HAL_I2C_Mem_Read(&hi2c1 ,(add<<1)|1,reg,I2C_MEMADD_SIZE_8BIT,&data,1,0xffff);
	return data;
}




uint16_t VL53L0X_ReadBytee_16Bit(uint8_t add,uint16_t reg)
{
    
    
	uint16_t data=0;
	uint8_t data2[2];
	HAL_I2C_Mem_Read(&hi2c1 ,(add<<1)|1,reg,I2C_MEMADD_SIZE_8BIT,data2,2,0xffff);
	data=data2[0];
	data=data<<8;
	data+=data2[1];
	
	return data;

}

Reference documentation

The document referenced here asks about the driver code of Arduino.
https://github.com/pololu/vl53l0x-arduino/tree/master

initialization

The initialization given in the reference program is shown below.
Insert image description here

Among them, sensor.init() is the module initial setting of VL53L0X.

// Initialize sensor using sequence based on VL53L0X_DataInit(),
// VL53L0X_StaticInit(), and VL53L0X_PerformRefCalibration().
// This function does not perform reference SPAD calibration
// (VL53L0X_PerformRefSpadManagement()), since the API user manual says that it
// is performed by ST on the bare modules; it seems like that should work well
// enough unless a cover glass is added.
// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8
// mode.
bool VL53L0X::init(bool io_2v8)
{
    
    
  // check model ID register (value specified in datasheet)
  if (readReg(IDENTIFICATION_MODEL_ID) != 0xEE) {
    
     return false; }

  // VL53L0X_DataInit() begin

  // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary
  if (io_2v8)
  {
    
    
    writeReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV,
      readReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0
  }

  // "Set I2C standard mode"
  writeReg(0x88, 0x00);

  writeReg(0x80, 0x01);
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);
  stop_variable = readReg(0x91);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks
  writeReg(MSRC_CONFIG_CONTROL, readReg(MSRC_CONFIG_CONTROL) | 0x12);

  // set final range signal rate limit to 0.25 MCPS (million counts per second)
  setSignalRateLimit(0.25);

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0xFF);

  // VL53L0X_DataInit() end

  // VL53L0X_StaticInit() begin

  uint8_t spad_count;
  bool spad_type_is_aperture;
  if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) {
    
     return false; }

  // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in
  // the API, but the same data seems to be more easily readable from
  // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there
  uint8_t ref_spad_map[6];
  readMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);

  // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid)

  writeReg(0xFF, 0x01);
  writeReg(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00);
  writeReg(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C);
  writeReg(0xFF, 0x00);
  writeReg(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4);

  uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad
  uint8_t spads_enabled = 0;

  for (uint8_t i = 0; i < 48; i++)
  {
    
    
    if (i < first_spad_to_enable || spads_enabled == spad_count)
    {
    
    
      // This bit is lower than the first one that should be enabled, or
      // (reference_spad_count) bits have already been enabled, so zero this bit
      ref_spad_map[i / 8] &= ~(1 << (i % 8));
    }
    else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1)
    {
    
    
      spads_enabled++;
    }
  }

  writeMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);

  // -- VL53L0X_set_reference_spads() end

  // -- VL53L0X_load_tuning_settings() begin
  // DefaultTuningSettings from vl53l0x_tuning.h

  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);

  writeReg(0xFF, 0x00);
  writeReg(0x09, 0x00);
  writeReg(0x10, 0x00);
  writeReg(0x11, 0x00);

  writeReg(0x24, 0x01);
  writeReg(0x25, 0xFF);
  writeReg(0x75, 0x00);

  writeReg(0xFF, 0x01);
  writeReg(0x4E, 0x2C);
  writeReg(0x48, 0x00);
  writeReg(0x30, 0x20);

  writeReg(0xFF, 0x00);
  writeReg(0x30, 0x09);
  writeReg(0x54, 0x00);
  writeReg(0x31, 0x04);
  writeReg(0x32, 0x03);
  writeReg(0x40, 0x83);
  writeReg(0x46, 0x25);
  writeReg(0x60, 0x00);
  writeReg(0x27, 0x00);
  writeReg(0x50, 0x06);
  writeReg(0x51, 0x00);
  writeReg(0x52, 0x96);
  writeReg(0x56, 0x08);
  writeReg(0x57, 0x30);
  writeReg(0x61, 0x00);
  writeReg(0x62, 0x00);
  writeReg(0x64, 0x00);
  writeReg(0x65, 0x00);
  writeReg(0x66, 0xA0);

  writeReg(0xFF, 0x01);
  writeReg(0x22, 0x32);
  writeReg(0x47, 0x14);
  writeReg(0x49, 0xFF);
  writeReg(0x4A, 0x00);

  writeReg(0xFF, 0x00);
  writeReg(0x7A, 0x0A);
  writeReg(0x7B, 0x00);
  writeReg(0x78, 0x21);

  writeReg(0xFF, 0x01);
  writeReg(0x23, 0x34);
  writeReg(0x42, 0x00);
  writeReg(0x44, 0xFF);
  writeReg(0x45, 0x26);
  writeReg(0x46, 0x05);
  writeReg(0x40, 0x40);
  writeReg(0x0E, 0x06);
  writeReg(0x20, 0x1A);
  writeReg(0x43, 0x40);

  writeReg(0xFF, 0x00);
  writeReg(0x34, 0x03);
  writeReg(0x35, 0x44);

  writeReg(0xFF, 0x01);
  writeReg(0x31, 0x04);
  writeReg(0x4B, 0x09);
  writeReg(0x4C, 0x05);
  writeReg(0x4D, 0x04);

  writeReg(0xFF, 0x00);
  writeReg(0x44, 0x00);
  writeReg(0x45, 0x20);
  writeReg(0x47, 0x08);
  writeReg(0x48, 0x28);
  writeReg(0x67, 0x00);
  writeReg(0x70, 0x04);
  writeReg(0x71, 0x01);
  writeReg(0x72, 0xFE);
  writeReg(0x76, 0x00);
  writeReg(0x77, 0x00);

  writeReg(0xFF, 0x01);
  writeReg(0x0D, 0x01);

  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x01);
  writeReg(0x01, 0xF8);

  writeReg(0xFF, 0x01);
  writeReg(0x8E, 0x01);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  // -- VL53L0X_load_tuning_settings() end

  // "Set interrupt config to new sample ready"
  // -- VL53L0X_SetGpioConfig() begin

  writeReg(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04);
  writeReg(GPIO_HV_MUX_ACTIVE_HIGH, readReg(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low
  writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);

  // -- VL53L0X_SetGpioConfig() end

  measurement_timing_budget_us = getMeasurementTimingBudget();

  // "Disable MSRC and TCC by default"
  // MSRC = Minimum Signal Rate Check
  // TCC = Target CentreCheck
  // -- VL53L0X_SetSequenceStepEnable() begin

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);

  // -- VL53L0X_SetSequenceStepEnable() end

  // "Recalculate timing budget"
  setMeasurementTimingBudget(measurement_timing_budget_us);

  // VL53L0X_StaticInit() end

  // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration())

  // -- VL53L0X_perform_vhv_calibration() begin

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0x01);
  if (!performSingleRefCalibration(0x40)) {
    
     return false; }

  // -- VL53L0X_perform_vhv_calibration() end

  // -- VL53L0X_perform_phase_calibration() begin

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
  if (!performSingleRefCalibration(0x00)) {
    
     return false; }

  // -- VL53L0X_perform_phase_calibration() end

  // "restore the previous Sequence Config"
  writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);

  // VL53L0X_PerformRefCalibration() end

  return true;
}

Since some macro definitions are commented out, you do not need to execute the instructions in the red box below.
Insert image description here

Single reading distance length

In the main program, the main execution is to obtain data in a single time.

Insert image description here

The corresponding source code is as follows.

// Returns a range reading in millimeters when continuous mode is active
// (readRangeSingleMillimeters() also calls this function after starting a
// single-shot range measurement)
uint16_t VL53L0X::readRangeContinuousMillimeters()
{
    
    
  startTimeout();
  while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
  {
    
    
    if (checkTimeoutExpired())
    {
    
    
      did_timeout = true;
      return 65535;
    }
  }

  // assumptions: Linearity Corrective Gain is 1000 (default);
  // fractional ranging is not enabled
  uint16_t range = readReg16Bit(RESULT_RANGE_STATUS + 10);

  writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);

  return range;
}

// Performs a single-shot range measurement and returns the reading in
// millimeters
// based on VL53L0X_PerformSingleRangingMeasurement()
uint16_t VL53L0X::readRangeSingleMillimeters()
{
    
    
  writeReg(0x80, 0x01);
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);
  writeReg(0x91, stop_variable);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  writeReg(SYSRANGE_START, 0x01);

  // "Wait until start bit has been cleared"
  startTimeout();
  while (readReg(SYSRANGE_START) & 0x01)
  {
    
    
    if (checkTimeoutExpired())
    {
    
    
      did_timeout = true;
      return 65535;
    }
  }

  return readRangeContinuousMillimeters();
}

The modified version is as follows.

// Returns a range reading in millimeters when continuous mode is active
// (readRangeSingleMillimeters() also calls this function after starting a
// single-shot range measurement)
uint16_t VL53L0X_readRangeContinuousMillimeters(uint8_t add)
{
    
    
  startTimeout();
uint16_t range;
  while ( (VL53L0X_ReadByte(add,RESULT_INTERRUPT_STATUS) & 0x07) == 0)
  {
    
    
    if (checkTimeoutExpired())
    {
    
    
      did_timeout = true;
      return 65535;
    }
  }
  // assumptions: Linearity Corrective Gain is 1000 (default);
  // fractional ranging is not enabled
   range= VL53L0X_ReadBytee_16Bit(add,RESULT_RANGE_STATUS + 10);

  VL53L0X_WriteByte(add,SYSTEM_INTERRUPT_CLEAR, 0x01);

  return range;
}


// Performs a single-shot range measurement and returns the reading in
// millimeters
// based on VL53L0X_PerformSingleRangingMeasurement()
uint16_t VL53L0X_readRangeSingleMillimeters(uint8_t add)
{
    
    
  VL53L0X_WriteByte(add,0x80, 0x01);
  VL53L0X_WriteByte(add,0xFF, 0x01);
  VL53L0X_WriteByte(add,0x00, 0x00);
  VL53L0X_WriteByte(add,0x91, stop_variable);
  VL53L0X_WriteByte(add,0x00, 0x01);
  VL53L0X_WriteByte(add,0xFF, 0x00);
  VL53L0X_WriteByte(add,0x80, 0x00);

  VL53L0X_WriteByte(add,SYSRANGE_START, 0x01);

  // "Wait until start bit has been cleared"
  startTimeout();
  while (VL53L0X_ReadByte(add,SYSRANGE_START) & 0x01)
  {
    
    
    if (checkTimeoutExpired())
    {
    
    
      did_timeout = true;
      return 65535;
    }
  }

  return VL53L0X_readRangeContinuousMillimeters(add);
}

Test Results

The test results are shown below.
Insert image description here

Guess you like

Origin blog.csdn.net/qq_24312945/article/details/132843722