diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..0ebf545 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,104 @@ +# 变更说明 + +本文记录本仓库当前未提交改动的背景、目的和验证结果,方便后续维护时快速了解这次迭代做了什么。 + +## 1. I2C 底层支持 16 位寄存器地址 + +### 改动 + +- `Inc/i2c.h` +- `Src/i2c.c` + +`i2c_write()` 和 `i2c_read()` 的 `reg_addr` 参数由 `uint8_t` 改为 `uint16_t`,发送寄存器地址时先发高字节、再发低字节。 + +### 目的 + +VL53L4CD 的所有寄存器地址都是 16 位,例如: + +- `IDENTIFICATION__MODEL_ID = 0x010F` +- `RESULT__RANGE_STATUS = 0x0089` +- `RESULT__DISTANCE = 0x0096` + +原来的 `uint8_t` 寄存器地址接口无法直接访问这些寄存器。 + +### 兼容性 + +对于寄存器地址仍为 8 位的普通 I2C 器件,调用方传入的地址会自动被当作低字节发送,高字节为 `0x00`,因此原有行为不变。 + +## 2. 新增 VL53L4CD 驱动 + +新增文件: + +- `Inc/VL53L4CD_Driver.h` +- `Src/VL53L4CD_Driver.c` + +驱动参考 ST VL53L4CD ULD 和 Pololu Arduino 实现,提供: + +- `vl53l4cd_init()`:读取型号、等待固件启动、写入默认配置、VHV 校准、设置 50ms 时序预算 +- `vl53l4cd_start_ranging()` / `vl53l4cd_stop_ranging()`:启动/停止连续测距 +- `vl53l4cd_data_ready()`:查询是否有新结果 +- `vl53l4cd_clear_interrupt()`:清除测距完成中断 +- `vl53l4cd_get_result()`:读取并解析 15 字节结果块 +- `vl53l4cd_set_range_timing()`:设置 10-200ms 时序预算,当前只支持连续模式 +- `vl53l4cd_read_reg8/16()`、`vl53l4cd_write_reg8/16()`:寄存器调试接口 + +结果结构体包含: + +- `distance_mm` +- `range_status` +- `number_of_spad` +- `signal_rate_kcps` +- `ambient_rate_kcps` +- `signal_per_spad_kcps` +- `ambient_per_spad_kcps` +- `sigma_mm` + +## 3. main.c 集成 + +在 `main.c` 中: + +- 初始化完成后调用 `vl53l4cd_init()` +- 成功后调用 `vl53l4cd_start_ranging()` +- 主循环每 500ms 检查 `vl53l4cd_data_ready()` +- 有结果时清除中断、读取结果,并通过 `printf` 输出距离、状态、信号、环境光和 Sigma + +输出示例: + +```text +Distance: 830 mm | status: 0 | signal: 1128 kcps | ambient: 72 kcps | sigma: 3 mm +``` + +## 4. 板级配置 + +`Inc/board_config.h` 当前状态: + +- `DEBUG_MODE = CFG_DISABLE`:关闭启动横幅,距离输出仍然保留 +- `DEBUG_VERBOSE = CFG_DISABLE`:关闭 IIC 调试打印和启动 I2C 扫描 +- `RS485_MODE = CFG_ENABLE`:使用 RS485 串口输出 +- I2C 使用 PA9/PA10,复用 AF4 + +IIC 源码中的调试 `printf` 已恢复并保留在 `DEBUG_VERBOSE` 条件编译中,需要排查 I2C 时可临时打开。 + +## 5. 构建系统 + +`CMakeLists.txt` 的 `TARGET_SRC` 已添加: + +```cmake +Src/VL53L4CD_Driver.c +``` + +## 6. 体积与验证 + +- MCU:GD32E230,Flash 16K,RAM 4K +- Release 编译:`text 14316 / data 116 / bss 2012` +- 编译通过,无警告 +- 已通过 DAPLink/CMSIS-DAP + OpenOCD 烧录 +- 已在 COM17 @ 115200 验证连续输出,间隔约 500ms +- `status: 0` 表示测距有效 + +## 7. 当前已知限制 + +- 驱动只支持连续测距模式,`inter_measurement_ms` 非 0 会返回失败 +- 尚未实现距离阈值中断、Offset/Crosstalk 校准、自主低功耗模式、ULP 模式 +- `DEBUG_VERBOSE` 全开时 IIC 调试打印会让 16K Flash 非常紧张,不建议常开 +- VL53L4CD 是单区传感器,不是多区测距 diff --git a/CMakeLists.txt b/CMakeLists.txt index 21fcbf3..e5b0bd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ set(TARGET_SRC Src/command.c Src/i2c.c Src/board_config.c + Src/VL53L4CD_Driver.c ) # 设置输出目录 @@ -92,4 +93,4 @@ add_custom_command(TARGET ${PROJECT_NAME} # 1. If you add new source files (e.g. Src/my_module.c), add them to TARGET_SRC above. # 2. If you add new header files or directories, add them to target_include_directories above. # 3. If you add new SDK modules, use add_subdirectory(SDK/YourModule) and link with target_link_libraries. -# ========================= \ No newline at end of file +# ========================= diff --git a/Inc/VL53L4CD_Driver.h b/Inc/VL53L4CD_Driver.h new file mode 100644 index 0000000..d2a4df2 --- /dev/null +++ b/Inc/VL53L4CD_Driver.h @@ -0,0 +1,162 @@ +/** + * @file VL53L4CD_Driver.h + * @brief VL53L4CD 测距传感器驱动接口 + * + * 基于 ST VL53L4CD ULD 和 Pololu 参考实现,寄存器地址均为 16 位, + * 通过项目现有硬件 I2C0 (PA9/PA10) 访问。 + */ +#ifndef VL53L4CD_DRIVER_H +#define VL53L4CD_DRIVER_H + +#include +#include + +/* 板载默认 7 位 I2C 地址,VL53L4CD 出厂默认 0x29 */ +#define VL53L4CD_I2C_ADDR 0x29U + +/* 寄存器地址 */ +#define VL53L4CD_REG_I2C_SLAVE_DEVICE_ADDRESS 0x0001U +#define VL53L4CD_REG_VHV_TIMEOUT_MACROP_LOOP_BOUND 0x0008U +#define VL53L4CD_REG_GPIO_HV_MUX_CTRL 0x0030U +#define VL53L4CD_REG_GPIO_TIO_HV_STATUS 0x0031U +#define VL53L4CD_REG_SYSTEM_INTERRUPT 0x0046U +#define VL53L4CD_REG_RANGE_CONFIG_A 0x005EU +#define VL53L4CD_REG_RANGE_CONFIG_B 0x0061U +#define VL53L4CD_REG_SIGMA_THRESHOLD 0x0064U +#define VL53L4CD_REG_MIN_COUNT_RATE_RTN_LIMIT 0x0066U +#define VL53L4CD_REG_INTERMEASUREMENT_MS 0x006CU +#define VL53L4CD_REG_THRESH_HIGH 0x0072U +#define VL53L4CD_REG_THRESH_LOW 0x0074U +#define VL53L4CD_REG_SYSTEM_INTERRUPT_CLEAR 0x0086U +#define VL53L4CD_REG_SYSTEM_START 0x0087U +#define VL53L4CD_REG_RESULT_RANGE_STATUS 0x0089U +#define VL53L4CD_REG_RESULT_SPAD_NB 0x008CU +#define VL53L4CD_REG_RESULT_SIGNAL_RATE 0x008EU +#define VL53L4CD_REG_RESULT_AMBIENT_RATE 0x0090U +#define VL53L4CD_REG_RESULT_SIGMA 0x0092U +#define VL53L4CD_REG_RESULT_DISTANCE 0x0096U +#define VL53L4CD_REG_RESULT_OSC_CALIBRATE_VAL 0x00DEU +#define VL53L4CD_REG_FIRMWARE_SYSTEM_STATUS 0x00E5U +#define VL53L4CD_REG_IDENTIFICATION_MODEL_ID 0x010FU + +/* 等待固件启动 / 数据就绪的最大时间 */ +#define VL53L4CD_BOOT_TIMEOUT_MS 200U +#define VL53L4CD_DATA_TIMEOUT_MS 500U + +/* 一次结果块读包含的字节数:0x89..0x97 */ +#define VL53L4CD_RESULT_BLOCK_SIZE 15U + +/* 测距状态码(已由原始状态映射为用户可读状态) */ +typedef enum { + VL53L4CD_STATUS_RANGE_VALID = 0, /* 测距有效 */ + VL53L4CD_STATUS_SIGMA_FAIL, /* Sigma 超限 */ + VL53L4CD_STATUS_SIGNAL_FAIL, /* 信号强度不足 */ + VL53L4CD_STATUS_MIN_RANGE_FAIL, /* 目标过近 */ + VL53L4CD_STATUS_PHASE_FAIL, /* 相位故障 */ + VL53L4CD_STATUS_HW_FAIL, /* 硬件故障 */ + VL53L4CD_STATUS_NO_TARGET, /* 未检测到目标 */ + VL53L4CD_STATUS_SIGNAL_FAIL_NEAR, /* 近距离信号不足 */ + VL53L4CD_STATUS_OUT_OF_BOUNDS, /* 超出边界 */ + VL53L4CD_STATUS_XTALK_SIGNAL_FAIL, /* 串扰信号失败 */ + VL53L4CD_STATUS_LOW_SIGNAL_FAIL, /* 低信号失败 */ + VL53L4CD_STATUS_CROSSTALK_SIGNAL_FAIL,/* 串扰信号失败 */ + VL53L4CD_STATUS_AMBIENT_SIGNAL_FAIL, /* 环境光信号失败 */ + VL53L4CD_STATUS_SNSPD_AMBIENT_FAIL, /* 单光子雪崩二极管环境光失败 */ + VL53L4CD_STATUS_UNKNOWN = 255 /* 未知状态 */ +} vl53l4cd_range_status_t; + +/* 单次测量结果 */ +typedef struct { + uint16_t distance_mm; /* 距离,单位 mm */ + uint8_t range_status; /* 测距状态码 */ + uint8_t number_of_spad; /* 有效 SPAD 数 */ + uint32_t signal_rate_kcps; /* 信号速率,单位 kcps */ + uint32_t ambient_rate_kcps; /* 环境光速率,单位 kcps */ + uint32_t signal_per_spad_kcps; /* 每 SPAD 信号速率 */ + uint32_t ambient_per_spad_kcps; /* 每 SPAD 环境光速率 */ + uint16_t sigma_mm; /* Sigma,单位 mm */ +} vl53l4cd_result_t; + +/** + * @brief 初始化 VL53L4CD + * @param[in] none + * @retval true: 初始化成功; false: 初始化失败 + */ +bool vl53l4cd_init(void); + +/** + * @brief 启动连续测距 + * @param[in] none + * @retval true: 启动成功; false: 启动失败 + */ +bool vl53l4cd_start_ranging(void); + +/** + * @brief 停止连续测距 + * @param[in] none + * @retval true: 停止成功; false: 停止失败 + */ +bool vl53l4cd_stop_ranging(void); + +/** + * @brief 检查是否有新测距结果 + * @param[in] none + * @retval true: 有新结果; false: 无新结果或读取失败 + */ +bool vl53l4cd_data_ready(void); + +/** + * @brief 清除测距完成中断 + * @param[in] none + * @retval true: 清除成功; false: 清除失败 + */ +bool vl53l4cd_clear_interrupt(void); + +/** + * @brief 读取并解析一次测距结果 + * @param[out] result: 结果结构体指针 + * @retval true: 读取成功; false: 读取失败 + */ +bool vl53l4cd_get_result(vl53l4cd_result_t *result); + +/** + * @brief 设置测距时序预算和测距周期 + * @param[in] timing_budget_ms: 10..200 ms + * @param[in] inter_measurement_ms: 当前驱动只支持 0(连续模式) + * @retval true: 设置成功; false: 参数无效或设置失败 + */ +bool vl53l4cd_set_range_timing(uint8_t timing_budget_ms, uint32_t inter_measurement_ms); + +/** + * @brief 读一个 8 位寄存器 + * @param[in] reg: 16 位寄存器地址 + * @param[out] value: 返回值指针 + * @retval true: 成功; false: 失败 + */ +bool vl53l4cd_read_reg8(uint16_t reg, uint8_t *value); + +/** + * @brief 读一个 16 位寄存器 + * @param[in] reg: 16 位寄存器地址 + * @param[out] value: 返回值指针 + * @retval true: 成功; false: 失败 + */ +bool vl53l4cd_read_reg16(uint16_t reg, uint16_t *value); + +/** + * @brief 写一个 8 位寄存器 + * @param[in] reg: 16 位寄存器地址 + * @param[in] value: 写入值 + * @retval true: 成功; false: 失败 + */ +bool vl53l4cd_write_reg8(uint16_t reg, uint8_t value); + +/** + * @brief 写一个 16 位寄存器 + * @param[in] reg: 16 位寄存器地址 + * @param[in] value: 写入值 + * @retval true: 成功; false: 失败 + */ +bool vl53l4cd_write_reg16(uint16_t reg, uint16_t value); + +#endif /* VL53L4CD_DRIVER_H */ diff --git a/Inc/board_config.h b/Inc/board_config.h index 707dc84..e92aa03 100644 --- a/Inc/board_config.h +++ b/Inc/board_config.h @@ -23,7 +23,7 @@ /* >>>>>>>>>>>>>>>>>>>>[DEBUG MODE]<<<<<<<<<<<<<<<<<<<< */ -#define DEBUG_MODE CFG_ENABLE // CFG_DISABLE: Release Mode; CFG_ENABLE: Debug Mode +#define DEBUG_MODE CFG_DISABLE // CFG_DISABLE: Release Mode; CFG_ENABLE: Debug Mode /* >>>>>>>>>>>>>>>>>>>>[COMMAND DEBUG]<<<<<<<<<<<<<<<<<<<< */ @@ -31,7 +31,7 @@ /* >>>>>>>>>>>>>>>>>>>>[DEBUG ASSERTIONS DEFINE]<<<<<<<<<<<<<<<<<<<< */ -#define DEBUG_VERBOSE CFG_ENABLE // CFG_DISABLE: Verbose debug off; CFG_ENABLE: Verbose debug on +#define DEBUG_VERBOSE CFG_DISABLE // CFG_DISABLE: Verbose debug off; CFG_ENABLE: Verbose debug on /* >>>>>>>>>>>>>>>>>>>>[SEGGER RTT DETECTION]<<<<<<<<<<<<<<<<<<<< */ diff --git a/Inc/i2c.h b/Inc/i2c.h index 1e09da2..a11938b 100644 --- a/Inc/i2c.h +++ b/Inc/i2c.h @@ -93,7 +93,7 @@ void i2c_scan(void); /*! \brief write 16-bit data to I2C device \param[in] slave_addr: 7-bit slave address - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[in] data: pointer to 2-byte data array \param[out] none \retval i2c_result_t @@ -119,17 +119,17 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data \param[out] none \retval i2c_result_t: operation result */ -i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length); +i2c_result_t i2c_write(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length); /*! \brief read data from I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[out] data: pointer to data buffer \param[in] length: number of bytes to read (1-255) \retval i2c_result_t: operation result */ -i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length); +i2c_result_t i2c_read(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length); /* Pure read response without sub-address phase (for commands that already sent read request parameters) */ /*! diff --git a/README.md b/README.md index 1470ef5..573f740 100644 --- a/README.md +++ b/README.md @@ -1,396 +1,214 @@ -# GD32E23x 工程模板 +# GD32E230 + VL53L4CD 测距项目 -本仓库为兆易创新 GD32E23x 系列 MCU 的 CMake + VSCode 工程模板,适合嵌入式开发快速上手和团队协作。 - ---- +本仓库是基于 GD32E230 的 VL53L4CD 时间飞行(ToF)测距项目,硬件兼容 Adafruit VL53L4CD 模块。固件通过 I2C 驱动传感器,每 500ms 输出一次距离和测距质量参数。 ## 目录 -- [适用范围](#适用范围) -- [默认配置](#默认配置) -- [快速开始](#快速开始) -- [工程结构](#工程结构) -- [板级配置](#板级配置) -- [Flash 偏移配置(配合 Bootloader)](#flash-偏移配置配合-bootloader) -- [工具链准备](#工具链准备) -- [使用说明](#使用说明) -- [时钟配置说明](#时钟配置说明) -- [vcpkg 依赖管理(可选)](#vcpkg-依赖管理可选) +- [硬件连接](#硬件连接) +- [功能开关](#功能开关) +- [编译与烧录](#编译与烧录) +- [串口输出](#串口输出) +- [VL53L4CD 驱动](#vl53l4cd-驱动) +- [测距结果字段](#测距结果字段) +- [测距状态码](#测距状态码) +- [I2C 16 位寄存器说明](#i2c-16-位寄存器说明) +- [Flash 体积说明](#flash-体积说明) +- [已知限制](#已知限制) +- [变更记录](#变更记录) ---- +## 硬件连接 -## 适用范围 +### 主控 -- 适用于兆易创新 GD32E23x 系列 Cortex-M23 内核单片机 -- 支持标准外设库开发 -- 推荐开发环境:VSCode + CMake + ARM GCC 工具链 +- MCU:GD32E230 系列,Cortex-M23 +- 系统时钟:72MHz +- Flash:16K(F4)/ 32K(F6)/ 64K(F8),上电自动识别 ---- +### VL53L4CD -## 默认配置 +| 信号 | 引脚 | 说明 | +|---|---|---| +| SCL | PA9 | I2C0 时钟,AF4 | +| SDA | PA10 | I2C0 数据,AF4 | +| VDD | 3.3V | 模块供电 | +| GND | GND | 共地 | -- MCU 主频:内部 RC 振荡器,系统时钟配置为 72MHz -- 调试串口:USART0(PA2 TX / PA3 RX),115200 波特率 -- I2C:默认硬件 I2C0(PF0 SDA / PF1 SCL),可通过 `board_config.h` 切换为软件 I2C +传感器 7 位 I2C 地址默认为 `0x29`。 ---- +### 串口 -## 快速开始 +| 信号 | 引脚 | 说明 | +|---|---|---| +| TX | PA2 | RS485 发送 | +| RX | PA3 | RS485 接收 | +| DE | PA1 | RS485 方向控制,硬件自动控制 | -### 基于模板创建新项目 +波特率 115200,8N1。 -1. **克隆或复制本仓库** - ```bash - git clone https://gitea.hulk.wang/hulk/gd32e23x_template_cmake_vscode.git my-new-project - cd my-new-project - ``` +### 其他 -2. **修改项目配置** — 编辑 `cmake/project_config.cmake`: - ```cmake - set(PROJECT_NAME "MyProject") # 项目名称 - set(BOARD_TYPE_CODE 20) # 板卡类型码(协议帧中的标识) - set(VERSION_MAJOR 1) # 主版本号 - set(VERSION_MINOR 0) # 次版本号 - set(VERSION_PATCH 0) # 修订号 - set(BUILD_VARIANT "APP") # 编译变体 - ``` +- LED:PB1 +- SWD:SWDIO、SWCLK、GND,用于 DAPLink 烧录与调试 -3. **添加业务源文件** — 编辑 `CMakeLists.txt`,在 `TARGET_SRC` 中添加你的 `.c` 文件。 +## 功能开关 -4. **配置板级引脚** — 编辑 `Inc/board_config.h`,修改 I2C、UART、LED 等引脚定义。 +所有开关集中在 `Inc/board_config.h`。 -5. **编译** - ```bash - cmake --preset Debug - cmake --build build/Debug - ``` - 产物在 `build/Debug/` 下,包含 `.elf`、`.hex`、`.bin`、`.map`、`.list`。 +| 宏 | 当前值 | 作用 | +|---|---|---| +| `DEBUG_MODE` | `CFG_DISABLE` | 关闭 `Hello World` 启动横幅;距离输出仍保留 | +| `DEBUG_VERBOSE` | `CFG_DISABLE` | 关闭 IIC 调试打印和启动 I2C 扫描 | +| `COM_DEBUG` | `CFG_DISABLE` | 关闭命令帧调试打印 | +| `SEGGER_RTT_DETECTION` | `CFG_DISABLE` | 关闭 SEGGER RTT | +| `RS485_MODE` | `CFG_ENABLE` | 使用 RS485 串口输出 | +| `SOFTWARE_IIC` | `CFG_DISABLE` | 使用硬件 I2C | -### 分支说明 +如果需要临时扫描 I2C 总线或查看 IIC 调试信息,把 `DEBUG_VERBOSE` 改为 `CFG_ENABLE`。注意 16K Flash 下全开会让固件体积非常紧张。 -| 分支 | 用途 | -|------|------| -| `template_pc` | **主模板**(推荐),用于桌面端 VSCode 开发 | -| `template_xl` | 小琅适配版 | -| `main` | 早期版本,不推荐使用 | - ---- - -## 工程结构 - -``` -. -├── CMakeLists.txt # 主构建文件 -├── CMakePresets.json # CMake 预设(Debug/Release) -├── cmake/ -│ ├── arm-none-eabi-gcc.cmake # ARM GCC 工具链配置 -│ ├── project.cmake # 编译选项(-Os/-O0, -mcpu=cortex-m23) -│ ├── project_config.cmake # 项目名/版本号/编译变体 -│ └── version.h.in # 自动生成固件版本头 -├── Inc/ # 头文件 -│ ├── board_config.h # 板级引脚定义 + 功能开关 -│ ├── command.h # 串口命令协议 -│ ├── i2c.h / led.h / systick.h / uart.h -│ └── uart_ring_buffer.h / gd32e23x_it.h / gd32e23x_libopt.h -├── Src/ # 源码 -│ ├── main.c # 入口函数 -│ ├── command.c # 命令解析处理 -│ ├── board_config.c # MCU 型号自动检测 -│ ├── i2c.c / led.c / systick.c / uart.c / uart_ring_buffer.c -│ └── gd32e23x_it.c / system_gd32e23x.c / syscalls.c -├── SDK/ -│ ├── CMSIS/ # ARM CMSIS Core (Cortex-M23) + GD 启动文件 -│ └── GD32E23x_standard_peripheral/ # GD32 标准外设库 -├── LD/gd32e23x_flash.ld # 链接脚本 -├── doc/ # 芯片数据手册 -└── .vscode/ # VSCode 调试/烧录配置 -``` - ---- - -## 板级配置(`Inc/board_config.h`) - -所有功能开关和引脚定义集中在 `Inc/board_config.h`。功能开关均为单行数值宏:将右侧的 `ENABLE` 或 `DISABLE` 改为另一值即可;其中 `ENABLE` 为 `1`,`DISABLE` 为 `0`。以下为完整的宏开关说明和推荐使用方式。 - -### 功能开关速查表 - -| 宏 | 作用 | 默认值 | Release 建议 | -|---|------|:---:|:---:| -| `SOFTWARE_IIC` | I2C 实现方式 | `DISABLE`(硬件) | `DISABLE` | -| `DEBUG_MODE` | printf 串口输出 | `DISABLE` | `DISABLE` | -| `COM_DEBUG` | 命令帧调试打印 | `DISABLE` | `DISABLE` | -| `DEBUG_VERBOSE` | 详细调试信息 | `DISABLE` | `DISABLE` | -| `SEGGER_RTT_DETECTION` | SEGGER RTT 支持 | `ENABLE` | `DISABLE` | - ---- - -### `SOFTWARE_IIC` — I2C 实现方式 - -选择 I2C 使用硬件外设还是软件 GPIO 模拟。 - -> 当前工程尚未实现 `SOFTWARE_IIC` 的条件编译驱动选择;该宏已迁移为数值配置,但改值不会在此版本切换 I2C 实现。 - -```c -#define SOFTWARE_IIC DISABLE // DISABLE: 硬件 I2C(默认);ENABLE: 软件 I2C(GPIO 模拟) -``` - -| 选项 | 优点 | 缺点 | -|------|------|------| -| 硬件 I2C | DMA 支持、CPU 占用低 | 仅限固定引脚、调试复杂 | -| 软件 I2C | 任意 GPIO、移植方便 | CPU 占用高、速率受限 | - -> 切换后需同步修改下方 I2C 引脚定义。 - ---- - -### `DEBUG_MODE` — 调试模式 - -开启后 USART0 输出 printf 调试信息。**Release 固件必须关闭。** - -```c -#define DEBUG_MODE DISABLE // DISABLE: 关闭(默认);ENABLE: 开启调试输出 -``` - -**影响范围:** -- 使能 `USART0` 初始化和 `printf` 重定向到串口 -- 会占用 PA2/PA3 引脚和 USART0 硬件资源 -- 增加 ROM 约 2~4KB(取决于 printf 调用量) - ---- - -### `COM_DEBUG` — 命令帧调试打印 - -开启后串口命令解析过程打印每帧的详细内容(地址、长度、数据、校验)。**仅调试通信协议时开启。** - -```c -#define COM_DEBUG DISABLE // DISABLE: 关闭(默认);ENABLE: 开启命令帧调试 -``` - -**依赖:** 需要先开启 `DEBUG_MODE`,否则输出无法外发。 - -**输出示例:** -```text -[CMD] ADDR=01 LEN=05 DATA: AA BB CC DD EE CHK=OK -``` - -> ⚠️ Release 必须关闭,否则大量串口输出会严重拖慢主循环。 - ---- - -### `DEBUG_VERBOSE` — 详细调试信息 - -在 `DEBUG_MODE` 基础上输出更底层的信息,如 I2C 总线扫描结果、MCU 型号识别等。 - -```c -#define DEBUG_VERBOSE DISABLE // DISABLE: 关闭(默认);ENABLE: 开启详细调试 -``` - -**额外输出:** -- 启动时打印 MCU Flash 容量检测结果 -- I2C 初始化时扫描总线上的设备地址 -- 其他诊断信息 - -> 依赖 `DEBUG_MODE`,开启后 ROM 进一步增加约 1~2KB。 - ---- - -### `SEGGER_RTT_DETECTION` — SEGGER RTT 支持 - -RTT(Real-Time Transfer)是 SEGGER 的调试通道技术,通过 SWD 接口传输数据,不占用串口引脚,速度远超 UART。 - -```c -#define SEGGER_RTT_DETECTION ENABLE // DISABLE: 禁用 RTT;ENABLE: 启用 RTT(默认) -``` - -**启用时:** -- 自动包含 `SEGGER_RTT.h`,提供 `RTT_printf` / `RTT_WriteString` / `RTT_PutChar` 宏 -- `SDK/SEGGER_RTT/` 模块参与编译和链接 -- 可用 J-Link RTT Viewer 或 VSCode + cortex-debug 查看实时日志 - -**禁用时:** -- 应用层 RTT 头文件引用与调用代码均在预处理阶段排除 -- RTT 宏展开为空操作;应用目标不生成 RTT 调用代码 -- 为保持现有 CMake SDK 加载方式,`SDK/SEGGER_RTT/` 仍会参与构建;静态库中未被引用的对象不会被链接器提取 - -> **Release 建议关闭** — RTT 依赖调试器连接,量产固件中无意义且占用 ROM。 - -**依赖关系总览:** -``` -COM_DEBUG ──── 依赖 ──→ DEBUG_MODE -DEBUG_VERBOSE ─ 依赖 ──→ DEBUG_MODE -SEGGER_RTT_DETECTION ─ 独立,与 DEBUG_MODE 并行 -``` - ---- - -### 引脚定义 - -所有引脚宏集中在 `board_config.h` 尾部,按需修改: - -```c -/* I2C */ -#define I2C_SCL_PORT GPIOF -#define I2C_SCL_PIN GPIO_PIN_1 -#define I2C_SDA_PORT GPIOF -#define I2C_SDA_PIN GPIO_PIN_0 - -/* LED */ -#define LED_RCU RCU_GPIOB -#define LED_PORT GPIOB -#define LED_PIN GPIO_PIN_1 - -/* UART */ -#define UART_GPIO_PORT GPIOA -#define UART_TX_PIN GPIO_PIN_2 -#define UART_RX_PIN GPIO_PIN_3 -#define UART_BAUDRATE 115200U -``` - -### MCU 型号自动检测 - -`board_config.c` 中的 `mcu_detect_and_config()` 上电自动识别 GD32E230 的 Flash 容量(F4=16K / F6=32K / F8=64K),结果存入全局变量 `g_mcu_flash_size`,并自动选择对应的 UART 外设(USART0 或 USART1)。 - ---- - -## Flash 偏移配置(配合 Bootloader) - -如果固件需要通过 Bootloader 启动(Bootloader 占用 Flash 前部区域),需修改两处: - -**1. 链接脚本** — `LD/gd32e23x_flash.ld` 第 15 行: -```c -// 默认:从 Flash 起始运行 -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K - -// 配合 Bootloader:前 8KB 留给 Bootloader,App 从 0x08002000 开始 -FLASH (rx) : ORIGIN = 0x08002000, LENGTH = 8K -``` - -**2. 向量表偏移** — `Src/system_gd32e23x.c` 第 44 行: -```c -// 默认 -#define VECT_TAB_OFFSET (uint32_t)0x00 - -// 配合 Bootloader(值 = Flash 偏移量,不含 0x0800 前缀) -#define VECT_TAB_OFFSET (uint32_t)0x2000 -``` - -> ⚠️ 两个偏移值必须对应修改:`LD` 中的 `ORIGIN` 减去 `0x08000000` 应等于 `VECT_TAB_OFFSET`。 - ---- - -## 工具链准备 - -### 1. ARM GCC 工具链 - -- **版本**:xpack-arm-none-eabi-gcc-11.3.1-1.1 -- **建议解压路径**:工程根目录下 `Toolchain/xpack-arm-none-eabi-gcc-11.3.1-1.1` -- **官方下载地址**:https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases -- **路径自定义**: - 如需自定义工具链路径,修改 `cmake/arm-none-eabi-gcc.cmake` 中的 `_TOOLCHAIN_CANDIDATES` 列表,或通过 CMake 参数传入: - ```bash - cmake --preset Debug -DTOOLCHAIN_DIRECTORY=/your/path/bin - ``` - -### 2. OpenOCD(调试/烧录) - -- **版本**:xpack-openocd-0.11.0-3 -- **建议解压路径**:任意位置(在 `.vscode/launch.json` 中配置路径) -- **获取地址**:https://github.com/burakenez/gd32-tools-xpack-openocd/tree/v0.11.0-3 -- **说明**: - - 本版本提取自 Embedded Builder V1.4.1.23782。 - - ⚠️ 请勿随意更换版本,因 GD32 MCU 支持有限,推荐严格使用此版本。 -- **路径自定义**: - 修改 `.vscode/launch.json` 中 `miDebuggerPath` 和 `serverpath` 指向你的 OpenOCD 路径。 - ---- - -## 使用说明 +## 编译与烧录 ### 编译 -```bash -# Debug 构建(-O0, -g3) -cmake --preset Debug -cmake --build build/Debug +在仓库根目录执行: -# Release 构建(-Os, -g0) +```bash cmake --preset Release -cmake --build build/Release +cmake --build Build/Release ``` +Debug 构建: + +```bash +cmake --preset Debug +cmake --build Build/Debug +``` + +产物位于 `Build/Release/Application/`: + +- `Application.elf` +- `Application.hex` +- `Application.bin` +- `Application.map` + ### 烧录 -通过 VSCode 任务栏运行 `Flash MCU` 任务,或命令行: +通过 DAPLink/CMSIS-DAP 烧录: ```bash -openocd -f interface/cmsis-dap.cfg -f target/gd32e23x.cfg -c "program build/Debug/Application.elf verify reset exit" +D:/toolchain/openocd/bin/openocd.exe \ + -s D:/toolchain/openocd/scripts \ + -f interface/cmsis-dap.cfg \ + -f target/gd32e23x.cfg \ + -c "reset_config srst_only connect_assert_srst; init; reset halt; flash write_image erase Build/Release/Application/Application.elf; reset; exit" ``` -### 产物 +也可以在 VSCode 中使用已配置的 Flash 任务。 -编译输出位于 `build//`: +## 串口输出 -| 文件 | 说明 | -|------|------| -| `Application.elf` | ELF 固件(调试用) | -| `{项目名}_{版本}_{编译条件}_{日期}.hex` | Hex 文件 | -| `{项目名}_{版本}_{编译条件}_{日期}.bin` | 二进制文件 | -| `{项目名}_{版本}_{编译条件}_{日期}.list` | 反汇编清单 | -| `{项目名}_{版本}_{编译条件}_{日期}.map` | 内存映射 | +连接串口后使用 115200 8N1 查看,例如 COM17: ---- +```text +Distance: 830 mm | status: 0 | signal: 1128 kcps | ambient: 72 kcps | sigma: 3 mm +``` -## 时钟配置说明 +默认每 500ms 输出一次。 -本工程默认系统时钟为内部 IRC8M 振荡器经 PLL 倍频后的 72MHz。 +## VL53L4CD 驱动 -如需修改主频或时钟源,请编辑 `Src/system_gd32e23x.c` 文件: +驱动文件: -1. 查找如下宏定义区: - ```c - // #define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL) - // #define __SYSTEM_CLOCK_8M_IRC8M (__IRC8M) - // #define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) - #define __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2 (uint32_t)(72000000) - ``` -2. 取消你需要的时钟方案的注释,并注释掉其它方案。 -3. 保存后重新编译工程即可生效。 +- `Inc/VL53L4CD_Driver.h` +- `Src/VL53L4CD_Driver.c` -详细时钟初始化流程可参考 `Src/system_gd32e23x.c` 文件中的 `system_clock_config` 及相关函数实现。 +### 主要接口 ---- +| 函数 | 作用 | +|---|---| +| `vl53l4cd_init()` | 初始化传感器,写入默认配置,默认 50ms 时序预算 | +| `vl53l4cd_start_ranging()` | 启动连续测距 | +| `vl53l4cd_stop_ranging()` | 停止连续测距 | +| `vl53l4cd_data_ready()` | 查询是否有新结果 | +| `vl53l4cd_clear_interrupt()` | 清除测距完成中断 | +| `vl53l4cd_get_result()` | 读取并解析测距结果 | +| `vl53l4cd_set_range_timing()` | 设置 10-200ms 时序预算,当前仅支持连续模式 | +| `vl53l4cd_read_reg8/16()` | 读 8/16 位寄存器 | +| `vl53l4cd_write_reg8/16()` | 写 8/16 位寄存器 | -## vcpkg 依赖管理(可选) +### 修改时序预算 -本工程可选支持 vcpkg 作为 C/C++ 工具链和构建工具的自动化依赖管理方案。 +在 `Src/VL53L4CD_Driver.c` 的 `vl53l4cd_init()` 末尾: -- 自动下载和管理如 CMake、Ninja 等构建工具,简化环境配置。 -- 可扩展用于第三方 C/C++ 库的统一管理。 +```c +return vl53l4cd_set_range_timing(50U, 0U); +``` -**启用方法**: -1. 在项目根目录创建 `vcpkg-configuration.json` 文件,内容如下: +第一个参数是时序预算,范围 10-200ms。第二个参数当前只能传 `0`,表示连续测距模式。 - ```json - { - "registries": [ - { - "name": "microsoft", - "location": "https://aka.ms/vcpkg-ce-default", - "kind": "artifact" - }, - { - "name": "arm", - "location": "https://aka.ms/vcpkg-artifacts-arm", - "kind": "artifact" - } - ], - "requires": { - "arm:tools/ninja-build/ninja": "^1.12.0", - "arm:tools/kitware/cmake": "^3.28.4" - } - } - ``` +### 修改输出间隔 -2. 启动 VSCode 或命令行,vcpkg 会自动检测并安装所需工具。 +在 `Src/main.c` 主循环中: -如不需要 vcpkg,可忽略本文件。 +```c +delay_ms(500); +``` + +改为需要的毫秒数即可。 + +## 测距结果字段 + +`vl53l4cd_get_result()` 返回 `vl53l4cd_result_t`: + +| 字段 | 含义 | +|---|---| +| `distance_mm` | 距离,单位 mm | +| `range_status` | 测距状态码 | +| `number_of_spad` | 有效 SPAD 数 | +| `signal_rate_kcps` | 目标反射信号强度 | +| `ambient_rate_kcps` | 环境光/背景噪声强度 | +| `signal_per_spad_kcps` | 每个 SPAD 的信号强度 | +| `ambient_per_spad_kcps` | 每个 SPAD 的环境光强度 | +| `sigma_mm` | 测距噪声/不确定度,单位 mm | + +## 测距状态码 + +| 状态码 | 含义 | +|---:|---| +| 0 | 测距有效 | +| 1 | Sigma 超限,结果不稳定 | +| 2 | 信号太弱,目标太远、太黑或反射率低 | +| 3 | 目标过近 | +| 4 | 相位故障 | +| 5 | 硬件故障 | +| 6 | 未检测到目标 | +| 7 | 近距离信号不足 | +| 255 | 未知状态 | + +## I2C 16 位寄存器说明 + +VL53L4CD 的寄存器地址是 16 位。本项目的 `i2c_read()` / `i2c_write()` 已改为支持 `uint16_t` 寄存器地址,发送时先发高字节、再发低字节。 + +对原有 8 位寄存器地址的 I2C 器件仍然兼容:地址高字节为 `0x00`,发送序列与原来一致。 + +## Flash 体积说明 + +当前 Release 固件约: + +```text +text 14316 +data 116 +bss 2012 +``` + +16K Flash 下仍有约 2K 余量。保留 `printf` 后体积会增加约 2.5K,因此不建议同时开启全部 IIC 调试打印。 + +## 已知限制 + +- 驱动只支持连续测距模式 +- 尚未实现距离阈值中断、Offset/Crosstalk 校准、自主低功耗模式、ULP 模式 +- VL53L4CD 是单区传感器,不是多区测距 +- `DEBUG_VERBOSE` 全开时 IIC 调试打印会让 Flash 非常紧张 + +## 变更记录 + +当前迭代的详细说明见 [CHANGES.md](CHANGES.md)。 diff --git a/Src/VL53L4CD_Driver.c b/Src/VL53L4CD_Driver.c new file mode 100644 index 0000000..d1bc4af --- /dev/null +++ b/Src/VL53L4CD_Driver.c @@ -0,0 +1,283 @@ +/** + * @file VL53L4CD_Driver.c + * @brief VL53L4CD 测距传感器驱动实现 + * + * 初始化配置参考 ST VL53L4CD ULD / Pololu Arduino 库, + * 使用 16 位寄存器地址,底层走项目现有硬件 I2C0。 + */ +#include "VL53L4CD_Driver.h" +#include "i2c.h" +#include "systick.h" + +/* 当前传感器 I2C 地址,默认 0x29 */ +static uint8_t g_vl53l4cd_addr = VL53L4CD_I2C_ADDR; + +/* VL53L4CD 默认配置,0x30..0x87 共 88 字节,来自 ST ULD */ +static const uint8_t g_vl53l4cd_default_config[88] = { + 0x11, 0x02, 0x00, 0x02, 0x08, 0x00, 0x08, 0x10, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0B, + 0x00, 0x00, 0x02, 0x14, 0x21, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x38, + 0xFF, 0x01, 0x00, 0x08, 0x00, 0x00, 0x01, 0xCC, + 0x07, 0x01, 0xF1, 0x05, 0x00, 0xA0, 0x00, 0x80, + 0x08, 0x38, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x07, 0x05, 0x06, 0x06, 0x00, 0x00, 0x02, 0xC7, + 0xFF, 0x9B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 +}; + +/** + * @brief 连续写多个寄存器字节 + * @param[in] reg: 起始 16 位寄存器地址 + * @param[in] data: 数据指针 + * @param[in] length: 字节数 + * @retval true: 成功; false: 失败 + */ +static bool vl53l4cd_write_regs(uint16_t reg, const uint8_t *data, uint8_t length) +{ + if (data == NULL || length == 0U) { + return false; + } + return (i2c_write(g_vl53l4cd_addr, reg, (uint8_t *)data, length) == I2C_RESULT_SUCCESS); +} + +bool vl53l4cd_read_reg8(uint16_t reg, uint8_t *value) +{ + if (value == NULL) { + return false; + } + return (i2c_read(g_vl53l4cd_addr, reg, value, 1U) == I2C_RESULT_SUCCESS); +} + +bool vl53l4cd_read_reg16(uint16_t reg, uint16_t *value) +{ + uint8_t buf[2]; + + if (value == NULL) { + return false; + } + if (i2c_read(g_vl53l4cd_addr, reg, buf, 2U) != I2C_RESULT_SUCCESS) { + return false; + } + *value = (uint16_t)(((uint16_t)buf[0] << 8) | buf[1]); + return true; +} + +bool vl53l4cd_write_reg8(uint16_t reg, uint8_t value) +{ + return vl53l4cd_write_regs(reg, &value, 1U); +} + +bool vl53l4cd_write_reg16(uint16_t reg, uint16_t value) +{ + uint8_t buf[2]; + + buf[0] = (uint8_t)(value >> 8); + buf[1] = (uint8_t)(value & 0xFF); + return vl53l4cd_write_regs(reg, buf, 2U); +} + +bool vl53l4cd_data_ready(void) +{ + uint8_t status = 0U; + + if (!vl53l4cd_read_reg8(VL53L4CD_REG_GPIO_TIO_HV_STATUS, &status)) { + return false; + } + /* 默认中断极性配置下,bit0=0 表示新结果就绪 */ + return ((status & 0x01U) == 0U); +} + +bool vl53l4cd_clear_interrupt(void) +{ + return vl53l4cd_write_reg8(VL53L4CD_REG_SYSTEM_INTERRUPT_CLEAR, 0x01U); +} + +bool vl53l4cd_get_result(vl53l4cd_result_t *result) +{ + static const uint8_t status_rtn[24] = { + 255, 255, 255, 5, 2, 4, 1, 7, 3, 0, + 255, 255, 9, 13, 255, 255, 255, 255, 10, 6, + 255, 255, 11, 12 + }; + uint8_t buf[VL53L4CD_RESULT_BLOCK_SIZE]; + uint8_t raw_status; + + if (result == NULL) { + return false; + } + if (i2c_read(g_vl53l4cd_addr, VL53L4CD_REG_RESULT_RANGE_STATUS, + buf, VL53L4CD_RESULT_BLOCK_SIZE) != I2C_RESULT_SUCCESS) { + return false; + } + + raw_status = buf[0] & 0x1FU; + result->range_status = (raw_status < 24U) ? status_rtn[raw_status] + : (uint8_t)VL53L4CD_STATUS_UNKNOWN; + result->number_of_spad = buf[3]; + result->signal_rate_kcps = ((uint32_t)buf[5] << 8 | buf[6]) * 8U; + result->ambient_rate_kcps = ((uint32_t)buf[7] << 8 | buf[8]) * 8U; + result->sigma_mm = ((uint16_t)buf[9] << 8 | buf[10]) / 4U; + result->distance_mm = (uint16_t)(((uint16_t)buf[13] << 8) | buf[14]); + + if (result->number_of_spad != 0U) { + result->signal_per_spad_kcps = result->signal_rate_kcps / result->number_of_spad; + result->ambient_per_spad_kcps = result->ambient_rate_kcps / result->number_of_spad; + } else { + result->signal_per_spad_kcps = 0U; + result->ambient_per_spad_kcps = 0U; + } + + return true; +} + +bool vl53l4cd_set_range_timing(uint8_t timing_budget_ms, uint32_t inter_measurement_ms) +{ + uint16_t osc_frequency = 0U; + uint32_t timing_budget_us; + uint32_t macro_period_us; + uint32_t tmp; + uint16_t ls_byte; + uint8_t ms_byte; + + if (timing_budget_ms < 10U || timing_budget_ms > 200U) { + return false; + } + if (inter_measurement_ms != 0U) { + /* 为节省 flash,本版本只支持连续模式 */ + return false; + } + if (!vl53l4cd_read_reg16(0x0006U, &osc_frequency) || osc_frequency == 0U) { + return false; + } + + timing_budget_us = (uint32_t)timing_budget_ms * 1000U; + macro_period_us = ((uint32_t)2304U * (0x40000000U / osc_frequency)) >> 6U; + + /* 连续模式:测量周期写 0 */ + { + uint8_t zero[4] = {0U, 0U, 0U, 0U}; + if (!vl53l4cd_write_regs(VL53L4CD_REG_INTERMEASUREMENT_MS, zero, 4U)) { + return false; + } + } + timing_budget_us -= 2500U; + + timing_budget_us <<= 12U; + + /* RANGE_CONFIG_A: 基于 16 的宏周期 */ + tmp = (macro_period_us * 16U) >> 6U; + ls_byte = (uint16_t)(((timing_budget_us + (tmp >> 1U)) / tmp) - 1U); + ms_byte = 0U; + while (ls_byte > 0xFFU) { + ls_byte >>= 1U; + ms_byte++; + } + if (!vl53l4cd_write_reg16(VL53L4CD_REG_RANGE_CONFIG_A, + (uint16_t)((uint16_t)ms_byte << 8 | ls_byte))) { + return false; + } + + /* RANGE_CONFIG_B: 基于 12 的宏周期 */ + tmp = (macro_period_us * 12U) >> 6U; + ls_byte = (uint16_t)(((timing_budget_us + (tmp >> 1U)) / tmp) - 1U); + ms_byte = 0U; + while (ls_byte > 0xFFU) { + ls_byte >>= 1U; + ms_byte++; + } + return vl53l4cd_write_reg16(VL53L4CD_REG_RANGE_CONFIG_B, + (uint16_t)((uint16_t)ms_byte << 8 | ls_byte)); +} + +bool vl53l4cd_init(void) +{ + uint16_t model_id = 0U; + uint8_t firmware_status = 0U; + uint32_t elapsed_ms = 0U; + uint16_t reg; + uint8_t count; + + /* 上电后固件需要一点启动时间,先重试读取型号 */ + while (elapsed_ms < VL53L4CD_BOOT_TIMEOUT_MS) { + if (vl53l4cd_read_reg16(VL53L4CD_REG_IDENTIFICATION_MODEL_ID, &model_id) && + model_id == 0xEBAAU) { + break; + } + delay_ms(2); + elapsed_ms += 2U; + } + if (model_id != 0xEBAAU) { + return false; + } + + /* 等待固件启动完成,FIRMWARE__SYSTEM_STATUS == 0x03 */ + elapsed_ms = 0U; + while (elapsed_ms < VL53L4CD_BOOT_TIMEOUT_MS) { + if (vl53l4cd_read_reg8(VL53L4CD_REG_FIRMWARE_SYSTEM_STATUS, &firmware_status) && + firmware_status == 0x03U) { + break; + } + delay_ms(2); + elapsed_ms += 2U; + } + if (firmware_status != 0x03U) { + return false; + } + + /* I2C/GPIO 上拉配置:本板按 3.3V AVDD 上拉处理 */ + if (!vl53l4cd_write_reg8(0x002DU, 0x00U) || + !vl53l4cd_write_reg8(0x002EU, 0x00U) || + !vl53l4cd_write_reg8(0x002FU, 0x00U)) { + return false; + } + + /* 写入 0x30..0x87 默认配置,分 3 次块写 */ + for (reg = 0x0030U; reg <= 0x0087U; reg += 30U) { + uint8_t block[30]; + count = 0U; + while (count < 30U && (reg + count) <= 0x0087U) { + block[count] = g_vl53l4cd_default_config[reg + count - 0x0030U]; + count++; + } + if (!vl53l4cd_write_regs(reg, block, count)) { + return false; + } + } + + /* 启动 VHV 校准并等待首个结果 */ + if (!vl53l4cd_write_reg8(VL53L4CD_REG_SYSTEM_START, 0x40U)) { + return false; + } + elapsed_ms = 0U; + while (!vl53l4cd_data_ready()) { + delay_ms(5); + elapsed_ms += 5U; + if (elapsed_ms >= VL53L4CD_DATA_TIMEOUT_MS) { + return false; + } + } + + if (!vl53l4cd_clear_interrupt() || + !vl53l4cd_stop_ranging() || + !vl53l4cd_write_reg8(VL53L4CD_REG_VHV_TIMEOUT_MACROP_LOOP_BOUND, 0x09U) || + !vl53l4cd_write_reg8(0x000BU, 0x00U) || + !vl53l4cd_write_reg16(0x0024U, 0x0500U)) { + return false; + } + + /* 默认 50ms 时序预算,连续模式 */ + return vl53l4cd_set_range_timing(50U, 0U); +} + +bool vl53l4cd_start_ranging(void) +{ + /* 本驱动固定使用连续模式 */ + return vl53l4cd_write_reg8(VL53L4CD_REG_SYSTEM_START, 0x21U); +} + +bool vl53l4cd_stop_ranging(void) +{ + return vl53l4cd_write_reg8(VL53L4CD_REG_SYSTEM_START, 0x80U); +} diff --git a/Src/i2c.c b/Src/i2c.c index b3efc84..705070c 100644 --- a/Src/i2c.c +++ b/Src/i2c.c @@ -640,13 +640,13 @@ i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data /*! \brief write data to I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[in] data: pointer to data buffer \param[in] length: number of bytes to write (1-255) \param[out] none \retval i2c_result_t: operation result */ -i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length) { +i2c_result_t i2c_write(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length) { i2c_state_t state = I2C_STATE_START; uint16_t timeout = 0; uint8_t retry_count = 0; @@ -727,8 +727,18 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint break; } - /* send register address */ - i2c_data_transmit(I2C0, reg_addr); + /* send register address, high byte first */ + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr >> 8)); + + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr & 0xFF)); state = I2C_STATE_TRANSMIT_DATA; break; @@ -814,12 +824,12 @@ i2c_result_t i2c_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint /*! \brief read data from I2C device with configurable length \param[in] slave_addr: slave device address (7-bit) - \param[in] reg_addr: register address + \param[in] reg_addr: register address (16-bit, high byte first) \param[out] data: pointer to data buffer \param[in] length: number of bytes to read (1-255) \retval i2c_result_t: operation result */ -i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t length) { +i2c_result_t i2c_read(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length) { i2c_state_t state = I2C_STATE_START; uint16_t timeout = 0; uint8_t retry_count = 0; @@ -937,8 +947,18 @@ i2c_result_t i2c_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8 break; } - /* send register address */ - i2c_data_transmit(I2C0, reg_addr); + /* send register address, high byte first */ + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr >> 8)); + + if(_i2c_wait_flag_timeout(I2C_FLAG_TBE) != I2C_RESULT_SUCCESS) { + state = I2C_STATE_ERROR; + break; + } + i2c_data_transmit(I2C0, (uint8_t)(reg_addr & 0xFF)); state = I2C_STATE_RESTART; break; diff --git a/Src/main.c b/Src/main.c index 8ef3410..9140353 100644 --- a/Src/main.c +++ b/Src/main.c @@ -41,6 +41,7 @@ OF SUCH DAMAGE. #include #include "i2c.h" #include "board_config.h" +#include "VL53L4CD_Driver.h" /*! \brief main function @@ -76,12 +77,35 @@ int main(void) i2c_bus_reset(); #endif + if (vl53l4cd_init()) { + vl53l4cd_start_ranging(); + printf("VL53L4CD ready\r\n"); + } else { + printf("VL53L4CD init failed\r\n"); + } + /* ========== Command Testing ========== */ /* ========== */ while(1){ command_process(); /* Process UART commands */ + + if (vl53l4cd_data_ready()) { + vl53l4cd_result_t result; + + vl53l4cd_clear_interrupt(); + if (vl53l4cd_get_result(&result)) { + printf("Distance: %u mm | status: %u | signal: %lu kcps | ambient: %lu kcps | sigma: %u mm\r\n", + result.distance_mm, + result.range_status, + (unsigned long)result.signal_rate_kcps, + (unsigned long)result.ambient_rate_kcps, + result.sigma_mm); + } + delay_ms(500); + } + delay_ms(10); } }