✨ feat(system-overview): 创建系统总览文档 - 描述项目背景与硬件平台配置 - 提供 FreeRTOS 任务拓扑表(任务优先级、栈大小、职责) - 详细说明系统启动序列和初始化依赖关系 - 绘制 2D/1D 状态机完整流程图 - 解释 TEMP_REQ 辅助通道工作机制 - 说明任务间同步机制(Frame_Ready_Flag、双缓冲 TX) ✨ feat(dvp-module-design): 创建 DVP 模块设计文档 - 提供 DVP 硬件连接引脚映射表 - 描述 DVP 时序配置(信号极性、工作模式) - 解释 DMA ping-pong 行缓冲机制和切换逻辑 - 说明 DVP IRQ 帧组装流程(STR_FRM/ROW_DONE) - 定义 FrameBuffer 数据格式和像素访问方式 - 说明 TMP 模式温度换算公式和字节序要求 ✨ feat(qdx-protocol-design): 创建 QDX 协议设计文档 - 描述完整 TLV 帧结构(FrameHeader + TLV + CRC) - 列出所有 Class/Type 映射表和用途说明 - 解释零拷贝 TX 缓冲区架构(HeadOffset 机制) - 说明分片机制和最大载荷限制 - 定义 Flags 字段各位含义和使用场景 ✨ feat(tcp-module-design): 创建 TCP 通信模块设计文档 - 描述双流连接架构(控制流 5511 / 数据流 5512) - 说明握手流程和连接建立时序 - 解释心跳机制和 TCP Keepalive 配置 - 描述配置下发与缓存机制 - 说明数据发送队列和背压处理策略 - 解释 WCHNET 网络栈驱动任务工作机制 ✨ feat(integration-guide): 创建对接集成指南 - 提供网络接入参数表(IP、端口、协议) - 详细说明握手流程和配置下发格式 - 提供 2D/1D 温度帧解析方法和示例代码 - 说明检测结果上报和 NG 响应机制 - 解释 TEMP_REQ 按需截图工作方式 - 列出错误码表和对接故障排查步骤
318 lines
13 KiB
Markdown
318 lines
13 KiB
Markdown
# 对接集成指南
|
||
|
||
> 文档版本:1.0 · 日期:2026-03-15 · 适用对象:ConfigServer 开发方
|
||
>
|
||
> 本文档描述如何对接 CH32V307 红外采集端,内容直接来源于固件代码,无需阅读源码即可完成对接开发。
|
||
|
||
---
|
||
|
||
## 1. 网络接入参数
|
||
|
||
### 1.1 固定参数
|
||
|
||
| 参数 | 值 |
|
||
|------|-----|
|
||
| MCU IP | `192.168.7.10` |
|
||
| MCU 子网掩码 | `255.255.255.0` |
|
||
| MCU 网关 | `192.168.7.1` |
|
||
| 以太网规格 | 1000M RGMII(标准以太网帧) |
|
||
|
||
### 1.2 服务端监听要求
|
||
|
||
Server 端需在 **192.168.7.50** 上监听两个 TCP 端口(MCU 主动连入):
|
||
|
||
| 端口 | 用途 | 连接方向 |
|
||
|------|------|---------|
|
||
| **5511** | 控制流(握手/配置/心跳) | MCU → Server |
|
||
| **5512** | 数据流(温度帧上报) | MCU → Server |
|
||
|
||
> 两个连接均为 MCU 主动发起,Server 侧需以 TCP Server(Listen)模式等待。
|
||
|
||
---
|
||
|
||
## 2. 握手流程
|
||
|
||
MCU 上电并完成内部初始化后,按以下顺序建立连接:
|
||
|
||
```
|
||
1. MCU 建立控制流 TCP 连接(Server 5511)
|
||
│
|
||
↓
|
||
2. MCU 发送 TYPE_HANDSHAKE(Class=CLASS_SYSTEM=0x04)
|
||
Value 结构(Handshake_t,46 字节):
|
||
├─ ProtocolVersion: uint16_t = 0x0200(协议 v2.0)
|
||
├─ DeviceUUID[16]: 6 字节 MAC 地址填入前 6 字节,其余填 0
|
||
├─ AuthToken[16]: 全零(当前版本不鉴权)
|
||
├─ HardwareVersion[8]: "CH32V307"(参考)
|
||
└─ FirmwareVersion[8]: 固件版本号
|
||
│
|
||
↓
|
||
3. Server 回复握手响应(同类型或 TYPE_ACK_PAYLOAD)
|
||
MCU 收到响应后标记控制流"就绪"
|
||
│
|
||
↓
|
||
4. MCU 建立数据流 TCP 连接(Server 5512)
|
||
│
|
||
↓
|
||
5. 系统就绪,开始接受配置并上报数据
|
||
```
|
||
|
||
**重要**:握手完成前,MCU 不会上报任何温度帧。如 Server 长时间未收到数据,请检查握手响应是否正确发送。
|
||
|
||
---
|
||
|
||
## 3. 配置下发
|
||
|
||
配置通过**控制流(5511)**发送,支持三种配置类型,可单独或组合发送。
|
||
|
||
### 3.1 Config2D — 2D 触发模式配置
|
||
|
||
**帧格式**:FrameHeader(Class=0x01, Type=0x22)+ TLV + Config2D_t
|
||
|
||
Config2D_t 全字段(按结构体内存顺序,均小端序):
|
||
|
||
| 字段 | 类型 | 偏移 | 说明 | 推荐初始值 |
|
||
|------|------|------|------|-----------|
|
||
| `Enabled` | uint8_t | 0 | 1=启用 2D 模式,0=禁用 | 1 |
|
||
| `IsLive` | uint8_t | 1 | 保留,填 0 | 0 |
|
||
| `DeviceId` | uint16_t | 2 | 设备 ID | 0 |
|
||
| `Width` | uint16_t | 4 | 传感器宽度(256) | 256 |
|
||
| `Height` | uint16_t | 6 | 传感器高度(192) | 192 |
|
||
| `Fps` | uint8_t | 8 | 期望帧率(参考值,DVP 硬件控制) | 25 |
|
||
| `Exposure` | uint32_t | 9 | 曝光时间(传感器支持时有效) | 0 |
|
||
| `AutoExposure` | uint8_t | 13 | 自动曝光(0/1) | 0 |
|
||
| `MaskEnabled` | uint8_t | 14 | 掩码使能 | 0 |
|
||
| `MaskThreshold` | int16_t | 15 | 掩码阈值(0.1°C/LSB) | 0 |
|
||
| `MaskWidth` | uint16_t | 17 | 掩码宽度 | 0 |
|
||
| `MaskHeight` | uint16_t | 19 | 掩码高度 | 0 |
|
||
| `Angle` | int16_t | 21 | 旋转角度(当前未使用) | 0 |
|
||
| `TargetWidth` | uint16_t | 23 | 输出 ROI 宽度(像素) | 64 |
|
||
| `TargetHeight` | uint16_t | 25 | 输出 ROI 高度(像素) | 64 |
|
||
| `TriggerMode` | uint8_t | 27 | **0=内部温度触发,1=外部 GPIO 触发** | 1 |
|
||
| `TriggerGpioLine` | uint8_t | 28 | 外部触发 GPIO 编号(参考,MCU 固定 PA15) | 0 |
|
||
| `TriggerDelayMs` | uint16_t | 29 | 触发后延迟采集的时间(ms) | 0 |
|
||
| `TriggerBurstCount` | uint8_t | 31 | 每次触发连拍帧数 | 3 |
|
||
| `TriggerInternalIntervalMs` | uint16_t | 32 | 连拍帧间隔(ms) | 200 |
|
||
| `TriggerTemperatureThreshold` | int16_t | 34 | 内部触发温度阈值(0.1°C/LSB),如 `800` = 80.0°C | 800 |
|
||
| `TriggerDebounceIntervalMs` | uint16_t | 36 | 外部触发防抖时间(ms) | 50 |
|
||
| `TriggerCondition` | uint8_t | 38 | 内部触发判据:**0=ROI 均值,1=ROI 最大值** | 1 |
|
||
| `TriggerRoiX` | uint16_t | 39 | 触发检测 ROI 左上角 X | 0 |
|
||
| `TriggerRoiY` | uint16_t | 41 | 触发检测 ROI 左上角 Y | 0 |
|
||
| `TriggerRoiW` | uint16_t | 43 | 触发检测 ROI 宽度(0=全帧) | 256 |
|
||
| `TriggerRoiH` | uint16_t | 45 | 触发检测 ROI 高度(0=全帧) | 192 |
|
||
| `NGioDelay` | uint16_t | 47 | NG 输出 GPIO 脉宽(ms) | 200 |
|
||
| `OutputGpioLine` | uint8_t | 49 | 输出 GPIO 编号(固定 PA8) | 0 |
|
||
| `AlarmGpioLine` | uint8_t | 50 | 告警 GPIO(当前未使用) | 0 |
|
||
| `AlarmHoldMs` | uint16_t | 51 | 告警持续时间(当前未使用) | 0 |
|
||
| `StoreNgImagesOnly` | uint8_t | 53 | 仅存储 NG 图像(当前未使用) | 0 |
|
||
| `TrainingEnabled` | uint8_t | 54 | 训练模式(当前未使用) | 0 |
|
||
| `TrainingSampleThreshold` | uint16_t | 55 | 训练样本阈值(当前未使用) | 0 |
|
||
| `ProcessingTimeoutMs` | uint16_t | 57 | 处理超时(ms,当前未使用) | 0 |
|
||
| `MaxProcessingQueueSize` | uint8_t | 59 | 最大处理队列(当前未使用) | 0 |
|
||
| `Reserved` | uint8_t | 60 | 保留,填 0 | 0 |
|
||
|
||
### 3.2 Config1D — 1D 采集模式配置
|
||
|
||
**帧格式**:FrameHeader(Class=0x01, Type=0x23)+ TLV + Config1D_t
|
||
|
||
Config1D_t 全字段:
|
||
|
||
| 字段 | 类型 | 偏移 | 说明 | 推荐初始值 |
|
||
|------|------|------|------|-----------|
|
||
| `Enabled` | uint8_t | 0 | 1=启用 1D 模式,0=禁用 | 0 |
|
||
| `RunMode` | uint8_t | 1 | **0=停止采集,1=运行** | 1 |
|
||
| `TriggerType` | uint8_t | 2 | **1=内部(连续热帧),2=外部(PA15 GPIO)** | 1 |
|
||
| `BufferSize` | uint16_t | 3 | 采集缓冲区大小(样本数,最大 512) | 200 |
|
||
| `TriggerTempLimit` | int16_t | 5 | 触发温度阈值(0.1°C/LSB) | 500 |
|
||
| `StartPointsToRemove` | uint16_t | 7 | 保留 | 0 |
|
||
| `ReferenceLength` | uint16_t | 9 | 参考长度(保留) | 0 |
|
||
| `HighTimerLimit` | uint16_t | 11 | 外部触发防抖等待时间(ms) | 100 |
|
||
| `TimerCLimit` | uint16_t | 13 | 保留 | 0 |
|
||
| `NgCountLimit` | uint8_t | 15 | 触发后连续低温帧数达此值则停止采集 | 5 |
|
||
| `LSizeStart` | uint16_t | 16 | 发送时去掉头部样本数(切片) | 0 |
|
||
| `RSizeStart` | uint16_t | 18 | 发送时去掉尾部样本数(切片) | 0 |
|
||
| `NGioDelay` | uint16_t | 20 | NG 输出脉宽(ms,1D 使用) | 200 |
|
||
| `OutputGpioLine` | uint8_t | 22 | 输出 GPIO(固定 PA8) | 0 |
|
||
| `AlarmGpioLine` | uint8_t | 23 | 告警 GPIO(未使用) | 0 |
|
||
| `AlarmHoldMs` | uint16_t | 24 | 告警持续(未使用) | 0 |
|
||
|
||
> **2D 与 1D 互斥**:`Config2D.Enabled=1` 时 MCU 进入 2D 模式;`Config1D.Enabled=1` 且 `Config2D.Enabled=0` 时进入 1D 模式。同时置 1 时 2D 优先。
|
||
|
||
---
|
||
|
||
## 4. 温度帧接收
|
||
|
||
温度帧通过**数据流(5512)**接收,帧类型为 `TYPE_TEMP_FRAME(0x10)`,Class=`CLASS_DATA(0x02)`。
|
||
|
||
### 4.1 2D 温度帧解析
|
||
|
||
Value 布局(TemperatureFrameHeader_t + 像素数组):
|
||
|
||
```
|
||
[TemperatureFrameHeader_t] 18 字节
|
||
├─ FrameNumber: uint32_t LE — 帧序号(与 DVP 帧计数对应)
|
||
├─ Width: uint16_t LE — 输出 ROI 宽度(TargetWidth 或有效宽度)
|
||
├─ Height: uint16_t LE — 输出 ROI 高度
|
||
├─ MinTemp: int16_t LE — ROI 内最低温度(0.1°C/LSB)
|
||
├─ MaxTemp: int16_t LE — ROI 内最高温度(0.1°C/LSB)
|
||
├─ AvgTemp: int16_t LE — ROI 内平均温度(0.1°C/LSB)
|
||
├─ RoiTemp: int16_t LE — TriggerRoi 区域特征温度(0.1°C/LSB)
|
||
├─ FrameType: uint8_t — 帧类型标识(0x01=触发帧,0x02=TEMP_REQ 帧)
|
||
├─ Status: uint8_t — 状态(0=正常)
|
||
├─ Is2D: uint8_t — 1=2D 矩阵帧
|
||
└─ Reserved: uint8_t — 填 0
|
||
|
||
[像素数组] Width × Height × 2 字节
|
||
每个像素:uint16_t 小端序
|
||
排列:行优先(row0 col0, row0 col1, ..., row0 colW-1, row1 col0, ...)
|
||
单位:0.1°C/LSB,转为摄氏度 = 原始值 × 0.1
|
||
```
|
||
|
||
**Python 解析示例**:
|
||
|
||
```python
|
||
import struct
|
||
|
||
def parse_2d_frame(data: bytes):
|
||
hdr_fmt = '<IHHhhhhhBBBB' # TemperatureFrameHeader_t
|
||
hdr_size = struct.calcsize(hdr_fmt)
|
||
frame_num, w, h, min_t, max_t, avg_t, roi_t, _, ft, st, is2d, _ = struct.unpack_from(hdr_fmt, data)
|
||
pixels_raw = struct.unpack_from(f'<{w*h}H', data, hdr_size)
|
||
pixels_celsius = [v * 0.1 for v in pixels_raw]
|
||
# pixels_celsius[row * w + col] = 第 row 行、第 col 列的温度(°C)
|
||
return {
|
||
'frame_num': frame_num, 'width': w, 'height': h,
|
||
'max_temp': max_t * 0.1, 'min_temp': min_t * 0.1,
|
||
'pixels': pixels_celsius
|
||
}
|
||
```
|
||
|
||
### 4.2 1D 温度帧解析
|
||
|
||
当 `Is2D=0` 时,像素数组替换为 1D 样本序列。
|
||
|
||
Value 布局(同 TemperatureFrameHeader_t,但 Width=点数,Height=1):
|
||
|
||
```
|
||
[TemperatureFrameHeader_t] 18 字节
|
||
├─ Width: 实际有效样本点数 N
|
||
├─ Height: 1
|
||
└─ Is2D: 0
|
||
|
||
[样本数组] N × 4 字节
|
||
每个样本(TempPoint1D_t):
|
||
├─ TimeOffset: uint16_t LE — 相对采集开始的 ms 偏移
|
||
└─ Temperature: uint16_t LE — 对应时刻温度(0.1°C/LSB)
|
||
```
|
||
|
||
**Python 解析示例**:
|
||
|
||
```python
|
||
def parse_1d_frame(data: bytes):
|
||
hdr_fmt = '<IHHhhhhhBBBB'
|
||
hdr_size = struct.calcsize(hdr_fmt)
|
||
frame_num, n, _, min_t, max_t, avg_t, _, _, ft, st, is2d, _ = struct.unpack_from(hdr_fmt, data)
|
||
points = []
|
||
for i in range(n):
|
||
t_off, temp = struct.unpack_from('<HH', data, hdr_size + i*4)
|
||
points.append({'time_ms': t_off, 'temp_c': temp * 0.1})
|
||
return {'frame_num': frame_num, 'points': points}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 检测结果下发与 NG 输出
|
||
|
||
### 5.1 下发检测结果
|
||
|
||
Server 在图像处理完成后,通过**控制流(5511)**发送 `TYPE_DETECTION_RESULT(0x40)`(Class=CLASS_CONTROL=0x01)。
|
||
|
||
Value 结构(DetectionResult_t,8 字节):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `FrameNumber` | uint32_t LE | 对应触发该结果的帧号 |
|
||
| `Result` | uint8_t | **0=NG(不合格),1=OK(合格)** |
|
||
| `Reserved[3]` | uint8_t[3] | 填 0 |
|
||
|
||
### 5.2 MCU NG 动作
|
||
|
||
收到 `Result=0`(NG)时:
|
||
- `PA8` 输出高电平
|
||
- 持续 `Config2D.NGioDelay`(默认 200 ms)后自动恢复低电平
|
||
- `NGioDelay` 可通过 Config2D 配置调整(1D 模式使用 Config1D.NGioDelay)
|
||
|
||
> 外部报警装置应接在 PA8,高电平有效,持续时间可配置。
|
||
|
||
---
|
||
|
||
## 6. TEMP_REQ 按需截图
|
||
|
||
Server 可随时通过控制流请求当前热场快照,无需等待触发。
|
||
|
||
**请求帧**:TYPE_CONFIG_COMMON(0x20,Class=CLASS_CONTROL=0x01)中包含 TEMP_REQ 字段,或通过专用命令触发(具体字段由 `TcpLogic` 内部解析),携带 `is2dRequest` 标志(1=请求 2D 帧,0=请求 1D 帧)。
|
||
|
||
**MCU 响应**:
|
||
- 下一个业务循环取当前 FrameBuffer 的最新帧
|
||
- `is2d=1`:执行 `Preprocess_Execute`,返回 ROI 裁切后的 2D 温度矩阵(FrameType=0x02)
|
||
- `is2d=0`:返回中心行 **30 个等间距采样点**,时间偏移仿真 0~600 ms(非真实时间序列)
|
||
- 仅在对应模式(`Config2D.Enabled` / `Config1D.Enabled`)为 1 时响应,否则忽略
|
||
|
||
---
|
||
|
||
## 7. 错误码参考
|
||
|
||
| 错误码 | 值 | 触发场景 | 建议处理 |
|
||
|--------|-----|---------|---------|
|
||
| `ERR_CRC` | `0x1001` | 帧 CRC 校验失败 | 检查发送数据是否正确,重发 |
|
||
| `ERR_VERSION` | `0x1002` | 协议版本不匹配(非 0x20) | 检查 Version 字段 |
|
||
| `ERR_LENGTH` | `0x1003` | FrameHeader.Length 与实际帧长不符 | 检查帧构造逻辑 |
|
||
| `ERR_AUTH` | `0x2001` | AuthToken 不匹配 | 当前版本 AuthToken 全零,不应触发 |
|
||
| `ERR_BUSY` | `0x2002` | 设备忙,拒绝处理 | 等待 100 ms 后重试 |
|
||
| `ERR_DEV_ID_CONFLICT` | `0x2003` | DevID 与已注册设备冲突 | 检查 DevID 分配 |
|
||
| `ERR_PARAM` | `0x3001` | 配置字段值非法 | 检查配置结构体字段范围 |
|
||
|
||
---
|
||
|
||
## 8. 对接故障排查
|
||
|
||
### 8.1 MCU 上电后无连接
|
||
|
||
- 检查 MCU IP 是否为 `192.168.7.10`,Server 是否在 `192.168.7.50`
|
||
- 确保 Server 在 5511 和 5512 端口上已 Listen
|
||
- 检查以太网连接(MCU 使用 1000M RGMII,需 1 Gbps 交换机或兼容网卡)
|
||
- 调试串口(USART3,921600)会打印 `TCP Connected, socket X` 确认连接
|
||
|
||
### 8.2 握手成功但无数据帧
|
||
|
||
- 确认已通过控制流下发 `Config2D`(`Enabled=1`)或 `Config1D`(`Enabled=1`)
|
||
- 查看调试串口是否打印 `TRIGGER frm=N`(2D 模式)或 `1D: internal trigger start`(1D 模式)
|
||
- 若无触发,检查 `TriggerTemperatureThreshold` 是否过高
|
||
|
||
### 8.3 数据帧偶发丢失
|
||
|
||
- 检查 Server 是否及时 ACK,TCP 窗口是否足够
|
||
- 调试串口打印 `2D TX fail` 或 `1D SEND ... ret=-1` 表示 MCU 侧发送失败(队列满或连接异常)
|
||
|
||
### 8.4 温度值异常(偏高或偏低约 10 倍)
|
||
|
||
- 确认传感器已配置为 **TMP 模式**(非 Y16 模式)。参见 `Doc/Mini212G2预配置指南.md`
|
||
- TMP 模式输出值已是 0.1°C/LSB,解析时乘以 0.1 即为摄氏度
|
||
|
||
### 8.5 NG 输出不动作
|
||
|
||
- 确认发送的 DetectionResult.Result = 0(NG),而非 1(OK)
|
||
- 检查 `Config2D.NGioDelay` 是否已配置(0 时默认 200 ms)
|
||
- 核查 PA8 GPIO 连接是否正确
|
||
|
||
---
|
||
|
||
## 9. 相关文档
|
||
|
||
| 文档 | 内容 |
|
||
|------|------|
|
||
| [系统总览.md](系统总览.md) | MCU 固件架构总览 |
|
||
| [QDX协议设计.md](QDX协议设计.md) | 帧结构详细说明(供深度对接参考) |
|
||
| [TCP通信模块设计.md](TCP通信模块设计.md) | MCU 侧连接管理实现细节 |
|
||
| [Mini212G2预配置指南.md](../Mini212G2预配置指南.md) | 传感器 TMP 模式配置 |
|