中文文档:VL53L0X-Distance-Sensor-User-Manual-CN.pdf
83225-cx41qv0ryvw.png

传感器介绍

VL53L0能够在一帧画面内完成全部测量操作,用时通常小于30ms,距离小于2米。这样的测距性能让拍照系统在摄像和连拍模式下瞬间对焦,甚至在低光或低对比度场景中保持同样的性能表现,而对于没有装备ToF技术的拍照系统,低光源或低对比度场景则是一个巨大的挑战。由于测距精确度非常出色,VL53L0还可提升智能手机应用性能,包括双摄像头深度图。意法半导体的VL53L0模块简单易用(6个有效引脚),提供I2C接口,配备全套的API驱动程序和技术文档,使系统集成变得快速、简单。模块封装支持回流焊工艺,符合RoHS有害物质限用标准,完全兼容各种玻璃盖板和外观设计。

ST为进一步加强其在飞行时间测距传感器(ToF)市场的领导地位,推出第二代FlightSense™ 技术,并用于新的VL53L0X激光测距传感器上。
92860-sfcj23ofz9.png
VL53L0X将ToF测距长度扩至两米,精确度在±3%范围内。测量速度较上一代产品更快,测距时间不足30ms;更高能效,正常工作模式下功耗仅20mW,待机功耗只有5μA。封装尺寸为2.4mm x 4.4mm x 1mm,在市面上同类产品中小。

不同于传统的红外接近检测传感器(infrared proximity sensors),VL53L0X输出的是精确到毫米的测距结果,目标物体的颜色和反射光不会影响测距结果。快速响应的FlightSenseTM技术,因被大品牌智能手机摄像头激光辅助自动对焦所采用而享誉业界,能够区分目标物体的横向或纵向移动。传感器内部完成测距计算,通过I2C总线接口输出数据,因而对系统主控制器的需求降至低。

内部激光器发射940nm波长非可见光,不伤害眼睛,消除了与其它红外接近检测传感器发射红光分散人们注意力的问题,增强了对外部光源的抗干扰性能。该传感器的数字架构和自动补偿功能使其在环境光强较高的场景中仍然具有很高的性能表现。

VL53L0X的感测能力可以支持各种功能,包括各种创新用户界面的手势感测或接近检测;扫地机器人等家电的墙壁探测、悬崖探测、碰撞探测;卫浴产品,例如:水龙头、皂液器、干手机和冲洗器;其它目标应用包括笔记本电脑用户存在检测或电源开关监控器、无人机和物联网(IoT)产品。
32322-5cd8aisqoug.png

距离值转模拟量:

/* This example shows how to use continuous mode to take
  range measurements with the VL53L0X. It is based on
  vl53l0x_ContinuousRanging_Example.c from the VL53L0X API.

  The range readings are in units of mm. */

#include <Wire.h>
#include <VL53L0X.h>

VL53L0X sensor;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  // Start continuous back-to-back mode (take readings as
  // fast as possible).  To use continuous timed mode
  // instead, provide a desired inter-measurement period in
  // ms (e.g. sensor.startContinuous(100)).
  sensor.startContinuous();
}

void loop()
{
  int L = sensor.readRangeContinuousMillimeters();

  if (L < 1024)
  {
    analogWrite(A0, L);
    Serial.print(L);
    Serial.println();
  }
  if (sensor.timeoutOccurred()) {
    Serial.println(" TIMEOUT");
  }

}

在这个例子中,我使用map函数将距离映射到LED的闪烁频率。你可以根据需要调整映射的范围。请注意,delay函数会阻塞代码执行,所以在实际项目中,你可能需要使用非阻塞的方式来实现类似的效果,以确保在等待LED状态改变的同时能够执行其他任务。

#include <Wire.h>
#include <VL53L0X.h>

VL53L0X sensor;
const int ledPin = 13; // 将LED连接到D13引脚

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  // Start continuous back-to-back mode (take readings as
  // fast as possible).  To use continuous timed mode
  // instead, provide a desired inter-measurement period in
  // ms (e.g. sensor.startContinuous(100)).
  sensor.startContinuous();
}

void loop()
{
  int distance = sensor.readRangeContinuousMillimeters();

  // 你可以根据距离来控制LED的闪烁频率
  if (distance > 2000) {
    digitalWrite(ledPin, HIGH);  // 距离大于2m时,常亮
  } else {
    int blinkDelay = map(distance, 0, 2000, 50, 1000); // 映射距离到闪烁频率的范围
    digitalWrite(ledPin, HIGH);
    delay(blinkDelay);
    digitalWrite(ledPin, LOW);
    delay(blinkDelay);
  }

  if (sensor.timeoutOccurred()) { 
    Serial.print(" TIMEOUT"); 
  }

  Serial.println();
}

程序详解

这个程序是为VL53L0X激光测距传感器编写的Arduino代码。以下是程序的主要模块和工作过程的简要解释:

  1. 常量定义:

    • 程序开始时,定义了一系列常量,这些常量对应VL53L0X传感器的寄存器地址和标识符。
  2. 全局变量:

    • gbuf 是一个用于存储从传感器读取的数据的缓冲区。
  3. setup 函数:

    • 初始化函数,在程序运行开始时调用。
    • 初始化Wire库,用于I2C通信。
    • 初始化串行通信以进行输出。
    • 打印一条开始测试的消息。
  4. loop 函数:

    • 主循环函数,在setup 函数执行后一直运行。
    • 打印开始测试的消息,调用test 函数执行传感器测试。
    • 打印结束测试的消息,等待1秒钟。
  5. test 函数:

    • 执行具体的传感器测试和数据读取。
    • 读取传感器的硬件和固件版本信息。
    • 打印预设和最终测距配置的VCSEL周期,并解码为实际时钟周期。
    • 启动测距测量。
    • 循环等待测距完成,最多等待1秒。
    • 读取测距结果的环境光计数、信号计数、距离和状态。
    • 打印这些结果。
  6. I2C通信相关函数:

    • write_byte_data:向传感器写入一个字节的数据。
    • write_byte_data_at:向传感器的特定寄存器写入一个字节的数据。
    • write_word_data_at:向传感器的特定寄存器写入一个字的数据。
    • read_byte_data:从传感器读取一个字节的数据。
    • read_byte_data_at:从传感器的特定寄存器读取一个字节的数据。
    • read_word_data_at:从传感器的特定寄存器读取一个字的数据。
    • read_block_data_at:从传感器的特定寄存器读取一块数据。
  7. 辅助函数:

    • bswap:将字节数组中的大端无符号短整数转换为小端无符号短整数。
    • makeuint16:构造16位无符号整数,接收最低字节(lsb)和最高字节(msb)。
  8. VL53L0X_decode_vcsel_period 函数:

    • 解码传感器配置中的VCSEL周期的编码,将其转换为实际时钟周期。

总体而言,这个程序通过I2C协议与VL53L0X传感器进行通信,读取其配置信息和测距结果,并在串行监视器中输出这些信息。

33415-vhblq2gvnrk.png
Arduino程序例程:

// 原始代码作者:Ted Meyers
// 原文链接:https://groups.google.com/d/msg/diyrovers/lc7NUZYuJOg/ICPrYNJGBgAJ

#include <Wire.h>

#define VL53L0X_REG_IDENTIFICATION_MODEL_ID         0xc0
#define VL53L0X_REG_IDENTIFICATION_REVISION_ID      0xc2
#define VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD   0x50
#define VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x70
#define VL53L0X_REG_SYSRANGE_START                  0x00
#define VL53L0X_REG_RESULT_INTERRUPT_STATUS         0x13
#define VL53L0X_REG_RESULT_RANGE_STATUS             0x14
#define address 0x29

byte gbuf[16];

void setup() {
  // 设置代码,只运行一次:
  Wire.begin();        // 加入 I2C 总线(主机可选地址)
  Serial.begin(9600);  // 开始串行通信以进行输出
  Serial.println("VLX53LOX 测试开始。");
}

void loop() {
  Serial.println("----- 开始测试 ----");
  test();
  Serial.println("----- 结束测试 ----");
  Serial.println("");
  delay(1000);
}

void test() {
  byte val1 = read_byte_data_at(VL53L0X_REG_IDENTIFICATION_REVISION_ID);
  Serial.print("Revision ID: "); Serial.println(val1);

  val1 = read_byte_data_at(VL53L0X_REG_IDENTIFICATION_MODEL_ID);
  Serial.print("Device ID: "); Serial.println(val1);

  val1 = read_byte_data_at(VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD);
  Serial.print("PRE_RANGE_CONFIG_VCSEL_PERIOD="); Serial.println(val1); 
  Serial.print(" 解码: "); Serial.println(VL53L0X_decode_vcsel_period(val1));

  val1 = read_byte_data_at(VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD);
  Serial.print("FINAL_RANGE_CONFIG_VCSEL_PERIOD="); Serial.println(val1);
  Serial.print(" 解码: "); Serial.println(VL53L0X_decode_vcsel_period(val1));

  write_byte_data_at(VL53L0X_REG_SYSRANGE_START, 0x01);

  byte val = 0;
  int cnt = 0;
  while (cnt < 100) { // 最长等待时间为 1 秒
    delay(10);
    val = read_byte_data_at(VL53L0X_REG_RESULT_RANGE_STATUS);
    if (val & 0x01) break;
    cnt++;
  }
  if (val & 0x01) Serial.println("准备就绪"); else Serial.println("未准备就绪");

  read_block_data_at(0x14, 12);
  uint16_t acnt = makeuint16(gbuf[7], gbuf[6]);
  uint16_t scnt = makeuint16(gbuf[9], gbuf[8]);
  uint16_t dist = makeuint16(gbuf[11], gbuf[10]);
  byte DeviceRangeStatusInternal = ((gbuf[0] & 0x78) >> 3);

  Serial.print("环境光计数: "); Serial.println(acnt);
  Serial.print("信号计数: ");  Serial.println(scnt);
  Serial.print("距离 ");       Serial.println(dist);
  Serial.print("状态: ");        Serial.println(DeviceRangeStatusInternal);
}

uint16_t bswap(byte b[]) {
  // 大端无符号短整型到小端无符号短整型
  uint16_t val = ((b[0] << 8) & b[1]);
  return val;
}

uint16_t makeuint16(int lsb, int msb) {
    return ((msb & 0xFF) << 8) | (lsb & 0xFF);
}

void write_byte_data(byte data) {
  Wire.beginTransmission(address);
  Wire.write(data);
  Wire.endTransmission();
}

void write_byte_data_at(byte reg, byte data) {
  // 在指定地址和寄存器写入数据字节
  Wire.beginTransmission(address);
  Wire.write(reg);
  Wire.write(data);
  Wire.endTransmission();
}

void write_word_data_at(byte reg, uint16_t data) {
  // 在指定地址和寄存器写入数据字
  byte b0 = (data &0xFF);
  byte b1 = ((data >> 8) && 0xFF);
    
  Wire.beginTransmission(address);
  Wire.write(reg);
  Wire.write(b0);
  Wire.write(b1);
  Wire.endTransmission();
}

byte read_byte_data() {
  Wire.requestFrom(address, 1);
  while (Wire.available() < 1) delay(1);
  byte b = Wire.read();
  return b;
}

byte read_byte_data_at(byte reg) {
  //write_byte_data((byte)0x00);
  write_byte_data(reg);
  Wire.requestFrom(address, 1);
  while (Wire.available() < 1) delay(1);
  byte b = Wire.read();
  return b;
}

uint16_t read_word_data_at(byte reg) {
  write_byte_data(reg);
  Wire.requestFrom(address, 2);
  while (Wire.available() < 2) delay(1);
  gbuf[0] = Wire.read();
  gbuf[1] = Wire.read();
  return bswap(gbuf); 
}

void read_block_data_at(byte reg, int sz) {
  int i = 0;
  write_byte_data(reg);
  Wire.requestFrom(address, sz);
  for (i=0; i<sz; i++) {
    while (Wire.available() < 1) delay(1);
    gbuf[i] = Wire.read();
  }
}

uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg) {
  // 将编码的 VCSEL 时钟周期寄存器值转换为真实的时钟周期
  uint16_t vcsel_period_pclks = (vcsel_period_reg + 1) << 1;
  return vcsel_period_pclks;
}

发表评论