跳转至

exs_vl53l1x 扩展库

作者:江访 | 最后修改:2026-08-06

一、概述

exs_vl53l1x 是基于 ST(意法半导体) 出品的 VL53L1X ToF 激光测距传感器开发的 LuatOS 扩展库。 VL53L1X 采用940nm不可见的1类激光发射器(对人眼安全),通过测量光子飞行时间实现绝对距离测量。

1.1 主要特性

  • 测距范围:最近 4cm,最远可达 4m(标称值,理想条件),典型可靠测距约 3.6m(暗室,Long 模式,90% 反射率目标)

  • 供电:2.8V 单电源(AVDD 和 AVDDVCSEL)

  • 输出单位:毫米(mm)

  • 接口:I2C,7 位固定地址 0x29

  • 支持三种测距模式:short(约 1.36m,抗强光)、standard(约 2.9m,默认)、long(约 3.6m)

  • 软件 I2C / 硬件 I2C 均可

  • 内置 I2C 总线卡死自动检测与恢复

  • 支持软件待机睡眠与唤醒

  • 支持串扰校准(用于带保护玻璃的模组)

  • 支持 GPIO1 中断(数据就绪通知,响应更快)

1.2 注意事项

  • 推荐适用软件 I2C 模式({scl=xx, sda=yy}:VL53L1X 在异常通信后可能锁死 SDA 总线,软件 I2C 可通过 GPIO 脉冲 SCL 恢复

  • 硬件 I2C + scl/sda 也支持自动恢复:同时传 i2c_idscl/sda 时,总线卡死后会将引脚临时切为 GPIO 进行脉冲恢复, 再重新初始化硬件 I2C 外设。仅传 i2c_id 不传 scl/sda 时不具自动恢复能力

  • 初始化后自动启动测距,无需额外调用 start 方法

  • 每次读取测距数据时自动跳过无效帧(如刚启动、刚唤醒时的第一帧)

  • 最低工作距离 4cm,低于此值距离值不准

  • SCL/SDA 需外接 4.7kΩ~10kΩ 上拉电阻

1.3 测距模式说明

模式 暗室最远 强光下最远 特点
short 约 1.36m 约 1.35m 抗环境光干扰最强
standard(默认) 约 2.9m 约 0.76m 一般场景均衡模式
long 约 3.6m 约 0.73m 远距离,暗光环境

1.4 GPIO1 中断说明

GPIO1 是 VL53L1X 的中断输出引脚,每次测距完成时自动输出一个低电平脉冲(约 10μs),通知主机有新数据就绪。

中断触发方式

  • GPIO1 默认配置为下降沿触发(ACTIVE_LOW),测距完成后引脚被拉低

  • 每次调用 get_data() 后,库会自动清除中断(写寄存器 0x0086=0x01),释放 GPIO1

  • 未使用 GPIO1 时,库通过 I2C 轮询内部寄存器判断数据是否就绪,速度稍慢但不影响功能

中断频率

  • 每次测距完成触发一次中断,频率取决于测距模式的测量周期

  • Standard Ranging 模式下连续测距,每次完成一帧立即触发下一次,约每秒 30~50 帧

  • 中断频率 = 1 /(实际测量时间)

使用方式

  • 通过 setup({int1 = {int_gpio = 引脚号}}) 注册中断引脚

  • cb 回调时:每帧测距完成自动调用回调函数(适合低频场景,如 1~10 帧/秒)

  • cb 回调时:通过 get_int_flag() 轮询中断标志(适合高频场景,如 30+ 帧/秒,比回调更可靠)

1.5 中断事件类型说明

通过 setup()config.int1 参数配置中断事件。详情见 setup 参数说明。

中断事件 触发时机 推荐用途
data_ready(int1.cb) 每次测距完成时触发。30~50 帧/秒高频场景不建议用 cb 回调,改用 get_int_flag() 轮询 实时数据采集

1.6 中断回调与 get_data() 返回数据说明

通过 setup()config.int1.cb 收到的 data 参数与 get_data() 返回的数据结构完全一致,包含以下字段:

data.distance     -- 测距距离,单位 mm,取值范围 4~4000
data.status       -- 测距状态码,0 表示数据可靠
                  --   0  = 测距成功
                  --   1  = Sigma 失效
                  --   2  = 信号失效
                  --   3  = 目标距离小于最小值
                  --   4  = 相位超出范围
                  --   5  = 硬件故障
                  --   6  = 测距成功(未绕行检查)
                  --   7  = 相位绕行
                  --   8  = 处理失败
                  --   9  = 串扰信号
                  --   10 = 同步中断
                  --   11 = 合并脉冲
                  --   12 = 信号弱
                  --   13 = 最小距离失败
                  --   14 = 范围无效
data.status_str   -- 测距状态中文描述,如 "测距成功"
data.stream_count -- 帧计数,0~255 循环,stream=0 的帧为无效帧

注意: - 回调中 data 的结构与 get_data() 返回值完全一致,可以共用同一处理函数。 - 使用 cb 回调时,get_int_flag() 仍能读到中断标志(冗余信息),但实际不需要。

1.7 芯片功耗参考

模式 功耗 说明
软件待机(sleep) 约 6μA 调用 sleep() 后进入
连续测距 约 20mW 正常测距时(按10Hz频率,每33ms完成一次测量计算)
激光发射峰值 40mA VCSEL 点亮的瞬间

1.8 接线方式

  ┌──────────────┐      ┌──────────────────┐
  │    主控      │      │   VL53L1X        │
  │              │      │                  │
  │ GPIO_SCL ────┼──────┼──→ SCL           │
  │ GPIO_SDA ────┼──────┼──→ SDA           │
  │ GPIO_INT ────┼──────┼──→ GPIO1 (可选)  │
  │ GPIO_RST ────┼──────┼──→ XSHUT (可选)  │
  │ VCC 3.3V ────┼──────┼──→ VIN           │
  │ GND      ────┼──────┼──→ GND           │
  └──────────────┘      └──────────────────┘

GPIO1(中断输出引脚):不接不影响测距功能。接上后可用中断通知代替 I2C 轮询,响应更快、省 I2C 总线。详见 1.4 节。

XSHUT(硬件复位引脚,低电平有效):不接时传感器上电自动启动。接上后 setup({xshut = pin}) 使用硬件复位(拉低再拉高),比软复位更彻底。 如果遇到初始化不稳定、I2C 通信异常,建议接上这个引脚。

1.9 加载方式

-- 加载扩展库
local exs_vl53l1x = require "exs_vl53l1x"

二、核心示例

2.1 标准测距

展示最基础的初始化、读取数据和关闭流程。

-- 加载扩展库
local exs_vl53l1x = require "exs_vl53l1x"

-- 主任务
local function demo()
    -- 初始化(软件 I2C:SCL=GPIO31, SDA=GPIO30)
    local result = exs_vl53l1x.setup({scl = 31, sda = 30})
    if not result then
        log.error("初始化失败,请检查接线")
        return
    end

    log.info("exs_vl53l1x", "版本:", exs_vl53l1x.version())

    sys.wait(200)                     -- 等待第一帧稳定
    for i = 1, 5 do
        local data = exs_vl53l1x.get_data()
        if data then
            log.info(string.format("距离:%dmm 状态=%s", data.distance, data.status_str))
        end
        sys.wait(500)                  -- 每 500ms 读一次
    end

    exs_vl53l1x.close()               -- 关闭传感器
end

sys.taskInit(demo)

2.2 测距模式切换示例

展示三种测距模式的用法。

-- 加载扩展库
local exs_vl53l1x = require "exs_vl53l1x"

local function demo()
    -- short 模式(抗强光,约 1.36m)
    local ok = exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "short"})
    if not ok then return end
    sys.wait(200)                     -- 等待第一帧稳定
    log.info("距离:", exs_vl53l1x.get_data().distance, "mm (short)")

    -- 切换 long 模式(远距离,约 3.6m)
    exs_vl53l1x.close()
    ok = exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "long"})
    if not ok then return end
    sys.wait(200)                     -- 等待第一帧稳定
    log.info("距离:", exs_vl53l1x.get_data().distance, "mm (long)")
end
sys.taskInit(demo)

2.3 中断回调读取

适合需要及时响应测距结果的场景,省去 I2C 轮询,响应更快。data_cb 收到的 data 结构说明详见 1.5 节

-- 加载扩展库
local exs_vl53l1x = require "exs_vl53l1x"

-- 中断回调函数,每帧测距完成后自动触发
-- data 结构见 1.5 节:{ distance, status, status_str, stream_count }
local function data_cb(data)
    log.info(string.format("距离:%dmm 状态=%s", data.distance, data.status_str))
end

local function demo()
    local result = exs_vl53l1x.setup({
        scl = 31, sda = 30,
        int1 = {int_gpio = 10, cb = data_cb},   -- GPIO1 接 GPIO10,注册回调
    })
    if not result then return end
    -- 回调自动处理数据,主线程可做其他事
end
sys.taskInit(demo)

也可以不传 cb,通过 get_int_flag() 轮询(适合高频场景):

-- 轮询任务
local function poll_task()
    while true do
        if exs_vl53l1x.get_int_flag() then        -- 检查中断标志
            local data = exs_vl53l1x.get_data()
            if data then
                log.info(string.format("距离:%dmm 状态=%s", data.distance, data.status_str))
            end
        end
        sys.wait(1)  -- 每 1ms 轮询一次中断标志
    end
end
sys.taskInit(poll_task)

-- 注册中断(不传 cb,只设引脚)
exs_vl53l1x.setup({scl = 31, sda = 30, int1 = {int_gpio = 10}})

2.4 休眠与唤醒

适合间歇性采样的场景,不需要时休眠省电。

exs_vl53l1x.sleep()          -- 休眠(约 6μA),自动停止测距
sys.wait(5000)  -- 等待 5 秒后唤醒

exs_vl53l1x.wakeup()         -- 唤醒,自动恢复测距
sys.wait(200)                -- 等待第一帧稳定
local data = exs_vl53l1x.get_data()

2.5 串扰校准

如果传感器装了保护玻璃或外壳,激光会在玻璃表面反射产生串扰,导致远距离测不准或信号弱。校准可以补偿这个误差。

校准前:在传感器正前方 400mm 处放一张纯白纸板。

-- 加载扩展库
local exs_vl53l1x = require "exs_vl53l1x"

local function calibration()
    -- 执行串扰校准
    local xtalk = exs_vl53l1x.calibrate_xtalk({
        scl = 31, sda = 30,
        target_distance_mm = 400,   -- 目标距离
        samples = 50,                -- 采样帧数
    })
    if not xtalk then return end
    log.info("校准完成,xtalk=" .. xtalk)

    -- 用校准值初始化(校准值掉电丢失,需在代码中保存)
    exs_vl53l1x.setup({scl = 31, sda = 30, xtalk_offset = xtalk})
end
sys.taskInit(calibration)

保存校准值:校准值掉电会丢失,需在代码中保存并在下次开机时传入。

-- 方案一:直接在代码中写死(适合量产时固定值)
local XTALK_FIXED = 45  -- calibrate_xtalk 返回的值

-- 方案二:写入可写文件系统(如 /lfs/)
io.writeFile("/lfs/xtalk.txt", tostring(xtalk))
local xtalk = tonumber(io.readFile("/lfs/xtalk.txt")) or 0

-- 使用时
exs_vl53l1x.setup({scl = 31, sda = 30, xtalk_offset = xtalk})

三、常量解释

扩展库常量,顾名思义是由合宙 LuatOS 扩展库中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用,本扩展库没有常量。


四、函数详解

4.1 exs_vl53l1x.setup(config)

功能

初始化传感器并启动测距。

参数

config

参数含义:初始化配置表
数据类型:table
取值范围:
{
    参数含义:SCL 时钟引脚 GPIO 编号
    数据类型:number
    取值范围:有效的 GPIO 编号
    是否必选:可选
    注意事项:
        - 单独传 scl+sda(不传 i2c_id)→ 软件 I2C,总线自动恢复(推荐)
        - 与 i2c_id 同时传 → 硬件 I2C + 总线自动恢复(scl/sda 用于脉冲恢复后重初始化硬件 I2C 外设)
        - 仅传 i2c_id 不传 scl/sda → 纯硬件 I2C,不具总线自动恢复能力
    参数示例:31
    config.scl ,

    参数含义:SDA 数据引脚 GPIO 编号
    数据类型:number
    取值范围:有效的 GPIO 编号
    是否必选:可选,与 scl 配合使用
    注意事项:I2C 模式下该引脚需外接 4.7kΩ~10kΩ 上拉电阻到 VCC
    参数示例:30
    config.sda ,

    参数含义:硬件 I2C 总线 ID
    数据类型:number
    取值范围:有效的 I2C 总线编号
    是否必选:可选
    注意事项:
        - 默认 0
        - 仅传 i2c_id(不传 scl/sda)→ 纯硬件 I2C,不具恢复能力
        - 同时传 i2c_id 和 scl/sda → 硬件 I2C + 总线自动恢复(scl/sda 用于脉冲恢复)
    参数示例:0
    config.i2c_id ,

    参数含义:XSHUT 复位引脚 GPIO 编号
    数据类型:number
    取值范围:有效的 GPIO 编号
    是否必选:可选
    注意事项:传此参数时使用 XSHUT 硬件复位(拉低再拉高),比软复位更彻底。不传时使用软件复位
    参数示例:32
    config.xshut ,

    参数含义:测距模式
    数据类型:string
    取值范围:"standard"(默认,约 2.9m)、"short"(约 1.36m,抗强光)、"long"(约 3.6m,远距离)
    是否必选:可选
    注意事项:short 模式在强光下表现最好;long 模式在强光下最远距离约 0.73m,建议室内或弱光使用
    参数示例:"short"
    config.range_mode ,

    参数含义:串扰校准偏移值
    数据类型:number
    取值范围:0~65535,由 calibrate_xtalk() 返回
    是否必选:可选
    注意事项:传入后自动写入串扰补偿寄存器。校准值掉电丢失,需在代码中保存
    参数示例:45
    config.xtalk_offset ,

    参数含义:GPIO1 中断配置
    数据类型:table
    是否必选:可选
    注意事项:格式为 {int_gpio = 引脚号, cb = 回调函数}。int_gpio 是 GPIO1 接的引脚,
            cb 是每次测距完成后的回调函数。cb 在 GPIO 中断中触发,频率约 30~50 次/秒,
            高频场景建议不传 cb,改用 get_int_flag() 轮询
    参数示例:{int_gpio = 10, cb = data_cb}
    config.int1 ,
}

是否必选:是
参数示例:
         -- 软件 I2C,默认模式(推荐:总线自动恢复)
         exs_vl53l1x.setup({scl = 31, sda = 30})
         -- 硬件 I2C + scl/sda(支持总线自动恢复)
         exs_vl53l1x.setup({i2c_id = 0, scl = 31, sda = 30})
         -- 纯硬件 I2C(不具自动恢复能力,需用户自己处理总线卡死)
         exs_vl53l1x.setup({i2c_id = 0})
         -- 远距离模式 + 串扰校准
         exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "long", xtalk_offset = 45})
         -- 注册 GPIO1 中断回调
         exs_vl53l1x.setup({scl = 31, sda = 30, int1 = {int_gpio = 10, cb = data_cb}})

返回值

local result = exs_vl53l1x.setup(config)

result

含义说明:初始化是否成功
数据类型:boolean
取值范围:true(成功),false(失败)
注意事项:失败时请检查接线、供电和 I2C 通信
返回示例:true

示例

-- 软件 I2C,默认 standard 模式(推荐:总线自动恢复)
local result = exs_vl53l1x.setup({scl = 31, sda = 30})
if not result then return end

-- 硬件 I2C + scl/sda(总线自动恢复)
local result = exs_vl53l1x.setup({i2c_id = 0, scl = 31, sda = 30})
if not result then return end

-- 纯硬件 I2C(不具自动恢复能力,请确保 I2C 总线可靠)
local result = exs_vl53l1x.setup({i2c_id = 0})
if not result then return end

-- 短距离模式(抗强光)
local result = exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "short"})
if not result then return end

-- 远距离模式 + XSHUT 硬件复位
local result = exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "long", xshut = 32})
if not result then return end

-- 串扰校准值 + GPIO1 中断回调
local result = exs_vl53l1x.setup({scl = 31, sda = 30, xtalk_offset = 45, int1 = {int_gpio = 10, cb = data_cb}})
if not result then return end

4.2 exs_vl53l1x.get_data()

功能

读取一帧测距数据。等待数据就绪后读取,自动跳过无效帧(首次启动/唤醒后的第一帧),读取后自动触发下一次测量。

参数

返回值

local data = exs_vl53l1x.get_data()

data

含义说明:测距数据表,失败返回 nil
数据类型:table 或 nil
取值范围:返回table 包含以下值
{
    参数含义:测距距离
    数据类型:number
    取值范围:4~4000,单位 mm
    注意事项:低于 4cm 时值不准确
    返回示例:235
    data.distance ,

    参数含义:测距状态码,0 表示数据可靠
    数据类型:number
    取值范围:0(测距成功)、1(Sigma 失效)、2(信号失效)、3(目标太近)、
            4(相位超出范围)、5(硬件故障)、6(测距成功但未绕行检查)、
            7(相位绕行)、8(处理失败)、9(串扰信号)、10(同步中断)、
            11(合并脉冲)、12(信号弱)、13(最小距离失败)、14(范围无效)
    注意事项:status=0 为测距成功,数据可靠
    返回示例:0
    data.status ,

    参数含义:测距状态描述
    数据类型:string
    取值范围:对应 status 的中文描述
    返回示例:"测距成功"
    data.status_str ,

    参数含义:帧计数
    数据类型:number
    取值范围:每次测距递增,0~255 循环
    注意事项:stream=0 的帧为无效帧,会自动跳过
    返回示例:15
    data.stream_count ,
}
返回示例:{distance = 235, status = 0, status_str = "测距成功", stream_count = 15}

示例

-- 读取测距数据
local data = exs_vl53l1x.get_data()
if data and data.status == 0 then    -- 只使用有效数据
    log.info("距离:", data.distance, "mm")
end

4.3 exs_vl53l1x.get_int_flag()

功能

查询 GPIO1 中断触发标志,查询后自动清除。适合不传 cb 时轮询用。

参数

返回值

local flag = exs_vl53l1x.get_int_flag()

flag

含义说明:是否触发中断
数据类型:boolean
取值范围:true(有中断),false(无中断)
注意事项:查询后自动清除标志
返回示例:true

示例

-- 轮询任务
local function poll_task()
    while true do
        if exs_vl53l1x.get_int_flag() then        -- 检查是否有新数据
            local data = exs_vl53l1x.get_data()
            if data then
                log.info(string.format("距离:%dmm 状态=%s", data.distance, data.status_str))
            end
        end
        sys.wait(1)  -- 每 1ms 轮询一次中断标志
    end
end
sys.taskInit(poll_task)

-- 注册中断引脚(不传 cb)
exs_vl53l1x.setup({scl = 31, sda = 30, int1 = {int_gpio = 10}})

4.4 exs_vl53l1x.sleep()

功能

进入软件待机,功耗约 6μA(芯片标称6μA,测试传感器功耗约320μA)。调用后自动停止测距,保持现有配置。

参数

返回值

示例

-- 休眠(自动停止测距)
exs_vl53l1x.sleep()

4.5 exs_vl53l1x.wakeup()

功能

从软件待机唤醒,自动恢复测距。与 sleep() 配对使用。

参数

返回值

local ok = exs_vl53l1x.wakeup()

ok

含义说明:唤醒是否成功
数据类型:boolean
取值范围:true(成功),false(失败,未执行 setup 时返回 false)
返回示例:true

示例

-- 唤醒
local ok = exs_vl53l1x.wakeup()
if ok then
    sys.wait(200)                     -- 等待第一帧稳定
    local data = exs_vl53l1x.get_data()
end

4.6 exs_vl53l1x.close()

功能

关闭传感器。停止测距并清空内部状态,再次使用前需要重新 setup。

参数

返回值

示例

-- 关闭传感器
exs_vl53l1x.close()

4.7 exs_vl53l1x.calibrate_xtalk(config)

功能

执行串扰校准。当传感器加了保护玻璃或外壳时,激光在玻璃表面反射会产生串扰信号,导致远距离测距不准。校准后数据更准确。

什么时候需要做:

  • 传感器装在带玻璃面板的外壳内

  • 更换了不同厚度的保护玻璃

  • 测量距离不准确或经常报信号失效

校准前准备:

  • 在传感器正前方 400mm 处放一张纯白纸板,环境光不要太强。校准过程约 2~4 秒。

参数

config

参数含义:校准配置表
数据类型:table
取值范围:
{

    参数含义:SCL 时钟引脚 GPIO 编号
    数据类型:number
    取值范围:有效的 GPIO 编号
    是否必选:推荐传入(用于总线恢复)
    参数示例:31
    config.scl ,

    参数含义:SDA 数据引脚 GPIO 编号
    数据类型:number
    取值范围:有效的 GPIO 编号
    是否必选:推荐传入
    参数示例:30
    config.sda ,

    参数含义:硬件 I2C 总线 ID
    数据类型:number
    取值范围:有效的 I2C 总线编号
    是否必选:可选,不传 scl/sda 时使用
    参数示例:0
    config.i2c_id ,

    参数含义:目标距离(传感器到校准平面的距离)
    数据类型:number
    取值范围:100~1000,单位 mm
    是否必选:否
    注意事项:默认 400,建议与官方校准距离一致
    参数示例:400
    config.target_distance_mm ,

    参数含义:采样帧数
    数据类型:number
    取值范围:10~200
    是否必选:否
    注意事项:默认 50。帧数越大越稳定,但校准时间越长(约 2~5 秒/50 帧)
    参数示例:100
    config.samples ,
}

是否必选:是
参数示例:
         exs_vl53l1x.calibrate_xtalk({scl = 31, sda = 30})

返回值

local xtalk = exs_vl53l1x.calibrate_xtalk(config)

xtalk

含义说明:串扰偏移值
数据类型:number 或 nil
取值范围:0~65535
注意事项:有效采样帧数不足 10 帧时返回 nil。返回值可直接传给 setup({xtalk_offset = xtalk})
返回示例:45

示例

-- 执行串扰校准
local xtalk = exs_vl53l1x.calibrate_xtalk({scl = 31, sda = 30})
if xtalk then
    log.info("校准完成, xtalk=" .. xtalk)
    -- 用校准值重新初始化
    exs_vl53l1x.setup({scl = 31, sda = 30, xtalk_offset = xtalk})
end

4.8 exs_vl53l1x.version()

功能

获取库版本号。

参数

返回值

local ver = exs_vl53l1x.version()

ver

含义说明:版本号字符串
数据类型:string
取值范围:格式 "yyyymmddhhmm",表示 yyyy年mm月dd日hh时mm分发布的版本
返回示例:"202607201200"

示例

-- 获取版本号
local ver = exs_vl53l1x.version()
log.info("exs_vl53l1x", "版本:", ver)

五、版本更新说明

版本号:202607211200

  1. 更新时间:2026-07-21

  2. 更新内容:

    • 支持软件 I2C、硬件 I2C 初始化

    • 支持标准测距、三档模式切换(standard/short/long)

    • 支持 I2C 总线卡死自动检测与恢复

    • 支持睡眠/唤醒/关闭,支持低功耗待机

六、产品支持说明

所有支持 luatos 二次开发的模块,具体可以查看选型手册

搜索