[i=s] 本帖最后由 流鱼 于 2026-7-3 13:52 编辑 [/i]
VL53L7CX 移植指南
将 ST VL53L7CX ULD (Ultra-Lite Driver) 官方库移植到 Zephyr RTOS 平台。
1. VL53L7CX 简介
VL53L7CX 是 STM意法半导体 的多区域飞行时间 (ToF) 传感器

支持:
- 4x4 / 8x8 区域分辨率
- I2C 通信(默认地址 0x52,7-bit)
- 多目标检测(每区域最多 4 个目标)
- 内部固件(上电后需通过 I2C 加载)
1.1 关键特性
| 特性 |
参数 |
| 测距范围 |
10mm~ 3500mm |
| 接口 |
I2C(最高 1MHz) |
| 工作电压 |
2.8V~ 3.3V |
| 分辨率 |
4x4 (16 区域) / 8x8 (64 区域) |
| 测距模式 |
连续 / 自主 |
| 中断输出 |
INT 引脚(低电平有效,开漏) |
| 控制引脚 |
LPN(低电平复位 / 低功耗) |
2. 官方库结构
ST 提供的 ULD 库包含以下文件:
VL53L7CX_ULD_API/
├── inc/
│ ├── vl53l7cx_api.h # 主 API 头文件
│ ├── vl53l7cx_buffers.h # 固件缓冲区定义
│ ├── vl53l7cx_plugin_xtalk.h # 串扰校准插件
│ ├── vl53l7cx_plugin_detection_thresholds.h # 检测阈值插件
│ └── vl53l7cx_plugin_motion_indicator.h # 运动指示器插件
└── src/
├── vl53l7cx_api.c # 主 API 实现
├── vl53l7cx_plugin_xtalk.c
├── vl53l7cx_plugin_detection_thresholds.c
└── vl53l7cx_plugin_motion_indicator.c
Platform/
├── platform.h # 平台适配层头文件(需用户实现)
└── platform.c # 平台适配层实现(需用户实现)
2.1 官方 API 核心函数
| 函数 |
说明 |
vl53l7cx_is_alive() |
检测传感器是否在线 |
vl53l7cx_init() |
初始化传感器(加载固件) |
vl53l7cx_start_ranging() |
开始测距 |
vl53l7cx_stop_ranging() |
停止测距 |
vl53l7cx_check_data_ready() |
检查数据是否就绪(轮询) |
vl53l7cx_get_ranging_data() |
获取测距数据 |
vl53l7cx_set_resolution() |
设置分辨率(4x4 / 8x8) |
vl53l7cx_set_ranging_frequency_hz() |
设置测距频率 |
vl53l7cx_set_integration_time_ms() |
设置积分时间 |
vl53l7cx_set_ranging_mode() |
设置测距模式(连续/自主) |
vl53l7cx_set_power_mode() |
设置功耗模式(唤醒/睡眠) |
2.2 平台适配层接口
官方库要求用户实现 platform.h 中定义的以下函数:
/* 读取单字节寄存器 */
uint8_t VL53L7CX_RdByte(VL53L7CX_Platform *p_platform,
uint16_t RegisterAdress, uint8_t *p_value);
/* 写入单字节寄存器 */
uint8_t VL53L7CX_WrByte(VL53L7CX_Platform *p_platform,
uint16_t RegisterAdress, uint8_t value);
/* 读取多字节寄存器 */
uint8_t VL53L7CX_RdMulti(VL53L7CX_Platform *p_platform,
uint16_t RegisterAdress,
uint8_t *p_values, uint32_t size);
/* 写入多字节寄存器 */
uint8_t VL53L7CX_WrMulti(VL53L7CX_Platform *p_platform,
uint16_t RegisterAdress,
uint8_t *p_values, uint32_t size);
/* 硬件复位传感器 */
uint8_t VL53L7CX_Reset_Sensor(VL53L7CX_Platform *p_platform);
/* 字节序交换(32-bit 字) */
void VL53L7CX_SwapBuffer(uint8_t *buffer, uint16_t size);
/* 毫秒延时 */
uint8_t VL53L7CX_WaitMs(VL53L7CX_Platform *p_platform, uint32_t TimeMs);
3. 移植架构
3.1 分层设计
┌─────────────────────────────────────────┐
│ 应用层 (example_x.c) │
│ 调用 vl53l7cx_* API 完成测距功能 │
├─────────────────────────────────────────┤
│ 官方 ULD API (vl53l7cx_api.c) │
│ 传感器配置、固件加载、数据处理 │
├─────────────────────────────────────────┤
│ 平台适配层 (platform.c) │
│ 将官方接口映射到 Zephyr I2C/GPIO API │
├─────────────────────────────────────────┤
│ Zephyr RTOS (I2C/GPIO 驱动) │
│ 硬件抽象,设备树绑定 │
├─────────────────────────────────────────┤
│ 硬件 (MCU + VL53L7CX 传感器) │
└─────────────────────────────────────────┘
3.2 关键设计决策
- 不修改官方 ULD API 源码 — 仅实现平台适配层
- 使用 Zephyr 设备树 — 引脚和 I2C 控制器通过 DTS 配置
- 使用 Zephyr I2C API —
i2c_transfer() 替代平台特定 I2C 代码
- 使用 Zephyr GPIO API — 控制 LPN 和 INT 引脚
- 中断驱动模式 — INT 引脚使用 GPIO 中断 + 信号量,替代轮询
4. 平台适配层实现
4.1 platform.h — 平台结构体定义
typedef struct {
uint16_t address; /* I2C 地址(默认 0x52) */
const struct device *i2c_dev; /* Zephyr I2C 设备绑定 */
} VL53L7CX_Platform;
移植要点:
address 字段是官方 API 必需的,用于 I2C 通信
i2c_dev 字段保留但实际未使用。I2C 设备通过 get_i2c_device() 函数从 DT_CHOSEN(zephyr_i2c) 动态获取,而非从结构体中读取
4.2 I2C 设备获取
/* 从设备树获取 VL53L7CX 节点 */
#define VL53L7CX_NODE DT_NODELABEL(vl53l7cx)
/* 从设备树获取 I2C 地址 */
#define VL53L7CX_I2C_ADDRESS DT_REG_ADDR(VL53L7CX_NODE)
/* 从 chosen 节点获取 I2C 控制器设备 */
static const struct device *get_i2c_device(void)
{
return DEVICE_DT_GET(DT_CHOSEN(zephyr_i2c));
}
关键点:
- 使用
DT_CHOSEN(zephyr_i2c) 获取默认 I2C 总线
- 在 overlay 中需设置
zephyr,i2c = &iic1;
4.3 I2C 读写字节
uint8_t VL53L7CX_RdByte(VL53L7CX_Platform *p_platform,
uint16_t RegisterAdress, uint8_t *p_value)
{
const struct device *i2c_dev = get_i2c_device();
struct i2c_msg msgs[2];
uint8_t reg_addr[2];
reg_addr[0] = (RegisterAdress >> 8) & 0xFF;
reg_addr[1] = RegisterAdress & 0xFF;
/* 写寄存器地址,然后带 RESTART 读取 */
msgs[0].buf = reg_addr;
msgs[0].len = 2;
msgs[0].flags = I2C_MSG_WRITE;
msgs[1].buf = p_value;
msgs[1].len = 1;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_RESTART | I2C_MSG_STOP;
int ret = i2c_transfer(i2c_dev, msgs, 2, p_platform->address);
return ret ? 255 : 0;
}
4.4 分块传输(大数据量)
VL53L7CX 的固件加载和数据读取涉及大量 I2C 传输。某些 I2C 控制器(如 Renesas RA IIC)有 FIFO 限制,需要分块:
#define VL53L7CX_MAX_BYTES_PER_I2C 32 /* 从设备树 max-i2c-bytes 属性获取 */
uint8_t VL53L7CX_WrMulti(VL53L7CX_Platform *p_platform,
uint16_t RegisterAdress,
uint8_t *p_values, uint32_t size)
{
uint8_t buffer[VL53L7CX_MAX_BYTES_PER_I2C + 2];
uint32_t count = 0;
while (count < size) {
uint32_t chunk = size - count;
if (chunk > VL53L7CX_MAX_BYTES_PER_I2C) {
chunk = VL53L7CX_MAX_BYTES_PER_I2C;
}
buffer[0] = ((RegisterAdress + count) >> 8) & 0xFF;
buffer[1] = (RegisterAdress + count) & 0xFF;
memcpy(&buffer[2], p_values + count, chunk);
struct i2c_msg msg = {
.buf = buffer,
.len = chunk + 2,
.flags = I2C_MSG_WRITE | I2C_MSG_STOP,
};
int ret = i2c_transfer(i2c_dev, &msg, 1, p_platform->address);
if (ret != 0) return 255;
count += chunk;
if (count < size) {
k_sleep(K_USEC(500)); /* 让传感器处理 */
}
}
return 0;
}
4.5 GPIO 与硬件复位
/* 从设备树获取引脚号 */
#define LPN_PIN_NUM DT_GPIO_PIN(VL53L7CX_NODE, lpn_gpios)
#define INT_PIN_NUM DT_GPIO_PIN(VL53L7CX_NODE, int_gpios)
/* 获取 GPIO 控制器 */
static const struct device *get_gpio_device(void)
{
return DEVICE_DT_GET(DT_GPIO_CTLR(VL53L7CX_NODE, lpn_gpios));
}
/* 硬件复位序列 */
static void vl53l7cx_hardware_reset(void)
{
const struct device *gpio = get_gpio_device();
gpio_pin_set_raw(gpio, LPN_PIN_NUM, 0); /* LPN 低电平 - 复位 */
k_sleep(K_MSEC(10));
gpio_pin_set_raw(gpio, LPN_PIN_NUM, 1); /* LPN 高电平 - 释放 */
k_sleep(K_MSEC(500)); /* 等待启动 */
}
4.6 中断驱动数据就绪
/* 信号量:中断触发数据就绪 */
static struct k_sem tof_data_ready_sem;
/* INT 引脚中断回调 */
static void int_pin_callback(const struct device *dev,
struct gpio_callback *cb, uint32_t pins)
{
k_sem_give(&tof_data_ready_sem);
}
/* 初始化 INT 引脚中断 */
uint8_t vl53l7cx_int_pin_init(void)
{
const struct device *gpio = get_gpio_device();
/* 配置为输入,上拉,下降沿触发 */
gpio_pin_configure(gpio, INT_PIN_NUM, GPIO_INPUT | GPIO_PULL_UP);
k_sem_init(&tof_data_ready_sem, 0, 1);
/* 注册回调 */
gpio_init_callback(&int_pin_cb_data, int_pin_callback, BIT(INT_PIN_NUM));
gpio_add_callback(gpio, &int_pin_cb_data);
/* 启用下降沿中断 */
gpio_pin_interrupt_configure(gpio, INT_PIN_NUM, GPIO_INT_EDGE_FALLING);
return 0;
}
/* 等待中断(替代轮询) */
int WaitForL5Interrupt(void *pDev)
{
k_sem_take(&tof_data_ready_sem, K_FOREVER);
return 1;
}
/**
* [url=/u/brief]@brief[/url] 获取数据就绪信号量指针
*
* 返回指向内部信号量的指针,可用于自定义等待逻辑,
* 例如使用 k_sem_take() 带超时参数。
*
* [url=/u/return]@return[/url] 指向数据就绪信号量的指针
*/
struct k_sem *vl53l7cx_get_data_ready_sem(void)
{
return &tof_data_ready_sem;
}
使用示例:
/* 获取信号量指针 */
struct k_sem *sem = vl53l7cx_get_data_ready_sem();
/* 带超时的等待(例如 1 秒超时) */
if (k_sem_take(sem, K_MSEC(1000)) == 0) {
/* 数据就绪,读取测距数据 */
vl53l7cx_get_ranging_data(&Dev, &Results);
} else {
/* 超时处理 */
}
4.7 字节序交换与延时
/* 字节序交换(官方 API 要求) */
void VL53L7CX_SwapBuffer(uint8_t *buffer, uint16_t size)
{
uint32_t i, tmp;
for (i = 0; i < size; i = i + 4) {
tmp = (buffer[i] << 24) | (buffer[i+1] << 16) |
(buffer[i+2] << 8) | buffer[i+3];
memcpy(&buffer[i], &tmp, 4);
}
}
/* 毫秒延时 */
uint8_t VL53L7CX_WaitMs(VL53L7CX_Platform *p_platform, uint32_t TimeMs)
{
k_sleep(K_MSEC(TimeMs));
return 0;
}
5. 设备树绑定
5.1 绑定文件 (st,vl53l7cx.yaml)
compatible: "st,vl53l7cx"
include: [sensor-device.yaml, i2c-device.yaml]
properties:
lpn-gpios:
type: phandle-array
required: true
description: LPN 引脚,用于硬件复位和低功耗控制
int-gpios:
type: phandle-array
required: true
description: |
INT 引脚,数据就绪中断(低电平有效,开漏)
max-i2c-bytes:
type: int
default: 32
description: |
单次 I2C 传输最大字节数(适配 I2C 控制器 FIFO 限制)
power-on-delay-ms:
type: int
default: 500
description: |
上电延迟时间(毫秒)。
LPN 引脚拉高后,等待传感器内部启动完成的时间。
默认值 500 ms 根据 VL53L7CX 数据手册推荐。
reset-delay-ms:
type: int
default: 10
description: |
复位延迟时间(毫秒)。
LPN 引脚拉低保持复位状态的时间。
默认值 10 ms 根据 VL53L7CX 数据手册推荐。
5.2 设备树覆盖 (overlay)

#include <zephyr/dt-bindings/gpio/gpio.h>
/ {
chosen {
zephyr,i2c = &iic1;
};
};
&iic1 {
status = "okay";
clock-frequency = <I2C_BITRATE_FAST>; /* 400kHz */
pinctrl-0 = <&iic1_default>;
pinctrl-names = "default";
vl53l7cx: vl53l7cx@29 {
compatible = "st,vl53l7cx";
reg = <0x29>; /* 7-bit 地址: 0x29 (8-bit: 0x52) */
status = "okay";
int-gpios = <&ioport3 5 GPIO_ACTIVE_LOW>; /* P3.5 */
lpn-gpios = <&ioport3 7 GPIO_ACTIVE_HIGH>; /* P3.7 */
};
};
注意事项:
reg = <0x29> 是 7-bit 地址,官方 API 中 VL53L7CX_DEFAULT_I2C_ADDRESS 为 0x52(8-bit)
int-gpios 需要配置中断 port_irq8
lpn-gpios 高电平有效,用于释放复位
6. Kconfig 配置
6.1 项目 Kconfig
menuconfig ENABLE_VL53L7CX
bool "Enable VL53L7CX ToF sensor"
default y
help
Enable VL53L7CX Time-of-Flight sensor driver and examples.
if ENABLE_VL53L7CX
menuconfig VL53L7CX_TIMING
bool "VL53L7CX timing configuration"
default y
help
Enable VL53L7CX timing parameters configuration.
if VL53L7CX_TIMING
config VL53L7CX_POWER_ON_DELAY_MS
int "Power-on delay (milliseconds)"
default 500
help
Delay after LPN goes high before sensor is ready.
config VL53L7CX_RESET_DELAY_MS
int "Reset delay (milliseconds)"
default 10
help
Delay while LPN is low during reset.
config VL53L7CX_I2C_BUFFER_SIZE
int "I2C buffer size (bytes)"
default 32
help
Maximum bytes per I2C transfer.
endif # VL53L7CX_TIMING
endif # ENABLE_VL53L7CX
menuconfig TOF_EXAMPLE
int "TOF example number (1-11)"
default 1
range 1 11
depends on ENABLE_VL53L7CX
choice TOF_RESOLUTION
prompt "TOF resolution"
default TOF_RESOLUTION_4X4
depends on ENABLE_VL53L7CX
config TOF_RESOLUTION_4X4
bool "4x4 (16 zones)"
config TOF_RESOLUTION_8X8
bool "8x8 (64 zones)"
endchoice
6.2 板级 prj.conf
CONFIG_I2C=y
CONFIG_GPIO=y
CONFIG_SENSOR=n # VL53L7CX 不使用 Zephyr sensor API
CONFIG_MAIN_STACK_SIZE=8192 # 固件加载需要较大栈
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_LOG_BACKEND_UART=y
7. CMake 构建集成
# VL53L7CX 驱动源文件
set(VL53L7CX_DIR ${CMAKE_CURRENT_SOURCE_DIR}/drv/vl53l7cx)
set(VL53L7CX_API_DIR ${VL53L7CX_DIR}/VL53L7CX_ULD_API)
set(VL53L7CX_PLATFORM_DIR ${VL53L7CX_DIR}/Platform)
set(VL53L7CX_SOURCES
${VL53L7CX_API_DIR}/src/vl53l7cx_api.c
${VL53L7CX_PLATFORM_DIR}/platform.c
)
set(VL53L7CX_INCLUDE_DIRS
${VL53L7CX_API_DIR}/inc
${VL53L7CX_PLATFORM_DIR}
)
# 条件编译
if(CONFIG_ENABLE_VL53L7CX)
target_sources(app PRIVATE ${VL53L7CX_SOURCES})
target_include_directories(app PRIVATE ${VL53L7CX_INCLUDE_DIRS})
# 根据示例编号添加对应的源文件
if(CONFIG_TOF_EXAMPLE_1)
target_sources(app PRIVATE example/example_1.c)
elseif(CONFIG_TOF_EXAMPLE_2)
target_sources(app PRIVATE example/example_2.c)
elseif(CONFIG_TOF_EXAMPLE_3)
target_sources(app PRIVATE example/example_3.c)
elseif(CONFIG_TOF_EXAMPLE_4)
target_sources(app PRIVATE example/example_4.c)
elseif(CONFIG_TOF_EXAMPLE_5)
target_sources(app PRIVATE example/example_5.c)
elseif(CONFIG_TOF_EXAMPLE_6)
target_sources(app PRIVATE example/example_6.c)
elseif(CONFIG_TOF_EXAMPLE_7)
target_sources(app PRIVATE example/example_7.c)
target_sources(app PRIVATE ${VL53L7CX_API_DIR}/src/vl53l7cx_plugin_xtalk.c)
elseif(CONFIG_TOF_EXAMPLE_8)
target_sources(app PRIVATE example/example_8.c)
target_sources(app PRIVATE ${VL53L7CX_API_DIR}/src/vl53l7cx_plugin_xtalk.c)
elseif(CONFIG_TOF_EXAMPLE_9)
target_sources(app PRIVATE example/example_9.c)
target_sources(app PRIVATE ${VL53L7CX_API_DIR}/src/vl53l7cx_plugin_detection_thresholds.c)
elseif(CONFIG_TOF_EXAMPLE_10)
target_sources(app PRIVATE example/example_10.c)
target_sources(app PRIVATE ${VL53L7CX_API_DIR}/src/vl53l7cx_plugin_motion_indicator.c)
elseif(CONFIG_TOF_EXAMPLE_11)
target_sources(app PRIVATE example/example_11.c)
target_sources(app PRIVATE ${VL53L7CX_API_DIR}/src/vl53l7cx_plugin_xtalk.c)
target_sources(app PRIVATE ${VL53L7CX_API_DIR}/src/vl53l7cx_plugin_detection_thresholds.c)
endif()
endif()
8. 应用层调用
8.1 初始化流程
/* 1. 声明配置结构体 */
static VL53L7CX_Configuration Dev;
/* 2. 设置平台实例 */
Dev.platform = zephyr_platform;
/* 3. 传感器上电(LPN 硬件复位) */
sensor_power_on();
/* 4. 检测传感器 */
uint8_t isAlive;
vl53l7cx_is_alive(&Dev, &isAlive);
if (!isAlive) {
/* 传感器未检测到,检查 I2C 连接 */
}
/* 5. 加载固件 */
vl53l7cx_init(&Dev);
/* 6. 配置 INT 引脚中断 */
vl53l7cx_int_pin_init();
8.2 测距流程
/* 开始测距 */
vl53l7cx_start_ranging(&Dev);
while (1) {
/* 等待 INT 中断(数据就绪) */
WaitForL5Interrupt(NULL);
/* 读取测距数据 */
VL53L7CX_ResultsData Results;
vl53l7cx_get_ranging_data(&Dev, &Results);
/* 处理数据 */
for (int i = 0; i < 64; i++) {
int16_t distance = Results.distance_mm[i];
uint8_t status = Results.target_status[i];
/* ... */
}
}
/* 停止测距 */
vl53l7cx_stop_ranging(&Dev);
8.3 main.c 入口分发
int main(void)
{
static VL53L7CX_Configuration Dev;
Dev.platform = zephyr_platform;
sensor_power_on();
uint8_t isAlive;
vl53l7cx_is_alive(&Dev, &isAlive);
vl53l7cx_init(&Dev);
vl53l7cx_int_pin_init();
/* 根据 Kconfig 分发到对应例程 */
#if defined(CONFIG_TOF_EXAMPLE_1)
example_1(&Dev);
#elif defined(CONFIG_TOF_EXAMPLE_2)
example_2(&Dev);
/* ... */
#endif
while (1) { k_sleep(K_SECONDS(1)); }
return 0;
}
9. 移植步骤清单
步骤 1:获取官方库
- 从 ST 官网下载 VL53L7CX ULD 包
- 将
VL53L7CX_ULD_API/ 目录复制到项目的 drv/vl53l7cx/ 下
步骤 2:实现平台适配层
- 修改
platform.h:
- 在
VL53L7CX_Platform 结构体中添加 const struct device *i2c_dev
- 声明
vl53l7cx_int_pin_init() 等 Zephyr 特有函数
- 实现
platform.c:
- 使用
DT_NODELABEL(vl53l7cx) 获取设备树节点
- 使用
DT_CHOSEN(zephyr_i2c) 获取 I2C 设备
- 实现
VL53L7CX_RdByte/WrByte/RdMulti/WrMulti 使用 Zephyr I2C API
- 实现
VL53L7CX_Reset_Sensor 使用 GPIO API 控制 LPN
- 实现
VL53L7CX_WaitMs 使用 k_sleep()
- 实现
VL53L7CX_SwapBuffer 直接复制官方代码
步骤 3:创建设备树绑定
- 创建
dts/bindings/sensor/st,vl53l7cx.yaml
- 定义
compatible = "st,vl53l7cx"
- 添加
lpn-gpios、int-gpios、max-i2c-bytes 属性
步骤 4:编写设备树覆盖
- 在
boards/xxx.overlay 中添加 VL53L7CX 节点
- 配置 I2C 控制器和引脚
步骤 5:配置 Kconfig 和 CMake
- 在
Kconfig 中添加 ENABLE_VL53L7CX 和 TOF_EXAMPLE 选项
- 在
CMakeLists.txt 中条件编译 VL53L7CX 源文件
步骤 6:编写应用层代码
- 参考官方
Example_1_ranging_basic.c 编写 example_1.c
- 将
printf 替换为 LOG_INF
- 将
DelayMs 替换为 k_sleep(K_MSEC())
- 将轮询
check_data_ready 替换为中断驱动 WaitForL5Interrupt
10. 成果展示
使用例程1进行演示,轮询简单4*4测距

截取两组往复测量数据,例程中是测试10次


11. 常见问题与解决方案
11.1 传感器未检测到 (isAlive = 0)
可能原因:
- I2C 地址错误(7-bit vs 8-bit)
- LPN 引脚未正确初始化
- I2C 控制器未启用
解决方案:
- 确认
reg = <0x29>(7-bit)而非 0x52(8-bit)
- 检查 LPN 引脚是否在
sensor_power_on() 中正确拉高
- 使用
i2c scan 命令扫描 I2C 总线
11.2 初始化超时
可能原因:
- 固件加载失败(I2C 传输不稳定)
- 分块传输大小不匹配
解决方案:
- 调整
max-i2c-bytes 属性(从 32 降到 16)
- 确保
VL53L7CX_WrMulti 分块间有适当延时
11.3 栈溢出
可能原因:
VL53L7CX_Configuration 结构体较大(包含 offset_data、xtalk_data、temp_buffer)
解决方案:
- 使用
static 声明 VL53L7CX_Configuration 变量
- 增大
CONFIG_MAIN_STACK_SIZE 到 8192
11.4 INT 中断不触发
可能原因:
- INT 引脚没有配置正确或者未进行中断配置
解决方案:
- 配置带中断分配的GPIO
- 检查 overlay 中
port_irq 配置是否正确
提供移植驱动库,后续实现完整项目整体提交
附件:vl53l7cx.zip