📝 docs(design): 新增系统设计文档集

 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 按需截图工作方式
- 列出错误码表和对接故障排查步骤
This commit is contained in:
zhoujie 2026-03-15 19:17:41 +08:00
parent c347c988f2
commit b69717b964
19 changed files with 1818 additions and 0 deletions

View File

@ -0,0 +1,198 @@
# DVP 模块设计文档
> 文档版本1.0 · 日期2026-03-15 · 状态:已发布
---
## 1. 模块概述
DVPDigital Video Port模块负责将 Mini212G2 传感器输出的 8-bit CMOS 并行数据转换为内存中可访问的帧缓冲FrameBuffer。整个采集链路通过硬件 DMA ping-pong 和快速中断实现CPU 占用极低。
源文件:`Debug/dvp.c` / `Debug/dvp.h`
---
## 2. DVP 硬件连接与引脚映射
所有 DVP 引脚配置为 **浮空输入GPIO_Mode_IN_FLOATING**
| 引脚 | DVP 信号 | 说明 |
|------|----------|------|
| PA4 | D0 | 数据位 0 |
| PA5 | D1 | 数据位 1 |
| PA6 | D2 | 数据位 2 |
| PA9 | D3 | 数据位 3 |
| PA10 | D4 | 数据位 4 |
| PC8 | D5 | 数据位 5 |
| PC9 | D6 | 数据位 6 |
| PC11 | D7 | 数据位 7 |
| PB3 | VSYNC | 帧同步信号 |
| PB8 | HSYNC | 行同步信号 |
| PB9 | PIXCLK | 像素时钟 |
---
## 3. DVP 时序配置
`DVP_Init()` 中完成以下配置:
### 3.1 工作模式
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 数据宽度 | `RB_DVP_D8_MOD` | 8-bit 并行数据 |
| 采集模式 | `Video_Mode` | 连续帧采集(非快照模式) |
| 捕获控制 | 持续捕获CR1 CM=0 | 不限帧数 |
### 3.2 信号极性
| 信号 | 寄存器位 | 配置值 | 说明 |
|------|----------|--------|------|
| VSYNC | `RB_DVP_V_POLAR` | **1高有效** | DIGITAL_FIELD_VALID 高电平期间为有效帧行 |
| HSYNC | `RB_DVP_H_POLAR` | 0高有效| 标准高有效行同步 |
| PCLK | `RB_DVP_P_POLAR` | 0上升沿采样| 正常时钟极性 |
> **与手册对应**Mini212G2 FIELD_VALID 高电平为有效期,因此 V_POLAR 置 1高有效与硬件手册时序图一致。
### 3.3 图像尺寸配置
| 寄存器 | 值 | 说明 |
|--------|-----|------|
| `DVP->COL_NUM` | 512`BYTES_PER_LINE` | 每行字节数 = 256 像素 × 2 字节 |
| `DVP->ROW_NUM` | 1 | 每行触发一次 ROW_DONE 中断 |
---
## 4. DMA ping-pong 行缓冲机制
### 4.1 缓冲区配置
```c
__attribute__((aligned(4))) uint8_t DMA_LineBuf0[512]; /* DMA_BUF0 */
__attribute__((aligned(4))) uint8_t DMA_LineBuf1[512]; /* DMA_BUF1 */
```
两个缓冲区分别挂载在 `DVP->DMA_BUF0``DVP->DMA_BUF1`DVP 硬件在两者之间自动切换,无需 CPU 干预。
### 4.2 缓冲切换逻辑
ROW_DONE 中断触发时,硬件已切换到 **下一个缓冲**开始写入。因此正确读取方法为:
```c
/* BUF_TOG=1 表示 DMA 正在写 BUF0此时应读 BUF1刚写完的那个 */
uint8_t *src = (DVP->CR1 & RB_DVP_BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1;
```
即:`BUF_TOG` 状态位指示**当前正在写入的缓冲**,应读取**另一个**刚刚完成写入的缓冲。
### 4.3 尺寸约束
修改传感器分辨率时,必须同时更新以下三个宏(`dvp.h`
```c
#define SENSOR_WIDTH 256 /* 像素列数 */
#define SENSOR_HEIGHT 192 /* 像素行数 */
#define BYTES_PER_LINE 512 /* SENSOR_WIDTH × 2 字节/像素 */
```
---
## 5. DVP IRQ 帧组装逻辑
中断处理函数声明:
```c
void DVP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
```
`WCH-Interrupt-fast` 属性使中断跳过通用寄存器入栈显著降低中断延迟确保在下一行像素时钟到来Mini212G2 @ 25 Hz每行约 42.67 µs前完成 512 B 的 `memcpy`
### 5.1 帧同步STR_FRM 中断)
VSYNC 上升沿触发,是帧同步的**唯一锚点**
```c
if (DVP->IFR & RB_DVP_IF_STR_FRM) {
DVP->IFR = RB_DVP_IF_STR_FRM;
current_line_idx = 0; /* 重置行计数器 */
dvp_frame_count++;
}
```
### 5.2 行完成ROW_DONE 中断)
每行像素传输完成后触发:
```c
if (DVP->IFR & RB_DVP_IF_ROW_DONE) {
DVP->IFR = RB_DVP_IF_ROW_DONE;
uint8_t *src = (DVP->CR1 & RB_DVP_BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1;
uint32_t idx = current_line_idx;
current_line_idx++;
if (idx < SENSOR_HEIGHT) {
memcpy(FrameBuffer[idx], src, BYTES_PER_LINE);
if (idx == SENSOR_HEIGHT - 1) { /* 第 191 行完成 */
Frame_Ready_Flag = 1; /* 通知业务任务 */
Ready_Frame_Count = dvp_frame_count; /* 记录帧号 */
}
}
}
```
> **注意:先读 `idx` 再递增**,避免 off-by-one。STR_FRM 将 `idx` 置 0首次 ROW_DONE 时 `idx=0`,将数据写入 `FrameBuffer[0]`,符合预期。
---
## 6. FrameBuffer 数据格式
```c
__attribute__((aligned(4))) uint8_t FrameBuffer[SENSOR_HEIGHT][BYTES_PER_LINE];
/* = uint8_t[192][512] */
```
### 6.1 像素访问方式
FrameBuffer 声明为 `uint8_t` 数组,实际存储 `uint16_t` 小端序温度值:
```c
/* 访问第 row 行、第 col 列的温度 */
uint16_t *pixels = (uint16_t *)FrameBuffer;
uint16_t raw_val = pixels[row * SENSOR_WIDTH + col];
```
### 6.2 温度换算TMP 模式)
```
温度°C= raw_val × 0.1
```
例:`raw_val = 370``37.0°C`
### 6.3 字节序说明
CH32V307 为**小端序**Little-Endian
| 偏移 | 内容 |
|------|------|
| `FrameBuffer[row][col*2 + 0]` | 温度低字节 |
| `FrameBuffer[row][col*2 + 1]` | 温度高字节 |
传感器必须配置为 **CMOS8(LSB) 模式**(低字节先发),否则字节顺序反转,温度值错误。
### 6.4 内存布局摘要
| 参数 | 值 |
|------|-----|
| 总大小 | 192 × 512 = 98,304 字节 ≈ 96 KB |
| 每行 | 512 字节 = 256 像素 |
| 像素格式 | uint16_t小端0.1°C/LSBTMP 模式绝对温度 |
| 对齐 | 4 字节对齐(`__attribute__((aligned(4)))` |
---
## 7. 相关文档
| 文档 | 内容 |
|------|------|
| [系统总览.md](系统总览.md) | FreeRTOS 任务拓扑、Frame_Ready_Flag 同步机制 |
| [Mini212G2预配置指南.md](../Mini212G2预配置指南.md) | CMOS8(LSB) + TMP 模式配置命令 |

View File

@ -0,0 +1,202 @@
# QDX 协议设计文档
> 文档版本1.0 · 日期2026-03-15 · 状态:已发布
---
## 1. 模块概述
QDXnetworkStack 是本固件使用的应用层通信协议栈,基于 TLVType-Length-Value帧格式运行在 TCP/IP 之上。协议设计重点:
- **零拷贝 TX**预留帧头空间HeadOffset像素数据直接写入发送缓冲区帧头在发送前原地写入
- **双流架构**控制流5511与数据流5512职责分离
- **平台无关**:所有结构体 `#pragma pack(push, 1)`1 字节对齐,可在任意平台解析
源文件:`Middle/QDXnetworkStack/`
---
## 2. 帧结构
### 2.1 完整帧布局
```
┌─────────────────────────────────────────────────────────┐
│ FrameHeader16 字节) │
│ TLV Header3 字节) │
│ ValueN 字节) │
│ CRC162 字节Modbus CRC16覆盖 FrameHeader+TLV+Value
└─────────────────────────────────────────────────────────┘
```
总帧长 = 16 + 3 + N + 2 = **N + 21** 字节
### 2.2 FrameHeader16 字节)
```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=MCU0x02=Server */
uint16_t DevID; /* [12-13]设备 IDLE */
uint8_t Class; /* [14] 消息类别 */
uint8_t Flags; /* [15] 控制标志位 */
} FrameHeader_t; /* 共 16 字节1 字节对齐 */
```
### 2.3 TLV Header3 字节)
```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/1DClass=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-30=最低) |
| `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]────────────────── HeadOffset64 字节预留区)
│ ← 发送前:写入 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_FRAGMENT0x20` 置位,接收端以此判断分片结束。中间片段该位为 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) | 外部接入方协议帧构造示例 |

View File

@ -0,0 +1,228 @@
# TCP 通信模块设计文档
> 文档版本1.0 · 日期2026-03-15 · 状态:已发布
---
## 1. 模块概述
TCP 通信模块(`TcpLogic`管理与上位机ConfigServer的所有网络连接包括连接建立、握手认证、配置接收与缓存、温度帧发送和断线重连。模块基于 WCHNET 协议栈构建,通过 QDX TLV 协议封装数据。
源文件:`Middle/QDXnetworkStack/qdx_tcp_logic.c` / `qdx_tcp_logic.h`
---
## 2. 双流连接架构
### 2.1 职责分工
| 流 | 端口 | 方向 | 职责 |
|----|------|------|------|
| 控制流 | srcport=**5511** | MCU 主动连接 Server | 握手、配置下发、心跳、TEMP_REQ、DetectionResult 上报 |
| 数据流 | desport=**5512** | MCU 主动连接 Server | 温度帧数据上报TYPE_TEMP_FRAME |
两流独立管理,数据流断开不影响控制流的配置和心跳功能。
```c
/* main.c 中的地址配置 */
u8 IPAddr[4] = {192, 168, 7, 10}; /* MCU IP */
u8 DESIP[4] = {192, 168, 7, 50}; /* Server IP */
u16 desport = 5512; /* 数据流目标端口 */
u16 srcport = 5511; /* 控制流源端口 */
```
### 2.2 连接建立时序
```
TcpLogic_Start()
├─ 1. 控制流 TCP Connect → Server:5511
│ 等待 SINT_STAT_CONNECT 事件
├─ 2. 发送 TYPE_HANDSHAKEClass=CLASS_SYSTEM
│ 含 ProtocolVersion=0x0200DeviceUUID=MAC 地址6 字节扩展为 UUID 格式)
│ AuthToken=全零(当前不鉴权)
├─ 3. 等待服务器握手响应
│ 收到响应后标记控制流"就绪"
└─ 4. 数据流 TCP Connect → Server:5512
连接成功后数据流"就绪"
```
---
## 3. 心跳与 Keepalive 机制
### 3.1 应用层心跳
`TcpLogic` 内部定时通过控制流发送 `TYPE_HEARTBEAT` 帧,包含:
```c
typedef struct {
uint32_t UpTime; /* 设备运行时间ms */
uint8_t CpuLoad; /* CPU 负载(预留,当前填 0 */
uint8_t MemUsage; /* 内存使用率(预留,当前填 0 */
} Heartbeat_t; /* 6 字节 */
```
### 3.2 TCP Keepalive
`main()` 中配置:
```c
struct _KEEP_CFG cfg = {
.idle = 20000, /* 空闲 20 秒后开始发送探测包 */
.interval = 15000, /* 每次探测间隔 15 秒 */
.count = 9 /* 最多 9 次无响应后关闭连接 */
};
WCHNET_ConfigKeepLive(&cfg);
```
| 参数 | 值 | 说明 |
|------|-----|------|
| idle | 20000 ms | 无数据交换后多久开始 Keepalive 探测 |
| interval | 15000 ms | 相邻两次探测的间隔 |
| count | 9 | 探测无响应的最大次数,超过则断链 |
**总体断线判定时间**最坏情况20 + 9×15 = **155 秒**
---
## 4. 配置下发与缓存机制
### 4.1 配置接收流程
```
Server 控制流发送配置帧(可一次发三种):
├─ TYPE_CONFIG_COMMONConfigCommon_t
├─ TYPE_CONFIG_2DConfig2D_t
└─ TYPE_CONFIG_1DConfig1D_t
TcpLogic 解析后缓存至内部 shadow 寄存器
触发 ConfigUpdateCallbackOnConfigUpdate
回调内调用 Preprocess_Settings_Change(cfg2d, cfg1d, common)
→ 更新预处理模块的 ROI、阈值、目标尺寸等参数
```
### 4.2 读取当前配置
业务任务每帧调用:
```c
ConfigCommon_t tc; Config2D_t t2; Config1D_t t1;
int8_t has_cfg = TcpLogic_GetLatestConfig(&tc, &t2, &t1);
```
返回值:
- `0`:有有效配置,`tc`/`t2`/`t1` 已填充
- `-1`:服务器尚未下发配置
### 4.3 无配置时的默认行为
`TcpLogic_GetLatestConfig` 返回 `-1` 时,`task_business_entry` 跳过 2D/1D 处理,仅响应 TEMP_REQ如有
默认 Burst 参数(代码中 fallback 常量):
| 参数 | 默认值 |
|------|--------|
| `DEFAULT_BURST_COUNT` | 3 帧 |
| `DEFAULT_BURST_INTERVAL_MS` | 200 ms |
---
## 5. 数据发送队列与背压处理
### 5.1 发送接口
```c
int8_t TcpLogic_BuildAndSendTemperatureFrame(
TcpTxBuffer_t *tx_buf,
PreprocessResult_t *meta,
uint8_t socket_type, /* 0x01=数据流0x02=控制流TEMP_REQ 响应) */
uint8_t is2d /* 1=2D矩阵0=1D时序 */
);
```
返回值:
- `0`:成功入队发送
- `< 0`:失败(连接断开或队列满)
### 5.2 发送失败处理
当前固件对发送失败仅打印 `DBG_ERR`**不重试**,帧直接丢弃。原因:热像数据具有实时性,重试旧帧无意义。
### 5.3 双缓冲保证
业务任务通过 `use_buffer_A` 标志交替使用 `g_TxNetBuffer_A` / `g_TxNetBuffer_B`
```
帧 N → g_TxNetBuffer_A → BuildAndSend入队
↓ 切换
帧 N+1 → g_TxNetBuffer_B → BuildAndSend入队
↓ 切换
帧 N+2 → g_TxNetBuffer_A ...
```
帧 N 入队后立即切换缓冲区,即使 WCHNET 尚未完成实际 TCP 发送,帧 N+1 的填充也不会覆盖帧 N。
---
## 6. WCHNET 网络栈驱动任务
### 6.1 任务职责
`task_wchnet_entry`(优先级 6每 5 ms 执行一次:
```c
static void task_wchnet_entry(void *pvParameters) {
while (1) {
qdx_port_net_lock(); /* 获取互斥量 */
WCHNET_MainTask(); /* 协议栈心跳TCP 超时检测、重传计时等) */
if (WCHNET_QueryGlobalInt())
WCHNET_HandleGlobalInt(); /* 分发 socket 事件 */
qdx_port_net_unlock(); /* 释放互斥量 */
vTaskDelay(pdMS_TO_TICKS(5));
}
}
```
### 6.2 事件分发路径
```
WCHNET_HandleGlobalInt()
└─ WCHNET_HandleSockInt(socketid, intstat)
├─ SINT_STAT_RECV → qdx_port_sock_recv_notify()
├─ SINT_STAT_CONNECT → WCHNET_ModifyRecvBuf() + qdx_port_sock_connect_notify()
├─ SINT_STAT_DISCONNECT → qdx_port_sock_disconnect_notify()
└─ SINT_STAT_TIM_OUT → qdx_port_sock_disconnect_notify()(视为断连)
```
### 6.3 互斥保护
`qdx_port_net_lock/unlock` 使用 FreeRTOS 互斥量保护所有 WCHNET API 调用,防止 `task_wchnet_entry``task_business_entry` 并发访问网络栈导致竞态。
> **设计说明**WCHNET 不是线程安全的,所有 WCHNET 调用(包括业务任务中的发送接口)均须在锁保护下进行。`task_wchnet_entry` 持锁 5 ms`task_business_entry` 在发送时短暂竞争该锁,实际竞争窗口极短。
---
## 7. 断线重连
- 控制流或数据流断开(`SINT_STAT_DISCONNECT` / `SINT_STAT_TIM_OUT`)后,`qdx_tcp_logic` 内部状态机自动重置并重新尝试连接
- 重连成功后重新发送握手帧
- 配置 shadow 寄存器在断线期间**保留**,重连后无需服务器重新下发配置即可继续工作
---
## 8. 相关文档
| 文档 | 内容 |
|------|------|
| [系统总览.md](系统总览.md) | TcpLogic_Start 在启动序列中的位置 |
| [QDX协议设计.md](QDX协议设计.md) | 零拷贝 TX 缓冲区、帧结构 |
| [对接集成指南.md](对接集成指南.md) | 上位机侧握手和配置下发操作 |

View File

@ -0,0 +1,317 @@
# 对接集成指南
> 文档版本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 ServerListen模式等待。
---
## 2. 握手流程
MCU 上电并完成内部初始化后,按以下顺序建立连接:
```
1. MCU 建立控制流 TCP 连接Server 5511
2. MCU 发送 TYPE_HANDSHAKEClass=CLASS_SYSTEM=0x04
Value 结构Handshake_t46 字节):
├─ 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 触发模式配置
**帧格式**FrameHeaderClass=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 采集模式配置
**帧格式**FrameHeaderClass=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 输出脉宽ms1D 使用) | 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_t8 字节):
| 字段 | 类型 | 说明 |
|------|------|------|
| `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_COMMON0x20Class=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 交换机或兼容网卡)
- 调试串口USART3921600会打印 `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 是否及时 ACKTCP 窗口是否足够
- 调试串口打印 `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 = 0NG而非 1OK
- 检查 `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 模式配置 |

View File

@ -0,0 +1,233 @@
# CH32V307 固件系统总览
> 文档版本1.0 · 日期2026-03-15 · 状态:已发布
---
## 1. 项目背景与硬件平台
### 1.1 项目概述
本固件运行于 CH32V307WCU6 微控制器,连接 Mini212G2256×192红外热像传感器通过 1000M RGMII 以太网实时向上位机ConfigServer上报温度数据。
系统支持两种主要工作模式,均在 FreeRTOS 多任务框架下运行:
- **2D 模式**:矩阵温度帧触发采集(外部 GPIO 触发或内部温度阈值触发)
- **1D 模式**:时间轴温度序列采集(外部 GPIO 触发或内部连续热帧检测)
### 1.2 硬件配置
| 项目 | 规格 |
|------|------|
| MCU | CH32V307WCU6RISC-V144 MHz |
| FLASH | 128 KBOption Bytes 配置 `FLASH_128_SRAM_192` |
| SRAM | 192 KB |
| FreeRTOS Heap | 16 KBHeap_4 |
| 传感器 | Mini212G2256×192TMP 模式0.1°C/LSB |
| 传感器接口 | DVP 8-bit CMOS硬件 DMA ping-pong |
| 以太网 | 1000M RGMII |
| 调试串口 | USART3PB10 TX / PB11 RX921600 baud |
| 外部触发输入 | PA15 / EXTI15上升沿内部下拉 |
| NG 输出 | PA8推挽高电平有效 |
> **TMP 模式说明**:传感器必须预配置为 TMP 模式(非 Y16 原始 ADC输出值为 0.1°C/LSB 的绝对温度,无需 MCU 换算。配置方法见 `Doc/Mini212G2预配置指南.md`
---
## 2. FreeRTOS 任务拓扑
### 2.1 任务列表
| 任务名 | 优先级 | 栈words | 条件 | 职责 |
|--------|--------|-------------|------|------|
| `task_wchnet_entry` | 6最高 | 512 | 始终创建 | WCHNET 协议栈驱动,每 5 ms 轮询 |
| `task_business_entry` | 5 | 512 | 始终创建 | DVP 帧消费、2D/1D 状态机、TCP 发送 |
| `task_test_pattern_entry` | 4 | 256 | `TEST_PATTERN_MODE=1` 时 | 仿真传感器数据不含真实DVP |
| `task_heartbeat_entry` | 3最低 | 256 | 始终创建 | 调试心跳打印,每 2 s 一次 |
### 2.2 任务间数据流
```
传感器DVP 硬件)
│ 每行 512 BDMA ping-pong
DVP_IRQHandlerWCH-Interrupt-fast
│ ① STR_FRM → current_line_idx = 0
│ ② ROW_DONE → memcpy → FrameBuffer[idx]
│ ③ idx == 191 → Frame_Ready_Flag = 1
task_business_entry优先级 5
│ 轮询 Frame_Ready_Flag
│ 读取 FrameBuffer共享内存
│ 执行 Preprocess_Execute / get_1d_sample
TcpLogic_BuildAndSendTemperatureFrame
│ 零拷贝写入帧头HeadOffset 空间复用)
task_wchnet_entry优先级 6
│ WCHNET_MainTask / WCHNET_HandleGlobalInt
以太网 → ConfigServer
```
**关键同步机制**
- `Frame_Ready_Flag``volatile uint8_t`IRQ 置 1业务任务读取后清 0
- `FrameBuffer``uint8_t[192][512]`IRQ 写入,业务任务只读
- 双缓冲 TX`g_TxNetBuffer_A` / `g_TxNetBuffer_B``use_buffer_A` 标志交替,保证一帧发送未完时下一帧可立即写入
- `qdx_port_net_lock/unlock`:互斥量保护 WCHNET API防止业务任务和网络任务并发
---
## 3. 系统启动序列
`main()` 按以下顺序执行初始化,各步骤依赖前序完成:
```
SystemCoreClockUpdate() → 更新 SystemCoreClock144 MHz
Delay_Init() → SysTick 初始化,提供 sys_tick_ms 计时
USART_Printf_Init(921600) → USART3 调试串口PB10/PB11
Config_Flash_SRAM(FLASH_128_SRAM_192) → Option BytesFLASH=128KSRAM=192K
(若已配置跳过;否则写入后系统复位)
DVP_Init() → GPIO 配置、DMA ping-pong 缓冲、IRQ 使能
TIM2_Init() → WCHNET 定时器1 ms 周期)
ExtTrigger_GPIO_Init() → PA15 EXTI15上升沿中断
NG_GPIO_Init() → PA8 推挽输出,默认低
ETH_LibInit(IP, GW, Mask, MAC) → WCHNET 协议栈初始化1000M RGMII
WCHNET_ConfigKeepLive(20000,15000,9) → TCP Keepalive
qdx_port_init() → QDX 网络层 socket/互斥量初始化
Preprocess_Init(256, 192) → 图像预处理模块
TcpLogic_Init(MACAddr, NULL) → TCP 逻辑层,注册 DeviceUUID=MAC
TcpLogic_Register*Callback() → 注册 OnConfigUpdate / OnDetectionResult / OnTempFrameRequest
TcpLogic_Start() → 启动后台连接状态机(控制流 → 握手 → 数据流)
xTaskCreate(×3 或 ×4) → 创建 RTOS 任务
vTaskStartScheduler() → 启动调度器(不返回)
```
> **Option Bytes 说明**:首次上电若 Flash/SRAM 分配不符,`Config_Flash_SRAM` 会写入 Option Bytes 并触发 `NVIC_SystemReset()`,复位后以新分配重新启动。正常运行时该函数直接返回。
---
## 4. 2D 状态机
2D 模式在 `task_business_entry` 中,每帧调用 `handle_2d_trigger()`,由 4 个状态变量组成隐式状态机:
### 4.1 状态转换图
```
┌─────────────────── IDLE ──────────────────────────┐
│ │
│ TriggerMode=1外部 │ TriggerMode=0内部
│ PA15 上升沿 → g_ext_trigger_flag=1 │ Preprocess_CheckInternalTrigger2D()==1
↓ ↓
DEBOUNCE (直接)
等待 DebounceIntervalMs │
│ │
└──────────────────────────────────────────────────→┤
DELAY
等待 TriggerDelayMs
BURST循环
发送第 1 帧(立即)
+ 剩余 BurstCount-1 帧
@ TriggerInternalIntervalMs 间隔
burst_remaining == 0
→ IDLE
```
### 4.2 状态参数
| 配置字段 | 默认值 | 说明 |
|----------|--------|------|
| `TriggerMode` | 0 | 0=内部温度阈值1=外部 GPIO |
| `TriggerDebounceIntervalMs` | 0 | 外部触发防抖等待时间ms |
| `TriggerDelayMs` | 0 | 触发确认到第一帧采集的延迟ms |
| `TriggerBurstCount` | 3 | 每次触发发送帧数DEFAULT_BURST_COUNT=3 |
| `TriggerInternalIntervalMs` | 200 | 连拍帧间隔DEFAULT_BURST_INTERVAL_MS=200 ms |
| `TriggerCondition` | 0 | 内部触发判据0=Avg1=Max |
| `TriggerTemperatureThreshold` | — | 内部触发温度阈值0.1°C/LSB |
| `TriggerRoiX/Y/W/H` | — | 内部触发检测 ROI 区域 |
### 4.3 双缓冲 TX 策略
每次 `do_2d_capture_send()` 交替选择 `g_TxNetBuffer_A` / `g_TxNetBuffer_B`,通过 `use_buffer_A` 标志取反实现切换。TCP 发送入队后立即切换,保证下一帧填充不覆盖正在传输的缓冲区。
---
## 5. 1D 状态机
1D 模式以每帧全帧最大温度作为一个采样点,在 `task_business_entry` 中每帧调用 `handle_1d_trigger()`
### 5.1 状态图
```
S1D_IDLE
├─ TriggerType=2外部PA15 上升沿
│ → S1D_DEBOUNCE等待 HighTimerLimit ms
│ → S1D_COLLECTING
└─ TriggerType=1内部维护 3 帧滑动窗口
连续 3 帧最大温度 ≥ TriggerTempLimit
→ S1D_COLLECTING直接含预触发 3 帧数据)
S1D_COLLECTING每帧采一个样本
├─ 记录 sample = 全帧最大温度time_offset = ms - start_time
├─ 热帧(≥ threshs1d_consec_hot++consec_cold=0
│ 满足 3 次热 → s1d_triggered=1
└─ 停止条件(任意一满足):
① s1d_count ≥ MAX_1D_POINTS512
② s1d_count ≥ BufferSize
③ s1d_triggered && s1d_consec_cold ≥ NgCountLimit
└─ 若 triggered → send_1d_collection()
否则 → 丢弃reset
→ S1D_IDLE
```
### 5.2 数据格式
每个样本 **4 字节**,小端序:
| 字节 | 字段 | 类型 | 说明 |
|------|------|------|------|
| 01 | `TimeOffset` | uint16_t LE | 相对采集开始的 ms 偏移 |
| 23 | `Temperature` | uint16_t LE | 全帧最大温度0.1°C/LSB |
### 5.3 切片参数
发送前对采集缓冲区切片:有效区间 = `[LSizeStart, count - RSizeStart)`,去除头尾噪声点。若切片越界则退化为全量发送并打印告警。
---
## 6. TEMP_REQ 辅助通道
服务器可通过控制流随时请求单帧快照,无需等待自主采集触发:
```
服务器发送 TEMP_REQ含 is2dRequest 字段)
→ OnTempFrameRequest() 回调
→ g_temp_req_pending = 1g_temp_req_is2d = is2dRequest
task_business_entry 业务循环检测到 g_temp_req_pending
→ TcpLogic_GetLatestConfig() 确认对应模式 Enabled
→ is2d=1Preprocess_Execute() + TcpLogic_BuildAndSendTemperatureFrameSocketType=0x02
is2d=0send_1d_snapshot()(中心行 30 个等间距采样点,时间偏移仿真 0~600 ms
```
**限制**:仅在对应模式(`t2.Enabled` / `t1.Enabled`)为 1 时响应;若未 Enabled打印 DBG_ERR 并忽略请求。
---
## 7. 相关文档
| 文档 | 内容 |
|------|------|
| [DVP模块设计.md](DVP模块设计.md) | DVP 硬件初始化、DMA、IRQ、FrameBuffer 格式 |
| [QDX协议设计.md](QDX协议设计.md) | TLV 帧结构、零拷贝 TX、分片机制 |
| [TCP通信模块设计.md](TCP通信模块设计.md) | 双流连接、心跳、配置缓存 |
| [对接集成指南.md](对接集成指南.md) | 外部对接方接入手册 |
| [Mini212G2预配置指南.md](../Mini212G2预配置指南.md) | 传感器 TMP 模式配置 HEX 命令 |

View File

@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-15

View File

@ -0,0 +1,50 @@
## Context
**项目背景**CH32V307WCU6 红外热像采集端固件,连接 Mini212G2 (256×192) 传感器,通过 1000M RGMII 以太网上报温度数据。核心功能已稳定运行,但缺少系统性设计文档。
**当前状态**:代码实现完整,包含 FreeRTOS 多任务、DVP+DMA 采集、QDX TLV 协议、双流 TCP 通信、2D/1D 双状态机。现有文档仅覆盖配置参数和协议接口,未形成模块级设计视图。
**读者**内部开发工程师理解实现逻辑、维护代码外部对接方ConfigServer/上位机开发团队,需接入协议)。
## Goals / Non-Goals
**Goals:**
- 为 5 个能力模块分别建立规范化的设计文档
- 通过 openspec specs 机制使文档可版本追踪和演进
- 提供外部对接方可直接使用的集成指南(无需阅读源码)
**Non-Goals:**
- 不修改任何代码
- 不覆盖硬件电气原理图(属于硬件文档)
- 不重复协议 HEX 命令表(已在 `Doc/Mini212G2预配置指南.md`
## Decisions
### 决策 1每个能力模块生成独立的 spec 文件Doc/ 目录存放最终可交付文档
**理由**openspec 的 `specs/<capability>/spec.md` 是规范锚点,实际可读性文档放在 `Doc/设计文档/` 下,分工清晰。每个 spec 描述 WHAT需求Doc 中文件描述 HOW实现细节
**备选方案**:一个大 spec 文件 → 拒绝spec 文件过大时 apply 阶段难以跟踪单个需求。
### 决策 2集成指南integration-guide作为独立能力单独维护
**理由**:外部对接方只需集成指南,不需要理解内部模块设计。独立文件避免外部文档被内部修改污染,版本可单独管控。
### 决策 3文档覆盖既有代码的实际行为不做超前设计
**理由**这是文档补全变更不是功能变更。spec 反映的是代码已实现的行为SHALL = 当前实现保证),不引入新约束。
### 决策 42D 状态机和 1D 状态机各自独立描述,不合并
**理由**两者触发逻辑、收集方式、发送格式完全不同2D 矩阵 vs 1D 时间序列)。合并描述会造成混淆。两者通过 `Config2D.Enabled` / `Config1D.Enabled` 互斥使能。
## Risks / Trade-offs
- **风险**:文档编写时代码继续演进,导致文档过时 → **缓解**:在 spec 中使用 SHALL 约束当前行为,代码变更时触发 spec 更新 PR 流程。
- **风险**integration-guide 中的协议字段描述与实际结构体不一致 → **缓解**:所有字段描述直接从 `qdx_protocol.h` 提取source of truth 为代码。
- **权衡**:文档详细程度 vs 维护成本 → 选择"关键路径详细,边缘情况参考注释"策略,避免过度文档导致维护负担。
## Open Questions
- Doc/ 下设计文档是否需要中英双语(供外部团队使用)?当前按中文优先。
- integration-guide 是否需要提供 Python/C# 示例解析代码片段?后续可作为独立变更补充。

View File

@ -0,0 +1,32 @@
# Proposal: CH32V307 固件系统软件设计文档
## Why
项目已完成核心功能开发,但缺乏系统级软件设计文档:内部无法快速理解任务拓扑和状态机逻辑,外部对接方缺乏集成参考。当前文档仅覆盖配置和协议层面,未形成完整的设计视图。
## What Changes
- 新增 **系统总览文档**FreeRTOS 任务拓扑任务职责、优先级、通信方式、2D/1D 双状态机完整流程图、网络栈初始化与运行流程
- 新增 **DVP 采集模块设计文档**DVP 硬件配置原理、DMA ping-pong 机制、IRQ 帧组装逻辑、FrameBuffer 数据格式
- 新增 **QDX 协议模块设计文档**TLV 帧结构、所有 Type 定义与用途、零拷贝 TX 缓冲区架构、分片机制
- 新增 **TCP 通信模块设计文档**双流5511 控制 / 5512 数据)连接管理、心跳机制、配置下发与缓存、数据发送队列
- 新增 **对接集成文档**:供上位机/ConfigServer 开发方参考的接入指南(握手流程、配置下发、数据帧解析、错误码)
## Capabilities
### New Capabilities
- `system-overview`: FreeRTOS 任务拓扑、2D 状态机、1D 状态机、系统启动序列
- `dvp-module-design`: DVP 硬件初始化、DMA ping-pong 行采集、帧组装、FrameBuffer 格式
- `qdx-protocol-design`: TLV 帧格式、Class/Type 定义、零拷贝 TX 架构、CRC、分片
- `tcp-module-design`: 双流连接管理、心跳、配置缓存、发送队列
- `integration-guide`: 外部对接方集成手册(握手、配置、数据解析、错误处理)
### Modified Capabilities
(无,本变更仅新增文档,不修改现有规范)
## Impact
- **新增文档目录**`Doc/设计文档/` 或各模块独立文件
- **不涉及**任何代码修改
- 影响范围内部开发参考、外部对接方ConfigServer 开发团队)

View File

@ -0,0 +1,49 @@
## ADDED Requirements
### Requirement: DVP 硬件初始化配置描述
文档 SHALL 描述 DVP 外设的完整初始化配置,包含 GPIO 引脚映射、工作模式和信号极性。
#### Scenario: 开发者核查 DVP GPIO 引脚分配
- **WHEN** 开发者进行硬件调试时
- **THEN** 文档 SHALL 提供引脚映射表PA4/5/6/9/10数据线 D0-D4、PC8/9/11D5-D7 + PCLK、PB3/8/9VSYNC/HSYNC/PIXCLK所有引脚配置为浮空输入
#### Scenario: 开发者理解 DVP 信号极性配置
- **WHEN** 开发者对接不同传感器时
- **THEN** 文档 SHALL 说明VSYNC 高有效RB_DVP_V_POLAR=1对应 DIGITAL_FIELD_VALID 高电平有效帧、HSYNC 高有效RB_DVP_H_POLAR=0、PCLK 上升沿采样RB_DVP_P_POLAR=0与 Mini212G2 手册时序图一致
### Requirement: DMA ping-pong 行缓冲机制描述
文档 SHALL 描述 DVP DMA 双缓冲的工作原理,包含缓冲切换逻辑和 IRQ 中的正确读取方式。
#### Scenario: 开发者理解 DMA 缓冲切换逻辑
- **WHEN** 开发者排查行数据乱序问题时
- **THEN** 文档 SHALL 说明DVP 硬件在 DMA_BUF0/BUF1 间自动切换ROW_DONE 中断触发时硬件已切换到下一个缓冲BUF_TOG=1 表示 DMA 正在写 BUF0此时应读 BUF1公式`src = (CR1 & BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1`
#### Scenario: 开发者理解行缓冲大小约束
- **WHEN** 开发者修改传感器分辨率时
- **THEN** 文档 SHALL 说明DMA_LineBuf0/1 各 512 字节256 像素 × 2 字节COL_NUM=512ROW_NUM=1每行触发一次 IRQ修改分辨率需同时更新 `BYTES_PER_LINE``SENSOR_WIDTH``SENSOR_HEIGHT`
### Requirement: DVP IRQ 帧组装逻辑描述
文档 SHALL 描述 `DVP_IRQHandler` 的完整逻辑,包含帧同步机制和 FrameBuffer 组装方式。
#### Scenario: 开发者理解帧同步锚点
- **WHEN** 开发者排查帧错位问题时
- **THEN** 文档 SHALL 说明STR_FRM 中断VSYNC 上升沿)将 `current_line_idx` 清零此为帧同步唯一锚点ROW_DONE 中断依次将行数据 memcpy 至 `FrameBuffer[current_line_idx]` 并递增
#### Scenario: 开发者理解帧完成标志
- **WHEN** 开发者需要知道何时帧数据可用时
- **THEN** 文档 SHALL 说明:当 `idx == SENSOR_HEIGHT - 1`(即第 191 行完成)时,`Frame_Ready_Flag` 置 1`Ready_Frame_Count` 更新为当前帧号;业务任务轮询此标志,读取后将其清零
#### Scenario: 开发者理解 IRQ 使用 fast interrupt 属性的原因
- **WHEN** 开发者修改 IRQ 属性时
- **THEN** 文档 SHALL 说明:`__attribute__((interrupt("WCH-Interrupt-fast")))` 使中断处理跳过寄存器入栈,减少中断延迟,确保在下一行像素时钟到来前完成 memcpy每行约 42.67µs @ 25Hz
### Requirement: FrameBuffer 数据格式描述
文档 SHALL 描述 FrameBuffer 的内存布局和像素值含义。
#### Scenario: 开发者访问特定像素的温度值
- **WHEN** 开发者需要读取第 row 行第 col 列的温度时
- **THEN** 文档 SHALL 说明:`FrameBuffer` 声明为 `uint8_t[192][512]`,通过 `(uint16_t*)FrameBuffer` 访问,像素值 = `((uint16_t*)FrameBuffer)[row * 256 + col]`,单位 0.1°C/LSBTMP 模式,需传感器预配置为 TMP
#### Scenario: 开发者理解字节序要求
- **WHEN** 开发者移植到大端序平台时
- **THEN** 文档 SHALL 说明CH32V307 为小端序,`uint8_t[0]` = 低字节,`uint8_t[1]` = 高字节;传感器必须配置为 CMOS8(LSB)(低字节先发),否则温度值字节反序

View File

@ -0,0 +1,58 @@
## ADDED Requirements
### Requirement: 对接方网络接入参数说明
文档 SHALL 提供上位机ConfigServer与采集端建立 TCP 连接所需的全部参数。
#### Scenario: 对接方配置 TCP Server 监听端口
- **WHEN** 对接方开发服务端程序时
- **THEN** 文档 SHALL 提供MCU 默认 IP=192.168.7.10Subnet=255.255.255.0Gateway=192.168.7.1Server 需在 192.168.7.50 监听两个端口5511控制流MCU 主动连入)和 5512数据流MCU 主动连入);以太网接口为 1000M RGMII
### Requirement: 握手流程说明
文档 SHALL 描述 MCU 上电后的握手过程,使对接方能正确识别采集端设备。
#### Scenario: 对接方接收第一个数据包并识别设备
- **WHEN** MCU 连接后发送握手帧时
- **THEN** 文档 SHALL 说明MCU 在控制流连接成功后发送 TYPE_HANDSHAKEClass=CLASS_SYSTEM含 ProtocolVersion=0x0200、DeviceUUID=MAC 地址、AuthToken=全零);服务器 SHALL 回复握手响应以完成握手;握手完成后 MCU 才开始正常数据上报
### Requirement: 配置下发格式说明
文档 SHALL 描述服务器向 MCU 下发配置的帧格式和所有配置字段含义。
#### Scenario: 对接方配置 2D 触发模式
- **WHEN** 对接方发送 Config2D 配置帧时
- **THEN** 文档 SHALL 提供 Config2D_t 所有字段Enabled(uint8_t)、TriggerMode(uint8_t, 0=内部温度/1=外部GPIO)、TriggerDebounceIntervalMs(uint16_t)、TriggerDelayMs(uint16_t)、TriggerBurstCount(uint8_t)、TriggerInternalIntervalMs(uint16_t)、TriggerTemperatureThreshold(int16_t, 0.1°C/LSB)、TriggerCondition(uint8_t, 0=Avg/1=Max)、TriggerRoiX/Y/W/H(uint16_t)、TargetWidth/Height(uint16_t)、NGioDelay(uint16_t, ms)
#### Scenario: 对接方配置 1D 采集模式
- **WHEN** 对接方发送 Config1D 配置帧时
- **THEN** 文档 SHALL 提供 Config1D_t 所有字段Enabled(uint8_t)、RunMode(uint8_t, 0=STOP/1=RUN)、TriggerType(uint8_t, 1=内部/2=外部)、TriggerTempLimit(int16_t)、HighTimerLimit(uint16_t, ms)、NgCountLimit(uint16_t, 帧数)、BufferSize(uint16_t)、LSizeStart/RSizeStart(uint16_t, 切片参数)
### Requirement: 温度帧数据包格式说明
文档 SHALL 描述 MCU 上报的温度帧数据包的完整格式,使对接方能正确解析。
#### Scenario: 对接方解析收到的 2D 温度帧
- **WHEN** 数据流收到 TYPE_TEMP_FRAME 包时
- **THEN** 文档 SHALL 说明Value 区域含 PreprocessResult 元数据ValidWidth × ValidHeight 像素数组);像素为 uint16_t little-endian单位 0.1°C/LSB元数据含 MaxTemp/MinTemp/AvgTemp/RoiTemp均 int16_t, 0.1°C/LSB、ValidWidth/ValidHeight实际 ROI 尺寸、FrameNumber
#### Scenario: 对接方解析收到的 1D 温度帧
- **WHEN** 数据流收到 1D TYPE_TEMP_FRAME 包时
- **THEN** 文档 SHALL 说明is2D=0每个样本 4 字节:[time_off_lo, time_off_hi, temp_lo, temp_hi]time_offset 为相对采集开始的 ms 偏移uint16_ttemp 为 uint16_t 0.1°C/LSB
### Requirement: 触发结果上报和 NG 响应机制说明
文档 SHALL 描述采集端如何响应服务器的 DetectionResult 决策和触发 NG 输出。
#### Scenario: 对接方下发检测结果
- **WHEN** 服务器完成图像分析后回传结果时
- **THEN** 文档 SHALL 说明:服务器发送 TYPE_DETECTION_RESULT含 frameNumber、resultStatus0=NG/1=OKMCU 收到 resultStatus=0 时触发 PA8 高电平 NGioDelay ms默认 200ms对接方可通过 NGioDelay 配置 NG 输出脉宽
### Requirement: TEMP_REQ 按需截图说明
文档 SHALL 描述服务器如何向 MCU 请求即时截图。
#### Scenario: 对接方请求单帧截图
- **WHEN** 服务器需要调试或核查当前热场时
- **THEN** 文档 SHALL 说明:控制流发送 TYPE_CONFIG_COMMON 或专用 TEMP_REQ 命令(含 is2dRequest 字段MCU 将在下一个业务循环发送当前最新帧2D 返回 ROI 裁切后温度矩阵1D 返回中心行 30 个等间距采样点;仅在对应模式已 Enabled 时响应
### Requirement: 错误码和异常处理说明
文档 SHALL 列出协议层所有错误码含义及对接方应采取的处理措施。
#### Scenario: 对接方收到 ERR 响应帧
- **WHEN** 服务器发送格式错误的配置帧时
- **THEN** 文档 SHALL 列出错误码含义ERR_CRC(0x1001)=CRC 校验失败、ERR_VERSION(0x1002)=协议版本不匹配、ERR_LENGTH(0x1003)=帧长度异常、ERR_AUTH(0x2001)=认证失败、ERR_BUSY(0x2002)=设备忙、ERR_PARAM(0x3001)=参数非法;收到错误码后对接方 SHALL 检查发送的帧格式后重试

View File

@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: TLV 帧结构完整描述
文档 SHALL 描述 QDX 协议的完整帧结构包含帧头各字段含义、TLV 格式和帧尾 CRC。
#### Scenario: 开发者手动构造一个协议帧
- **WHEN** 开发者需要调试发送内容时
- **THEN** 文档 SHALL 提供帧结构:[FrameHeader 16B] + [TLV_Header 3B] + [Value NB] + [CRC16 2B],其中 FrameHeader = {Magic(2)=0x55AA, Version(1)=0x20, Length(2), Sequence(2), Timestamp(4), Source(1), DevID(2), Class(1), Flags(1)}
#### Scenario: 开发者理解 Class 和 TLV Type 的对应关系
- **WHEN** 开发者解析收到的帧时
- **THEN** 文档 SHALL 列出完整的 Class/Type 映射表CLASS_CONTROL(0x01)、CLASS_DATA(0x02)、CLASS_RESPONSE(0x03)、CLASS_SYSTEM(0x04)Type 包含 TYPE_HANDSHAKE(0x01)、TYPE_HEARTBEAT(0x02)、TYPE_TEMP_FRAME(0x10)、TYPE_CONFIG_COMMON(0x20)、TYPE_CONFIG_2D(0x22)、TYPE_CONFIG_1D(0x23)、TYPE_ACK_PAYLOAD(0x30)、TYPE_DETECTION_RESULT(0x40) 等
### Requirement: 零拷贝 TX 缓冲区架构描述
文档 SHALL 描述 TcpTxBuffer_t 的双缓冲零拷贝发送架构和 HeadOffset 的使用方式。
#### Scenario: 开发者理解零拷贝的实现方式
- **WHEN** 开发者需要在减少内存拷贝的前提下添加新的帧类型时
- **THEN** 文档 SHALL 说明:`TcpTxBuffer_t` 包含 pBuffer9216B、HeadOffset64B、ValidPayloadLen图像数据由预处理模块写入 `pBuffer + HeadOffset` 开始的位置;`TcpLogic_BuildAndSendTemperatureFrame` 在 HeadOffset 前的 64 字节内直接写入帧头FrameHeader + TLV**无需额外 memcpy**
#### Scenario: 开发者计算单次可发送的最大像素数
- **WHEN** 开发者调整 ROI 大小时
- **THEN** 文档 SHALL 说明:有效载荷容量 = TotalCapacity(9216) - HeadOffset(64) - CRC(2) - TLV_Header(3) - FrameHeader(16) = 9131 字节,每像素 2 字节,最多 4565 个像素(约 67×67 ROI
### Requirement: 分片机制描述
文档 SHALL 描述协议层的分片上限和分片标志用法。
#### Scenario: 开发者发送超大载荷时
- **WHEN** 开发者发送超过 MAX_FRAGMENT_PAYLOAD(1400B) 的数据时
- **THEN** 文档 SHALL 说明分片最大载荷为 1400B受 TCP MSS=1460 限制),大帧通过 FLAG_LAST_FRAGMENT(0x20) 标志标识末片;当前 TX 缓冲区为 9216B单次发送通常不超过此限制
### Requirement: Flags 字段用法描述
文档 SHALL 描述 FrameHeader.Flags 各位的含义和当前固件的使用情况。
#### Scenario: 开发者解析收到的 Flags 字段
- **WHEN** 上位机解析帧时
- **THEN** 文档 SHALL 说明FLAG_PRIORITY_MASK(0x03)=优先级0-3、FLAG_COMPRESSED(0x04)=压缩当前未使用、FLAG_ENCRYPTED(0x08)=加密当前未使用、FLAG_ACK_REQ(0x10)=需要 ACK、FLAG_LAST_FRAGMENT(0x20)=末片标志

View File

@ -0,0 +1,56 @@
## ADDED Requirements
### Requirement: FreeRTOS 任务拓扑描述
文档 SHALL 列出所有 RTOS 任务的名称、优先级、栈大小、职责及任务间通信方式。
#### Scenario: 开发者查阅任务优先级
- **WHEN** 开发者需要了解任务调度顺序时
- **THEN** 文档 SHALL 提供任务优先级表包含task_wchnet优先级 6512 字、task_business优先级 5512 字、task_heartbeat优先级 3256 字、task_test_pattern优先级 4256 字,仅 TEST_PATTERN_MODE=1 时存在)
#### Scenario: 开发者理解任务间数据流
- **WHEN** 开发者需要理解 DVP → 业务任务的数据传递时
- **THEN** 文档 SHALL 说明通过 `Frame_Ready_Flag`volatile 标志)和 `FrameBuffer`共享内存传递帧数据IRQ 置位,业务任务轮询消费
### Requirement: 系统启动序列描述
文档 SHALL 描述 `main()` 的初始化顺序,涵盖所有外设和模块的初始化步骤及其依赖关系。
#### Scenario: 开发者定位初始化顺序问题
- **WHEN** 开发者遇到启动相关 bug 时
- **THEN** 文档 SHALL 提供启动序列SystemClock → USART3/debug → Flash/SRAM 配置128K+192K→ DVP_Init → TIM2_Init → ExtTrigger_GPIO → NG_GPIO → ETH_LibInit → qdx_port_init → Preprocess_Init → TcpLogic_Init → 注册回调 → TcpLogic_Start → 创建 RTOS 任务 → vTaskStartScheduler
### Requirement: 2D 状态机完整描述
文档 SHALL 描述 2D 触发状态机的所有状态、转换条件和行为,包含外部触发和内部触发两种模式。
#### Scenario: 开发者理解 2D 外部触发流程
- **WHEN** 开发者排查外部触发抖动问题时
- **THEN** 文档 SHALL 描述IDLE → [PA15 上升沿] → DEBOUNCE等待 DebounceIntervalMs → DELAY等待 TriggerDelayMs → BURST发 BurstCount 帧,间隔 TriggerInternalIntervalMs → IDLE
#### Scenario: 开发者理解 2D 内部触发流程
- **WHEN** 开发者配置温度阈值触发时
- **THEN** 文档 SHALL 描述:每帧调用 `Preprocess_CheckInternalTrigger2D`,在 TriggerRoi 区域内 Max/Avg 超过 TriggerTemperatureThreshold 时触发,跳过 DEBOUNCE 直接进入 DELAY → BURST
#### Scenario: 开发者理解双缓冲 TX 逻辑
- **WHEN** 开发者排查发送顺序问题时
- **THEN** 文档 SHALL 说明 `use_buffer_A` 标志在每次发送时取反g_TxNetBuffer_A / g_TxNetBuffer_B 交替使用,避免前一帧发送未完成时被覆盖
### Requirement: 1D 状态机完整描述
文档 SHALL 描述 1D 采集状态机的所有状态、转换条件、采集逻辑和发送条件。
#### Scenario: 开发者理解 1D 外部触发流程
- **WHEN** 开发者配置 TriggerType=2 时
- **THEN** 文档 SHALL 描述S1D_IDLE → [PA15 上升沿] → S1D_DEBOUNCE等待 HighTimerLimit ms → S1D_COLLECTING采集 temp+time 样本直到停止条件)→ 发送 → S1D_IDLE
#### Scenario: 开发者理解 1D 内部触发流程
- **WHEN** 开发者配置 TriggerType=1 时
- **THEN** 文档 SHALL 描述S1D_IDLE 中维护 3 帧滑动窗口,连续 3 帧帧最大温度 ≥ TriggerTempLimit 时进入 S1D_COLLECTING停止条件样本数达 BufferSize 或触发后连续 NgCountLimit 帧低温
#### Scenario: 开发者理解 1D 数据格式
- **WHEN** 开发者解析 1D 数据时
- **THEN** 文档 SHALL 说明每个样本 4 字节:[time_offset_lo, time_offset_hi, temp_lo, temp_hi]time_offset 为相对采集开始的 ms 偏移temp 为 0.1°C/LSB 的 uint16_t
### Requirement: TEMP_REQ 辅助通道描述
文档 SHALL 描述服务器按需截图TEMP_REQ的触发流程和限制条件。
#### Scenario: 开发者理解 TEMP_REQ 工作方式
- **WHEN** 服务器发送 TEMP_REQ 命令时
- **THEN** 文档 SHALL 说明:`g_temp_req_pending` 置位,业务任务在下次循环读取当前 FrameBuffer 发送is2D=1 走 Preprocess_Execute + 2D 封包is2D=0 走 send_1d_snapshot30 个等间距采样点);仅在对应模式 Enabled 时响应

View File

@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: 双流连接架构描述
文档 SHALL 描述控制流5511和数据流5512的职责分工、连接方式和状态管理。
#### Scenario: 开发者理解双流设计意图
- **WHEN** 开发者排查连接问题时
- **THEN** 文档 SHALL 说明:控制流 srcport=5511MCU 主动连接 Server用于握手、配置下发、心跳、TEMP_REQ、DetectionResult 上报;数据流 desport=5512MCU 主动连接 Server用于温度帧数据上报两流独立管理数据流断开不影响控制流
#### Scenario: 开发者理解 TCP 连接建立时序
- **WHEN** 开发者排查首次握手失败时
- **THEN** 文档 SHALL 说明:`TcpLogic_Start` 派生后台连接状态机,先尝试控制流连接 → 连接成功后发送 TYPE_HANDSHAKE含 DeviceUUID=MAC地址、AuthToken=NULL→ 收到服务器握手响应后标记连接就绪 → 同步尝试数据流连接
### Requirement: 心跳机制描述
文档 SHALL 描述心跳帧的发送周期、TCP Keepalive 配置和连接保活策略。
#### Scenario: 开发者调整心跳参数
- **WHEN** 网络中间件NAT/防火墙)超时断连时
- **THEN** 文档 SHALL 说明:应用层心跳 TYPE_HEARTBEAT 由 TcpLogic 内部定时发送TCP Keepalive 配置:空闲 20 秒(`idle=20000ms`)后开始探测,每次间隔 15 秒(`interval=15000ms`),最多 9 次(`count=9`)探测无响应则断链重连
### Requirement: 配置下发与缓存机制描述
文档 SHALL 描述服务器配置的接收、缓存和通知流程。
#### Scenario: 开发者追踪配置更新流程
- **WHEN** 服务器下发配置后行为未按预期改变时
- **THEN** 文档 SHALL 描述:服务器发送 TYPE_CONFIG_COMMON + TYPE_CONFIG_2D + TYPE_CONFIG_1D → TcpLogic 解析并缓存至内部 shadow 寄存器 → 触发 ConfigUpdateCallback`OnConfigUpdate`)→ 回调内调用 `Preprocess_Settings_Change` 更新预处理参数;`TcpLogic_GetLatestConfig` 可随时读取最新配置副本
#### Scenario: 开发者理解无配置时的默认行为
- **WHEN** 设备启动后服务器尚未下发配置时
- **THEN** 文档 SHALL 说明:`TcpLogic_GetLatestConfig` 返回 -1业务任务跳过 2D/1D 处理,仅响应 TEMP_REQ若有默认 burst 参数 DEFAULT_BURST_COUNT=3、DEFAULT_BURST_INTERVAL_MS=200ms 作为 fallback
### Requirement: 数据发送队列和背压处理描述
文档 SHALL 描述温度帧发送的队列机制和发送失败时的处理策略。
#### Scenario: 开发者排查数据帧丢失问题
- **WHEN** 网络繁忙时出现帧丢失时
- **THEN** 文档 SHALL 说明:`TcpLogic_BuildAndSendTemperatureFrame` 返回 0 表示成功入队,返回 <0 表示发送失败队列满或连接断开当前固件对发送失败仅打印 DBG_ERR不重试双缓冲 TX 保证当前帧被入队时下一帧可立即写入另一个缓冲
### Requirement: WCHNET 网络栈驱动任务描述
文档 SHALL 描述 task_wchnet 的职责和与 WCHNET 库的交互方式。
#### Scenario: 开发者理解网络栈如何被驱动
- **WHEN** 开发者排查网络响应迟缓问题时
- **THEN** 文档 SHALL 说明:`task_wchnet_entry` 每 5ms 轮询一次,调用 `WCHNET_MainTask`(协议栈心跳)和 `WCHNET_HandleGlobalInt`socket 事件分发socket 接收/连接/断开事件通过 `qdx_port_sock_*_notify` 转发给 QDX 网络层;`qdx_port_net_lock/unlock` 用互斥量保护 WCHNET 调用,防止业务任务和网络任务并发访问

View File

@ -0,0 +1,48 @@
## 1. 系统总览文档
- [x] 1.1 创建 `Doc/设计文档/系统总览.md`包含项目背景和硬件平台MCU、传感器、网络概述
- [x] 1.2 编写 FreeRTOS 任务拓扑表:四个任务的名称、优先级、栈大小、职责
- [x] 1.3 编写系统启动序列说明,覆盖外设初始化到 vTaskStartScheduler 的完整步骤
- [x] 1.4 编写 2D 状态机流程IDLE → DEBOUNCE → DELAY → BURST含触发条件和参数
- [x] 1.5 编写 1D 状态机流程S1D_IDLE → S1D_DEBOUNCE → S1D_COLLECTING含停止条件
- [x] 1.6 编写 TEMP_REQ 实时温度请求处理流程(按需单帧下发逻辑)
- [x] 1.7 补充任务间同步机制说明Frame_Ready_Flag、信号量、帧缓冲双 Buffer 策略)
## 2. DVP 模块设计文档
- [x] 2.1 创建 `Doc/设计文档/DVP模块设计.md`,包含 DVP 硬件连接引脚说明
- [x] 2.2 描述 DVP 时序配置VSYNC 极性、PCLK 极性、CMOS-8bit 模式
- [x] 2.3 描述 DMA ping-pong 机制:两个 DMA 通道交替接收行数据512 B/行)
- [x] 2.4 描述 DVP IRQ 处理逻辑STR_FRM 重置行计数、ROW_DONE 累积行、第 191 行置就绪标志
- [x] 2.5 描述帧缓冲格式:`uint8_t[192][512]` = 256 列 × 2 字节/像素,小端序 uint16_t单位 0.1°C
- [x] 2.6 描述 TMP 模式像素换算公式uint16_t × 0.1 = 摄氏度)
## 3. QDX 协议设计文档
- [x] 3.1 创建 `Doc/设计文档/QDX协议设计.md`,描述帧头 FrameHeader_t 各字段含义和长度
- [x] 3.2 描述 TLV 结构Type(1 B) + Length(2 B, LE) + Value(N B) 及嵌套帧 = FrameHeader + TLV
- [x] 3.3 列出所有帧类型Class × TypeHANDSHAKE、HEARTBEAT、TEMP_FRAME、CONFIG_COMMON 等
- [x] 3.4 描述零拷贝 TX 方案TcpTxBuffer 容量 9216 BHeadOffset=64帧头在发送前向前写入
- [x] 3.5 描述 TX 分片规则:单 TLV Value 超过 9131 B 时触发分片Flags.FRAGMENT 置位
- [x] 3.6 描述 Sequence 字段单调递增规则和接收端顺序验证要求
## 4. TCP 通信模块设计文档
- [x] 4.1 创建 `Doc/设计文档/TCP通信模块设计.md`说明双流架构控制流5511+ 数据流5512
- [x] 4.2 描述各套接字专属职责:控制流负责握手/配置/心跳,数据流负责温度帧推送
- [x] 4.3 描述 TCP Keepalive 参数idle=20000 msinterval=15000 mscount=9
- [x] 4.4 描述服务端断线重连机制和连接状态的管理方式
- [x] 4.5 描述配置缓存机制:配置通过控制流接收后缓存,重连后自动恢复
- [x] 4.6 描述 WCHNET 驱动任务task_wchnet_entry的轮询周期和优先级要求
## 5. 对接集成指南
- [x] 5.1 创建 `Doc/设计文档/对接集成指南.md`包含网络接入参数表IP、端口、协议
- [x] 5.2 编写握手流程章节双流连接顺序、HANDSHAKE 帧格式、DevID 验证
- [x] 5.3 编写配置下发章节Config2D_t 全字段说明和推荐初始值表
- [x] 5.4 编写 1D 配置下发章节Config1D_t 全字段说明、TriggerType 枚举值
- [x] 5.5 编写温度帧接收章节2D 矩阵帧解析步骤(像素排列、单位换算)
- [x] 5.6 编写 1D 时序帧接收章节Sample 结构4 Btime_lo/hi + temp_lo/hi解析步骤
- [x] 5.7 编写 DetectionResult 和 NG 输出说明DetectionResult TLV 字段、NG GPIOPA8电平含义
- [x] 5.8 编写 TEMP_REQ 请求方法:主动下发 TEMP_REQ 帧触发单帧返回
- [x] 5.9 编写常见错误码表和对接故障排查步骤

View File

@ -0,0 +1,53 @@
# dvp-module-design Specification
## Purpose
TBD - created by archiving change software-design-doc. Update Purpose after archive.
## Requirements
### Requirement: DVP 硬件初始化配置描述
文档 SHALL 描述 DVP 外设的完整初始化配置,包含 GPIO 引脚映射、工作模式和信号极性。
#### Scenario: 开发者核查 DVP GPIO 引脚分配
- **WHEN** 开发者进行硬件调试时
- **THEN** 文档 SHALL 提供引脚映射表PA4/5/6/9/10数据线 D0-D4、PC8/9/11D5-D7 + PCLK、PB3/8/9VSYNC/HSYNC/PIXCLK所有引脚配置为浮空输入
#### Scenario: 开发者理解 DVP 信号极性配置
- **WHEN** 开发者对接不同传感器时
- **THEN** 文档 SHALL 说明VSYNC 高有效RB_DVP_V_POLAR=1对应 DIGITAL_FIELD_VALID 高电平有效帧、HSYNC 高有效RB_DVP_H_POLAR=0、PCLK 上升沿采样RB_DVP_P_POLAR=0与 Mini212G2 手册时序图一致
### Requirement: DMA ping-pong 行缓冲机制描述
文档 SHALL 描述 DVP DMA 双缓冲的工作原理,包含缓冲切换逻辑和 IRQ 中的正确读取方式。
#### Scenario: 开发者理解 DMA 缓冲切换逻辑
- **WHEN** 开发者排查行数据乱序问题时
- **THEN** 文档 SHALL 说明DVP 硬件在 DMA_BUF0/BUF1 间自动切换ROW_DONE 中断触发时硬件已切换到下一个缓冲BUF_TOG=1 表示 DMA 正在写 BUF0此时应读 BUF1公式`src = (CR1 & BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1`
#### Scenario: 开发者理解行缓冲大小约束
- **WHEN** 开发者修改传感器分辨率时
- **THEN** 文档 SHALL 说明DMA_LineBuf0/1 各 512 字节256 像素 × 2 字节COL_NUM=512ROW_NUM=1每行触发一次 IRQ修改分辨率需同时更新 `BYTES_PER_LINE``SENSOR_WIDTH``SENSOR_HEIGHT`
### Requirement: DVP IRQ 帧组装逻辑描述
文档 SHALL 描述 `DVP_IRQHandler` 的完整逻辑,包含帧同步机制和 FrameBuffer 组装方式。
#### Scenario: 开发者理解帧同步锚点
- **WHEN** 开发者排查帧错位问题时
- **THEN** 文档 SHALL 说明STR_FRM 中断VSYNC 上升沿)将 `current_line_idx` 清零此为帧同步唯一锚点ROW_DONE 中断依次将行数据 memcpy 至 `FrameBuffer[current_line_idx]` 并递增
#### Scenario: 开发者理解帧完成标志
- **WHEN** 开发者需要知道何时帧数据可用时
- **THEN** 文档 SHALL 说明:当 `idx == SENSOR_HEIGHT - 1`(即第 191 行完成)时,`Frame_Ready_Flag` 置 1`Ready_Frame_Count` 更新为当前帧号;业务任务轮询此标志,读取后将其清零
#### Scenario: 开发者理解 IRQ 使用 fast interrupt 属性的原因
- **WHEN** 开发者修改 IRQ 属性时
- **THEN** 文档 SHALL 说明:`__attribute__((interrupt("WCH-Interrupt-fast")))` 使中断处理跳过寄存器入栈,减少中断延迟,确保在下一行像素时钟到来前完成 memcpy每行约 42.67µs @ 25Hz
### Requirement: FrameBuffer 数据格式描述
文档 SHALL 描述 FrameBuffer 的内存布局和像素值含义。
#### Scenario: 开发者访问特定像素的温度值
- **WHEN** 开发者需要读取第 row 行第 col 列的温度时
- **THEN** 文档 SHALL 说明:`FrameBuffer` 声明为 `uint8_t[192][512]`,通过 `(uint16_t*)FrameBuffer` 访问,像素值 = `((uint16_t*)FrameBuffer)[row * 256 + col]`,单位 0.1°C/LSBTMP 模式,需传感器预配置为 TMP
#### Scenario: 开发者理解字节序要求
- **WHEN** 开发者移植到大端序平台时
- **THEN** 文档 SHALL 说明CH32V307 为小端序,`uint8_t[0]` = 低字节,`uint8_t[1]` = 高字节;传感器必须配置为 CMOS8(LSB)(低字节先发),否则温度值字节反序

View File

@ -0,0 +1,62 @@
# integration-guide Specification
## Purpose
TBD - created by archiving change software-design-doc. Update Purpose after archive.
## Requirements
### Requirement: 对接方网络接入参数说明
文档 SHALL 提供上位机ConfigServer与采集端建立 TCP 连接所需的全部参数。
#### Scenario: 对接方配置 TCP Server 监听端口
- **WHEN** 对接方开发服务端程序时
- **THEN** 文档 SHALL 提供MCU 默认 IP=192.168.7.10Subnet=255.255.255.0Gateway=192.168.7.1Server 需在 192.168.7.50 监听两个端口5511控制流MCU 主动连入)和 5512数据流MCU 主动连入);以太网接口为 1000M RGMII
### Requirement: 握手流程说明
文档 SHALL 描述 MCU 上电后的握手过程,使对接方能正确识别采集端设备。
#### Scenario: 对接方接收第一个数据包并识别设备
- **WHEN** MCU 连接后发送握手帧时
- **THEN** 文档 SHALL 说明MCU 在控制流连接成功后发送 TYPE_HANDSHAKEClass=CLASS_SYSTEM含 ProtocolVersion=0x0200、DeviceUUID=MAC 地址、AuthToken=全零);服务器 SHALL 回复握手响应以完成握手;握手完成后 MCU 才开始正常数据上报
### Requirement: 配置下发格式说明
文档 SHALL 描述服务器向 MCU 下发配置的帧格式和所有配置字段含义。
#### Scenario: 对接方配置 2D 触发模式
- **WHEN** 对接方发送 Config2D 配置帧时
- **THEN** 文档 SHALL 提供 Config2D_t 所有字段Enabled(uint8_t)、TriggerMode(uint8_t, 0=内部温度/1=外部GPIO)、TriggerDebounceIntervalMs(uint16_t)、TriggerDelayMs(uint16_t)、TriggerBurstCount(uint8_t)、TriggerInternalIntervalMs(uint16_t)、TriggerTemperatureThreshold(int16_t, 0.1°C/LSB)、TriggerCondition(uint8_t, 0=Avg/1=Max)、TriggerRoiX/Y/W/H(uint16_t)、TargetWidth/Height(uint16_t)、NGioDelay(uint16_t, ms)
#### Scenario: 对接方配置 1D 采集模式
- **WHEN** 对接方发送 Config1D 配置帧时
- **THEN** 文档 SHALL 提供 Config1D_t 所有字段Enabled(uint8_t)、RunMode(uint8_t, 0=STOP/1=RUN)、TriggerType(uint8_t, 1=内部/2=外部)、TriggerTempLimit(int16_t)、HighTimerLimit(uint16_t, ms)、NgCountLimit(uint16_t, 帧数)、BufferSize(uint16_t)、LSizeStart/RSizeStart(uint16_t, 切片参数)
### Requirement: 温度帧数据包格式说明
文档 SHALL 描述 MCU 上报的温度帧数据包的完整格式,使对接方能正确解析。
#### Scenario: 对接方解析收到的 2D 温度帧
- **WHEN** 数据流收到 TYPE_TEMP_FRAME 包时
- **THEN** 文档 SHALL 说明Value 区域含 PreprocessResult 元数据ValidWidth × ValidHeight 像素数组);像素为 uint16_t little-endian单位 0.1°C/LSB元数据含 MaxTemp/MinTemp/AvgTemp/RoiTemp均 int16_t, 0.1°C/LSB、ValidWidth/ValidHeight实际 ROI 尺寸、FrameNumber
#### Scenario: 对接方解析收到的 1D 温度帧
- **WHEN** 数据流收到 1D TYPE_TEMP_FRAME 包时
- **THEN** 文档 SHALL 说明is2D=0每个样本 4 字节:[time_off_lo, time_off_hi, temp_lo, temp_hi]time_offset 为相对采集开始的 ms 偏移uint16_ttemp 为 uint16_t 0.1°C/LSB
### Requirement: 触发结果上报和 NG 响应机制说明
文档 SHALL 描述采集端如何响应服务器的 DetectionResult 决策和触发 NG 输出。
#### Scenario: 对接方下发检测结果
- **WHEN** 服务器完成图像分析后回传结果时
- **THEN** 文档 SHALL 说明:服务器发送 TYPE_DETECTION_RESULT含 frameNumber、resultStatus0=NG/1=OKMCU 收到 resultStatus=0 时触发 PA8 高电平 NGioDelay ms默认 200ms对接方可通过 NGioDelay 配置 NG 输出脉宽
### Requirement: TEMP_REQ 按需截图说明
文档 SHALL 描述服务器如何向 MCU 请求即时截图。
#### Scenario: 对接方请求单帧截图
- **WHEN** 服务器需要调试或核查当前热场时
- **THEN** 文档 SHALL 说明:控制流发送 TYPE_CONFIG_COMMON 或专用 TEMP_REQ 命令(含 is2dRequest 字段MCU 将在下一个业务循环发送当前最新帧2D 返回 ROI 裁切后温度矩阵1D 返回中心行 30 个等间距采样点;仅在对应模式已 Enabled 时响应
### Requirement: 错误码和异常处理说明
文档 SHALL 列出协议层所有错误码含义及对接方应采取的处理措施。
#### Scenario: 对接方收到 ERR 响应帧
- **WHEN** 服务器发送格式错误的配置帧时
- **THEN** 文档 SHALL 列出错误码含义ERR_CRC(0x1001)=CRC 校验失败、ERR_VERSION(0x1002)=协议版本不匹配、ERR_LENGTH(0x1003)=帧长度异常、ERR_AUTH(0x2001)=认证失败、ERR_BUSY(0x2002)=设备忙、ERR_PARAM(0x3001)=参数非法;收到错误码后对接方 SHALL 检查发送的帧格式后重试

View File

@ -0,0 +1,41 @@
# qdx-protocol-design Specification
## Purpose
TBD - created by archiving change software-design-doc. Update Purpose after archive.
## Requirements
### Requirement: TLV 帧结构完整描述
文档 SHALL 描述 QDX 协议的完整帧结构包含帧头各字段含义、TLV 格式和帧尾 CRC。
#### Scenario: 开发者手动构造一个协议帧
- **WHEN** 开发者需要调试发送内容时
- **THEN** 文档 SHALL 提供帧结构:[FrameHeader 16B] + [TLV_Header 3B] + [Value NB] + [CRC16 2B],其中 FrameHeader = {Magic(2)=0x55AA, Version(1)=0x20, Length(2), Sequence(2), Timestamp(4), Source(1), DevID(2), Class(1), Flags(1)}
#### Scenario: 开发者理解 Class 和 TLV Type 的对应关系
- **WHEN** 开发者解析收到的帧时
- **THEN** 文档 SHALL 列出完整的 Class/Type 映射表CLASS_CONTROL(0x01)、CLASS_DATA(0x02)、CLASS_RESPONSE(0x03)、CLASS_SYSTEM(0x04)Type 包含 TYPE_HANDSHAKE(0x01)、TYPE_HEARTBEAT(0x02)、TYPE_TEMP_FRAME(0x10)、TYPE_CONFIG_COMMON(0x20)、TYPE_CONFIG_2D(0x22)、TYPE_CONFIG_1D(0x23)、TYPE_ACK_PAYLOAD(0x30)、TYPE_DETECTION_RESULT(0x40) 等
### Requirement: 零拷贝 TX 缓冲区架构描述
文档 SHALL 描述 TcpTxBuffer_t 的双缓冲零拷贝发送架构和 HeadOffset 的使用方式。
#### Scenario: 开发者理解零拷贝的实现方式
- **WHEN** 开发者需要在减少内存拷贝的前提下添加新的帧类型时
- **THEN** 文档 SHALL 说明:`TcpTxBuffer_t` 包含 pBuffer9216B、HeadOffset64B、ValidPayloadLen图像数据由预处理模块写入 `pBuffer + HeadOffset` 开始的位置;`TcpLogic_BuildAndSendTemperatureFrame` 在 HeadOffset 前的 64 字节内直接写入帧头FrameHeader + TLV**无需额外 memcpy**
#### Scenario: 开发者计算单次可发送的最大像素数
- **WHEN** 开发者调整 ROI 大小时
- **THEN** 文档 SHALL 说明:有效载荷容量 = TotalCapacity(9216) - HeadOffset(64) - CRC(2) - TLV_Header(3) - FrameHeader(16) = 9131 字节,每像素 2 字节,最多 4565 个像素(约 67×67 ROI
### Requirement: 分片机制描述
文档 SHALL 描述协议层的分片上限和分片标志用法。
#### Scenario: 开发者发送超大载荷时
- **WHEN** 开发者发送超过 MAX_FRAGMENT_PAYLOAD(1400B) 的数据时
- **THEN** 文档 SHALL 说明分片最大载荷为 1400B受 TCP MSS=1460 限制),大帧通过 FLAG_LAST_FRAGMENT(0x20) 标志标识末片;当前 TX 缓冲区为 9216B单次发送通常不超过此限制
### Requirement: Flags 字段用法描述
文档 SHALL 描述 FrameHeader.Flags 各位的含义和当前固件的使用情况。
#### Scenario: 开发者解析收到的 Flags 字段
- **WHEN** 上位机解析帧时
- **THEN** 文档 SHALL 说明FLAG_PRIORITY_MASK(0x03)=优先级0-3、FLAG_COMPRESSED(0x04)=压缩当前未使用、FLAG_ENCRYPTED(0x08)=加密当前未使用、FLAG_ACK_REQ(0x10)=需要 ACK、FLAG_LAST_FRAGMENT(0x20)=末片标志

View File

@ -0,0 +1,60 @@
# system-overview Specification
## Purpose
TBD - created by archiving change software-design-doc. Update Purpose after archive.
## Requirements
### Requirement: FreeRTOS 任务拓扑描述
文档 SHALL 列出所有 RTOS 任务的名称、优先级、栈大小、职责及任务间通信方式。
#### Scenario: 开发者查阅任务优先级
- **WHEN** 开发者需要了解任务调度顺序时
- **THEN** 文档 SHALL 提供任务优先级表包含task_wchnet优先级 6512 字、task_business优先级 5512 字、task_heartbeat优先级 3256 字、task_test_pattern优先级 4256 字,仅 TEST_PATTERN_MODE=1 时存在)
#### Scenario: 开发者理解任务间数据流
- **WHEN** 开发者需要理解 DVP → 业务任务的数据传递时
- **THEN** 文档 SHALL 说明通过 `Frame_Ready_Flag`volatile 标志)和 `FrameBuffer`共享内存传递帧数据IRQ 置位,业务任务轮询消费
### Requirement: 系统启动序列描述
文档 SHALL 描述 `main()` 的初始化顺序,涵盖所有外设和模块的初始化步骤及其依赖关系。
#### Scenario: 开发者定位初始化顺序问题
- **WHEN** 开发者遇到启动相关 bug 时
- **THEN** 文档 SHALL 提供启动序列SystemClock → USART3/debug → Flash/SRAM 配置128K+192K→ DVP_Init → TIM2_Init → ExtTrigger_GPIO → NG_GPIO → ETH_LibInit → qdx_port_init → Preprocess_Init → TcpLogic_Init → 注册回调 → TcpLogic_Start → 创建 RTOS 任务 → vTaskStartScheduler
### Requirement: 2D 状态机完整描述
文档 SHALL 描述 2D 触发状态机的所有状态、转换条件和行为,包含外部触发和内部触发两种模式。
#### Scenario: 开发者理解 2D 外部触发流程
- **WHEN** 开发者排查外部触发抖动问题时
- **THEN** 文档 SHALL 描述IDLE → [PA15 上升沿] → DEBOUNCE等待 DebounceIntervalMs → DELAY等待 TriggerDelayMs → BURST发 BurstCount 帧,间隔 TriggerInternalIntervalMs → IDLE
#### Scenario: 开发者理解 2D 内部触发流程
- **WHEN** 开发者配置温度阈值触发时
- **THEN** 文档 SHALL 描述:每帧调用 `Preprocess_CheckInternalTrigger2D`,在 TriggerRoi 区域内 Max/Avg 超过 TriggerTemperatureThreshold 时触发,跳过 DEBOUNCE 直接进入 DELAY → BURST
#### Scenario: 开发者理解双缓冲 TX 逻辑
- **WHEN** 开发者排查发送顺序问题时
- **THEN** 文档 SHALL 说明 `use_buffer_A` 标志在每次发送时取反g_TxNetBuffer_A / g_TxNetBuffer_B 交替使用,避免前一帧发送未完成时被覆盖
### Requirement: 1D 状态机完整描述
文档 SHALL 描述 1D 采集状态机的所有状态、转换条件、采集逻辑和发送条件。
#### Scenario: 开发者理解 1D 外部触发流程
- **WHEN** 开发者配置 TriggerType=2 时
- **THEN** 文档 SHALL 描述S1D_IDLE → [PA15 上升沿] → S1D_DEBOUNCE等待 HighTimerLimit ms → S1D_COLLECTING采集 temp+time 样本直到停止条件)→ 发送 → S1D_IDLE
#### Scenario: 开发者理解 1D 内部触发流程
- **WHEN** 开发者配置 TriggerType=1 时
- **THEN** 文档 SHALL 描述S1D_IDLE 中维护 3 帧滑动窗口,连续 3 帧帧最大温度 ≥ TriggerTempLimit 时进入 S1D_COLLECTING停止条件样本数达 BufferSize 或触发后连续 NgCountLimit 帧低温
#### Scenario: 开发者理解 1D 数据格式
- **WHEN** 开发者解析 1D 数据时
- **THEN** 文档 SHALL 说明每个样本 4 字节:[time_offset_lo, time_offset_hi, temp_lo, temp_hi]time_offset 为相对采集开始的 ms 偏移temp 为 0.1°C/LSB 的 uint16_t
### Requirement: TEMP_REQ 辅助通道描述
文档 SHALL 描述服务器按需截图TEMP_REQ的触发流程和限制条件。
#### Scenario: 开发者理解 TEMP_REQ 工作方式
- **WHEN** 服务器发送 TEMP_REQ 命令时
- **THEN** 文档 SHALL 说明:`g_temp_req_pending` 置位,业务任务在下次循环读取当前 FrameBuffer 发送is2D=1 走 Preprocess_Execute + 2D 封包is2D=0 走 send_1d_snapshot30 个等间距采样点);仅在对应模式 Enabled 时响应

View File

@ -0,0 +1,48 @@
# tcp-module-design Specification
## Purpose
TBD - created by archiving change software-design-doc. Update Purpose after archive.
## Requirements
### Requirement: 双流连接架构描述
文档 SHALL 描述控制流5511和数据流5512的职责分工、连接方式和状态管理。
#### Scenario: 开发者理解双流设计意图
- **WHEN** 开发者排查连接问题时
- **THEN** 文档 SHALL 说明:控制流 srcport=5511MCU 主动连接 Server用于握手、配置下发、心跳、TEMP_REQ、DetectionResult 上报;数据流 desport=5512MCU 主动连接 Server用于温度帧数据上报两流独立管理数据流断开不影响控制流
#### Scenario: 开发者理解 TCP 连接建立时序
- **WHEN** 开发者排查首次握手失败时
- **THEN** 文档 SHALL 说明:`TcpLogic_Start` 派生后台连接状态机,先尝试控制流连接 → 连接成功后发送 TYPE_HANDSHAKE含 DeviceUUID=MAC地址、AuthToken=NULL→ 收到服务器握手响应后标记连接就绪 → 同步尝试数据流连接
### Requirement: 心跳机制描述
文档 SHALL 描述心跳帧的发送周期、TCP Keepalive 配置和连接保活策略。
#### Scenario: 开发者调整心跳参数
- **WHEN** 网络中间件NAT/防火墙)超时断连时
- **THEN** 文档 SHALL 说明:应用层心跳 TYPE_HEARTBEAT 由 TcpLogic 内部定时发送TCP Keepalive 配置:空闲 20 秒(`idle=20000ms`)后开始探测,每次间隔 15 秒(`interval=15000ms`),最多 9 次(`count=9`)探测无响应则断链重连
### Requirement: 配置下发与缓存机制描述
文档 SHALL 描述服务器配置的接收、缓存和通知流程。
#### Scenario: 开发者追踪配置更新流程
- **WHEN** 服务器下发配置后行为未按预期改变时
- **THEN** 文档 SHALL 描述:服务器发送 TYPE_CONFIG_COMMON + TYPE_CONFIG_2D + TYPE_CONFIG_1D → TcpLogic 解析并缓存至内部 shadow 寄存器 → 触发 ConfigUpdateCallback`OnConfigUpdate`)→ 回调内调用 `Preprocess_Settings_Change` 更新预处理参数;`TcpLogic_GetLatestConfig` 可随时读取最新配置副本
#### Scenario: 开发者理解无配置时的默认行为
- **WHEN** 设备启动后服务器尚未下发配置时
- **THEN** 文档 SHALL 说明:`TcpLogic_GetLatestConfig` 返回 -1业务任务跳过 2D/1D 处理,仅响应 TEMP_REQ若有默认 burst 参数 DEFAULT_BURST_COUNT=3、DEFAULT_BURST_INTERVAL_MS=200ms 作为 fallback
### Requirement: 数据发送队列和背压处理描述
文档 SHALL 描述温度帧发送的队列机制和发送失败时的处理策略。
#### Scenario: 开发者排查数据帧丢失问题
- **WHEN** 网络繁忙时出现帧丢失时
- **THEN** 文档 SHALL 说明:`TcpLogic_BuildAndSendTemperatureFrame` 返回 0 表示成功入队,返回 <0 表示发送失败队列满或连接断开当前固件对发送失败仅打印 DBG_ERR不重试双缓冲 TX 保证当前帧被入队时下一帧可立即写入另一个缓冲
### Requirement: WCHNET 网络栈驱动任务描述
文档 SHALL 描述 task_wchnet 的职责和与 WCHNET 库的交互方式。
#### Scenario: 开发者理解网络栈如何被驱动
- **WHEN** 开发者排查网络响应迟缓问题时
- **THEN** 文档 SHALL 说明:`task_wchnet_entry` 每 5ms 轮询一次,调用 `WCHNET_MainTask`(协议栈心跳)和 `WCHNET_HandleGlobalInt`socket 事件分发socket 接收/连接/断开事件通过 `qdx_port_sock_*_notify` 转发给 QDX 网络层;`qdx_port_net_lock/unlock` 用互斥量保护 WCHNET 调用,防止业务任务和网络任务并发访问