概述
VL53L0X是新一代飛行時(shí)間(ToF)激光測(cè)距模塊(不同于傳統(tǒng)技術(shù)),采用目前市場(chǎng)上最小的封裝,無論目標(biāo)反射率如何,都能提供精確的距離測(cè)量。它可以測(cè)量2m的絕對(duì)距離,為測(cè)距性能等級(jí)設(shè)定了新的基準(zhǔn),為各種新應(yīng)用打開了大門。
最近在弄ST的課程,需要樣片的可以加群申請(qǐng):615061293 。
VL53L0X集成了一個(gè)領(lǐng)先的SPAD陣列(單光子雪崩二極管),并內(nèi)嵌ST的第二代FlightSense?專利技術(shù)。
VL53L0X的940nm VCSEL發(fā)射器(垂直腔面發(fā)射激光器)完全不為人眼所見,加上內(nèi)置的物理紅外濾光片,使其測(cè)距距離更長(zhǎng),對(duì)環(huán)境光的免疫性更強(qiáng),對(duì)蓋片的光學(xué)串?dāng)_具有更好的穩(wěn)定性。
![最近在弄ST的課程,需要樣片的可以加群申請(qǐng):615061293 。]
視頻教學(xué)
[https://www.bilibili.com/video/BV1dH4y1D7Px/]
樣品申請(qǐng)
[https://www.wjx.top/vm/OhcKxJk.aspx#]
源碼下載
[https://download.csdn.net/download/qq_24312945/88332771](
所有功能
● 完全集成的小型化模塊
○ 940 nm 激光器 VCSEL
○ VCSEL驅(qū)動(dòng)器
○ 測(cè)距傳感器,內(nèi)嵌高級(jí)微控制器
○ 4.4 x 2.4 x 1.0 mm
● 快速,精確測(cè)距
○ 測(cè)量的絕對(duì)距離達(dá)到2m
○ 報(bào)告的距離與目標(biāo)反射率無關(guān)
○ 先進(jìn)的嵌入式光學(xué)串?dāng)_補(bǔ)償,簡(jiǎn)化蓋片的選擇
● 人眼安全
○ 1類激光器件,符合最新標(biāo)準(zhǔn)IEC 60825-1:2014(第3版)要求
● 方便集成
○ 單回流焊元件
○ 無附加光學(xué)元件
○ 單電源
○ 用于器件控制和數(shù)據(jù)傳輸?shù)?a href="http://wenjunhu.com/tags/i2c/" target="_blank">I2C接口
○ Xshutdown(復(fù)位)和中斷 GPIO
○ 可編程I2C地址
技術(shù)規(guī)范
該模塊的供電要求為2.8V,適合于低電壓應(yīng)用場(chǎng)景。它通過I2C接口進(jìn)行主機(jī)控制和數(shù)據(jù)通信,方便與其他設(shè)備的集成。支持最大快速模式速率,達(dá)到400k,確保高效的數(shù)據(jù)傳輸。
最后,VL53L0X模塊具有一個(gè)默認(rèn)地址為0x29的設(shè)備地址,這樣在多個(gè)I2C設(shè)備共享同一總線時(shí),可以輕松管理和區(qū)分不同的模塊。
測(cè)量范圍
接口
VL53L0X模塊接口的示意圖如下所示。
接口說明
最小系統(tǒng)圖
IIC配置
在這個(gè)應(yīng)用中,VL53L0X模塊通過I2C(IIC)接口與主控器通信。具體來說,VL53L0X 模塊的I2C引腳連接到主控器的PB6(引腳B6)和PB7(引腳B7)兩個(gè)IO口。
這種連接方式確保了模塊與主控器之間的可靠數(shù)據(jù)傳輸和通信。PB6作為I2C總線的串行數(shù)據(jù)線(SDA),負(fù)責(zé)數(shù)據(jù)的傳輸和接收。而PB7則充當(dāng)I2C總線的串行時(shí)鐘線(SCL),用于同步數(shù)據(jù)傳輸?shù)臅r(shí)序。
配置IIC為快速模式,速度為400k。
串口重定向
打開魔術(shù)棒,勾選MicroLIB
在main.c中,添加頭文件,若不添加會(huì)出現(xiàn) identifier "FILE" is undefined報(bào)錯(cuò)。
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
函數(shù)聲明和串口重定向:
/* 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 */
模塊片選
根據(jù)提供的表格信息,我們可以得知VL53L0X模塊的XSHUT 引腳用作片選腳(Chip Enable),這是Xshutdown引腳,它是一個(gè)數(shù)字輸入,當(dāng)處于低電平狀態(tài)(Active LOW)時(shí),可以用來關(guān)閉(即"shutdown")傳感器。這通常用于重置傳感器或在不需要傳感器測(cè)量時(shí)將其關(guān)閉以節(jié)省功耗。
查看手冊(cè)可以得知,對(duì)應(yīng)的IO為PB2和PB4。
在STM32CUBEMX中配置如下所示。
模塊地址
VL53L0X模塊的默認(rèn)設(shè)備地址為0x29。設(shè)備地址是用來識(shí)別和通信特定設(shè)備的標(biāo)識(shí)符。通過將VL53L0X模塊的設(shè)備地址設(shè)置為0x29,您可以確保與該模塊進(jìn)行正常的通信和控制。
若添加讀寫位,寫地址為0x52,讀地址為0x53。
對(duì)于VL53L0X模塊,默認(rèn)的7位地址是0x29(二進(jìn)制為010 1001),加上寫位后為0x52(二進(jìn)制為0101 0010),加上讀位后為0x53(二進(jìn)制為0101 0011)。
這意味著當(dāng)主設(shè)備與VL53L0X模塊進(jìn)行通信時(shí),要發(fā)送0x52地址字節(jié)進(jìn)行寫操作,或發(fā)送0x53地址字節(jié)進(jìn)行讀取操作。
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;
}
參考文檔
這里參考的文檔問arduino的驅(qū)動(dòng)代碼。
https://github.com/pololu/vl53l0x-arduino/tree/master
初始化
參考程序中給出的初始化如下所示。
其中sensor.init()是VL53L0X的模塊初始設(shè)置。
// 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;
}
由于一些宏定義都是注釋掉了的,所以可以不去執(zhí)行下面紅框的指令。
單次讀取距離長(zhǎng)度
在主程序中,主要執(zhí)行的是單次獲取數(shù)據(jù)。
對(duì)應(yīng)源碼如下所示。
// 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();
}
修改后如下所示。
// 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);
}
測(cè)試結(jié)果
測(cè)試結(jié)果如下所示。
-
單模塊
+關(guān)注
關(guān)注
0文章
4瀏覽量
6366 -
TOF
+關(guān)注
關(guān)注
9文章
483瀏覽量
36349 -
stm32cubemx
+關(guān)注
關(guān)注
5文章
283瀏覽量
14807 -
VL53L0X
+關(guān)注
關(guān)注
0文章
7瀏覽量
2488
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論