✨ 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 按需截图工作方式 - 列出错误码表和对接故障排查步骤
13 KiB
对接集成指南
文档版本: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 解析示例:
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 解析示例:
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 | MCU 固件架构总览 |
| QDX协议设计.md | 帧结构详细说明(供深度对接参考) |
| TCP通信模块设计.md | MCU 侧连接管理实现细节 |
| Mini212G2预配置指南.md | 传感器 TMP 模式配置 |