105
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-13
|
||||
@@ -1,27 +0,0 @@
|
||||
## Context
|
||||
|
||||
目前项目采用 CH32V30x 微控制器的 DVP (Digital Video Port) 外设,结合 DMA 进行红外图像数据的采集。
|
||||
为保证后续的图像处理或网络传输顺利进行,必须确认目前的 DVP 和 DMA 的配置是正确的,能够稳定、及时、无损地将红外图像帧搬运到 RAM 区,并正确处理帧中断。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 验证 DVP 引脚配置、时序极性和中断设置是否符合所搭配红外传感器输出时序要求。
|
||||
- 验证 DMA 数据搬运目标地址是否正确分配且对齐,大小是否匹配一帧红外数据。
|
||||
- 确认 DMA/DVP 中断服务程序(ISR)中能有效判断帧结束和 DMA 传输完成,不发生漏帧或混乱。
|
||||
- 添加必要的轻量级日志或断点测试方法来确认取到的数据。
|
||||
|
||||
**Non-Goals:**
|
||||
- 不涉及深度的图像识别或处理算法的开发。
|
||||
- 不涉及改变传感器本身的输出模式配置(除非影响获取数据的基础正确性)。
|
||||
|
||||
## Decisions
|
||||
|
||||
- **检查代码而不是立即重写**: 首先静态走查相关的配置(如 `dvp.c`, `dvp.h`),与 CH32 参考手册进行比对。
|
||||
- **内存块分配策略验证**: 确定 DVP DMA 配置的两块(双缓冲或单缓冲)地址机制,确认没有指针越界风险。
|
||||
- **测试方法**: 在帧结束中断或主循环中计算短时间内获取的有效帧数,并提取部分像素输出串口打印。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Risk] 数据量大可能引起总线或者处理迟滞 → 中断处理要足够短小,不能阻塞下一次 DMA。
|
||||
- [Risk] 直接修改中断服务可能会影响原有稳定的网络栈通信延时 → 在验证阶段避免添加过多阻塞的 printf 等操作。
|
||||
@@ -1,23 +0,0 @@
|
||||
## Why
|
||||
|
||||
The current project utilizes the DVP peripheral along with DMA to acquire infrared image data. It is crucial to verify that this configuration operates normally, correctly buffers the frames without corruption, and properly handles interrupts. Ensuring this functionality is fundamental to any downstream vision processing or data transmission.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Comprehensive review of the DVP and DMA initialization configuration.
|
||||
- Verification of memory allocation, frame buffer sizes, and alignment for IR data.
|
||||
- Validation of the DMA complete/error interrupts and data handling routines.
|
||||
- If necessary, adding or enabling diagnostic outputs (logs) or tests to confirm data integrity.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `dvp-dma-ir-capture`: Verifying and ensuring the reliable capture of infrared image data via the CH32V30x DVP peripheral and DMA channels.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
- DVP and DMA initialization code (`prj/TCPClient/Debug/dvp.c`, `prj/TCPClient/Debug/dvp.h`).
|
||||
- Interrupt service routines for DMA/DVP (`prj/TCPClient/User/ch32v30x_it.c`, `prj/TCPClient/User/main.c`).
|
||||
- System memory and buffering allocations.
|
||||
@@ -1,22 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: DVP configuration verification
|
||||
系统必须具备按预期传感器时序要求正确初始化的外设 DVP,并在捕捉时确保引脚映射和控制极性符合期望。
|
||||
|
||||
#### Scenario: DVP Peripheral correctly starts capture
|
||||
- **WHEN** 系统启动且开启红外捕捉模式
|
||||
- **THEN** DVP 能够捕捉到有效场同步和行同步信号并进行同步
|
||||
|
||||
### Requirement: DMA memory transport correctness
|
||||
DMA 通道必须绑定至 DVP 数据寄存器,并以与图像帧空间匹配(甚至双缓冲)的配置分配目的内存区域,地址不得非法越界。
|
||||
|
||||
#### Scenario: Frame mapped successfully to RAM buffer
|
||||
- **WHEN** DVP 传输满一整帧
|
||||
- **THEN** DMA 完整将其写入指定的 RAM buffer 并生成 DMA 满完成中断。
|
||||
|
||||
### Requirement: Diagnostic outputs for infrared data validation
|
||||
应当包含不阻塞中断流的轻微日志来输出当前捕捉到的前 N 个像素或帧计数,以在运行时供检查验证。
|
||||
|
||||
#### Scenario: User checks IR camera status from terminal
|
||||
- **WHEN** 持续采集数据
|
||||
- **THEN** 主循环能够根据完成标志打印当前帧率及简易采集信息进行调试。
|
||||
@@ -1,17 +0,0 @@
|
||||
## 1. 代码配置静态走查
|
||||
|
||||
- [x] 1.1 审查 `prj/TCPClient/Debug/dvp.c` 中的 DVP GPIO 引脚初始化,验证时钟/数据/同步极性设定。
|
||||
- [x] 1.2 审查 `dvp.c` 中相关的 DMA (通常是 DMA2) 源地址和宿地址的初始化是否将 DVP R13 数据正确搬移到 RAM 中。
|
||||
- [x] 1.3 确认定义的 Frame Buffer 内存大小和对齐方式能够完整装载设定的红外单帧尺寸。
|
||||
|
||||
## 2. 检查中断处理和运行时行为
|
||||
|
||||
- [x] 2.1 审查 `prj/TCPClient/User/ch32v30x_it.c` 中的 DMA 中断服务函数和 DVP 帧完成中断,评估是否能可靠地设置标志位。
|
||||
- [x] 2.2 审查主循环 `main.c` 中关于采集标志位的判定清除以及 Buffer 锁机制。
|
||||
- [x] 2.3 (如果原存在)确保代码不存在意外清除或漏检标志位的竞态条件。
|
||||
|
||||
## 3. 添加初步的诊断验证方法
|
||||
|
||||
- [x] 3.1 在主循环成功获取完整一帧处,添加一个基于计数的帧率和帧数调试 `printf` 打印输出。
|
||||
- [x] 3.2 打印接收到的图像数据的前几个字节,用于人工核对同步字或者数据有效性。
|
||||
- [x] 3.3 编译并运行工程,监控终端输出以给出当前 DVP 和 DMA 是否能正常获取红外数据的测试结论。
|
||||
@@ -1,2 +0,0 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-13
|
||||
@@ -1,29 +0,0 @@
|
||||
## Context
|
||||
|
||||
目前的系统已经具备了以太网 TCP Socket 基础和 DVP+DMA 红外数据矩阵的采集能力。然而主程序目前仅仅是将 DVP 采集到的流进行无差别循环上报,没有执行复杂的预处理逻辑。
|
||||
根据给定的架构设计文档 (`CH32二维运行结构概览.md` 与 `函数调用指南.md`),我们需要:
|
||||
1. 导入由第三方或算法工程师提供的闭源/封装好的预处理算法库 (`Preprocess_`) 和底层 TCP 控制流协议封装 (`TcpLogic_`)。
|
||||
2. 废弃当前无差别的整帧暴力上传阻塞逻辑。
|
||||
3. 利用 `TcpTxBuffer` 的带有 offset 缓冲池机制实现零拷贝的网络流装配上传。
|
||||
4. 提供上位机参数回调和错误处理功能(如通过 `TcpLogic_RegisterConfigCallback` 和 NG 控制逻辑)。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 在 `main.c` 和 `dvp.c` 中成功注册、初始化并接入 `Preprocess_xxx` 与 `TcpLogic_xxx` 的 API 生命周期。
|
||||
- 实现一个不产生内存额外拷贝 (Zero-Copy) 的缓冲区数据流,由预处理根据特定感兴趣区域截取有效温度点后,送入 TCP 模块利用预留位添加封包帧头发出。
|
||||
- 加入上位机动态热更新配置和外部气缸硬件IO回调处理。
|
||||
|
||||
**Non-Goals:**
|
||||
- 具体的红外滑窗滤波或 TCP 报文组装的底层逻辑编写(这部分属于已封装库,只做 API 调用层面的业务整合)。
|
||||
|
||||
## Decisions
|
||||
|
||||
- **API 包装策略**: 为了避免 DVP 中断卡死主线程流,将 DVP 完成一帧(软触发)的逻辑校验放到业务主循环 `main` 之内。DVP 中断仅仅传递 `Line_Ready_Flag` 或全局的缓冲区双指针结束标志给主循环去执行高耗时的 `Preprocess_CheckInternalTrigger2D` 操作。
|
||||
- **内存池预分配模型**: 在全局申请两个支持偏移量设定的发送大缓存 (`TcpTxBuffer_t`) 并常驻内存,确保网络封装只需向 Buffer 首部写入 TLV 标头而无需搬运大块图像数据。
|
||||
- **配置与剔除反馈同步**: 所有与上位机的异步通信指令解析都由 `TcpLogic` 完成,主程序在此阶段只需挂载好 callback 并使用 `volatile` 配置影寄存器或标志位与主循环交互即可。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Risk] 零拷贝模型的数组越界风险 → 确保 `Preprocess` 的目标裁断 Buffer 的尺寸 > (`Target Width*Height*2 + HeadOffset`) 且首字节地址分配精确。
|
||||
- [Risk] DMA 和 主循环取图并发导致冲突 (Tearing) → 要求 DVP 在更新下一帧时利用乒乓机制避免同址同时写读,主逻辑处理时需进行资源锁屏。
|
||||
@@ -1,25 +0,0 @@
|
||||
## Why
|
||||
|
||||
当前 CH32V30x 项目已经实现了基础的 ETH 和 DVP DMA 数据采集,但缺乏核心的业务逻辑运转。根据《CH32二维运行结构概览》和《函数调用指南》,我们需要将采集到的原始图像数据进行“掩膜过滤 - 滑窗极值捕捉 - 预处理裁切”,然后通过自定义的 TCP 栈进行零拷贝封装与双流(5511控制流、5512数据流)通信,形成完整的闭环机制。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 引入并使用 `Preprocess_` 相关 API 进行内部软触发判定与数据裁切(滑窗算法)。
|
||||
- 引入外部分配缓冲内存池 `TcpTxBuffer_t`。
|
||||
- 修改主循环/中断,加入触发判断。
|
||||
- 采用 `TcpLogic_` 库提供的封包方法替换现有的普通 TCP 发送。
|
||||
- 加入上位机动态参数热更新的更新回调 `TcpLogic_RegisterConfigCallback` 机制和 NG 外部继电器 (DO) 控制逻辑。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `image-preprocess-filter`: 处理红外传感器捕获画面的掩膜过滤、滑动窗口平均温极值捕捉以及零拷贝剪裁提取。
|
||||
- `tcp-stream-logic`: 控制流(5511)与数据流(5512)管理、重连、TLV 封包与零拷贝发送,及热更新回调注册等功能。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
- 受影响的文件主要在主应用层:`prj/TCPClient/User/main.c` (调度流血重构)。
|
||||
- 内存分配策略改变(需要保留 `HeadOffset` 并在全局开辟外部分配预留池)。
|
||||
- 依赖网络基础模块提供的 `qdx_port_tcp_send` 接口和硬件 IO 库(若要驱动剔除气缸/报警灯)。
|
||||
@@ -1,15 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Matrix triggering via Preprocess library
|
||||
系统必须能够在主循环中捕获到 DVP 采集到的满帧,并将图像缓冲(按照 `RawImageBuffer_t` 结构体规范)投喂给 `Preprocess_CheckInternalTrigger2D` 以判断是否满足内部的高温触发标准。
|
||||
|
||||
#### Scenario: Full frame is captured and inspected
|
||||
- **WHEN** DVP 产生满帧完成标志位并被主程序获取时
|
||||
- **THEN** 单片机将调用触发函数,并在命中时冻结该目标帧缓冲阻隔覆盖。
|
||||
|
||||
### Requirement: Zero-Copy cropping algorithm extraction
|
||||
利用前向保留(Offset)特性规划的缓冲池(`TcpTxBuffer_t`),将锁定的被触发二维矩阵图像原始数据通过 `Preprocess_Execute` 去掉无关低温部分后提取并原样写入缓冲有效区域内。
|
||||
|
||||
#### Scenario: Sub-frame extracting with proper length
|
||||
- **WHEN** 图像完成触发并执行截断输出操作时
|
||||
- **THEN** `PreprocessResult_t` 中的统计结果将被正确填充,且目标数据只在 `pBuffer + HeadOffset` 开始出现,不在内存间产生额外拷贝拖死中断。
|
||||
@@ -1,26 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Independent Twin-Port connection tracking
|
||||
必须引入并运用 `TcpLogic_Start` 抽象接管双端口(5511, 5512) TCP 栈的心跳维持与发包等工作。不再直接向 CH32 WCHNET Socket 原口写入业务。
|
||||
|
||||
#### Scenario: Business TCP tasks initialize and start
|
||||
- **WHEN** main 函数结束物理层网卡 ETH Setup 之后
|
||||
- **THEN** 调用 `TcpLogic_Init` 并传入 UUID 开启内部监控服务,随后的状态流转由库自动接管。
|
||||
|
||||
### Requirement: Offset-based direct packetization
|
||||
调用 `TcpLogic_BuildAndSendTemperatureFrame` 获取带偏移量的截断目标发送缓冲 `TcpTxBuffer_t`。由库在头部(`HeadOffset`)安全组装 TLV 等标头进而发送到 5512 端口发出。完全消除向以太网 Socket 递送前的高耗时大范围 Memcpy 拼包。
|
||||
|
||||
#### Scenario: Sub-frame sent efficiently after processed
|
||||
- **WHEN** 触发的图像提取阶段完成后提交至 TCP 管理模块
|
||||
- **THEN** 系统通过位运算写入帧头与校验位,在指定字节空间内原地打包并最终推入以太网发送缓冲区。
|
||||
|
||||
### Requirement: Remote Parameters updating via callbacks
|
||||
必须注册实现监听由上位机下发的配置动作以及废料剔除等行为(基于 `ConfigUpdateCallback_t` 和 `DetectionResultCallback_t` 句柄),让这批回调充当系统动态控制信号输入源。
|
||||
|
||||
#### Scenario: Update Triggering config on the fly
|
||||
- **WHEN** 通过 5511 端口收到控制主机的有效组包设置参数报文时
|
||||
- **THEN** 主程序的事件机制会触发,自动更新 `Config2D_t` 结构体并通过 `Preprocess_Settings_Change` 影响处理流。
|
||||
|
||||
#### Scenario: NG defect removal trigger by Host
|
||||
- **WHEN** 上台判别系统对某帧评估失败(NG)并发回报错标志包时
|
||||
- **THEN** `OnHardwareReject` 回调函数被触发并让设定的 DO GPIO 电平翻转以排废。
|
||||
@@ -1,19 +0,0 @@
|
||||
## 1. 原网络及采集链路调整与重构
|
||||
|
||||
- [ ] 1.1 在 `prj/TCPClient/User/main.c` 中删除或注释掉陈旧的一般 TCP 阻塞式轮询测试发送代码/函数。
|
||||
- [ ] 1.2 在全局预分配 2 个带偏移量预留空间的网络封包静态池结构 `TcpTxBuffer_t`。
|
||||
- [ ] 1.3 移除 DVP 中断或其附带任务中直接封包或打印大量数据的行为,只保留 `Line_Ready_Flag` 或向外吐出可用 Buffer 的指针指示功能。
|
||||
|
||||
## 2. 图像预处理流水线接入
|
||||
|
||||
- [ ] 2.1 在 `main` 初始化中添加 `Preprocess_Init` 例程并配置默认的二维参数最大宽高。
|
||||
- [ ] 2.2 在主循环里,当获取到一完整帧缓存后,包装成 `RawImageBuffer_t`。
|
||||
- [ ] 2.3 调用 `Preprocess_CheckInternalTrigger2D` 进行掩膜触发扫描校验,如果返回值验证被触发通过(1),进入提取分支,否则略过。
|
||||
- [ ] 2.4 在触发分支内应用被锁定的物理 Frame 内存和空闲的一侧 `TcpTxBuffer`,调用 `Preprocess_Execute` 零拷贝执行滑窗提取操作获取有效数据负载与温度统计极值。
|
||||
|
||||
## 3. TCP 协议接管与外部交互
|
||||
|
||||
- [ ] 3.1 在 `main` 初始化网卡层等底层网络完成后,调用 `TcpLogic_Init` 和 `TcpLogic_Start`。
|
||||
- [ ] 3.2 定义系统被上位机修改参数时的回调 `ConfigUpdateCallback_t`,在其内部联级调用 `Preprocess_Settings_Change` 来影响全局阈值。
|
||||
- [ ] 3.3 定义接收上位机检测下发判断的 `DetectionResultCallback_t`,预留好对某一设定 GPIO 口拉高(作为报警/废品剔除气缸动作)响应函数。
|
||||
- [ ] 3.4 经过 `Preprocess_Execute` 获取到的有效内容数组,作为参量通过调用 `TcpLogic_BuildAndSendTemperatureFrame` 执行偏移组包完成以太网 DMA 数据包压入队列工作。
|
||||
@@ -1,2 +0,0 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-14
|
||||
@@ -1,93 +0,0 @@
|
||||
## Context
|
||||
|
||||
当前 TCPClient 工程运行在裸机模式下,主循环直接轮询 WCHNET 和 DVP 任务。QDX 网络栈的 `qdx_tcp_logic` 被设计为多线程架构(3 个后台线程:1 个连接管理 + 2 个接收线程),依赖 `qdx_port.h` 定义的 11 个 HAL 函数(线程、互斥锁、延时、TCP socket),但这些函数在 `qdx_port_template.c` 中全部为空 stub。
|
||||
|
||||
关键约束:
|
||||
- CH32V307 拥有 64KB SRAM,FreeRTOS 堆配置为 12KB
|
||||
- WCHNET 是 WCH 私有 TCP/IP 协议栈,**非 BSD socket 模型**——采用中断回调通知 + 同步轮询接收的混合模式
|
||||
- WCHNET 必须周期性调用 `WCHNET_MainTask()` 和全局中断处理才能驱动协议栈运转
|
||||
- 当前 `net_config.h` 中 `WCHNET_NUM_TCP = 1`,但双流架构(5511 控制流 + 5512 数据流)需要 2 个 TCP socket
|
||||
- FreeRTOS 的 RISC-V 移植已存在于 `prj/FreeRTOS_Core/FreeRTOS/portable/GCC/RISC-V/`
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 实现 `qdx_port.c` 全部 11 个 HAL 函数,使 `qdx_tcp_logic` 三线程架构实际运行
|
||||
- 将裸机主循环迁移为 FreeRTOS 多任务模型
|
||||
- 桥接 WCHNET 回调模型与 `qdx_port_tcp_recv` 的阻塞/半阻塞语义
|
||||
- 补全 `OnConfigUpdate` 和 `OnDetectionResult` 回调逻辑
|
||||
|
||||
**Non-Goals:**
|
||||
- 不修改 `qdx_tcp_logic.c`、`qdx_protocol.c`、`qdx_preprocess.c` 的内部逻辑
|
||||
- 不实现外部硬件 DI 触发模式和连拍功能(属于后续 change)
|
||||
- 不移植到 LwIP 或其他 TCP/IP 协议栈
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1:FreeRTOS 任务划分
|
||||
|
||||
将系统拆分为 4+3 个 FreeRTOS 任务:
|
||||
|
||||
| 任务 | 优先级 | 栈大小 | 职责 |
|
||||
|------|--------|--------|------|
|
||||
| `task_wchnet` | 6 (高) | 1024 words | 周期调用 `WCHNET_MainTask()` + 全局中断处理 |
|
||||
| `task_business` | 5 | 1024 words | DVP 采集轮询 + 触发判定 + 预处理 + 封包发送 |
|
||||
| `tcp_mgr` | 3 | 512 words | 由 `TcpLogic_Start()` 创建,连接管理与心跳 |
|
||||
| `tcp_rx_c` | 4 | 512 words | 由 `TcpLogic_Start()` 创建,控制流接收 |
|
||||
| `tcp_rx_d` | 4 | 512 words | 由 `TcpLogic_Start()` 创建,数据流接收 |
|
||||
|
||||
**理由**:`task_wchnet` 优先级最高,因为 WCHNET 协议栈需要及时处理底层以太网帧和 TCP 状态机;`task_business` 次之,确保 DVP 帧不丢失;TCP 后台线程优先级最低,属于非实时任务。
|
||||
|
||||
**备选方案**:将 WCHNET 轮询放在定时器回调中而非独立任务——但 `WCHNET_MainTask()` 执行时间不确定,不适合放在中断上下文。
|
||||
|
||||
### Decision 2:WCHNET 接收桥接方案——信号量 + 环形缓冲
|
||||
|
||||
WCHNET 通过 `SINT_STAT_RECV` 中断通知数据到达,而 `qdx_port_tcp_recv` 被调用方期望为可阻塞/超时返回。桥接方案:
|
||||
|
||||
1. 为每个 socket 维护一个**接收环形缓冲区**(`RxRingBuf`,2920 字节)和一个**二值信号量**(`xSemaphoreRx`)
|
||||
2. 在 `WCHNET_HandleSockInt` 的 `SINT_STAT_RECV` 分支中:调用 `WCHNET_SocketRecv()` 将数据读入 `RxRingBuf`,然后 `xSemaphoreGiveFromISR(xSemaphoreRx)`
|
||||
3. `qdx_port_tcp_recv()` 实现:先检查 `RxRingBuf` 是否有数据,有则直接拷贝返回;无则 `xSemaphoreTake(xSemaphoreRx, pdMS_TO_TICKS(100))` 阻塞等待最多 100ms,超时返回 0
|
||||
|
||||
**理由**:这种方式让 recv 线程在无数据时让出 CPU(通过信号量阻塞),同时避免了忙等 10ms 轮询的 CPU 浪费。环形缓冲解耦了中断上下文读取和应用层消费的速率差异。
|
||||
|
||||
**备选方案**:FreeRTOS Stream Buffer——语义更匹配但引入额外依赖,且 WCHNET 的 `SocketRecv` 已提供了长度信息,环形缓冲更简单可控。
|
||||
|
||||
### Decision 3:Socket 映射管理
|
||||
|
||||
WCHNET 使用 `uint8_t socketid`(0~30)标识 socket,而 `qdx_port.h` 使用 `void* qdx_socket_t` 不透明句柄。映射方案:
|
||||
|
||||
- 维护一个静态数组 `SocketCtx_t g_sock_ctx[MAX_SOCKETS]`(MAX_SOCKETS = 2),每个元素包含:
|
||||
- `uint8_t wchnet_sock_id` — WCHNET socket ID
|
||||
- `uint8_t connected` — 是否已连接
|
||||
- `RxRingBuf_t rx_ring` — 接收环形缓冲
|
||||
- `SemaphoreHandle_t rx_sem` — 接收通知信号量
|
||||
- `qdx_port_tcp_connect()` 分配空闲的 `SocketCtx_t`,调用 `WCHNET_SocketCreat` + `WCHNET_SocketConnect`,返回 `&g_sock_ctx[i]` 作为句柄
|
||||
- `qdx_port_tcp_send/recv/close()` 从句柄中提取 `wchnet_sock_id` 操作 WCHNET API
|
||||
|
||||
**Config 变更**:`WCHNET_NUM_TCP` 从 1 改为 2,`WCHNET_MAX_SOCKET_NUM` 相应变为 2。
|
||||
|
||||
### Decision 4:TIM2 共享——FreeRTOS Tick + WCHNET Timer
|
||||
|
||||
当前 TIM2 以 10ms 周期驱动 `WCHNET_TimeIsr()`。FreeRTOS 需要 2ms tick(`configTICK_RATE_HZ = 500`)。方案:
|
||||
|
||||
- 将 TIM2 周期改为 **2ms**(匹配 FreeRTOS tick)
|
||||
- TIM2 ISR 中每次调用 `xPortSysTickHandler()`(FreeRTOS tick)
|
||||
- 设软件计数器,每累计 5 次(= 10ms)调用一次 `WCHNET_TimeIsr(WCHNETTIMERPERIOD)`
|
||||
|
||||
**理由**:共用一个硬件定时器节省外设资源,软件分频几乎无开销。
|
||||
|
||||
### Decision 5:`qdx_port_tcp_connect` 中的连接等待
|
||||
|
||||
WCHNET 的 `WCHNET_SocketConnect()` 是异步的——它发起三次握手后立即返回,连接完成通过 `SINT_STAT_CONNECT` 中断通知。但 `qdx_port_tcp_connect` 语义是阻塞直到连接建立。
|
||||
|
||||
方案:在 `SocketCtx_t` 中增加 `SemaphoreHandle_t connect_sem`。调用 `WCHNET_SocketConnect` 后 `xSemaphoreTake(connect_sem, pdMS_TO_TICKS(5000))` 阻塞。在 `SINT_STAT_CONNECT` 回调中 `xSemaphoreGive(connect_sem)`。超时返回 NULL。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[RAM 占用偏紧]** → FreeRTOS 堆 12KB + 5 个任务栈约 9KB + WCHNET 内部约 10KB + 2×2920 socket 缓冲 + 2×2920 环形缓冲 + 2×10KB 发送缓冲区 ≈ 52KB / 64KB。**缓解**:严格控制任务栈大小,使用 `uxTaskGetStackHighWaterMark` 运行时监测;发送缓冲保持 10KB 不变(已经预分配)。如仍紧张可将 FreeRTOS 堆缩减至 8KB。
|
||||
|
||||
**[WCHNET 非线程安全]** → WCHNET API 未声明线程安全性,多个 FreeRTOS 任务可能并发调用 send/recv。**缓解**:所有 WCHNET API 调用(send、recv、socket 操作)统一通过 `task_wchnet` 任务的消息队列委托执行,或者使用全局互斥锁保护。初期采用互斥锁方案,复杂度更低。
|
||||
|
||||
**[中断上下文限制]** → `WCHNET_HandleSockInt` 在中断上下文被调用(通过 `WCHNET_HandleGlobalInt` → TIM2/ETH ISR 链),其中调用 `WCHNET_SocketRecv` 和 `xSemaphoreGiveFromISR` 必须确保安全。**缓解**:将 `WCHNET_HandleGlobalInt` 移出 ISR,改为在 `task_wchnet` 中轮询调用 `WCHNET_QueryGlobalInt`,这样 recv 回调运行在任务上下文,可安全使用 FreeRTOS API。
|
||||
|
||||
**[双 Socket 内存增长]** → `WCHNET_NUM_TCP` 从 1 变为 2,`WCHNET_MEM_HEAP_SIZE` 和 `WCHNET_NUM_POOL_BUF` 相应增加约 3KB。**缓解**:CH32V307 有 64KB SRAM,增量可接受。
|
||||
@@ -1,31 +0,0 @@
|
||||
## Why
|
||||
|
||||
当前 QDX 网络栈的硬件抽象层 (`qdx_port_template.c`) 全部 11 个函数均为空 stub,导致 `qdx_tcp_logic` 的双流管理、心跳重连以及 `qdx_preprocess` 的并发互斥等核心功能无法真正运行。该 port 层需要对接 CH32V307 平台的 WCHNET 协议栈和 FreeRTOS 操作系统原语,使整条"采集→预处理→TCP 封包发送"的业务流水线实际贯通。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 将 FreeRTOS 内核合入 TCPClient 工程(源码已存在于 `prj/FreeRTOS_Core/`),完成编译链接集成。
|
||||
- 新增 `qdx_port.c`(基于 `qdx_port_template.c`),使用 FreeRTOS + WCHNET API 实现全部 port 函数:
|
||||
- 时间与延时:`qdx_port_get_tick_ms` / `qdx_port_delay_ms` → `xTaskGetTickCount` / `vTaskDelay`。
|
||||
- 互斥锁:`qdx_port_mutex_*` → `xSemaphoreCreateMutex` / `xSemaphoreTake` / `xSemaphoreGive`。
|
||||
- 线程:`qdx_port_thread_create` → `xTaskCreate`。
|
||||
- TCP Socket:`qdx_port_tcp_connect` / `send` / `recv` / `close` → 封装 WCHNET 的 socket 创建、`WCHNET_SocketSend`、接收回调缓冲与 `WCHNET_SocketClose`。
|
||||
- **BREAKING**:`main.c` 主循环重构为 FreeRTOS 任务调度模型,`main()` 末尾调用 `vTaskStartScheduler()` 替代裸机 `while(1)` 轮询。
|
||||
- 补全 `OnConfigUpdate` 回调,联级调用 `Preprocess_Settings_Change` 使上位机参数热更新生效。
|
||||
- 补全 `OnDetectionResult` 回调,实现 NG 剔除 GPIO DO 输出及定时器延时复位。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `freertos-wchnet-port`: 基于 FreeRTOS 和 WCHNET 实现 `qdx_port.h` 定义的全部硬件/OS 抽象接口(时间、延时、互斥锁、线程、TCP socket),使 QDX 网络栈在 CH32V307 上实际运行。
|
||||
|
||||
### Modified Capabilities
|
||||
- `tcp-stream-logic`: 回调实现补全——`ConfigUpdateCallback_t` 内联级调用 `Preprocess_Settings_Change`;`DetectionResultCallback_t` 内驱动 DO GPIO 执行 NG 剔除动作及定时复位。
|
||||
|
||||
## Impact
|
||||
|
||||
- **构建系统**:TCPClient 工程需链接 FreeRTOS 源文件(tasks.c、queue.c、list.c、timers.c、port.c、heap_4.c)和对应的 include 路径。
|
||||
- **main.c**:从裸机 `while(1)` 重构为 RTOS 多任务——主业务任务(DVP 采集 + 预处理 + 发送)、WCHNET 轮询任务、以及 `qdx_tcp_logic` 创建的 3 个后台线程。
|
||||
- **内存**:FreeRTOS 内核 + 任务栈额外占用约 8-12 KB RAM,需确认 CH32V307 64KB SRAM 余量充足。
|
||||
- **中断**:TIM2 中断需同时服务 FreeRTOS tick 和 WCHNET 定时器。
|
||||
- **依赖**:`FreeRTOSConfig.h` 需要适配 CH32V307 时钟频率与中断优先级。
|
||||
@@ -1,119 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 系统时间获取
|
||||
`qdx_port_get_tick_ms()` 必须返回自系统启动以来的毫秒级单调递增时间戳,精度不低于 FreeRTOS tick 周期(2ms)。
|
||||
|
||||
#### Scenario: 获取系统运行时间
|
||||
- **WHEN** 任意任务或模块调用 `qdx_port_get_tick_ms()`
|
||||
- **THEN** 返回值为基于 `xTaskGetTickCount()` 转换的毫秒数,且数值不会回绕至 0(在 uint32_t 溢回之前)
|
||||
|
||||
### Requirement: 任务级阻塞延时
|
||||
`qdx_port_delay_ms()` 必须让当前 FreeRTOS 任务挂起指定毫秒数,期间让出 CPU 给其他任务。
|
||||
|
||||
#### Scenario: 延时期间 CPU 让出
|
||||
- **WHEN** 后台线程调用 `qdx_port_delay_ms(100)`
|
||||
- **THEN** 该任务进入阻塞态约 100ms,其余就绪任务在此期间获得调度
|
||||
|
||||
### Requirement: 互斥锁创建与操作
|
||||
`qdx_port_mutex_create/lock/unlock/delete` 必须基于 FreeRTOS 互斥信号量实现,支持跨任务的临界区保护和优先级继承。
|
||||
|
||||
#### Scenario: 并发访问配置结构体
|
||||
- **WHEN** `tcp_rx_c` 线程正在更新 `ConfigCommon_t` 且 `task_business` 同时读取该结构体
|
||||
- **THEN** 互斥锁确保同一时刻仅一个任务可访问,防止数据撕裂
|
||||
|
||||
#### Scenario: 互斥锁删除
|
||||
- **WHEN** 调用 `qdx_port_mutex_delete` 并传入有效句柄
|
||||
- **THEN** FreeRTOS 信号量资源被释放,句柄失效
|
||||
|
||||
### Requirement: 线程(任务)创建
|
||||
`qdx_port_thread_create` 必须通过 `xTaskCreate` 创建 FreeRTOS 任务,并正确映射名称、入口函数、栈大小和优先级参数。
|
||||
|
||||
#### Scenario: TcpLogic_Start 创建三个后台任务
|
||||
- **WHEN** `TcpLogic_Start()` 依次调用 `qdx_port_thread_create` 创建 `tcp_mgr`、`tcp_rx_c`、`tcp_rx_d`
|
||||
- **THEN** 三个 FreeRTOS 任务被成功创建并开始调度执行
|
||||
|
||||
### Requirement: TCP Socket 连接建立
|
||||
`qdx_port_tcp_connect` 必须创建 WCHNET TCP socket、配置目标 IP/端口,发起连接并阻塞等待三次握手完成(或超时 5 秒返回 NULL)。
|
||||
|
||||
#### Scenario: 成功连接至上位机
|
||||
- **WHEN** 调用 `qdx_port_tcp_connect("192.168.1.50", 5511)` 且上位机正在监听
|
||||
- **THEN** WCHNET 完成 TCP 三次握手后,`SINT_STAT_CONNECT` 中断释放连接信号量,函数返回有效的 `qdx_socket_t` 句柄
|
||||
|
||||
#### Scenario: 连接超时
|
||||
- **WHEN** 调用 `qdx_port_tcp_connect` 但目标主机不可达
|
||||
- **THEN** 阻塞等待 5 秒后超时,释放已分配的 WCHNET socket 资源,返回 NULL
|
||||
|
||||
### Requirement: TCP 数据发送
|
||||
`qdx_port_tcp_send` 必须将指定缓冲区数据通过 `WCHNET_SocketSend` 写入 TCP 发送队列,并返回实际发送的字节数。
|
||||
|
||||
#### Scenario: 发送温度帧数据
|
||||
- **WHEN** `TcpLogic_BuildAndSendTemperatureFrame` 组装完成后调用 `qdx_port_tcp_send` 发送到数据流 socket
|
||||
- **THEN** 数据被提交至 WCHNET 发送缓冲,函数返回已发送的字节数
|
||||
|
||||
#### Scenario: 连接已断开时发送
|
||||
- **WHEN** 对一个已断开的 socket 调用 `qdx_port_tcp_send`
|
||||
- **THEN** 函数返回 < 0 表示错误
|
||||
|
||||
### Requirement: TCP 数据接收(信号量阻塞模式)
|
||||
`qdx_port_tcp_recv` 必须从 socket 关联的接收环形缓冲中读取数据。若缓冲为空,则阻塞等待信号量通知(最长 100ms 超时),超时返回 0。
|
||||
|
||||
#### Scenario: 接收上位机配置指令
|
||||
- **WHEN** 上位机通过 5511 端口发送配置报文,WCHNET 中断触发将数据写入环形缓冲并释放信号量
|
||||
- **THEN** `tcp_rx_c` 线程从阻塞中唤醒,`qdx_port_tcp_recv` 返回实际读取的字节数和数据
|
||||
|
||||
#### Scenario: 无数据超时
|
||||
- **WHEN** 100ms 内未收到任何数据
|
||||
- **THEN** 函数返回 0,调用方继续执行心跳或重连检查逻辑
|
||||
|
||||
#### Scenario: 对端关闭连接
|
||||
- **WHEN** WCHNET 检测到对端断开(`SINT_STAT_DISCONNECT`)
|
||||
- **THEN** 函数返回 < 0,通知调用方连接已失效
|
||||
|
||||
### Requirement: TCP Socket 关闭
|
||||
`qdx_port_tcp_close` 必须调用 `WCHNET_SocketClose` 释放 WCHNET socket 资源,清空关联的环形缓冲,并将 `SocketCtx_t` 标记为空闲。
|
||||
|
||||
#### Scenario: 正常关闭
|
||||
- **WHEN** 连接管理线程检测到超时并调用 `qdx_port_tcp_close`
|
||||
- **THEN** WCHNET socket 被关闭,环形缓冲清零,`SocketCtx_t.connected = 0`,该槽位可被后续连接复用
|
||||
|
||||
### Requirement: WCHNET 协议栈周期驱动
|
||||
系统必须在独立的高优先级 FreeRTOS 任务 `task_wchnet` 中周期调用 `WCHNET_MainTask()` 和 `WCHNET_HandleGlobalInt()`,确保以太网帧收发和 TCP 状态机正常运转。
|
||||
|
||||
#### Scenario: 协议栈持续驱动
|
||||
- **WHEN** FreeRTOS 调度器启动后
|
||||
- **THEN** `task_wchnet` 以约 5~10ms 周期持续驱动 WCHNET 协议栈,socket 中断事件被及时处理
|
||||
|
||||
### Requirement: Socket 映射与上下文管理
|
||||
系统必须维护静态 `SocketCtx_t` 数组(容量 = 2),管理 WCHNET socket ID 到 `qdx_socket_t` 不透明句柄的映射,以及每个 socket 关联的环形接收缓冲和信号量。
|
||||
|
||||
#### Scenario: 双流同时活跃
|
||||
- **WHEN** 控制流 (5511) 和数据流 (5512) 同时建立连接
|
||||
- **THEN** 两个 `SocketCtx_t` 槽位被分别占用,各自拥有独立的 WCHNET socket ID、接收缓冲和信号量
|
||||
|
||||
### Requirement: net_config.h 双 Socket 配置
|
||||
`WCHNET_NUM_TCP` 必须从 1 修改为 2,以支持控制流和数据流两个并发 TCP 连接,相关宏(`WCHNET_MAX_SOCKET_NUM`、`WCHNET_NUM_POOL_BUF`、`WCHNET_MEM_HEAP_SIZE` 等)随之联动更新。
|
||||
|
||||
#### Scenario: 两个 TCP socket 可同时创建
|
||||
- **WHEN** 系统初始化后依次创建控制流和数据流 socket
|
||||
- **THEN** 两个 `WCHNET_SocketCreat` 调用均成功返回不同的 socket ID
|
||||
|
||||
### Requirement: TIM2 共享 FreeRTOS Tick 与 WCHNET Timer
|
||||
TIM2 必须配置为 2ms 周期,ISR 中每次调用 FreeRTOS tick handler,并以软件计数器每 5 次(10ms)调用一次 `WCHNET_TimeIsr()`。
|
||||
|
||||
#### Scenario: 双定时源正常工作
|
||||
- **WHEN** 系统运行中
|
||||
- **THEN** FreeRTOS 以 500Hz 频率获得 tick 中断,WCHNET 以 100Hz 频率获得定时服务,两者互不干扰
|
||||
|
||||
### Requirement: main 函数重构为 RTOS 启动模型
|
||||
`main()` 在完成硬件初始化和 WCHNET 初始化后,必须创建 `task_wchnet` 和 `task_business` 两个初始任务,然后调用 `vTaskStartScheduler()` 启动调度器,不再使用裸机 `while(1)` 主循环。
|
||||
|
||||
#### Scenario: 调度器启动
|
||||
- **WHEN** `main()` 完成 ETH_LibInit、DVP_Init、Preprocess_Init、TcpLogic_Init 后
|
||||
- **THEN** 调用 `vTaskStartScheduler()` 进入 RTOS 调度,所有业务逻辑在任务上下文中执行
|
||||
|
||||
### Requirement: FreeRTOS 内核集成
|
||||
TCPClient 工程必须链接 FreeRTOS 内核源文件(tasks.c、queue.c、list.c、timers.c、port.c、heap_4.c)和 RISC-V 移植文件,并配置 `FreeRTOSConfig.h` 适配 CH32V307 时钟和中断。
|
||||
|
||||
#### Scenario: 工程编译通过
|
||||
- **WHEN** 将 FreeRTOS 源文件和 include 路径加入 TCPClient 构建配置
|
||||
- **THEN** 工程编译无错误,FreeRTOS API 可正常调用
|
||||
@@ -1,12 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Remote Parameters updating via callbacks
|
||||
必须注册实现监听由上位机下发的配置动作以及废料剔除等行为(基于 `ConfigUpdateCallback_t` 和 `DetectionResultCallback_t` 句柄),让这批回调充当系统动态控制信号输入源。回调函数体必须包含完整的业务逻辑实现。
|
||||
|
||||
#### Scenario: Update Triggering config on the fly
|
||||
- **WHEN** 通过 5511 端口收到控制主机的有效组包设置参数报文时
|
||||
- **THEN** `OnConfigUpdate` 回调被触发,内部必须调用 `Preprocess_Settings_Change(cfg2d, cfg1d, common)` 将新参数同步至预处理模块,使后续帧立即使用更新后的阈值与裁剪尺寸
|
||||
|
||||
#### Scenario: NG defect removal trigger by Host
|
||||
- **WHEN** 上位机判别系统对某帧评估失败(NG)并发回 `DetectionResult_t` 报文(`resultStatus != 0`)时
|
||||
- **THEN** `OnDetectionResult` 回调被触发,必须拉高预设 DO GPIO 引脚(驱动剔除气缸/报警灯),启动软件定时器维持高电平 `NGioDelay` 毫秒后自动拉低复位
|
||||
@@ -1,62 +0,0 @@
|
||||
## 1. 构建系统与 FreeRTOS 集成
|
||||
|
||||
- [x] 1.1 将 FreeRTOS 内核源文件(tasks.c、queue.c、list.c、timers.c)和内存管理(MemMang/heap_4.c)复制或链接到 TCPClient 工程目录
|
||||
- [x] 1.2 将 RISC-V 移植文件(portable/GCC/RISC-V/port.c、portASM.S、portmacro.h)加入工程
|
||||
- [x] 1.3 将 `FreeRTOSConfig.h` 从 `prj/FreeRTOS_Core/User/` 复制到 `prj/TCPClient/User/` 并适配:确认 `configTICK_RATE_HZ=500`、`configTOTAL_HEAP_SIZE=12288`、`configCPU_CLOCK_HZ=SystemCoreClock`
|
||||
- [x] 1.4 更新 TCPClient 工程 makefile/构建配置,添加 FreeRTOS 源文件和 include 路径,确保编译通过
|
||||
|
||||
## 2. net_config.h 双 Socket 配置
|
||||
|
||||
- [x] 2.1 修改 `net_config.h` 中 `WCHNET_NUM_TCP` 从 1 改为 2
|
||||
- [x] 2.2 验证 `WCHNET_MAX_SOCKET_NUM`、`WCHNET_NUM_POOL_BUF`、`WCHNET_NUM_TCP_SEG`、`WCHNET_MEM_HEAP_SIZE` 等联动宏自动调整正确
|
||||
- [x] 2.3 扩展 `main.c` 中 `SocketRecvBuf` 数组尺寸为 `[2][RECE_BUF_LEN]`,`socket` 数组为 `[2]`
|
||||
|
||||
## 3. TIM2 重构——共享 FreeRTOS Tick 与 WCHNET Timer
|
||||
|
||||
- [x] 3.1 修改 `TIM2_Init` 将定时周期从 10ms 改为 2ms(`TIM_Period = 2000 - 1`)
|
||||
- [x] 3.2 修改 `TIM2_IRQHandler`:每次调用 `xPortSysTickHandler()` 驱动 FreeRTOS tick,并用静态计数器每 5 次调用一次 `WCHNET_TimeIsr(WCHNETTIMERPERIOD)`
|
||||
- [x] 3.3 维护 `sys_tick_ms` 每次 +2 替代原来 +10
|
||||
|
||||
## 4. qdx_port.c 核心实现——时间、延时、互斥、线程
|
||||
|
||||
- [x] 4.1 新建 `prj/TCPClient/Middle/QDXnetworkStack/qdx_port.c`(基于 template),include FreeRTOS headers
|
||||
- [x] 4.2 实现 `qdx_port_get_tick_ms()`:返回 `xTaskGetTickCount() * portTICK_PERIOD_MS`
|
||||
- [x] 4.3 实现 `qdx_port_delay_ms()`:调用 `vTaskDelay(pdMS_TO_TICKS(ms))`
|
||||
- [x] 4.4 实现 `qdx_port_mutex_create/lock/unlock/delete`:封装 `xSemaphoreCreateMutex`、`xSemaphoreTake(portMAX_DELAY)`、`xSemaphoreGive`、`vSemaphoreDelete`
|
||||
- [x] 4.5 实现 `qdx_port_thread_create`:调用 `xTaskCreate` 映射 name、entry、arg、stack_size、priority
|
||||
|
||||
## 5. qdx_port.c Socket 层实现——映射与桥接
|
||||
|
||||
- [x] 5.1 定义 `SocketCtx_t` 结构体(wchnet_sock_id、connected、rx_ring、rx_sem、connect_sem)和静态数组 `g_sock_ctx[2]`
|
||||
- [x] 5.2 实现环形缓冲 `RxRingBuf_t` 的 init/write/read/available 操作(容量 2920 字节)
|
||||
- [x] 5.3 实现 `qdx_port_tcp_connect`:分配空闲 SocketCtx → 填充 SOCK_INF(PROTO_TYPE_TCP、目标 IP/Port)→ `WCHNET_SocketCreat` → `WCHNET_SocketConnect` → `xSemaphoreTake(connect_sem, 5000ms)` 阻塞等待连接完成
|
||||
- [x] 5.4 实现 `qdx_port_tcp_send`:校验句柄有效性 → 互斥锁保护 → `WCHNET_SocketSend(ctx->wchnet_sock_id, data, &len)`
|
||||
- [x] 5.5 实现 `qdx_port_tcp_recv`:检查 rx_ring 有数据直接读取返回;否则 `xSemaphoreTake(rx_sem, 100ms)` 阻塞 → 唤醒后再次检查 rx_ring → 返回数据或 0(超时)或 <0(连接断开)
|
||||
- [x] 5.6 实现 `qdx_port_tcp_close`:`WCHNET_SocketClose(ctx->wchnet_sock_id, 0)` → 清空 rx_ring → 标记 ctx 为空闲
|
||||
|
||||
## 6. WCHNET 中断回调改造
|
||||
|
||||
- [x] 6.1 修改 `WCHNET_HandleSockInt` 的 `SINT_STAT_RECV` 分支:通过 wchnet_sock_id 查找对应 SocketCtx → `WCHNET_SocketRecv` 读数据写入 rx_ring → `xSemaphoreGive(rx_sem)` 通知接收线程
|
||||
- [x] 6.2 修改 `SINT_STAT_CONNECT` 分支:查找对应 SocketCtx → 标记 `connected = 1` → `xSemaphoreGive(connect_sem)` 唤醒等待线程
|
||||
- [x] 6.3 修改 `SINT_STAT_DISCONNECT` 和 `SINT_STAT_TIM_OUT` 分支:标记 `connected = 0` → `xSemaphoreGive(rx_sem)` 确保阻塞中的 recv 能被唤醒退出
|
||||
|
||||
## 7. main.c 重构为 RTOS 多任务
|
||||
|
||||
- [x] 7.1 新增 `task_wchnet_entry` 函数:循环调用 `WCHNET_MainTask()` + `WCHNET_QueryGlobalInt` / `WCHNET_HandleGlobalInt`,周期约 5ms
|
||||
- [x] 7.2 新增 `task_business_entry` 函数:将原 `while(1)` 中的 DVP_Task + Frame 触发判定 + 预处理 + 封包发送逻辑迁入
|
||||
- [x] 7.3 修改 `main()` 函数:保留硬件初始化(SystemCoreClockUpdate、USART、DVP_Init、TIM2_Init、ETH_LibInit)→ Preprocess_Init → TcpLogic_Init → TcpLogic_RegisterCallbacks → `xTaskCreate(task_wchnet)` → `xTaskCreate(task_business)` → `vTaskStartScheduler()`
|
||||
- [x] 7.4 删除 `main()` 中的 `while(1)` 裸机主循环
|
||||
|
||||
## 8. 回调逻辑补全
|
||||
|
||||
- [x] 8.1 补全 `OnConfigUpdate`:内部调用 `Preprocess_Settings_Change(cfg2d, cfg1d, common)` 将新参数同步至预处理模块
|
||||
- [x] 8.2 补全 `OnDetectionResult`:当 `isOK == 0` 时拉高 NG DO GPIO 引脚,启动 FreeRTOS 软件定时器(`xTimerCreate`)延时 `NGioDelay` ms 后在回调中拉低复位
|
||||
- [x] 8.3 初始化 NG DO GPIO 引脚为推挽输出(在 main 硬件初始化阶段添加)
|
||||
|
||||
## 9. 验证与调试
|
||||
|
||||
- [ ] 9.1 编译整个 TCPClient 工程,修复链接错误
|
||||
- [ ] 9.2 使用 `uxTaskGetStackHighWaterMark` 验证各任务栈余量,防止溢出
|
||||
- [ ] 9.3 连接上位机测试控制流(5511)与数据流(5512)双连接建立、心跳和重连
|
||||
- [ ] 9.4 触发一帧图像,验证完整链路:DVP 采集 → 触发判定 → 预处理提取 → TCP 封包发送 → 上位机收到数据
|
||||
- [ ] 9.5 测试上位机下发参数更新,验证 Preprocess 参数热更新生效
|
||||
Reference in New Issue
Block a user