# 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 字节) ```c 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 字节) ```c 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 结构 ```c 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 双缓冲设计 ```c /* 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](系统总览.md) | 双缓冲 TX 策略、任务调度 | | [TCP通信模块设计.md](TCP通信模块设计.md) | `TcpLogic_BuildAndSendTemperatureFrame` 调用链 | | [对接集成指南.md](对接集成指南.md) | 外部接入方协议帧构造示例 |