✨ 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 按需截图工作方式 - 列出错误码表和对接故障排查步骤
7.2 KiB
7.2 KiB
QDX 协议设计文档
文档版本:1.0 · 日期:2026-03-15 · 状态:已发布
1. 模块概述
QDXnetworkStack 是本固件使用的应用层通信协议栈,基于 TLV(Type-Length-Value)帧格式,运行在 TCP/IP 之上。协议设计重点:
- 零拷贝 TX:预留帧头空间(HeadOffset),像素数据直接写入发送缓冲区,帧头在发送前原地写入
- 双流架构:控制流(5511)与数据流(5512)职责分离
- 平台无关:所有结构体
#pragma pack(push, 1),1 字节对齐,可在任意平台解析
源文件:Middle/QDXnetworkStack/
2. 帧结构
2.1 完整帧布局
┌─────────────────────────────────────────────────────────┐
│ FrameHeader(16 字节) │
│ TLV Header(3 字节) │
│ Value(N 字节) │
│ CRC16(2 字节,Modbus CRC16,覆盖 FrameHeader+TLV+Value)│
└─────────────────────────────────────────────────────────┘
总帧长 = 16 + 3 + N + 2 = N + 21 字节
2.2 FrameHeader(16 字节)
typedef struct {
uint16_t Magic; /* [0-1] 0x55AA — 帧同步魔数 */
uint8_t Version; /* [2] 0x20 — 协议版本 v2.0 */
uint16_t Length; /* [3-4] 整帧字节数(含 Header,不含 CRC) */
uint16_t Sequence; /* [5-6] 单调递增帧序号(LE) */
uint32_t Timestamp; /* [7-10] Unix 时间戳或 ms 计数(LE) */
uint8_t Source; /* [11] 0x01=MCU,0x02=Server */
uint16_t DevID; /* [12-13]设备 ID(LE) */
uint8_t Class; /* [14] 消息类别 */
uint8_t Flags; /* [15] 控制标志位 */
} FrameHeader_t; /* 共 16 字节,1 字节对齐 */
2.3 TLV Header(3 字节)
typedef struct {
uint8_t Type; /* [0] 帧类型 */
uint16_t Length; /* [1-2] Value 区域字节数(LE) */
} TLV_t;
3. Class 与 Type 映射表
3.1 消息类别(Class)
| 常量 | 值 | 说明 |
|---|---|---|
CLASS_CONTROL |
0x01 |
配置 / 控制命令(服务器 → MCU) |
CLASS_DATA |
0x02 |
实时数据上报(MCU → 服务器) |
CLASS_RESPONSE |
0x03 |
ACK / NACK / 错误响应 |
CLASS_SYSTEM |
0x04 |
握手 / 心跳 / 系统同步 |
3.2 帧类型(Type)
| 常量 | 值 | 方向 | 说明 |
|---|---|---|---|
TYPE_HANDSHAKE |
0x01 |
双向 | 握手,Class=SYSTEM |
TYPE_HEARTBEAT |
0x02 |
双向 | 心跳保活,Class=SYSTEM |
TYPE_SYNC_TIME |
0x03 |
Server→MCU | 时间同步 |
TYPE_DEVID_ASSIGN |
0x05 |
Server→MCU | 设备 ID 分配 |
TYPE_TEMP_FRAME |
0x10 |
MCU→Server | 温度帧(2D/1D),Class=DATA |
TYPE_RAW_FRAME |
0x11 |
MCU→Server | 原始帧(预留) |
TYPE_CONFIG_COMMON |
0x20 |
Server→MCU | 公共配置 |
TYPE_CONFIG_2D |
0x22 |
Server→MCU | 2D 模式配置 |
TYPE_CONFIG_1D |
0x23 |
Server→MCU | 1D 模式配置 |
TYPE_ACK_PAYLOAD |
0x30 |
双向 | ACK 响应体 |
TYPE_DETECTION_RESULT |
0x40 |
Server→MCU | 检测结果(含 NG 判定) |
3.3 Flags 字段位定义
| 位掩码 | 常量 | 说明 |
|---|---|---|
0x03 |
FLAG_PRIORITY_MASK |
优先级 0-3(0=最低) |
0x04 |
FLAG_COMPRESSED |
压缩标志(当前固件未使用) |
0x08 |
FLAG_ENCRYPTED |
加密标志(当前固件未使用) |
0x10 |
FLAG_ACK_REQ |
要求接收方回复 ACK |
0x20 |
FLAG_LAST_FRAGMENT |
分片末片标志 |
4. 零拷贝 TX 架构
4.1 TcpTxBuffer_t 结构
typedef struct {
uint8_t *pBuffer; /* 分配的内存起点 */
uint32_t TotalCapacity; /* 总容量 = 9216 字节 */
uint32_t HeadOffset; /* 预留帧头空间 = 64 字节 */
uint32_t ValidPayloadLen;/* 当前有效 Value 数据长度 */
} TcpTxBuffer_t;
4.2 缓冲区内存布局
pBuffer
│
├──[0 ... 63]────────────────── HeadOffset(64 字节预留区)
│ ← 发送前:写入 FrameHeader(16B) + TLV_Header(3B) = 19 字节
│ 从 HeadOffset - 19 = 偏移 45 处开始写入(帧头向前填充)
│
├──[64 ... 64+ValidPayloadLen-1]── Value 数据(Preprocess 写入)
│ ← 图像像素 / 1D 样本 / 其他 payload
│
└──[64+ValidPayloadLen ... 9215] ── 未使用空间
+ [尾部 2 字节] CRC16(追加在 Value 之后)
4.3 有效载荷容量计算
最大可用 Value 大小
= TotalCapacity(9216) - HeadOffset(64) - CRC(2) - TLV_Header(3) - FrameHeader(16)
= 9131 字节
对于 2D 温度帧(每像素 2 字节):
最多传输 9131 / 2 = 4565 个像素(约 67×67 ROI)
4.4 双缓冲设计
/* main.c 中声明 */
uint8_t g_TxNetBuffer_A_Mem[9216];
uint8_t g_TxNetBuffer_B_Mem[9216];
TcpTxBuffer_t g_TxNetBuffer_A = { .pBuffer = g_TxNetBuffer_A_Mem, .TotalCapacity = 9216, .HeadOffset = 64 };
TcpTxBuffer_t g_TxNetBuffer_B = { .pBuffer = g_TxNetBuffer_B_Mem, .TotalCapacity = 9216, .HeadOffset = 64 };
切换逻辑:use_buffer_A 标志在每次调用发送函数时取反,保证本帧发送入队后下一帧可立即填写另一个缓冲区。
5. 分片机制
5.1 分片限制
| 参数 | 值 | 说明 |
|---|---|---|
MAX_FRAGMENT_PAYLOAD |
1400 字节 | 受 TCP MSS=1460 制约(1460 - HEADER=1400) |
| TX 缓冲区容量 | 9216 字节 | 单次发送通常不超过此限制,无需分片 |
5.2 分片标志
末片帧的 Flags 字段 FLAG_LAST_FRAGMENT(0x20) 置位,接收端以此判断分片结束。中间片段该位为 0。
当前固件实践:绝大多数 2D 温度帧(最大 9131 字节)直接作为单个 TCP send 调用发送。WCHNET 底层在必要时分拆为多个 TCP 段,但协议层不显式分片。
6. Sequence 字段规则
Sequence字段为uint16_t,每发送一帧单调递增- 溢出后自然回滚(65535 → 0)
- 接收端 不强制要求连续,但可通过 Sequence 检测丢包(非连续 = 有帧丢失)
- 不同流(控制流 / 数据流)的 Sequence 互相独立计数
7. 错误码
| 常量 | 值 | 触发场景 |
|---|---|---|
ERR_NONE |
0x0000 |
无错误 |
ERR_CRC |
0x1001 |
CRC 校验失败 |
ERR_VERSION |
0x1002 |
协议版本不匹配 |
ERR_LENGTH |
0x1003 |
帧长度字段与实际不符 |
ERR_AUTH |
0x2001 |
认证失败(AuthToken 不匹配) |
ERR_BUSY |
0x2002 |
设备忙,拒绝处理 |
ERR_DEV_ID_CONFLICT |
0x2003 |
DevID 冲突 |
ERR_PARAM |
0x3001 |
参数非法(配置值越界等) |
8. 相关文档
| 文档 | 内容 |
|---|---|
| 系统总览.md | 双缓冲 TX 策略、任务调度 |
| TCP通信模块设计.md | TcpLogic_BuildAndSendTemperatureFrame 调用链 |
| 对接集成指南.md | 外部接入方协议帧构造示例 |