100
This commit is contained in:
parent
a87625df90
commit
ccac60a1e9
162
doc/函数调用指南.md
162
doc/函数调用指南.md
@ -204,6 +204,115 @@ void OnConfigUpdated(const ConfigCommon_t* common, const Config2D_t* cfg2d, cons
|
|||||||
Preprocess_Settings_Change(cfg2d, cfg1d, common);
|
Preprocess_Settings_Change(cfg2d, cfg1d, common);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## 4.6 TcpLogic_InjectTestConfig(测试模式配置注入)
|
||||||
|
|
||||||
|
功能:在不依赖服务器的情况下,直接向 TCP 逻辑模块注入配置参数。注入后走与服务器下发完全相同的缓存 + 回调路径。仅非 NULL 的参数会被更新。
|
||||||
|
|
||||||
|
原型:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void TcpLogic_InjectTestConfig(const ConfigCommon_t *common, const Config2D_t *cfg2d, const Config1D_t *cfg1d);
|
||||||
|
```
|
||||||
|
|
||||||
|
入参:
|
||||||
|
- `common`:通用配置指针,NULL 表示不更新
|
||||||
|
- `cfg2d`:2D 配置指针,NULL 表示不更新
|
||||||
|
- `cfg1d`:1D 配置指针,NULL 表示不更新
|
||||||
|
|
||||||
|
内部行为:
|
||||||
|
1. 互斥锁保护下写入内部缓存
|
||||||
|
2. 设置 `has_valid_config = 1`
|
||||||
|
3. 触发已注册的 `ConfigUpdateCallback`
|
||||||
|
|
||||||
|
使用场景:
|
||||||
|
- 测试模式(`TEST_PATTERN_MODE=1`)启动时注入默认 2D 配置,使自主触发管线无需服务器即可工作
|
||||||
|
- 注入后若服务器连接并下发新配置,新配置会覆盖注入的默认值
|
||||||
|
|
||||||
|
示例:
|
||||||
|
```c
|
||||||
|
Config2D_t test_cfg2d;
|
||||||
|
memset(&test_cfg2d, 0, sizeof(test_cfg2d));
|
||||||
|
test_cfg2d.Enabled = 1;
|
||||||
|
test_cfg2d.TriggerMode = 1; // 内部触发
|
||||||
|
test_cfg2d.TargetWidth = 64;
|
||||||
|
test_cfg2d.TargetHeight = 64;
|
||||||
|
test_cfg2d.TriggerTemperatureThreshold = 800; // 80.0°C
|
||||||
|
test_cfg2d.TriggerBurstCount = 3;
|
||||||
|
TcpLogic_InjectTestConfig(NULL, &test_cfg2d, NULL);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. MCU 实际业务调用流程(CH32V307 实现)
|
||||||
|
|
||||||
|
以下为 MCU 中 main.c 的实际初始化和业务循环流程,作为第 7 节伪代码的具体实现参考:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// ===== 初始化阶段 (main 函数) =====
|
||||||
|
|
||||||
|
// 1. 硬件初始化
|
||||||
|
USART_Printf_Init(921600);
|
||||||
|
Config_Flash_SRAM(FLASH_128_SRAM_192); // 128K Flash + 192K SRAM
|
||||||
|
#if !TEST_PATTERN_MODE
|
||||||
|
DVP_Init(); // 正常模式初始化 DVP
|
||||||
|
#endif
|
||||||
|
TIM2_Init(); // WCHNET 10ms 定时基准
|
||||||
|
ExtTrigger_GPIO_Init(); // PA15/EXTI15 外部触发
|
||||||
|
NG_GPIO_Init(); // PA8 NG 输出
|
||||||
|
|
||||||
|
// 2. 网络初始化
|
||||||
|
ETH_LibInit(IPAddr, GWIPAddr, IPMask, MACAddr);
|
||||||
|
qdx_port_init();
|
||||||
|
|
||||||
|
// 3. 库初始化 + 回调注册
|
||||||
|
Preprocess_Init(SENSOR_WIDTH, SENSOR_HEIGHT);
|
||||||
|
TcpLogic_Init(MACAddr, NULL);
|
||||||
|
TcpLogic_RegisterConfigCallback(OnConfigUpdate);
|
||||||
|
TcpLogic_RegisterDetectionCallback(OnDetectionResult);
|
||||||
|
TcpLogic_RegisterTempFrameRequestCallback(OnTempFrameRequest);
|
||||||
|
|
||||||
|
// 4. 测试模式注入默认配置
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
TcpLogic_InjectTestConfig(NULL, &test_cfg2d, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 5. 启动 TCP 引擎 + RTOS 任务
|
||||||
|
TcpLogic_Start();
|
||||||
|
xTaskCreate(task_wchnet_entry, "wchnet", 512, NULL, 6, NULL);
|
||||||
|
xTaskCreate(task_business_entry, "business", 512, NULL, 5, NULL);
|
||||||
|
xTaskCreate(task_heartbeat_entry,"hb", 256, NULL, 3, NULL);
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
xTaskCreate(task_test_pattern_entry, "testpat", 256, NULL, 4, NULL);
|
||||||
|
#endif
|
||||||
|
vTaskStartScheduler();
|
||||||
|
|
||||||
|
// ===== 业务循环 (task_business_entry) =====
|
||||||
|
// 全部行为由运行时 Config 驱动,无编译期功能开关
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// NG 脉冲超时检查
|
||||||
|
if (g_ng_off_time && sys_tick_ms >= g_ng_off_time)
|
||||||
|
GPIO_ResetBits(NG_GPIO_PORT, NG_GPIO_PIN);
|
||||||
|
|
||||||
|
#if !TEST_PATTERN_MODE
|
||||||
|
DVP_Task(); // 行 DMA 搬运
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 辅助通道:TEMP_REQ 服务器按需截图
|
||||||
|
if (g_temp_req_pending) {
|
||||||
|
// → 2D: Preprocess_Execute → TcpLogic_BuildAndSendTemperatureFrame
|
||||||
|
// → 1D: send_1d_snapshot (30点空间采样)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主管线:自主触发
|
||||||
|
if (Frame_Ready_Flag) {
|
||||||
|
TcpLogic_GetLatestConfig(&tc, &t2, &t1);
|
||||||
|
if (t2.Enabled)
|
||||||
|
handle_2d_trigger(&raw, &t2, &use_buf); // 2D 状态机
|
||||||
|
else if (t1.Enabled)
|
||||||
|
handle_1d_trigger(&raw, &t1, &use_buf); // 1D 状态机
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Page 5
|
# Page 5
|
||||||
|
|
||||||
6. 函数调⽤时序图 (Control Flow)
|
6. 函数调⽤时序图 (Control Flow)
|
||||||
@ -235,3 +344,56 @@ TCP 数据流返回 Defect Result (NG)
|
|||||||
拉⾼ DO1 定时执⾏剔除动作
|
拉⾼ DO1 定时执⾏剔除动作
|
||||||
主机/中断(Main) TcpLogic (⽹络库) Preprocess (预处理库) ConfigServer (上位机)
|
主机/中断(Main) TcpLogic (⽹络库) Preprocess (预处理库) ConfigServer (上位机)
|
||||||
|
|
||||||
|
# Page 7
|
||||||
|
|
||||||
|
## 7. 多级调试打印系统
|
||||||
|
|
||||||
|
### 7.1 概述
|
||||||
|
|
||||||
|
调试打印通过 `qdx_port.h` 中的宏体系实现,支持 **5 个级别** 和 **7 个分类**。仅需修改一个宏即可全局控制输出量:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define DBG_LEVEL DBG_LEVEL_NORMAL // 修改此值控制输出详细度
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 级别定义
|
||||||
|
|
||||||
|
| 值 | 宏名 | 说明 | 适用场景 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 0 | `DBG_LEVEL_NONE` | 完全关闭 | 量产固件 |
|
||||||
|
| 1 | `DBG_LEVEL_ERR` | 仅错误 | 最小输出排障 |
|
||||||
|
| 2 | `DBG_LEVEL_BRIEF` | + 服务器配置/网络事件 | **推荐日常使用** |
|
||||||
|
| 3 | `DBG_LEVEL_NORMAL` | + 触发/数据/初始化 | 开发调试(默认) |
|
||||||
|
| 4 | `DBG_LEVEL_VERBOSE` | + 心跳/TLV详情 | 深度调试 |
|
||||||
|
|
||||||
|
### 7.3 分类宏及打印级别
|
||||||
|
|
||||||
|
| 宏 | 级别阈值 | 前缀 | 用途 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `DBG_ERR` | ≥1 (ERR) | `[ERR]` | HardFault、CRC校验失败、连接失败、PP失败 |
|
||||||
|
| `DBG_CFG` | ≥2 (BRIEF) | `[CFG]` | **服务器下发的所有配置**(Config2D/1D/Common、DevID、ACK、DetResult、TempReq) |
|
||||||
|
| `DBG_NET` | ≥2 (BRIEF) | `[NET]` | TCP连接/断开、PHY状态、Socket事件 |
|
||||||
|
| `DBG_TRIG` | ≥3 (NORMAL) | `[TRIG]` | 外部/内部触发、防抖、Burst启停 |
|
||||||
|
| `DBG_DATA` | ≥3 (NORMAL) | `[DATA]` | 2D/1D帧发送结果、TEMP_REQ发送 |
|
||||||
|
| `DBG_INIT` | ≥3 (NORMAL) | `[INIT]` | 开机初始化(传感器、DVP、WCHNET、RTOS) |
|
||||||
|
| `DBG_HB` | ≥4 (VERBOSE) | `[HB]` | 心跳打印、TLV包头详情(高频) |
|
||||||
|
|
||||||
|
### 7.4 使用示例
|
||||||
|
|
||||||
|
```c
|
||||||
|
DBG_ERR("CRC fail: calc=0x%04X recv=0x%04X\r\n", calc, recv);
|
||||||
|
DBG_CFG("<< Config2D: En=%d %dx%d Trig=%d\r\n", en, w, h, trig);
|
||||||
|
DBG_NET("[Control] Connected to %s:%d\r\n", ip, port);
|
||||||
|
DBG_TRIG("2D ext trigger\r\n");
|
||||||
|
DBG_DATA("2D SEND frm=%d ret=%d\r\n", frm, ret);
|
||||||
|
DBG_INIT("Sensor: Y16 OK\r\n");
|
||||||
|
DBG_HB("%d tick=%d frm=%d\r\n", cnt, tick, frm);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.5 各级别输出预期
|
||||||
|
|
||||||
|
- **NONE (0)**:静默,仅 HardFault 的 raw printf 输出
|
||||||
|
- **ERR (1)**:错误信息 — 约 13 条打印点
|
||||||
|
- **BRIEF (2)**:+ 服务器配置全量打印 + 网络事件 — 上位机交互一目了然
|
||||||
|
- **NORMAL (3)**:+ 触发事件、数据发送、初始化 — 标准开发模式
|
||||||
|
- **VERBOSE (4)**:+ 心跳(每2秒)、每个TLV包详情 — 仅深度调试使用
|
||||||
693
doc/模式配置与功能说明.md
693
doc/模式配置与功能说明.md
@ -1,24 +1,32 @@
|
|||||||
# CH32V307 TCPClient 模式配置与功能说明
|
# CH32V307 TCPClient 模式配置与功能说明
|
||||||
|
|
||||||
|
> **版本**: v2.0 — 自主触发架构
|
||||||
|
> **更新日期**: 2026-03
|
||||||
|
> **适用代码**: main.c 自主触发架构(已移除所有 `TEST_ENABLE_*` 编译开关)
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
1. [系统架构概述](#1-系统架构概述)
|
1. [系统架构概述](#1-系统架构概述)
|
||||||
2. [运行模式说明](#2-运行模式说明)
|
2. [运行模式说明](#2-运行模式说明)
|
||||||
3. [宏开关配置总表](#3-宏开关配置总表)
|
3. [编译期常量总表](#3-编译期常量总表)
|
||||||
4. [测试模式详解](#4-测试模式详解)
|
4. [自主触发架构详解](#4-自主触发架构详解)
|
||||||
5. [正常模式详解](#5-正常模式详解)
|
5. [2D 触发状态机](#5-2d-触发状态机)
|
||||||
6. [网络与服务器配置](#6-网络与服务器配置)
|
6. [1D 采集状态机](#6-1d-采集状态机)
|
||||||
7. [配置下发流程](#7-配置下发流程)
|
7. [TEMP_REQ 辅助通道](#7-temp_req-辅助通道)
|
||||||
8. [RTOS 任务架构](#8-rtos-任务架构)
|
8. [测试模式详解](#8-测试模式详解)
|
||||||
9. [切换模式操作指南](#9-切换模式操作指南)
|
9. [正常模式详解](#9-正常模式详解)
|
||||||
10. [常见配置场景](#10-常见配置场景)
|
10. [网络与服务器配置](#10-网络与服务器配置)
|
||||||
11. [已知注意事项](#11-已知注意事项)
|
11. [配置下发流程](#11-配置下发流程)
|
||||||
|
12. [RTOS 任务架构](#12-rtos-任务架构)
|
||||||
|
13. [GPIO 引脚分配](#13-gpio-引脚分配)
|
||||||
|
14. [功能完整性对照表](#14-功能完整性对照表)
|
||||||
|
15. [已知注意事项](#15-已知注意事项)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 系统架构概述
|
## 1. 系统架构概述
|
||||||
|
|
||||||
本系统基于 CH32V307WCU6 (RISC-V) 微控制器,运行 FreeRTOS 实时操作系统,通过 WCHNET TCP/IP 协议栈与上位机通信。系统支持两种运行模式:**测试模式**(软件生成模拟热成像数据)和**正常模式**(通过 DVP 接口采集 Mini212G2 红外传感器真实数据)。
|
本系统基于 CH32V307WCU6 (RISC-V) 微控制器,运行 FreeRTOS 实时操作系统,通过 WCHNET TCP/IP 协议栈与上位机通信。系统行为完全由运行时服务器配置驱动——服务器下发 Config2D/Config1D 并设置 `Enabled=1` 后,MCU 自主执行触发检测和数据采集上报。
|
||||||
|
|
||||||
### 硬件资源
|
### 硬件资源
|
||||||
|
|
||||||
@ -37,57 +45,63 @@
|
|||||||
|------|------|
|
|------|------|
|
||||||
| FreeRTOS v202112.00 | 实时操作系统,Heap=16KB |
|
| FreeRTOS v202112.00 | 实时操作系统,Heap=16KB |
|
||||||
| WCHNET | 万瑞 TCP/IP 协议栈 |
|
| WCHNET | 万瑞 TCP/IP 协议栈 |
|
||||||
| QDX 协议栈 | 自定义 TLV 二进制通信协议 |
|
| QDX 协议栈 | 自定义 TLV 二进制通信协议 (0x55AA, v2.0) |
|
||||||
| 预处理模块 | 滑窗 ROI 搜索 + 温度统计 |
|
| 预处理模块 | 滑窗 ROI 搜索 + 温度统计 + 自动降尺寸 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 运行模式说明
|
## 2. 运行模式说明
|
||||||
|
|
||||||
|
系统仅保留一个编译期宏开关 `TEST_PATTERN_MODE`,控制数据来源。所有业务功能(触发、采集、NG 输出、心跳)在两种模式下均始终启用,行为由服务器运行时配置驱动。
|
||||||
|
|
||||||
### 两种模式对比
|
### 两种模式对比
|
||||||
|
|
||||||
| 对比项 | 测试模式 (`TEST_PATTERN_MODE=1`) | 正常模式 (`TEST_PATTERN_MODE=0`) |
|
| 对比项 | 测试模式 (`TEST_PATTERN_MODE=1`) | 正常模式 (`TEST_PATTERN_MODE=0`) |
|
||||||
|--------|------|------|
|
|--------|------|------|
|
||||||
| **数据来源** | 软件生成模拟热图 (~10 FPS) | Mini212G2 传感器 DVP/DMA 采集 |
|
| **数据来源** | 软件生成模拟热图 (~10 FPS) | Mini212G2 传感器 DVP/DMA 采集 |
|
||||||
| **硬件依赖** | 仅需网口,无需传感器 | 需连接 Mini212G2 + DVP 线缆 |
|
| **硬件依赖** | 仅需网口,无需传感器 | 需连接 Mini212G2 + DVP 线缆 |
|
||||||
| **初始化** | 跳过 `Mini212G2_Init()` 和 `DVP_Init()` | 执行传感器初始化 + DVP 配置 |
|
| **初始化** | 跳过 `DVP_Init()` | 执行 DVP 初始化 |
|
||||||
| **帧生成** | `task_test_pattern_entry` 任务周期填充 | DVP 行中断逐行 DMA 搬运 |
|
| **帧生成** | `task_test_pattern_entry` 任务周期填充 | DVP 行中断逐行 DMA 搬运 |
|
||||||
| **心跳显示** | `[HB] N tick=X TEST_MODE frm=Y` | `[HB] N tick=X dvp_frm=Y row_irq=Z` |
|
| **默认配置** | 自动注入测试配置(2D Enabled=1, 内部触发, 80°C) | 等待服务器下发,默认不使能 |
|
||||||
| **默认配置** | 自动加载测试配置(64×64, 80°C 阈值) | 等待服务器下发,默认 1×1 |
|
| **自主触发** | ✅ 立即工作(无需服务器) | ✅ 需服务器下发 Enabled=1 后激活 |
|
||||||
| **适用场景** | 软件调试、协议验证、无硬件开发 | 生产环境、实际传感器采集 |
|
| **TEMP_REQ** | ✅ 可用(需对应模式 Enabled=1) | ✅ 可用 |
|
||||||
|
| **NG GPIO** | ✅ PA8 始终初始化 | ✅ PA8 始终初始化 |
|
||||||
|
| **外部触发 GPIO** | ✅ PA0/EXTI0 始终初始化 | ✅ PA0/EXTI0 始终初始化 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 宏开关配置总表
|
## 3. 编译期常量总表
|
||||||
|
|
||||||
所有宏定义位于 `prj/TCPClient/User/main.c` 文件头部。
|
所有宏定义位于 `prj/TCPClient/User/main.c` 文件头部。
|
||||||
|
|
||||||
### 3.1 主模式开关
|
### 3.1 主模式开关
|
||||||
|
|
||||||
| 宏名 | 文件位置 | 当前值 | 说明 |
|
| 宏名 | 当前值 | 说明 |
|
||||||
|------|---------|--------|------|
|
|------|--------|------|
|
||||||
| `TEST_PATTERN_MODE` | main.c:18 | **1** | **主开关**:1=测试模式,0=正常模式 |
|
| `TEST_PATTERN_MODE` | **1** | **唯一编译开关**:1=测试模式,0=正常模式 |
|
||||||
|
|
||||||
### 3.2 功能子开关
|
### 3.2 业务常量
|
||||||
|
|
||||||
| 宏名 | 当前值 | 控制功能 | 依赖条件 |
|
|
||||||
|------|--------|---------|---------|
|
|
||||||
| `TEST_ENABLE_HEARTBEAT` | **1** | 周期心跳调试打印(2秒间隔) | 无 |
|
|
||||||
| `TEST_ENABLE_TRIGGER` | **0** | 内部触发 + 连拍上传 | 需配合阈值配置 |
|
|
||||||
| `TEST_ENABLE_NG_GPIO` | **0** | PA8 NG GPIO 脉冲输出 | 需硬件连接 |
|
|
||||||
| `TEST_ENABLE_TEMP_REQ` | **1** | 服务器按需帧请求响应 | 需服务器发送请求 |
|
|
||||||
| `TEST_ENABLE_TCP_SEND` | **1** | TCP 数据通道发送 | 需网络连接 |
|
|
||||||
|
|
||||||
### 3.3 其他编译期常量
|
|
||||||
|
|
||||||
| 宏名 | 值 | 说明 |
|
| 宏名 | 值 | 说明 |
|
||||||
|------|-----|------|
|
|------|-----|------|
|
||||||
| `KEEPALIVE_ENABLE` | 1 | TCP KeepAlive 保持(20s 探测) |
|
| `DEFAULT_BURST_COUNT` | 3 | 服务器未配置时的默认连拍帧数 |
|
||||||
| `DEFAULT_BURST_COUNT` | 3 | 默认连拍帧数 |
|
| `DEFAULT_BURST_INTERVAL_MS` | 200 | 服务器未配置时的默认连拍间隔 (ms) |
|
||||||
| `DEFAULT_BURST_INTERVAL_MS` | 200 | 默认连拍帧间隔 (ms) |
|
|
||||||
| `MAX_TCP_PAYLOAD_SIZE` | 10240 | TCP 发送缓冲区大小 (字节) |
|
| `MAX_TCP_PAYLOAD_SIZE` | 10240 | TCP 发送缓冲区大小 (字节) |
|
||||||
| `TEST_FPS_DELAY_MS` | 100 | 测试模式帧间隔 (~10 FPS) |
|
| `KEEPALIVE_ENABLE` | 1 | TCP KeepAlive(20s 空闲 / 15s 探测 / 9 次) |
|
||||||
| `TEST_1D_POINTS` | 30 | 1D 模式采样点数 |
|
| `TEST_FPS_DELAY_MS` | 100 | 测试模式帧生成间隔 (~10 FPS) |
|
||||||
|
| `SNAPSHOT_1D_POINTS` | 30 | TEMP_REQ 1D 空间采样点数 |
|
||||||
|
| `MAX_1D_POINTS` | 512 | 1D 采集缓冲最大样本数 |
|
||||||
|
| `NG_PULSE_MS` | 200 | NG 脉冲默认宽度(服务器可通过 NGioDelay 覆盖) |
|
||||||
|
|
||||||
|
### 3.3 GPIO 引脚宏
|
||||||
|
|
||||||
|
| 宏名 | 值 | 说明 |
|
||||||
|
|------|-----|------|
|
||||||
|
| `EXT_TRIG_GPIO_PORT` | GPIOA | 外部触发输入端口 |
|
||||||
|
| `EXT_TRIG_GPIO_PIN` | GPIO_Pin_15 | PA15 |
|
||||||
|
| `EXT_TRIG_EXTI_LINE` | EXTI_Line15 | 外部中断线 |
|
||||||
|
| `NG_GPIO_PORT` | GPIOA | NG 输出端口 |
|
||||||
|
| `NG_GPIO_PIN` | GPIO_Pin_8 | PA8 |
|
||||||
|
|
||||||
### 3.4 传感器相关宏 (dvp.h)
|
### 3.4 传感器相关宏 (dvp.h)
|
||||||
|
|
||||||
@ -104,15 +118,222 @@
|
|||||||
|
|
||||||
| 宏名 | 当前值 | 说明 |
|
| 宏名 | 当前值 | 说明 |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
| `SENSOR_USE_USART3` | 0 | 启用后必须为 1(USART2 不可用) |
|
| `SENSOR_UART_ENABLE` | **0 (已禁用)** | 传感器通过 USB 预配置 |
|
||||||
| `SENSOR_UART_ENABLE` | **0 (已禁用)** | MCU UART 配置已禁用,传感器通过 USB 预配置 |
|
|
||||||
| `SENSOR_UART_BAUD` | 115200 | 传感器 UART 波特率(未使用) |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 测试模式详解
|
## 4. 自主触发架构详解
|
||||||
|
|
||||||
### 4.1 启用方式
|
### 4.1 核心设计原则
|
||||||
|
|
||||||
|
系统的所有业务行为由运行时服务器配置驱动,不再使用编译期功能开关:
|
||||||
|
|
||||||
|
```
|
||||||
|
服务器下发 Config2D (Enabled=1) → MCU 自主执行 2D 触发/连拍/上报
|
||||||
|
服务器下发 Config1D (Enabled=1) → MCU 自主执行 1D 采集/上报
|
||||||
|
两者互斥:Config2D.Enabled 优先级高于 Config1D.Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 主循环逻辑流程
|
||||||
|
|
||||||
|
```
|
||||||
|
task_business_entry (优先级 5, 2ms 轮询)
|
||||||
|
│
|
||||||
|
├── [1] NG 脉冲超时检查 → 到期则拉低 PA8
|
||||||
|
│
|
||||||
|
├── [2] DVP_Task() (仅正常模式)
|
||||||
|
│ ├── 行 DMA 完成 → 复制到 FrameBuffer
|
||||||
|
│ └── 最后一行 → Frame_Ready_Flag=1
|
||||||
|
│
|
||||||
|
├── [3] TEMP_REQ 辅助通道(服务器按需截图)
|
||||||
|
│ ├── 前提:对应模式 Enabled=1
|
||||||
|
│ ├── is2D=1 → Preprocess_Execute() → TCP 发送 (frameType=0x02)
|
||||||
|
│ └── is2D=0 → send_1d_snapshot() → 30 点空间采样
|
||||||
|
│
|
||||||
|
└── [4] 自主触发主管线
|
||||||
|
│ 前提:Frame_Ready_Flag=1 && TcpLogic_GetLatestConfig OK
|
||||||
|
│
|
||||||
|
├── Config2D.Enabled=1 → handle_2d_trigger()
|
||||||
|
│ (2D 触发状态机:消抖 → 延迟 → 连拍)
|
||||||
|
│
|
||||||
|
└── Config1D.Enabled=1 → handle_1d_trigger()
|
||||||
|
(1D 采集状态机:IDLE → DEBOUNCE → COLLECTING)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 回调注册
|
||||||
|
|
||||||
|
| 回调 | 注册函数 | 触发时机 | 处理 |
|
||||||
|
|------|---------|---------|------|
|
||||||
|
| `OnConfigUpdate` | `TcpLogic_RegisterConfigCallback` | 服务器下发配置 | → `Preprocess_Settings_Change()` |
|
||||||
|
| `OnDetectionResult` | `TcpLogic_RegisterDetectionCallback` | 服务器返回检测结果 (resultStatus=0 为 NG) | → PA8 NG 脉冲 |
|
||||||
|
| `OnTempFrameRequest` | `TcpLogic_RegisterTempFrameRequestCallback` | 服务器请求截图 | → 设置 `g_temp_req_pending` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 2D 触发状态机
|
||||||
|
|
||||||
|
### 5.1 状态机流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐
|
||||||
|
│ IDLE │
|
||||||
|
│ 等待触发 │
|
||||||
|
└──────┬──────┘
|
||||||
|
│
|
||||||
|
┌────────────┼────────────┐
|
||||||
|
│ TriggerMode=0 │ TriggerMode=1
|
||||||
|
│ (外部触发) │ (内部触发)
|
||||||
|
▼ ▼
|
||||||
|
PA0/EXTI0 上升沿 Preprocess_CheckInternalTrigger2D()
|
||||||
|
│ │ (TriggerRoi 区域 Max/Avg > Threshold)
|
||||||
|
▼ │
|
||||||
|
┌──────────────┐ │
|
||||||
|
│ DEBOUNCE │ │
|
||||||
|
│ DebounceMs │ │
|
||||||
|
└──────┬───────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────┬───────────────┘
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ DELAY │
|
||||||
|
│ DelayMs │
|
||||||
|
└──────┬───────┘
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ BURST │──┐
|
||||||
|
│ 发送第1帧 │ │ 剩余 BurstCount-1 帧
|
||||||
|
└──────────────┘ │ 间隔 InternalIntervalMs
|
||||||
|
▲ │
|
||||||
|
└──────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Burst 完成 → 回到 IDLE
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 关键 Config2D 字段
|
||||||
|
|
||||||
|
| 字段 | 类型 | 2D 触发中的作用 |
|
||||||
|
|------|------|----------------|
|
||||||
|
| `Enabled` | u8 | 1=启用 2D 管线 |
|
||||||
|
| `TriggerMode` | u8 | 0=外部(GPIO), 1=内部(温度) |
|
||||||
|
| `TriggerDebounceIntervalMs` | u16 | 外部触发消抖等待 (ms) |
|
||||||
|
| `TriggerDelayMs` | u16 | 触发确认后延迟采集 (ms) |
|
||||||
|
| `TriggerBurstCount` | u8 | 连拍帧数 |
|
||||||
|
| `TriggerInternalIntervalMs` | u16 | 连拍帧间隔 (ms) |
|
||||||
|
| `TriggerCondition` | u8 | 内部触发判定:0=平均温度, 1=最大温度 |
|
||||||
|
| `TriggerTemperatureThreshold` | i16 | 内部触发温度阈值 (0.1°C/LSB) |
|
||||||
|
| `TriggerRoiX/Y/W/H` | u16×4 | 内部触发检测区域 |
|
||||||
|
| `TargetWidth` / `TargetHeight` | u16 | 预处理输出尺寸 |
|
||||||
|
| `NGioDelay` | u16 | NG 脉冲宽度 (ms) |
|
||||||
|
|
||||||
|
### 5.3 2D 帧发送路径
|
||||||
|
|
||||||
|
```
|
||||||
|
handle_2d_trigger() → start_2d_burst()
|
||||||
|
→ do_2d_capture_send()
|
||||||
|
→ Preprocess_Execute(raw, tx_buf, &meta) // 滑窗 ROI 搜索
|
||||||
|
→ TcpLogic_BuildAndSendTemperatureFrame(tx_buf, &meta, 0x01, 1)
|
||||||
|
// frameType=TRIGGER, is2D=1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 1D 采集状态机
|
||||||
|
|
||||||
|
### 6.1 状态机流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐
|
||||||
|
│ S1D_IDLE │ (RunMode 必须=1)
|
||||||
|
│ 等待触发 │
|
||||||
|
└──────┬──────┘
|
||||||
|
│
|
||||||
|
┌────────────┼────────────┐
|
||||||
|
│ TriggerType=0 │ TriggerType=1
|
||||||
|
│ (外部触发) │ (内部触发)
|
||||||
|
▼ ▼
|
||||||
|
PA0/EXTI0 上升沿 前置环形缓冲(3样本)
|
||||||
|
│ 连续3个 ≥ TriggerTempLimit
|
||||||
|
▼ │
|
||||||
|
┌───────────────┐ │
|
||||||
|
│ S1D_DEBOUNCE │ │
|
||||||
|
│ HighTimerLimit │ │
|
||||||
|
└──────┬────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────┬───────────────┘
|
||||||
|
▼
|
||||||
|
┌────────────────┐
|
||||||
|
│ S1D_COLLECTING │
|
||||||
|
│ 逐帧采集温度点 │
|
||||||
|
│ (中心行最大值) │
|
||||||
|
└───────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────┼────────┐
|
||||||
|
│ 停止条件1 │ 停止条件2
|
||||||
|
│ BufferSize 满 │ 已触发 + NgCountLimit 连续冷点
|
||||||
|
│ │
|
||||||
|
└────────┬────────┘
|
||||||
|
▼
|
||||||
|
切片 [LSizeStart, -RSizeStart]
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
send_1d_collection() → TCP 发送
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
回到 S1D_IDLE
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 1D 温度采样方式
|
||||||
|
|
||||||
|
每个 DVP 帧产生一个温度样本:取中心行所有像素的最大温度值(`get_1d_sample()`)。
|
||||||
|
|
||||||
|
### 6.3 内部触发预环形缓冲
|
||||||
|
|
||||||
|
内部触发模式维护长度为 3 的环形缓冲:
|
||||||
|
- 每帧写入一个样本到环形缓冲
|
||||||
|
- 当 3 个样本全部 ≥ `TriggerTempLimit` → 进入 COLLECTING 状态
|
||||||
|
- 环形缓冲内容(最旧在前)写入采集数组前 3 位
|
||||||
|
- 从第 4 位起继续采集
|
||||||
|
|
||||||
|
### 6.4 关键 Config1D 字段
|
||||||
|
|
||||||
|
| 字段 | 类型 | 1D 采集中的作用 |
|
||||||
|
|------|------|----------------|
|
||||||
|
| `Enabled` | u8 | 1=启用 1D 管线 |
|
||||||
|
| `RunMode` | u8 | 0=STOP, 1=RUN |
|
||||||
|
| `TriggerType` | u8 | 0=外部(GPIO), 1=内部(温度) |
|
||||||
|
| `BufferSize` | u16 | 单次采集最大样本数(停止条件) |
|
||||||
|
| `TriggerTempLimit` | i16 | 触发/采集温度阈值 (0.1°C/LSB) |
|
||||||
|
| `HighTimerLimit` | u16 | 外部触发消抖时间 (ms) |
|
||||||
|
| `NgCountLimit` | u8 | 连续冷点数→停止采集 |
|
||||||
|
| `LSizeStart` | u16 | 切片左起始偏移 |
|
||||||
|
| `RSizeStart` | u16 | 切片右侧移除点数 |
|
||||||
|
|
||||||
|
### 6.5 1D 数据打包格式
|
||||||
|
|
||||||
|
每个样本点 4 字节 (Little-Endian):
|
||||||
|
|
||||||
|
| 偏移 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 0-1 | u16 LE | TimeOffset (距采集开始的毫秒偏移) |
|
||||||
|
| 2-3 | u16 LE | Temperature (0.1°C/LSB) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. TEMP_REQ 辅助通道
|
||||||
|
|
||||||
|
TEMP_REQ 是服务器主动请求的按需截图通道,与自主触发管线独立工作:
|
||||||
|
|
||||||
|
- **2D 截图**:`Preprocess_Execute()` → 滑窗搜索 → 发送 `frameType=0x02`(MASKED)
|
||||||
|
- **1D 截图**:`send_1d_snapshot()` → 从中心行等间距采样 30 点 → 发送
|
||||||
|
|
||||||
|
前提:对应模式 `Enabled=1`,否则请求被忽略并打印 `TEMP_REQ ignored`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 测试模式详解
|
||||||
|
|
||||||
|
### 8.1 启用方式
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define TEST_PATTERN_MODE 1
|
#define TEST_PATTERN_MODE 1
|
||||||
@ -120,7 +341,7 @@
|
|||||||
|
|
||||||
编译烧录即可,**无需连接任何传感器硬件**,仅需以太网连接。
|
编译烧录即可,**无需连接任何传感器硬件**,仅需以太网连接。
|
||||||
|
|
||||||
### 4.2 测试图案
|
### 8.2 测试图案
|
||||||
|
|
||||||
系统自动循环生成 4 种测试图案(256×192 Y16 格式):
|
系统自动循环生成 4 种测试图案(256×192 Y16 格式):
|
||||||
|
|
||||||
@ -133,65 +354,50 @@
|
|||||||
|
|
||||||
总周期:40 帧 ≈ 4 秒循环一次。
|
总周期:40 帧 ≈ 4 秒循环一次。
|
||||||
|
|
||||||
### 4.3 默认配置参数
|
### 8.3 自动注入测试配置
|
||||||
|
|
||||||
测试模式启动时自动加载以下配置(无需服务器下发即可工作):
|
测试模式启动时通过 `TcpLogic_InjectTestConfig()` 注入以下配置(走标准缓存+回调路径):
|
||||||
|
|
||||||
```
|
```
|
||||||
TargetWidth = 64 # 预处理输出宽度
|
Config2D:
|
||||||
TargetHeight = 64 # 预处理输出高度
|
Enabled = 1 # 自动启用 2D 管线
|
||||||
TriggerRoiX = 0 # 触发检测区域起点 X
|
TriggerMode = 1 # 内部触发(温度检测)
|
||||||
TriggerRoiY = 0 # 触发检测区域起点 Y
|
TargetWidth = 64 # 预处理输出 64×64
|
||||||
TriggerRoiW = 256 # 触发检测区域宽度(全幅)
|
TargetHeight = 64
|
||||||
TriggerRoiH = 192 # 触发检测区域高度(全幅)
|
TriggerRoiX/Y = 0, 0 # 全幅扫描
|
||||||
TriggerCondition = 1 # 触发条件:1=最大温度
|
TriggerRoiW/H = 256, 192
|
||||||
TriggerTemperatureThreshold = 800 # 80.0°C (0.1°C/LSB)
|
TriggerCondition = 1 # 最大温度
|
||||||
TriggerBurstCount = 3 # 连拍帧数
|
TriggerTemperatureThreshold = 800 # 80.0°C
|
||||||
TriggerInternalIntervalMs = 200 # 连拍间隔 (ms)
|
TriggerBurstCount = 3 # 3 帧连拍
|
||||||
NGioDelay = 200 # NG 脉冲宽度 (ms)
|
TriggerInternalIntervalMs = 200
|
||||||
|
NGioDelay = 200
|
||||||
```
|
```
|
||||||
|
|
||||||
> **注意**:如果服务器连接后下发了 Config2D,将覆盖上述默认配置。
|
> **注入路径**:`TcpLogic_InjectTestConfig()` → 内部缓存 + `has_valid_config=1` → 触发 `OnConfigUpdate` 回调 → `Preprocess_Settings_Change()`。与服务器下发配置走完全相同的路径。
|
||||||
|
|
||||||
### 4.4 测试模式数据流
|
### 8.4 启动串口输出示例
|
||||||
|
|
||||||
```
|
```
|
||||||
task_test_pattern_entry (优先级 4, 100ms 周期)
|
TCPClient
|
||||||
│ 生成 256×192 Y16 测试图案
|
|
||||||
│ 写入 FrameBuffer[]
|
|
||||||
│ 设置 Frame_Ready_Flag=1
|
|
||||||
▼
|
|
||||||
task_business_entry (优先级 5)
|
|
||||||
├── 检查 g_temp_req_pending (服务器请求)
|
|
||||||
│ ├── is2D=1 → Preprocess_Execute() → 滑窗搜索 → 发送 2D 帧
|
|
||||||
│ └── is2D=0 → send_1d_frame_from_raw() → 采样中心行 → 发送 1D 帧
|
|
||||||
│
|
|
||||||
└── 检查 Frame_Ready_Flag (仅 TRIGGER 启用时)
|
|
||||||
└── Preprocess_CheckInternalTrigger2D()
|
|
||||||
└── 触发 → 连拍发送
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.5 启动串口输出示例
|
|
||||||
|
|
||||||
```
|
|
||||||
TCPClient Test
|
|
||||||
SystemClk:144000000
|
SystemClk:144000000
|
||||||
=== Feature Switches ===
|
TEST_PATTERN=1
|
||||||
PATTERN=1 TRIGGER=0 NG_GPIO=0 TEMP_REQ=1 TCP_SEND=1 HB=1
|
|
||||||
UserByte: c0
|
UserByte: c0
|
||||||
=== TEST PATTERN MODE === No sensor/DVP hardware needed
|
=== TEST PATTERN MODE === No sensor/DVP hardware needed
|
||||||
net version:1c
|
net version:1c
|
||||||
WCHNET_LibInit Success
|
WCHNET_LibInit Success
|
||||||
Test default config loaded: trigger thresh=800 ROI=full burst=3
|
Test config injected: En=1 TrigMode=Internal thresh=800 Tgt=64x64 burst=3
|
||||||
[HB] 0 tick=1010 TEST_MODE frm=9
|
[HB] 0 tick=1010 TEST_MODE frm=9
|
||||||
[HB] 1 tick=3010 TEST_MODE frm=29
|
2D internal trigger
|
||||||
|
TRIGGER frm=21
|
||||||
|
2D SEND frm=21 64x64 ret=0
|
||||||
|
Burst start: 3 frames, interval=200 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. 正常模式详解
|
## 9. 正常模式详解
|
||||||
|
|
||||||
### 5.1 启用方式
|
### 9.1 启用方式
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define TEST_PATTERN_MODE 0
|
#define TEST_PATTERN_MODE 0
|
||||||
@ -199,9 +405,9 @@ Test default config loaded: trigger thresh=800 ROI=full burst=3
|
|||||||
|
|
||||||
需要:
|
需要:
|
||||||
1. Mini212G2 传感器正确连接 DVP 接口
|
1. Mini212G2 传感器正确连接 DVP 接口
|
||||||
2. 传感器已通过 USB 或 UART 预配置为 CMOS/DVP Y16 输出
|
2. 传感器已通过 USB 预配置为 CMOS/DVP Y16 输出
|
||||||
|
|
||||||
### 5.2 硬件连接
|
### 9.2 硬件连接
|
||||||
|
|
||||||
#### DVP 引脚定义
|
#### DVP 引脚定义
|
||||||
|
|
||||||
@ -231,20 +437,7 @@ Test default config loaded: trigger thresh=800 ROI=full burst=3
|
|||||||
|
|
||||||
> 注意:启用 USART3 传感器通信会占用 printf 调试端口,需通过 JTAG 调试。
|
> 注意:启用 USART3 传感器通信会占用 printf 调试端口,需通过 JTAG 调试。
|
||||||
|
|
||||||
### 5.3 传感器配置选项
|
### 9.3 正常模式数据流
|
||||||
|
|
||||||
修改 `mini212g2.h`:
|
|
||||||
|
|
||||||
```c
|
|
||||||
// 当前状态:MCU UART 配置已禁用(#if 0)
|
|
||||||
// 模组已通过 USB 预配置,无需 MCU 配置
|
|
||||||
|
|
||||||
// 如需重新启用 MCU UART 配置,将 mini212g2.h 中的 #if 0 改为 #if 1
|
|
||||||
// 仅可使用 USART3 (PB10/PB11),USART2 引脚已被占用
|
|
||||||
// ⚠ 启用后 printf 串口不可用,需通过 JTAG 调试
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.4 正常模式数据流
|
|
||||||
|
|
||||||
```
|
```
|
||||||
DVP 硬件中断 (DVP_IRQHandler)
|
DVP 硬件中断 (DVP_IRQHandler)
|
||||||
@ -257,40 +450,23 @@ task_business_entry (优先级 5)
|
|||||||
│ ├── 复制 DMA 行缓冲 → FrameBuffer[line_idx]
|
│ ├── 复制 DMA 行缓冲 → FrameBuffer[line_idx]
|
||||||
│ └── 最后一行 → Frame_Ready_Flag=1
|
│ └── 最后一行 → Frame_Ready_Flag=1
|
||||||
▼
|
▼
|
||||||
├── 检查 g_temp_req_pending (服务器请求)
|
├── [TEMP_REQ] 服务器按需截图
|
||||||
│ ├── is2D=1 → Preprocess_Execute() → 发送
|
|
||||||
│ └── is2D=0 → 1D 采样 → 发送
|
|
||||||
│
|
│
|
||||||
└── 检查 Frame_Ready_Flag (TRIGGER 启用时)
|
└── [自主触发] Config2D.Enabled → 2D 状态机
|
||||||
└── Preprocess_CheckInternalTrigger2D()
|
Config1D.Enabled → 1D 状态机
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5.5 正常模式注意事项
|
### 9.4 正常模式注意事项
|
||||||
|
|
||||||
- **无默认预处理参数**:正常模式不加载测试默认配置,`Preprocess_Init()` 仅设置 `TargetWidth=1, TargetHeight=1`。必须等服务器下发 Config2D 后才能获取正确的目标尺寸。
|
- **无默认配置**:正常模式不注入测试配置,`Preprocess_Init()` 仅设置 `TargetWidth=1, TargetHeight=1`。必须等服务器下发 Config2D/Config1D 后才能工作。
|
||||||
- **传感器初始化检查**:可通过 JTAG 查看 `sensor_init_ok` / `sensor_init_fail` 变量确认初始化结果。
|
- **传感器初始化**:模组已通过 USB 预配置,`mini212g2.h` 中 UART 配置已关闭。
|
||||||
- **帧率取决于传感器**:Mini212G2 在 DVP 模式下的帧率由传感器内部配置决定,不通过软件控制。
|
- **帧率取决于传感器**:Mini212G2 在 DVP 模式下的帧率由传感器内部配置决定。
|
||||||
|
|
||||||
### 5.6 启动串口输出示例(预期)
|
|
||||||
|
|
||||||
```
|
|
||||||
TCPClient Test
|
|
||||||
SystemClk:144000000
|
|
||||||
=== Feature Switches ===
|
|
||||||
PATTERN=0 TRIGGER=0 NG_GPIO=0 TEMP_REQ=1 TCP_SEND=1 HB=1
|
|
||||||
UserByte: c0
|
|
||||||
[DVP] Init done CR0=0x2C CR1=0x04 ROW=1 COL=512
|
|
||||||
[DVP] DMA_BUF0=0x200XXXXX DMA_BUF1=0x200XXXXX
|
|
||||||
net version:1c
|
|
||||||
WCHNET_LibInit Success
|
|
||||||
[HB] 0 tick=1010 dvp_frm=0 row_irq=0
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 网络与服务器配置
|
## 10. 网络与服务器配置
|
||||||
|
|
||||||
### 6.1 MCU 网络参数
|
### 10.1 MCU 网络参数
|
||||||
|
|
||||||
定义位置:`main.c`
|
定义位置:`main.c`
|
||||||
|
|
||||||
@ -301,7 +477,7 @@ WCHNET_LibInit Success
|
|||||||
| 子网掩码 | 255.255.255.0 | C 类子网 |
|
| 子网掩码 | 255.255.255.0 | C 类子网 |
|
||||||
| MAC 地址 | 由芯片自动获取 | 硬件 MAC |
|
| MAC 地址 | 由芯片自动获取 | 硬件 MAC |
|
||||||
|
|
||||||
### 6.2 服务器连接参数
|
### 10.2 服务器连接参数
|
||||||
|
|
||||||
定义位置:`qdx_tcp_logic.c`
|
定义位置:`qdx_tcp_logic.c`
|
||||||
|
|
||||||
@ -311,47 +487,62 @@ WCHNET_LibInit Success
|
|||||||
| `CONTROL_PORT` | 5511 | 控制通道端口 |
|
| `CONTROL_PORT` | 5511 | 控制通道端口 |
|
||||||
| `DATA_PORT` | 5512 | 数据通道端口 |
|
| `DATA_PORT` | 5512 | 数据通道端口 |
|
||||||
|
|
||||||
> **修改方法**:直接编辑 `qdx_tcp_logic.c` 中的常量值,重新编译。
|
### 10.3 TCP KeepAlive
|
||||||
|
|
||||||
### 6.3 TCP KeepAlive
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define KEEPALIVE_ENABLE 1 // main.c
|
#define KEEPALIVE_ENABLE 1 // main.c
|
||||||
// 探测配置: 20s 空闲后每 15s 探测一次,最多 9 次
|
// 探测配置: 20s 空闲后每 15s 探测一次,最多 9 次
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 10.4 TCP 内部参数
|
||||||
|
|
||||||
|
| 参数 | 值 | 说明 |
|
||||||
|
|------|-----|------|
|
||||||
|
| `HEARTBEAT_INTERVAL_MS` | 2000 | 心跳发送间隔 |
|
||||||
|
| `SERVER_TIMEOUT_MS` | 6000 | 服务器超时断连 |
|
||||||
|
| `RECONNECT_DELAY_MS` | 3000 | 断连后重连等待 |
|
||||||
|
| `MAX_FRAGMENT_PAYLOAD` | 1400 | 分片最大载荷 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. 配置下发流程
|
## 11. 配置下发流程
|
||||||
|
|
||||||
### 7.1 TLV 配置类型
|
### 11.1 TLV 消息类型
|
||||||
|
|
||||||
| TLV Type | 名称 | 说明 |
|
| TLV Type | 名称 | 方向 | 说明 |
|
||||||
|----------|------|------|
|
|----------|------|------|------|
|
||||||
| 0x20 | TYPE_CONFIG_COMMON | 通用配置(模式、严格标记等) |
|
| 0x01 | TYPE_HANDSHAKE | ↔ | 握手(UUID、版本) |
|
||||||
| 0x22 | TYPE_CONFIG_2D | 2D 配置(目标尺寸、触发、连拍等) |
|
| 0x02 | TYPE_HEARTBEAT | ↔ | 心跳(上行/下行) |
|
||||||
| 0x23 | TYPE_CONFIG_1D | 1D 配置 |
|
| 0x05 | TYPE_DEVID_ASSIGN | ← | 设备 ID 分配(触发重连) |
|
||||||
|
| 0x10 | TYPE_TEMP_FRAME | ← | 请求温度帧(TEMP_REQ) |
|
||||||
|
| 0x20 | TYPE_CONFIG_COMMON | ← | 通用配置 |
|
||||||
|
| 0x22 | TYPE_CONFIG_2D | ← | 2D 配置 |
|
||||||
|
| 0x23 | TYPE_CONFIG_1D | ← | 1D 配置 |
|
||||||
|
| 0x30 | TYPE_ACK_PAYLOAD | ← | ACK 响应 |
|
||||||
|
| 0x40 | TYPE_DETECTION_RESULT | ← | 检测结果(OK/NG) |
|
||||||
|
|
||||||
### 7.2 Config2D 关键字段
|
### 11.2 Config2D 关键字段
|
||||||
|
|
||||||
| 字段 | 偏移 | 类型 | 说明 |
|
| 字段 | 偏移 | 类型 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| Width | 4 | u16 LE | 设备/传感器分辨率宽度(仅标记,不影响预处理) |
|
| Enabled | 0 | u8 | **使能开关** |
|
||||||
| Height | 6 | u16 LE | 设备/传感器分辨率高度(仅标记,不影响预处理) |
|
| Width | 4 | u16 LE | 设备分辨率宽度(仅标记) |
|
||||||
| **TargetWidth** | **23** | **u16 LE** | **预处理输出宽度(直接控制帧大小)** |
|
| Height | 6 | u16 LE | 设备分辨率高度(仅标记) |
|
||||||
| **TargetHeight** | **25** | **u16 LE** | **预处理输出高度(直接控制帧大小)** |
|
| **TargetWidth** | **23** | **u16 LE** | **预处理输出宽度** |
|
||||||
| Fps | 8 | u16 LE | 帧率 |
|
| **TargetHeight** | **25** | **u16 LE** | **预处理输出高度** |
|
||||||
| TriggerCondition | 27 | u8 | 0=平均, 1=最大 |
|
| TriggerMode | 21 | u8 | 0=外部, 1=内部 |
|
||||||
| TriggerTemperatureThreshold | 28 | u16 LE | 触发温度阈值 (0.1°C/LSB) |
|
| TriggerCondition | 37 | u8 | 0=平均, 1=最大 |
|
||||||
|
| TriggerTemperatureThreshold | 28 | i16 LE | 触发阈值 (0.1°C/LSB) |
|
||||||
| TriggerBurstCount | 30 | u8 | 连拍帧数 |
|
| TriggerBurstCount | 30 | u8 | 连拍帧数 |
|
||||||
| TriggerInternalIntervalMs | 31 | u16 LE | 连拍帧间隔 (ms) |
|
| TriggerInternalIntervalMs | 31 | u16 LE | 连拍间隔 (ms) |
|
||||||
| TriggerRoiX/Y/W/H | 33-40 | u16 LE ×4 | 触发检测 ROI 区域 |
|
| TriggerDebounceIntervalMs | 35 | u16 LE | 外部触发消抖 (ms) |
|
||||||
|
| TriggerDelayMs | 23 | u16 LE | 触发后延迟 (ms) |
|
||||||
|
| TriggerRoiX/Y/W/H | 33-40 | u16 LE ×4 | 内部触发检测 ROI |
|
||||||
|
| NGioDelay | 41 | u16 LE | NG 脉冲宽度 (ms) |
|
||||||
|
|
||||||
> **重要**:Width/Height 与 TargetWidth/TargetHeight 是两组不同的字段!
|
> **重要**:`Width/Height` 与 `TargetWidth/TargetHeight` 是两组不同的字段!前者仅做标识,后者直接控制帧大小。
|
||||||
> - `Width/Height`:设备分辨率标记(偏移 4-7),**不影响预处理**
|
|
||||||
> - `TargetWidth/TargetHeight`:预处理实际输出尺寸(偏移 23-26),**直接决定帧数据大小**
|
|
||||||
|
|
||||||
### 7.3 缓冲区容量约束
|
### 11.3 缓冲区容量约束
|
||||||
|
|
||||||
```
|
```
|
||||||
MAX_TCP_PAYLOAD_SIZE = 10240 字节
|
MAX_TCP_PAYLOAD_SIZE = 10240 字节
|
||||||
@ -360,189 +551,119 @@ HeadOffset = 64 字节(协议头预留)
|
|||||||
最大像素数 = 10176 / 2 = 5088 像素
|
最大像素数 = 10176 / 2 = 5088 像素
|
||||||
|
|
||||||
推荐安全目标尺寸:
|
推荐安全目标尺寸:
|
||||||
64 × 64 = 4096 像素 = 8192 字节 ✓ (< 10176)
|
64 × 64 = 4096 像素 = 8192 字节 ✓
|
||||||
71 × 71 = 5041 像素 = 10082 字节 ✓ (< 10176)
|
71 × 71 = 5041 像素 = 10082 字节 ✓
|
||||||
72 × 72 = 5184 像素 = 10368 字节 ✗ (> 10176,会触发降尺寸)
|
72 × 72 = 5184 像素 = 10368 字节 ✗ (触发降尺寸)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.4 自动降尺寸保护
|
### 11.4 自动降尺寸保护
|
||||||
|
|
||||||
当服务器下发的 TargetWidth × TargetHeight × 2 超过可用缓冲区时,MCU 会自动等比减半目标尺寸直到适合:
|
TargetWidth × TargetHeight × 2 超过可用缓冲区时,自动等比减半:
|
||||||
|
|
||||||
```
|
```
|
||||||
示例:服务器设 TargetWidth=185, TargetHeight=70
|
示例:服务器设 185×70 → 185×70×2=25900 > 10176 → 减半 → 93×35
|
||||||
185 × 70 × 2 = 25,900 > 10,176 → 减半
|
日志:PP: target clamped 185x70 -> 93x35 (buf=10176)
|
||||||
93 × 35 × 2 = 6,510 < 10,176 → 使用 93×35
|
|
||||||
日志输出:PP: target clamped 185x70 -> 93x35 (buf=10176)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.5 配置传递链
|
### 11.5 配置传递链
|
||||||
|
|
||||||
```
|
```
|
||||||
服务器 TCP 下发 TLV
|
服务器 TCP 下发 TLV
|
||||||
→ qdx_tcp_logic.c: parse_and_dispatch_tlv()
|
→ qdx_tcp_logic.c: parse_and_dispatch_tlv()
|
||||||
→ 反序列化 → cached_cfg2d / cached_cfg1d / cached_common
|
→ 反序列化 → cached_cfg{2d,1d,common}
|
||||||
→ OnConfigUpdate() 回调
|
→ OnConfigUpdate() 回调
|
||||||
→ Preprocess_Settings_Change() 互斥锁更新
|
→ Preprocess_Settings_Change() 互斥锁更新
|
||||||
→ 下次 Preprocess_Execute() 使用新配置
|
→ 下次帧处理使用新配置
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. RTOS 任务架构
|
## 12. RTOS 任务架构
|
||||||
|
|
||||||
| 任务名 | 优先级 | 栈 (words) | 条件 | 功能 |
|
| 任务名 | 优先级 | 栈 (words) | 条件 | 功能 |
|
||||||
|--------|--------|-----------|------|------|
|
|--------|--------|-----------|------|------|
|
||||||
| `wchnet` | 6 (最高) | 512 | 始终创建 | WCHNET 协议栈轮询 |
|
| `wchnet` | 6 (最高) | 512 | 始终创建 | WCHNET 协议栈轮询 (5ms) |
|
||||||
| `business` | 5 | 512 | `TEST_ENABLE_TCP_SEND=1` | DVP/预处理/发送主循环 |
|
| `business` | 5 | 512 | 始终创建 | DVP + 触发状态机 + 发送 |
|
||||||
| `testpat` | 4 | 256 | `TEST_PATTERN_MODE=1` | 测试图案生成 |
|
| `testpat` | 4 | 256 | `TEST_PATTERN_MODE=1` | 测试图案生成 (~10 FPS) |
|
||||||
| `hb` | 3 (最低) | 256 | `TEST_ENABLE_HEARTBEAT=1` | 心跳调试打印 |
|
| `hb` | 3 (最低) | 256 | 始终创建 | 心跳调试打印 (2s) |
|
||||||
| TcpLogic 内部 | - | - | 始终创建 | 管理线程 (连接/心跳) |
|
| TcpLogic 内部 | - | - | 始终创建 | 管理线程 (连接/心跳/重连) |
|
||||||
|
|
||||||
> 测试模式创建 4 个任务,正常模式创建 3 个(无 testpat)。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. 切换模式操作指南
|
## 13. GPIO 引脚分配
|
||||||
|
|
||||||
### 9.1 从测试模式 → 正常模式
|
| GPIO | 功能 | 方向 | 配置 | 说明 |
|
||||||
|
|------|------|------|------|------|
|
||||||
1. **修改宏**:打开 `prj/TCPClient/User/main.c`,将:
|
| PA15 | 外部触发输入 | 输入 | 下拉 + EXTI15 上升沿 | 2D/1D 共用(互斥) |
|
||||||
```c
|
| PA8 | NG 输出 | 推挽输出 | 低速 2MHz | 检测 NG 时脉冲拉高 |
|
||||||
#define TEST_PATTERN_MODE 1
|
| PA4-PA6, PA9-PA10, PB3, PB8-PB9 | DVP 数据线 | 输入 | 浮空 | 仅正常模式使用 |
|
||||||
```
|
| PC8, PC9, PC11 | DVP HSYNC/VSYNC/PCLK | 输入 | - | 仅正常模式使用 |
|
||||||
改为:
|
| PA2, PA3 | **已占用** | - | - | USART2,不可用 |
|
||||||
```c
|
|
||||||
#define TEST_PATTERN_MODE 0
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **检查传感器配置**:当前模组已通过 USB 预配置,`mini212g2.h` 中 UART 配置已用 `#if 0` 关闭,无需修改。如需重新启用,将 `#if 0` 改为 `#if 1`。
|
|
||||||
|
|
||||||
3. **检查功能开关**:根据需要启用/禁用各子功能:
|
|
||||||
```c
|
|
||||||
#define TEST_ENABLE_TRIGGER 1 // 启用内部触发
|
|
||||||
#define TEST_ENABLE_NG_GPIO 1 // 启用 NG 输出(需接 PA8)
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **编译烧录**:使用 MRS IDE 编译并烧录。
|
|
||||||
|
|
||||||
### 9.2 从正常模式 → 测试模式
|
|
||||||
|
|
||||||
1. **修改宏**:`TEST_PATTERN_MODE` 改为 `1`
|
|
||||||
2. **无需断开传感器**:测试模式跳过所有硬件初始化,传感器接线不影响
|
|
||||||
3. **编译烧录**
|
|
||||||
|
|
||||||
### 9.3 功能开关独立配置
|
|
||||||
|
|
||||||
各功能子开关在两种模式下均可独立启用/禁用,无互相依赖:
|
|
||||||
|
|
||||||
| 开关组合 | 行为 |
|
|
||||||
|---------|------|
|
|
||||||
| PATTERN=1, TRIGGER=1 | 测试模式 + 内部触发连拍(测试热点图案会触发) |
|
|
||||||
| PATTERN=1, TRIGGER=0, TEMP_REQ=1 | 测试模式 + 服务器按需请求(当前配置) |
|
|
||||||
| PATTERN=0, TRIGGER=1, TEMP_REQ=1 | 正常模式 + 自动触发 + 服务器请求(完整功能) |
|
|
||||||
| PATTERN=0, TRIGGER=0, TEMP_REQ=1 | 正常模式 + 仅服务器请求(最简运行) |
|
|
||||||
| TCP_SEND=0 | 禁用 business 任务,系统仅运行网络协议但不处理/发送帧 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. 常见配置场景
|
## 14. 功能完整性对照表
|
||||||
|
|
||||||
### 场景 A:纯软件协议调试(无硬件)
|
### 协议规范 vs MCU 实现
|
||||||
|
|
||||||
```c
|
| 协议功能 | 实现状态 | 备注 |
|
||||||
#define TEST_PATTERN_MODE 1
|
|---------|---------|------|
|
||||||
#define TEST_ENABLE_HEARTBEAT 1
|
| 握手 (Handshake) | ✅ | UUID + HW/FW 版本 |
|
||||||
#define TEST_ENABLE_TRIGGER 0
|
| 心跳 (Heartbeat) | ✅ | 2s 间隔,6s 超时断连 |
|
||||||
#define TEST_ENABLE_NG_GPIO 0
|
| 设备 ID 分配 (DevID Assign) | ✅ | 触发自动重连 |
|
||||||
#define TEST_ENABLE_TEMP_REQ 1
|
| Config Common/2D/1D 下发 | ✅ | 解析+缓存+回调 |
|
||||||
#define TEST_ENABLE_TCP_SEND 1
|
| ACK 响应 | ✅ | 发送+接收 |
|
||||||
```
|
| TEMP_REQ 截图 | ✅ | 2D 预处理 / 1D 空间采样 |
|
||||||
|
| Detection Result | ✅ | NG → PA8 脉冲 |
|
||||||
**适用**:验证 TLV 协议、握手流程、Config 下发、帧分片发送等。
|
| 2D 外部触发 | ✅ | GPIO → 消抖 → 延迟 → 连拍 |
|
||||||
|
| 2D 内部触发 | ✅ | ROI Max/Avg → 延迟 → 连拍 |
|
||||||
### 场景 B:测试内部触发 + 连拍
|
| 1D 外部触发 | ✅ | GPIO → 消抖 → 采集 → 切片 |
|
||||||
|
| 1D 内部触发 | ✅ | 3 样本预环形 → 采集 → 切片 |
|
||||||
```c
|
| 温度矩阵分片发送 | ✅ | 每片 ≤1400 字节 |
|
||||||
#define TEST_PATTERN_MODE 1
|
| TCP KeepAlive | ✅ | 20s/15s/9次 |
|
||||||
#define TEST_ENABLE_TRIGGER 1 // ← 打开
|
| CRC16-MODBUS 校验 | ✅ | 每帧校验 |
|
||||||
#define TEST_ENABLE_NG_GPIO 0
|
| IsLive 实时流 | ❌ | MCU 资源不足,触发场景不需要 |
|
||||||
#define TEST_ENABLE_TEMP_REQ 1
|
| Training 训练模式 | ❌ | 服务器端完成 |
|
||||||
#define TEST_ENABLE_TCP_SEND 1
|
| SyncTime 时间同步 | ❌ | 协议已定义 (0x03),未解析 |
|
||||||
```
|
| StatusEvent 状态上报 | ❌ | 服务器通过心跳判断在线 |
|
||||||
|
| Alarm GPIO | ❌ | 结构体有字段,硬件未接线 |
|
||||||
**适用**:验证 80°C 触发阈值、3 帧连拍、连拍间隔。热点和棋盘格图案会触发。
|
| 1D StartPointsToRemove / ReferenceLength / TimerCLimit | ❌ | 已解析到结构体,业务逻辑未使用 |
|
||||||
|
|
||||||
### 场景 C:正常模式完整功能
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define TEST_PATTERN_MODE 0
|
|
||||||
#define TEST_ENABLE_TRIGGER 1
|
|
||||||
#define TEST_ENABLE_NG_GPIO 1 // ← PA8 接报警
|
|
||||||
#define TEST_ENABLE_TEMP_REQ 1
|
|
||||||
#define TEST_ENABLE_TCP_SEND 1
|
|
||||||
```
|
|
||||||
|
|
||||||
**适用**:生产运行,传感器实时采集 + 自动触发 + NG 报警 + 服务器请求。
|
|
||||||
|
|
||||||
### 场景 D:正常模式最小功能验证
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define TEST_PATTERN_MODE 0
|
|
||||||
#define TEST_ENABLE_TRIGGER 0
|
|
||||||
#define TEST_ENABLE_NG_GPIO 0
|
|
||||||
#define TEST_ENABLE_TEMP_REQ 1 // 仅响应服务器请求
|
|
||||||
#define TEST_ENABLE_TCP_SEND 1
|
|
||||||
```
|
|
||||||
|
|
||||||
**适用**:验证传感器数据采集 + 网络发送,无自动触发逻辑干扰。
|
|
||||||
|
|
||||||
### 场景 E:网络连接诊断
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define TEST_PATTERN_MODE 1
|
|
||||||
#define TEST_ENABLE_HEARTBEAT 1
|
|
||||||
#define TEST_ENABLE_TRIGGER 0
|
|
||||||
#define TEST_ENABLE_NG_GPIO 0
|
|
||||||
#define TEST_ENABLE_TEMP_REQ 0 // 关闭帧请求
|
|
||||||
#define TEST_ENABLE_TCP_SEND 0 // 关闭 business 任务
|
|
||||||
```
|
|
||||||
|
|
||||||
**适用**:仅测试 TCP 连接/断开/重连,不涉及数据帧处理。心跳打印确认系统活跃。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. 已知注意事项
|
## 15. 已知注意事项
|
||||||
|
|
||||||
### 11.1 温度单位
|
### 15.1 温度单位
|
||||||
|
|
||||||
系统统一使用 **0.1°C/LSB**:
|
系统统一使用 **0.1°C/LSB**:
|
||||||
- 温度值 350 = 35.0°C
|
- 温度值 350 = 35.0°C
|
||||||
- 温度值 900 = 90.0°C
|
- 温度值 900 = 90.0°C
|
||||||
- `TEMP_RAW(deg_c) = (uint16_t)(deg_c * 10)`
|
- `TEMP_RAW(deg_c) = (uint16_t)(deg_c * 10)`
|
||||||
|
|
||||||
### 11.2 TriggerTemperatureThreshold 值
|
### 15.2 Config2D 字段区分
|
||||||
|
|
||||||
温度阈值使用 0.1°C/LSB 单位,与系统所有温度值一致。当前测试默认 `TriggerTemperatureThreshold = 800`(80.0°C),测试热点图案 90.0°C(值 900)可正常触发。
|
- **Width / Height**:设备分辨率标识(如 256×192),不影响 MCU 预处理
|
||||||
|
- **TargetWidth / TargetHeight**:预处理实际输出尺寸,**必须正确设置**
|
||||||
|
|
||||||
### 11.3 服务器 Config2D 字段区分
|
### 15.3 正常模式默认目标尺寸
|
||||||
|
|
||||||
- **Width / Height**(偏移 4-7):设备分辨率标识(如 256×192),不影响 MCU 预处理
|
`TargetWidth=1, TargetHeight=1`(最小默认),必须等服务器下发 Config2D 后才能正常输出。
|
||||||
- **TargetWidth / TargetHeight**(偏移 23-26):预处理输出目标尺寸,**必须正确设置**
|
|
||||||
|
|
||||||
如果服务器只设了 Width/Height=64×64 而未设 TargetWidth/TargetHeight,MCU 仍会使用超大目标尺寸(或上次缓存值),导致 `PP fail ret=-3` 或触发自动降尺寸。
|
### 15.4 IP 与端口硬编码
|
||||||
|
|
||||||
### 11.4 正常模式默认目标尺寸
|
网络参数为编译期常量,更改需修改源码并重新编译:
|
||||||
|
|
||||||
正常模式启动时 `TargetWidth=1, TargetHeight=1`(最小默认),必须等服务器下发 Config2D 后才会更新为正确值。测试模式自动加载 64×64。
|
|
||||||
|
|
||||||
### 11.5 IP 与端口硬编码
|
|
||||||
|
|
||||||
当前网络参数(IP、端口)为编译期常量,更改需修改源码并重新编译:
|
|
||||||
- MCU IP:`main.c` → `IPAddr[]`
|
- MCU IP:`main.c` → `IPAddr[]`
|
||||||
- 服务器 IP:`qdx_tcp_logic.c` → `SERVER_IP`
|
- 服务器 IP:`qdx_tcp_logic.c` → `SERVER_IP`
|
||||||
- 端口号:`qdx_tcp_logic.c` → `CONTROL_PORT` / `DATA_PORT`
|
- 端口号:`qdx_tcp_logic.c` → `CONTROL_PORT` / `DATA_PORT`
|
||||||
|
|
||||||
### 11.6 printf 限制
|
### 15.5 printf 限制
|
||||||
|
|
||||||
使用 newlib-nano,仅支持 `%d, %x, %s` 格式。所有 `uint32_t` 变量必须强制转换 `(int)` 后打印,否则可能 HardFault。不支持 `%u, %lu, %f`。
|
使用 newlib-nano,仅支持 `%d, %x, %s` 格式。所有 `uint32_t` 必须 `(int)` 强转后打印,否则 HardFault。不支持 `%u, %lu, %f`。
|
||||||
|
|
||||||
|
### 15.6 双缓冲发送
|
||||||
|
|
||||||
|
系统使用 A/B 交替 `TcpTxBuffer_t`,避免发送过程中缓冲区被覆盖。`use_buffer_A` 标志在每次发送后翻转。
|
||||||
|
|
||||||
|
### 15.7 2D/1D 互斥
|
||||||
|
|
||||||
|
主循环中 `Config2D.Enabled` 优先级高于 `Config1D.Enabled`。若两者同时 Enabled=1,仅 2D 管线运行。
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include "ch32v30x_dvp.h"
|
#include "ch32v30x_dvp.h"
|
||||||
#include "eth_driver.h"
|
#include "eth_driver.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
#include "qdx_port.h"
|
||||||
|
|
||||||
__attribute__((aligned(4))) uint8_t DMA_LineBuf0[BYTES_PER_LINE];
|
__attribute__((aligned(4))) uint8_t DMA_LineBuf0[BYTES_PER_LINE];
|
||||||
__attribute__((aligned(4))) uint8_t DMA_LineBuf1[BYTES_PER_LINE];
|
__attribute__((aligned(4))) uint8_t DMA_LineBuf1[BYTES_PER_LINE];
|
||||||
@ -63,10 +64,10 @@ void DVP_Init(void)
|
|||||||
DVP->CR1 = RB_DVP_DMA_EN; /* DMA on, CM=0 continuous, no reset bits */
|
DVP->CR1 = RB_DVP_DMA_EN; /* DMA on, CM=0 continuous, no reset bits */
|
||||||
DVP->CR0 |= RB_DVP_ENABLE;
|
DVP->CR0 |= RB_DVP_ENABLE;
|
||||||
|
|
||||||
printf("[DVP] Init done CR0=0x%02x CR1=0x%02x ROW=%d COL=%d\r\n",
|
DBG_INIT("DVP CR0=0x%02x CR1=0x%02x ROW=%d COL=%d\r\n",
|
||||||
(int)(DVP->CR0 & 0xFF), (int)(DVP->CR1 & 0xFF),
|
(int)(DVP->CR0 & 0xFF), (int)(DVP->CR1 & 0xFF),
|
||||||
(int)DVP->ROW_NUM, (int)DVP->COL_NUM);
|
(int)DVP->ROW_NUM, (int)DVP->COL_NUM);
|
||||||
printf("[DVP] DMA_BUF0=0x%08x DMA_BUF1=0x%08x\r\n",
|
DBG_INIT("DVP DMA_BUF0=0x%08x DMA_BUF1=0x%08x\r\n",
|
||||||
(int)DVP->DMA_BUF0, (int)DVP->DMA_BUF1);
|
(int)DVP->DMA_BUF0, (int)DVP->DMA_BUF1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "mini212g2.h"
|
#include "mini212g2.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
#include "qdx_port.h"
|
||||||
|
|
||||||
#if SENSOR_UART_ENABLE
|
#if SENSOR_UART_ENABLE
|
||||||
|
|
||||||
@ -165,7 +166,7 @@ int Mini212G2_SendCmd(const uint8_t *cmd, uint8_t len)
|
|||||||
if (n == ACK_LEN && memcmp(resp, ACK_PATTERN, ACK_LEN) == 0)
|
if (n == ACK_LEN && memcmp(resp, ACK_PATTERN, ACK_LEN) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
printf("[Sensor] resp(%d):", n);
|
DBG_INIT("Sensor resp(%d):", n);
|
||||||
for (int j = 0; j < n; j++) printf(" %02x", resp[j]);
|
for (int j = 0; j < n; j++) printf(" %02x", resp[j]);
|
||||||
printf("\r\n");
|
printf("\r\n");
|
||||||
return -1;
|
return -1;
|
||||||
@ -176,18 +177,18 @@ int Mini212G2_SendCmd(const uint8_t *cmd, uint8_t len)
|
|||||||
int Mini212G2_Init(void)
|
int Mini212G2_Init(void)
|
||||||
{
|
{
|
||||||
#if !SENSOR_UART_ENABLE
|
#if !SENSOR_UART_ENABLE
|
||||||
printf("[Sensor] UART disabled, assuming pre-configured via USB\r\n");
|
DBG_INIT("Sensor: UART disabled, pre-configured via USB\r\n");
|
||||||
printf("[Sensor] Expected: CMOS output, CMOS8(MSB), Y16, 30Hz\r\n");
|
DBG_INIT("Sensor: CMOS output, CMOS8(MSB), Y16, 30Hz\r\n");
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
int ok = 0, fail = 0;
|
int ok = 0, fail = 0;
|
||||||
|
|
||||||
Sensor_UART_Init();
|
Sensor_UART_Init();
|
||||||
printf("[Sensor] UART init %d, baud=%d\r\n",
|
DBG_INIT("Sensor: UART%d baud=%d\r\n",
|
||||||
(int)(SENSOR_UART == USART3 ? 3 : 2), (int)SENSOR_UART_BAUD);
|
(int)(SENSOR_UART == USART3 ? 3 : 2), (int)SENSOR_UART_BAUD);
|
||||||
|
|
||||||
/* Sensor needs several seconds to boot after power-on */
|
/* Sensor needs several seconds to boot after power-on */
|
||||||
printf("[Sensor] Waiting 3s for sensor boot...\r\n");
|
DBG_INIT("Sensor: Waiting 3s for boot...\r\n");
|
||||||
Delay_Ms(3000);
|
Delay_Ms(3000);
|
||||||
|
|
||||||
/* === Communication test: send status query === */
|
/* === Communication test: send status query === */
|
||||||
@ -204,51 +205,51 @@ int Mini212G2_Init(void)
|
|||||||
}
|
}
|
||||||
Delay_Ms(200);
|
Delay_Ms(200);
|
||||||
|
|
||||||
printf("[Sensor] Configuring CMOS/DVP output...\r\n");
|
DBG_INIT("Sensor: Configuring CMOS/DVP output...\r\n");
|
||||||
|
|
||||||
/* 1. Shutter compensation */
|
/* 1. Shutter compensation */
|
||||||
if (Mini212G2_SendCmd(CMD_SHUTTER, sizeof(CMD_SHUTTER)) == 0)
|
if (Mini212G2_SendCmd(CMD_SHUTTER, sizeof(CMD_SHUTTER)) == 0)
|
||||||
{ ok++; printf("[Sensor] Shutter OK\r\n"); }
|
{ ok++; DBG_INIT("Sensor: Shutter OK\r\n"); }
|
||||||
else
|
else
|
||||||
{ fail++; printf("[Sensor] Shutter FAIL\r\n"); }
|
{ fail++; DBG_ERR("Sensor: Shutter FAIL\r\n"); }
|
||||||
Delay_Ms(200);
|
Delay_Ms(200);
|
||||||
|
|
||||||
/* 2. Digital port → CMOS */
|
/* 2. Digital port → CMOS */
|
||||||
if (Mini212G2_SendCmd(CMD_DIGITAL_CMOS, sizeof(CMD_DIGITAL_CMOS)) == 0)
|
if (Mini212G2_SendCmd(CMD_DIGITAL_CMOS, sizeof(CMD_DIGITAL_CMOS)) == 0)
|
||||||
{ ok++; printf("[Sensor] Digital->CMOS OK\r\n"); }
|
{ ok++; DBG_INIT("Sensor: Digital->CMOS OK\r\n"); }
|
||||||
else
|
else
|
||||||
{ fail++; printf("[Sensor] Digital->CMOS FAIL\r\n"); }
|
{ fail++; DBG_ERR("Sensor: Digital->CMOS FAIL\r\n"); }
|
||||||
Delay_Ms(200);
|
Delay_Ms(200);
|
||||||
|
|
||||||
/* 3. CMOS interface → 8-bit MSB */
|
/* 3. CMOS interface → 8-bit MSB */
|
||||||
if (Mini212G2_SendCmd(CMD_CMOS8_MSB, sizeof(CMD_CMOS8_MSB)) == 0)
|
if (Mini212G2_SendCmd(CMD_CMOS8_MSB, sizeof(CMD_CMOS8_MSB)) == 0)
|
||||||
{ ok++; printf("[Sensor] CMOS8(MSB) OK\r\n"); }
|
{ ok++; DBG_INIT("Sensor: CMOS8(MSB) OK\r\n"); }
|
||||||
else
|
else
|
||||||
{ fail++; printf("[Sensor] CMOS8(MSB) FAIL\r\n"); }
|
{ fail++; DBG_ERR("Sensor: CMOS8(MSB) FAIL\r\n"); }
|
||||||
Delay_Ms(200);
|
Delay_Ms(200);
|
||||||
|
|
||||||
/* 4. CMOS content → Y16 (raw 16-bit thermal) */
|
/* 4. CMOS content → Y16 (raw 16-bit thermal) */
|
||||||
if (Mini212G2_SendCmd(CMD_CONTENT_Y16, sizeof(CMD_CONTENT_Y16)) == 0)
|
if (Mini212G2_SendCmd(CMD_CONTENT_Y16, sizeof(CMD_CONTENT_Y16)) == 0)
|
||||||
{ ok++; printf("[Sensor] Y16 OK\r\n"); }
|
{ ok++; DBG_INIT("Sensor: Y16 OK\r\n"); }
|
||||||
else
|
else
|
||||||
{ fail++; printf("[Sensor] Y16 FAIL\r\n"); }
|
{ fail++; DBG_ERR("Sensor: Y16 FAIL\r\n"); }
|
||||||
Delay_Ms(200);
|
Delay_Ms(200);
|
||||||
|
|
||||||
/* 5. Frame rate → 30Hz */
|
/* 5. Frame rate → 30Hz */
|
||||||
if (Mini212G2_SendCmd(CMD_FPS_30HZ, sizeof(CMD_FPS_30HZ)) == 0)
|
if (Mini212G2_SendCmd(CMD_FPS_30HZ, sizeof(CMD_FPS_30HZ)) == 0)
|
||||||
{ ok++; printf("[Sensor] 30Hz OK\r\n"); }
|
{ ok++; DBG_INIT("Sensor: 30Hz OK\r\n"); }
|
||||||
else
|
else
|
||||||
{ fail++; printf("[Sensor] 30Hz FAIL\r\n"); }
|
{ fail++; DBG_ERR("Sensor: 30Hz FAIL\r\n"); }
|
||||||
Delay_Ms(200);
|
Delay_Ms(200);
|
||||||
|
|
||||||
/* 6. Save settings to flash */
|
/* 6. Save settings to flash */
|
||||||
if (Mini212G2_SendCmd(CMD_SAVE, sizeof(CMD_SAVE)) == 0)
|
if (Mini212G2_SendCmd(CMD_SAVE, sizeof(CMD_SAVE)) == 0)
|
||||||
{ ok++; printf("[Sensor] Save OK\r\n"); }
|
{ ok++; DBG_INIT("Sensor: Save OK\r\n"); }
|
||||||
else
|
else
|
||||||
{ fail++; printf("[Sensor] Save FAIL\r\n"); }
|
{ fail++; DBG_ERR("Sensor: Save FAIL\r\n"); }
|
||||||
Delay_Ms(500);
|
Delay_Ms(500);
|
||||||
|
|
||||||
printf("[Sensor] Config result: %d ok, %d fail\r\n", ok, fail);
|
DBG_INIT("Sensor: %d ok, %d fail\r\n", ok, fail);
|
||||||
sensor_init_ok = (uint8_t)ok;
|
sensor_init_ok = (uint8_t)ok;
|
||||||
sensor_init_fail = (uint8_t)fail;
|
sensor_init_fail = (uint8_t)fail;
|
||||||
|
|
||||||
@ -268,7 +269,7 @@ void Mini212G2_PrintDigitalVideoStatus(void)
|
|||||||
|
|
||||||
int n = Sensor_ReadResp(resp, sizeof(resp), 500);
|
int n = Sensor_ReadResp(resp, sizeof(resp), 500);
|
||||||
if (n < 24) {
|
if (n < 24) {
|
||||||
printf("[Sensor] Query failed (got %d bytes)\r\n", n);
|
DBG_ERR("Sensor: Query failed (got %d bytes)\r\n", n);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,16 +293,16 @@ void Mini212G2_PrintDigitalVideoStatus(void)
|
|||||||
uint8_t fps = resp[9];
|
uint8_t fps = resp[9];
|
||||||
uint8_t clk_phase = resp[11];
|
uint8_t clk_phase = resp[11];
|
||||||
|
|
||||||
printf("[Sensor] Digital Video Status:\r\n");
|
DBG_INIT("Sensor Digital Video Status:\r\n");
|
||||||
printf(" Port: %s (%d)\r\n",
|
DBG_INIT(" Port: %s (%d)\r\n",
|
||||||
(port_type < 10) ? port_names[port_type] : "?", (int)port_type);
|
(port_type < 10) ? port_names[port_type] : "?", (int)port_type);
|
||||||
printf(" Content: %s (%d)\r\n",
|
DBG_INIT(" Content: %s (%d)\r\n",
|
||||||
(content < 6) ? content_names[content] : "?", (int)content);
|
(content < 6) ? content_names[content] : "?", (int)content);
|
||||||
printf(" Interface: %s (%d)\r\n",
|
DBG_INIT(" Interface: %s (%d)\r\n",
|
||||||
(iface < 3) ? iface_names[iface] : "?", (int)iface);
|
(iface < 3) ? iface_names[iface] : "?", (int)iface);
|
||||||
printf(" FPS: %s (%d)\r\n",
|
DBG_INIT(" FPS: %s (%d)\r\n",
|
||||||
(fps < 4) ? fps_names[fps] : "?", (int)fps);
|
(fps < 4) ? fps_names[fps] : "?", (int)fps);
|
||||||
printf(" ClkPhase: %s (%d)\r\n",
|
DBG_INIT(" ClkPhase: %s (%d)\r\n",
|
||||||
(clk_phase == 0) ? "Rising" : "Falling", (int)clk_phase);
|
(clk_phase == 0) ? "Rising" : "Falling", (int)clk_phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -176,7 +176,7 @@ void qdx_port_sock_recv_notify(uint8_t sockid)
|
|||||||
total += len;
|
total += len;
|
||||||
if (err != WCHNET_ERR_SUCCESS) break;
|
if (err != WCHNET_ERR_SUCCESS) break;
|
||||||
}
|
}
|
||||||
DBG_PORT("recv_notify sock%d: %u bytes, ring=%u\r\n",
|
DBG_HB("recv_notify sock%d: %u bytes, ring=%u\r\n",
|
||||||
sockid, (unsigned)total, (unsigned)ring_available(&ctx->rx_ring));
|
sockid, (unsigned)total, (unsigned)ring_available(&ctx->rx_ring));
|
||||||
/* Wake blocking recv thread */
|
/* Wake blocking recv thread */
|
||||||
xSemaphoreGive(ctx->rx_sem);
|
xSemaphoreGive(ctx->rx_sem);
|
||||||
@ -187,7 +187,7 @@ void qdx_port_sock_connect_notify(uint8_t sockid)
|
|||||||
SocketCtx_t *ctx = find_ctx_by_wchnet_id(sockid);
|
SocketCtx_t *ctx = find_ctx_by_wchnet_id(sockid);
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
ctx->connected = 1;
|
ctx->connected = 1;
|
||||||
DBG_PORT("connect_notify sock%d\r\n", sockid);
|
DBG_NET("connect_notify sock%d\r\n", sockid);
|
||||||
xSemaphoreGive(ctx->connect_sem);
|
xSemaphoreGive(ctx->connect_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ void qdx_port_sock_disconnect_notify(uint8_t sockid)
|
|||||||
SocketCtx_t *ctx = find_ctx_by_wchnet_id(sockid);
|
SocketCtx_t *ctx = find_ctx_by_wchnet_id(sockid);
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
ctx->connected = 0;
|
ctx->connected = 0;
|
||||||
DBG_PORT("disconnect_notify sock%d\r\n", sockid);
|
DBG_NET("disconnect_notify sock%d\r\n", sockid);
|
||||||
/* Wake recv thread so it can detect disconnect */
|
/* Wake recv thread so it can detect disconnect */
|
||||||
xSemaphoreGive(ctx->rx_sem);
|
xSemaphoreGive(ctx->rx_sem);
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ void qdx_port_init(void)
|
|||||||
{
|
{
|
||||||
memset(g_sock_ctx, 0, sizeof(g_sock_ctx));
|
memset(g_sock_ctx, 0, sizeof(g_sock_ctx));
|
||||||
g_wchnet_mutex = xSemaphoreCreateMutex();
|
g_wchnet_mutex = xSemaphoreCreateMutex();
|
||||||
DBG_PORT("init done, mutex=%p\r\n", g_wchnet_mutex);
|
DBG_INIT("qdx_port init done, mutex=%p\r\n", g_wchnet_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@ -268,7 +268,7 @@ int8_t qdx_port_thread_create(const char *name, qdx_thread_entry_t entry,
|
|||||||
BaseType_t ret = xTaskCreate((TaskFunction_t)entry, name,
|
BaseType_t ret = xTaskCreate((TaskFunction_t)entry, name,
|
||||||
(uint16_t)stack_words, arg,
|
(uint16_t)stack_words, arg,
|
||||||
(UBaseType_t)priority, NULL);
|
(UBaseType_t)priority, NULL);
|
||||||
DBG_PORT("thread_create \"%s\" stack=%d pri=%d -> %s\r\n",
|
DBG_INIT("thread_create \"%s\" stack=%d pri=%d -> %s\r\n",
|
||||||
name, (int)stack_words, (int)priority, (ret == pdPASS) ? "OK" : "FAIL");
|
name, (int)stack_words, (int)priority, (ret == pdPASS) ? "OK" : "FAIL");
|
||||||
return (ret == pdPASS) ? 0 : -1;
|
return (ret == pdPASS) ? 0 : -1;
|
||||||
}
|
}
|
||||||
@ -281,15 +281,15 @@ qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port)
|
|||||||
{
|
{
|
||||||
uint8_t dest_ip[4];
|
uint8_t dest_ip[4];
|
||||||
if (parse_ip(ip, dest_ip) != 0) {
|
if (parse_ip(ip, dest_ip) != 0) {
|
||||||
DBG_PORT("bad IP \"%s\"\r\n", ip);
|
DBG_ERR("bad IP \"%s\"\r\n", ip);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBG_PORT("connecting to %s:%d\r\n", ip, port);
|
DBG_NET("connecting to %s:%d\r\n", ip, port);
|
||||||
|
|
||||||
SocketCtx_t *ctx = alloc_sock_ctx();
|
SocketCtx_t *ctx = alloc_sock_ctx();
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
DBG_PORT("no free SocketCtx\r\n");
|
DBG_ERR("no free SocketCtx\r\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port)
|
|||||||
sock_inf.DesPort = port;
|
sock_inf.DesPort = port;
|
||||||
memcpy(sock_inf.IPAddr, dest_ip, 4);
|
memcpy(sock_inf.IPAddr, dest_ip, 4);
|
||||||
|
|
||||||
DBG_PORT("SOCK_INF: proto=%d dst=%d.%d.%d.%d:%d\r\n",
|
DBG_NET("SOCK_INF: proto=%d dst=%d.%d.%d.%d:%d\r\n",
|
||||||
sock_inf.ProtoType,
|
sock_inf.ProtoType,
|
||||||
sock_inf.IPAddr[0], sock_inf.IPAddr[1],
|
sock_inf.IPAddr[0], sock_inf.IPAddr[1],
|
||||||
sock_inf.IPAddr[2], sock_inf.IPAddr[3],
|
sock_inf.IPAddr[2], sock_inf.IPAddr[3],
|
||||||
@ -313,11 +313,11 @@ qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port)
|
|||||||
xSemaphoreGive(g_wchnet_mutex);
|
xSemaphoreGive(g_wchnet_mutex);
|
||||||
|
|
||||||
if (err != WCHNET_ERR_SUCCESS) {
|
if (err != WCHNET_ERR_SUCCESS) {
|
||||||
DBG_PORT("SocketCreat fail %02X\r\n", err);
|
DBG_ERR("SocketCreat fail %02X\r\n", err);
|
||||||
free_sock_ctx(ctx);
|
free_sock_ctx(ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
DBG_PORT("SocketCreat OK, wchnet_id=%d\r\n", wchnet_id);
|
DBG_NET("SocketCreat OK, wchnet_id=%d\r\n", wchnet_id);
|
||||||
|
|
||||||
ctx->wchnet_sock_id = wchnet_id;
|
ctx->wchnet_sock_id = wchnet_id;
|
||||||
|
|
||||||
@ -330,12 +330,12 @@ qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port)
|
|||||||
xSemaphoreGive(g_wchnet_mutex);
|
xSemaphoreGive(g_wchnet_mutex);
|
||||||
|
|
||||||
if (err != WCHNET_ERR_SUCCESS) {
|
if (err != WCHNET_ERR_SUCCESS) {
|
||||||
DBG_PORT("SocketConnect fail %02X\r\n", err);
|
DBG_ERR("SocketConnect fail %02X\r\n", err);
|
||||||
WCHNET_SocketClose(wchnet_id, TCP_CLOSE_RST);
|
WCHNET_SocketClose(wchnet_id, TCP_CLOSE_RST);
|
||||||
free_sock_ctx(ctx);
|
free_sock_ctx(ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
DBG_PORT("SocketConnect OK (err=0x%02X), waiting connect_sem (5s)...\r\n", err);
|
DBG_NET("SocketConnect OK (err=0x%02X), waiting connect_sem (5s)...\r\n", err);
|
||||||
|
|
||||||
/* Block until SINT_STAT_CONNECT or 5s timeout */
|
/* Block until SINT_STAT_CONNECT or 5s timeout */
|
||||||
uint32_t t0 = xTaskGetTickCount();
|
uint32_t t0 = xTaskGetTickCount();
|
||||||
@ -343,14 +343,14 @@ qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port)
|
|||||||
uint32_t elapsed = (xTaskGetTickCount() - t0) * portTICK_PERIOD_MS;
|
uint32_t elapsed = (xTaskGetTickCount() - t0) * portTICK_PERIOD_MS;
|
||||||
|
|
||||||
if (sem_ret != pdTRUE) {
|
if (sem_ret != pdTRUE) {
|
||||||
DBG_PORT("connect_sem TIMEOUT after %d ms -> %d.%d.%d.%d:%d\r\n",
|
DBG_ERR("connect_sem TIMEOUT after %d ms -> %d.%d.%d.%d:%d\r\n",
|
||||||
(int)elapsed, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], port);
|
(int)elapsed, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], port);
|
||||||
DBG_PORT(" ctx->connected=%d wchnet_id=%d\r\n", ctx->connected, wchnet_id);
|
DBG_ERR(" ctx->connected=%d wchnet_id=%d\r\n", ctx->connected, wchnet_id);
|
||||||
WCHNET_SocketClose(wchnet_id, TCP_CLOSE_RST);
|
WCHNET_SocketClose(wchnet_id, TCP_CLOSE_RST);
|
||||||
free_sock_ctx(ctx);
|
free_sock_ctx(ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
DBG_PORT("connect_sem got after %d ms, connected=%d\r\n", (int)elapsed, ctx->connected);
|
DBG_NET("connect_sem got after %d ms, connected=%d\r\n", (int)elapsed, ctx->connected);
|
||||||
|
|
||||||
if (!ctx->connected) {
|
if (!ctx->connected) {
|
||||||
WCHNET_SocketClose(wchnet_id, TCP_CLOSE_RST);
|
WCHNET_SocketClose(wchnet_id, TCP_CLOSE_RST);
|
||||||
@ -362,7 +362,7 @@ qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port)
|
|||||||
WCHNET_SocketSetKeepLive(wchnet_id, ENABLE);
|
WCHNET_SocketSetKeepLive(wchnet_id, ENABLE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DBG_PORT("connected sock %d -> %d.%d.%d.%d:%d\r\n",
|
DBG_NET("connected sock %d -> %d.%d.%d.%d:%d\r\n",
|
||||||
wchnet_id, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], port);
|
wchnet_id, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], port);
|
||||||
return (qdx_socket_t)ctx;
|
return (qdx_socket_t)ctx;
|
||||||
}
|
}
|
||||||
@ -388,7 +388,7 @@ int32_t qdx_port_tcp_send(qdx_socket_t sock, const uint8_t *data, uint32_t len)
|
|||||||
if (err != WCHNET_ERR_SUCCESS && send_len == 0) {
|
if (err != WCHNET_ERR_SUCCESS && send_len == 0) {
|
||||||
/* WCHNET send buffer full — yield so wchnet task can flush */
|
/* WCHNET send buffer full — yield so wchnet task can flush */
|
||||||
if (++retries > 50) {
|
if (++retries > 50) {
|
||||||
DBG_PORT("send fail after retries, err=0x%02X\r\n", err);
|
DBG_ERR("send fail after retries, err=0x%02X\r\n", err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
vTaskDelay(pdMS_TO_TICKS(2));
|
vTaskDelay(pdMS_TO_TICKS(2));
|
||||||
|
|||||||
@ -18,29 +18,48 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Debug Print Macros (set to 0 to disable, 1 to enable)
|
* Multi-Level Debug Print System
|
||||||
|
*
|
||||||
|
* Verbosity levels (set DBG_LEVEL to control output volume):
|
||||||
|
* 0 = NONE — all debug prints disabled
|
||||||
|
* 1 = ERR — errors only
|
||||||
|
* 2 = BRIEF — + server config / network events (recommended)
|
||||||
|
* 3 = NORMAL — + trigger / data / init events (default)
|
||||||
|
* 4 = VERBOSE — + heartbeat / detailed internals
|
||||||
|
*
|
||||||
|
* Category macros — each prints when DBG_LEVEL >= its threshold:
|
||||||
|
* DBG_ERR (>=1) Errors, HardFault, CRC fail, bad frames
|
||||||
|
* DBG_CFG (>=2) Server-received config (Config2D/1D/Common, DevID, ACK, DetResult)
|
||||||
|
* DBG_NET (>=2) TCP connect/disconnect, PHY change, socket events
|
||||||
|
* DBG_TRIG (>=3) Trigger events (ext/int), burst start/complete
|
||||||
|
* DBG_DATA (>=3) Frame send results (2D/1D/TEMP_REQ)
|
||||||
|
* DBG_INIT (>=3) Boot-time init messages (sensor, DVP, WCHNET)
|
||||||
|
* DBG_HB (>=4) Heartbeat prints (high-frequency, debug only)
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
#define QDX_DEBUG_PORT 1 /* qdx_port.c: socket/mutex/thread ops */
|
#define DBG_LEVEL_NONE 0
|
||||||
#define QDX_DEBUG_LOGIC 1 /* qdx_tcp_logic.c: protocol/TLV parsing */
|
#define DBG_LEVEL_ERR 1
|
||||||
#define QDX_DEBUG_APP 1 /* main.c: application-level events */
|
#define DBG_LEVEL_BRIEF 2
|
||||||
|
#define DBG_LEVEL_NORMAL 3
|
||||||
|
#define DBG_LEVEL_VERBOSE 4
|
||||||
|
|
||||||
#if QDX_DEBUG_PORT
|
/* >>> Change this single value to control output volume <<< */
|
||||||
#define DBG_PORT(fmt, ...) printf("[PORT] " fmt, ##__VA_ARGS__)
|
#define DBG_LEVEL DBG_LEVEL_NORMAL
|
||||||
#else
|
|
||||||
#define DBG_PORT(fmt, ...) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if QDX_DEBUG_LOGIC
|
#define DBG_PRINT_(threshold, tag, fmt, ...) \
|
||||||
#define DBG_LOGIC(fmt, ...) printf("[LOGIC] " fmt, ##__VA_ARGS__)
|
do { if (DBG_LEVEL >= (threshold)) printf(tag fmt, ##__VA_ARGS__); } while(0)
|
||||||
#else
|
|
||||||
#define DBG_LOGIC(fmt, ...) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if QDX_DEBUG_APP
|
#define DBG_ERR(fmt, ...) DBG_PRINT_(DBG_LEVEL_ERR, "[ERR] ", fmt, ##__VA_ARGS__)
|
||||||
#define DBG_APP(fmt, ...) printf("[APP] " fmt, ##__VA_ARGS__)
|
#define DBG_CFG(fmt, ...) DBG_PRINT_(DBG_LEVEL_BRIEF, "[CFG] ", fmt, ##__VA_ARGS__)
|
||||||
#else
|
#define DBG_NET(fmt, ...) DBG_PRINT_(DBG_LEVEL_BRIEF, "[NET] ", fmt, ##__VA_ARGS__)
|
||||||
#define DBG_APP(fmt, ...) ((void)0)
|
#define DBG_TRIG(fmt, ...) DBG_PRINT_(DBG_LEVEL_NORMAL, "[TRIG] ", fmt, ##__VA_ARGS__)
|
||||||
#endif
|
#define DBG_DATA(fmt, ...) DBG_PRINT_(DBG_LEVEL_NORMAL, "[DATA] ", fmt, ##__VA_ARGS__)
|
||||||
|
#define DBG_INIT(fmt, ...) DBG_PRINT_(DBG_LEVEL_NORMAL, "[INIT] ", fmt, ##__VA_ARGS__)
|
||||||
|
#define DBG_HB(fmt, ...) DBG_PRINT_(DBG_LEVEL_VERBOSE, "[HB] ", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/* Legacy compatibility aliases — avoid using in new code */
|
||||||
|
#define DBG_PORT(fmt, ...) DBG_INIT(fmt, ##__VA_ARGS__)
|
||||||
|
#define DBG_LOGIC(fmt, ...) DBG_CFG(fmt, ##__VA_ARGS__)
|
||||||
|
#define DBG_APP(fmt, ...) DBG_DATA(fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Time & Delay
|
* Time & Delay
|
||||||
|
|||||||
@ -109,7 +109,7 @@ int8_t Preprocess_Execute(const RawImageBuffer_t *input,
|
|||||||
tgt_w = (tgt_w + 1) / 2;
|
tgt_w = (tgt_w + 1) / 2;
|
||||||
tgt_h = (tgt_h + 1) / 2;
|
tgt_h = (tgt_h + 1) / 2;
|
||||||
}
|
}
|
||||||
DBG_PORT("PP: target clamped %dx%d -> %dx%d (buf=%d)\r\n",
|
DBG_INIT("PP: target clamped %dx%d -> %dx%d (buf=%d)\r\n",
|
||||||
(int)orig_w, (int)orig_h, (int)tgt_w, (int)tgt_h, (int)max_payload);
|
(int)orig_w, (int)orig_h, (int)tgt_w, (int)tgt_h, (int)max_payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,7 +69,7 @@ static void tcp_stream_init(TcpStreamCtx_t *ctx, const char *label) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_stream_disconnect(TcpStreamCtx_t *ctx) {
|
static void tcp_stream_disconnect(TcpStreamCtx_t *ctx) {
|
||||||
DBG_LOGIC("!! [%s] Disconnected\r\n", ctx->label);
|
DBG_NET("[%s] Disconnected\r\n", ctx->label);
|
||||||
ctx->is_connected = 0;
|
ctx->is_connected = 0;
|
||||||
if (ctx->sock) {
|
if (ctx->sock) {
|
||||||
qdx_port_tcp_close(ctx->sock);
|
qdx_port_tcp_close(ctx->sock);
|
||||||
@ -79,10 +79,10 @@ static void tcp_stream_disconnect(TcpStreamCtx_t *ctx) {
|
|||||||
|
|
||||||
static int8_t tcp_stream_connect(TcpStreamCtx_t *ctx, const char *ip,
|
static int8_t tcp_stream_connect(TcpStreamCtx_t *ctx, const char *ip,
|
||||||
uint16_t port) {
|
uint16_t port) {
|
||||||
DBG_LOGIC("[%s] connecting %s:%d...\r\n", ctx->label, ip, port);
|
DBG_NET("[%s] connecting %s:%d...\r\n", ctx->label, ip, port);
|
||||||
ctx->sock = qdx_port_tcp_connect(ip, port);
|
ctx->sock = qdx_port_tcp_connect(ip, port);
|
||||||
if (ctx->sock == NULL) {
|
if (ctx->sock == NULL) {
|
||||||
DBG_LOGIC("[%s] connect FAILED\r\n", ctx->label);
|
DBG_ERR("[%s] connect FAILED\r\n", ctx->label);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ static int8_t tcp_stream_connect(TcpStreamCtx_t *ctx, const char *ip,
|
|||||||
ctx->last_activity_ms = qdx_port_get_tick_ms();
|
ctx->last_activity_ms = qdx_port_get_tick_ms();
|
||||||
ctx->last_heartbeat_ms = ctx->last_activity_ms;
|
ctx->last_heartbeat_ms = ctx->last_activity_ms;
|
||||||
ctx->recv_len = 0;
|
ctx->recv_len = 0;
|
||||||
DBG_LOGIC(">> [%s] Connected to %s:%d\r\n", ctx->label, ip, port);
|
DBG_NET("[%s] Connected to %s:%d\r\n", ctx->label, ip, port);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ static int32_t tcp_send_frame(TcpStreamCtx_t *ctx, uint8_t msg_class,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_send_handshake(TcpStreamCtx_t *ctx) {
|
static void tcp_send_handshake(TcpStreamCtx_t *ctx) {
|
||||||
DBG_LOGIC(">> [%s] Handshake (DevID=%d)\r\n", ctx->label, (int)g_TcpLogic.dev_id);
|
DBG_NET("[%s] Handshake (DevID=%d)\r\n", ctx->label, (int)g_TcpLogic.dev_id);
|
||||||
uint8_t payload[54];
|
uint8_t payload[54];
|
||||||
memset(payload, 0, sizeof(payload));
|
memset(payload, 0, sizeof(payload));
|
||||||
qdx_write_u16_le(payload + 0, 0x0200);
|
qdx_write_u16_le(payload + 0, 0x0200);
|
||||||
@ -250,17 +250,17 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
|
|
||||||
uint8_t cfg_updated = 0;
|
uint8_t cfg_updated = 0;
|
||||||
|
|
||||||
DBG_LOGIC("[%s] TLV pkt: Seq=%d, PayloadLen=%d\r\n", ctx->label,
|
DBG_HB("[%s] TLV pkt: Seq=%d Len=%d\r\n", ctx->label,
|
||||||
hdr_seq, payload_len);
|
hdr_seq, payload_len);
|
||||||
|
|
||||||
while (parsed_len <= payload_len - 3) {
|
while (parsed_len <= payload_len - 3) {
|
||||||
uint8_t type = packet[offset];
|
uint8_t type = packet[offset];
|
||||||
uint16_t len = qdx_read_u16_le(packet + offset + 1);
|
uint16_t len = qdx_read_u16_le(packet + offset + 1);
|
||||||
|
|
||||||
DBG_LOGIC("[%s] TLV: Type=0x%02X, Len=%d\r\n", ctx->label, type, len);
|
DBG_HB("[%s] TLV: Type=0x%02X Len=%d\r\n", ctx->label, type, len);
|
||||||
|
|
||||||
if (parsed_len + 3 + len > payload_len) {
|
if (parsed_len + 3 + len > payload_len) {
|
||||||
DBG_LOGIC("[%s] ! TLV truncated (need %d, have %d)\r\n",
|
DBG_ERR("[%s] TLV truncated (need %d, have %d)\r\n",
|
||||||
ctx->label, len, payload_len - parsed_len - 3);
|
ctx->label, len, payload_len - parsed_len - 3);
|
||||||
break; /* Malformed */
|
break; /* Malformed */
|
||||||
}
|
}
|
||||||
@ -278,16 +278,16 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
uint16_t ack_seq = qdx_read_u16_le(value);
|
uint16_t ack_seq = qdx_read_u16_le(value);
|
||||||
uint8_t status = value[2];
|
uint8_t status = value[2];
|
||||||
uint16_t err_code = (len >= 5) ? qdx_read_u16_le(value + 3) : 0;
|
uint16_t err_code = (len >= 5) ? qdx_read_u16_le(value + 3) : 0;
|
||||||
DBG_LOGIC("<< [%s] ACK: seq=%d status=%d err=0x%04x\r\n",
|
DBG_CFG("<< ACK: seq=%d status=%d err=0x%04x\r\n",
|
||||||
ctx->label, (int)ack_seq, (int)status, (int)err_code);
|
(int)ack_seq, (int)status, (int)err_code);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_DEVID_ASSIGN: {
|
case TYPE_DEVID_ASSIGN: {
|
||||||
if (len >= sizeof(DevIDAssignment_t)) {
|
if (len >= sizeof(DevIDAssignment_t)) {
|
||||||
uint16_t new_id = qdx_read_u16_le(value);
|
uint16_t new_id = qdx_read_u16_le(value);
|
||||||
DBG_LOGIC("<< [%s] DevID assigned: %d -> %d\r\n",
|
DBG_CFG("<< DevID assigned: %d -> %d\r\n",
|
||||||
ctx->label, (int)g_TcpLogic.dev_id, (int)new_id);
|
(int)g_TcpLogic.dev_id, (int)new_id);
|
||||||
g_TcpLogic.pending_new_dev_id = new_id;
|
g_TcpLogic.pending_new_dev_id = new_id;
|
||||||
tcp_send_ack(ctx, hdr_seq, 0, 0);
|
tcp_send_ack(ctx, hdr_seq, 0, 0);
|
||||||
}
|
}
|
||||||
@ -300,8 +300,7 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
g_TcpLogic.has_valid_config = 1;
|
g_TcpLogic.has_valid_config = 1;
|
||||||
cfg_updated = 1;
|
cfg_updated = 1;
|
||||||
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
||||||
DBG_LOGIC("<< [%s] ConfigCommon: Mode=%d Tag=%d Strict=%d\r\n",
|
DBG_CFG("<< ConfigCommon: Mode=%d Tag=%d Strict=%d\r\n",
|
||||||
ctx->label,
|
|
||||||
(int)g_TcpLogic.cached_common.WorkMode,
|
(int)g_TcpLogic.cached_common.WorkMode,
|
||||||
(int)g_TcpLogic.cached_common.ConfigTag,
|
(int)g_TcpLogic.cached_common.ConfigTag,
|
||||||
(int)g_TcpLogic.cached_common.StrictnessLevel);
|
(int)g_TcpLogic.cached_common.StrictnessLevel);
|
||||||
@ -315,10 +314,9 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
g_TcpLogic.has_valid_config = 1;
|
g_TcpLogic.has_valid_config = 1;
|
||||||
cfg_updated = 1;
|
cfg_updated = 1;
|
||||||
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
||||||
DBG_LOGIC("<< [%s] Config2D: En=%d %dx%d Tgt=%dx%d Fps=%d "
|
DBG_CFG("<< Config2D: En=%d %dx%d Tgt=%dx%d Fps=%d "
|
||||||
"Trig=%d Burst=%d Intv=%d Thresh=%d "
|
"Trig=%d Burst=%d Intv=%d Thresh=%d "
|
||||||
"ROI(%d,%d,%d,%d) NGio=%d\r\n",
|
"ROI(%d,%d,%d,%d) NGio=%d\r\n",
|
||||||
ctx->label,
|
|
||||||
(int)g_TcpLogic.cached_cfg2d.Enabled,
|
(int)g_TcpLogic.cached_cfg2d.Enabled,
|
||||||
(int)g_TcpLogic.cached_cfg2d.Width,
|
(int)g_TcpLogic.cached_cfg2d.Width,
|
||||||
(int)g_TcpLogic.cached_cfg2d.Height,
|
(int)g_TcpLogic.cached_cfg2d.Height,
|
||||||
@ -335,8 +333,8 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
(int)g_TcpLogic.cached_cfg2d.TriggerRoiH,
|
(int)g_TcpLogic.cached_cfg2d.TriggerRoiH,
|
||||||
(int)g_TcpLogic.cached_cfg2d.NGioDelay);
|
(int)g_TcpLogic.cached_cfg2d.NGioDelay);
|
||||||
} else {
|
} else {
|
||||||
DBG_LOGIC("<< [%s] Config2D bad len=%d (need %d)\r\n",
|
DBG_ERR("<< Config2D bad len=%d (need %d)\r\n",
|
||||||
ctx->label, len, (int)sizeof(Config2D_t));
|
len, (int)sizeof(Config2D_t));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -347,9 +345,8 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
g_TcpLogic.has_valid_config = 1;
|
g_TcpLogic.has_valid_config = 1;
|
||||||
cfg_updated = 1;
|
cfg_updated = 1;
|
||||||
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
||||||
DBG_LOGIC("<< [%s] Config1D: En=%d RunMode=%d TrigType=%d "
|
DBG_CFG("<< Config1D: En=%d RunMode=%d TrigType=%d "
|
||||||
"BufSz=%d TempLim=%d NGio=%d\r\n",
|
"BufSz=%d TempLim=%d NGio=%d\r\n",
|
||||||
ctx->label,
|
|
||||||
(int)g_TcpLogic.cached_cfg1d.Enabled,
|
(int)g_TcpLogic.cached_cfg1d.Enabled,
|
||||||
(int)g_TcpLogic.cached_cfg1d.RunMode,
|
(int)g_TcpLogic.cached_cfg1d.RunMode,
|
||||||
(int)g_TcpLogic.cached_cfg1d.TriggerType,
|
(int)g_TcpLogic.cached_cfg1d.TriggerType,
|
||||||
@ -360,13 +357,13 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_TEMP_FRAME: {
|
case TYPE_TEMP_FRAME: {
|
||||||
DBG_LOGIC("<< [%s] TempFrame request (len=%d)\r\n", ctx->label, (int)len);
|
DBG_CFG("<< TempFrame request (len=%d)\r\n", (int)len);
|
||||||
if (g_TcpLogic.temp_req_cb) {
|
if (g_TcpLogic.temp_req_cb) {
|
||||||
uint8_t is2d = 0;
|
uint8_t is2d = 0;
|
||||||
if (len >= 18) {
|
if (len >= 18) {
|
||||||
is2d = value[18];
|
is2d = value[18];
|
||||||
}
|
}
|
||||||
DBG_LOGIC(" -> callback: is2D=%d\r\n", (int)is2d);
|
DBG_CFG(" -> callback: is2D=%d\r\n", (int)is2d);
|
||||||
g_TcpLogic.temp_req_cb(is2d);
|
g_TcpLogic.temp_req_cb(is2d);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -375,8 +372,8 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
if (len >= sizeof(DetectionResult_t)) {
|
if (len >= sizeof(DetectionResult_t)) {
|
||||||
uint32_t frame_num = qdx_read_u32_le(value);
|
uint32_t frame_num = qdx_read_u32_le(value);
|
||||||
uint8_t result_status = value[4];
|
uint8_t result_status = value[4];
|
||||||
DBG_LOGIC("<< [%s] DetectionResult: frm=%d result=%d\r\n",
|
DBG_CFG("<< DetectionResult: frm=%d result=%d\r\n",
|
||||||
ctx->label, (int)frame_num, (int)result_status);
|
(int)frame_num, (int)result_status);
|
||||||
if (g_TcpLogic.detect_cb)
|
if (g_TcpLogic.detect_cb)
|
||||||
g_TcpLogic.detect_cb(frame_num, result_status);
|
g_TcpLogic.detect_cb(frame_num, result_status);
|
||||||
}
|
}
|
||||||
@ -386,8 +383,7 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
/* Server echoes handshake info — safe to ignore */
|
/* Server echoes handshake info — safe to ignore */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DBG_LOGIC("<< [%s] Unknown TLV type=0x%02x len=%d\r\n",
|
DBG_ERR("Unknown TLV type=0x%02x len=%d\r\n", (int)type, (int)len);
|
||||||
ctx->label, (int)type, (int)len);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +392,7 @@ static void parse_and_dispatch_tlv(TcpStreamCtx_t *ctx, const uint8_t *packet,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cfg_updated && g_TcpLogic.config_cb && g_TcpLogic.has_valid_config) {
|
if (cfg_updated && g_TcpLogic.config_cb && g_TcpLogic.has_valid_config) {
|
||||||
DBG_LOGIC("<< [%s] Config updated -> notify app\r\n", ctx->label);
|
DBG_CFG("Config updated -> notify app\r\n");
|
||||||
qdx_port_mutex_lock(g_TcpLogic.config_mutex);
|
qdx_port_mutex_lock(g_TcpLogic.config_mutex);
|
||||||
g_TcpLogic.config_cb(&g_TcpLogic.cached_common, &g_TcpLogic.cached_cfg2d,
|
g_TcpLogic.config_cb(&g_TcpLogic.cached_common, &g_TcpLogic.cached_cfg2d,
|
||||||
&g_TcpLogic.cached_cfg1d);
|
&g_TcpLogic.cached_cfg1d);
|
||||||
@ -438,7 +434,7 @@ static void tcp_process_rx_buffer(TcpStreamCtx_t *ctx) {
|
|||||||
uint16_t length = qdx_read_u16_le(ctx->recv_buffer + 3);
|
uint16_t length = qdx_read_u16_le(ctx->recv_buffer + 3);
|
||||||
|
|
||||||
if (version != PROTO_VERSION || length < HEADER_SIZE + CRC_SIZE) {
|
if (version != PROTO_VERSION || length < HEADER_SIZE + CRC_SIZE) {
|
||||||
DBG_LOGIC("[%s] bad header: ver=0x%02X(exp 0x%02X) len=%d\r\n",
|
DBG_ERR("[%s] bad header: ver=0x%02X(exp 0x%02X) len=%d\r\n",
|
||||||
ctx->label, version, PROTO_VERSION, length);
|
ctx->label, version, PROTO_VERSION, length);
|
||||||
memmove(ctx->recv_buffer, ctx->recv_buffer + 2, ctx->recv_len - 2);
|
memmove(ctx->recv_buffer, ctx->recv_buffer + 2, ctx->recv_len - 2);
|
||||||
ctx->recv_len -= 2;
|
ctx->recv_len -= 2;
|
||||||
@ -446,7 +442,7 @@ static void tcp_process_rx_buffer(TcpStreamCtx_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length > sizeof(ctx->recv_buffer)) {
|
if (length > sizeof(ctx->recv_buffer)) {
|
||||||
DBG_LOGIC("[%s] frame too large: %d > %d\r\n", ctx->label,
|
DBG_ERR("[%s] frame too large: %d > %d\r\n", ctx->label,
|
||||||
length, (int)sizeof(ctx->recv_buffer));
|
length, (int)sizeof(ctx->recv_buffer));
|
||||||
ctx->recv_len = 0;
|
ctx->recv_len = 0;
|
||||||
break;
|
break;
|
||||||
@ -464,7 +460,7 @@ static void tcp_process_rx_buffer(TcpStreamCtx_t *ctx) {
|
|||||||
/* 3. Dispatch */
|
/* 3. Dispatch */
|
||||||
parse_and_dispatch_tlv(ctx, ctx->recv_buffer, length);
|
parse_and_dispatch_tlv(ctx, ctx->recv_buffer, length);
|
||||||
} else {
|
} else {
|
||||||
DBG_LOGIC("[%s] CRC fail: calc=0x%04X recv=0x%04X len=%d\r\n",
|
DBG_ERR("[%s] CRC fail: calc=0x%04X recv=0x%04X len=%d\r\n",
|
||||||
ctx->label, calculated_crc, received_crc, length);
|
ctx->label, calculated_crc, received_crc, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,6 +638,22 @@ void TcpLogic_RegisterTempFrameRequestCallback(TempFrameRequestCallback_t cb) {
|
|||||||
g_TcpLogic.temp_req_cb = cb;
|
g_TcpLogic.temp_req_cb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TcpLogic_InjectTestConfig(const ConfigCommon_t *common,
|
||||||
|
const Config2D_t *cfg2d,
|
||||||
|
const Config1D_t *cfg1d) {
|
||||||
|
qdx_port_mutex_lock(g_TcpLogic.config_mutex);
|
||||||
|
if (common) memcpy(&g_TcpLogic.cached_common, common, sizeof(*common));
|
||||||
|
if (cfg2d) memcpy(&g_TcpLogic.cached_cfg2d, cfg2d, sizeof(*cfg2d));
|
||||||
|
if (cfg1d) memcpy(&g_TcpLogic.cached_cfg1d, cfg1d, sizeof(*cfg1d));
|
||||||
|
g_TcpLogic.has_valid_config = 1;
|
||||||
|
qdx_port_mutex_unlock(g_TcpLogic.config_mutex);
|
||||||
|
if (g_TcpLogic.config_cb) {
|
||||||
|
g_TcpLogic.config_cb(&g_TcpLogic.cached_common,
|
||||||
|
&g_TcpLogic.cached_cfg2d,
|
||||||
|
&g_TcpLogic.cached_cfg1d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Zero-Copy Frame Building & Fragmentation for Temperature Data
|
* Zero-Copy Frame Building & Fragmentation for Temperature Data
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
@ -658,7 +670,7 @@ TcpLogic_BuildAndSendTemperatureFrame(TcpTxBuffer_t *io_buffer,
|
|||||||
|
|
||||||
g_TcpLogic.frame_count++;
|
g_TcpLogic.frame_count++;
|
||||||
|
|
||||||
DBG_LOGIC(">> [Data] TempFrame #%d: %dx%d type=%d is2D=%d payload=%d\r\n",
|
DBG_DATA(">> TempFrame #%d: %dx%d type=%d is2D=%d payload=%d\r\n",
|
||||||
(int)processMeta->FrameNumber,
|
(int)processMeta->FrameNumber,
|
||||||
(int)processMeta->ValidWidth, (int)processMeta->ValidHeight,
|
(int)processMeta->ValidWidth, (int)processMeta->ValidHeight,
|
||||||
(int)frameType, (int)is2D, (int)io_buffer->ValidPayloadLen);
|
(int)frameType, (int)is2D, (int)io_buffer->ValidPayloadLen);
|
||||||
@ -725,7 +737,7 @@ TcpLogic_BuildAndSendTemperatureFrame(TcpTxBuffer_t *io_buffer,
|
|||||||
uint32_t frag_count =
|
uint32_t frag_count =
|
||||||
(total_tlv_len + MAX_FRAGMENT_PAYLOAD - 1) / MAX_FRAGMENT_PAYLOAD;
|
(total_tlv_len + MAX_FRAGMENT_PAYLOAD - 1) / MAX_FRAGMENT_PAYLOAD;
|
||||||
|
|
||||||
DBG_LOGIC(">> [Data] Fragmented: %d frags, total=%d\r\n",
|
DBG_DATA(">> Fragmented: %d frags, total=%d\r\n",
|
||||||
(int)frag_count, (int)total_tlv_len);
|
(int)frag_count, (int)total_tlv_len);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < frag_count; i++) {
|
for (uint32_t i = 0; i < frag_count; i++) {
|
||||||
|
|||||||
@ -105,6 +105,16 @@ typedef void (*TempFrameRequestCallback_t)(uint8_t is2dRequest);
|
|||||||
*/
|
*/
|
||||||
void TcpLogic_RegisterTempFrameRequestCallback(TempFrameRequestCallback_t cb);
|
void TcpLogic_RegisterTempFrameRequestCallback(TempFrameRequestCallback_t cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inject configuration directly (for test mode without server).
|
||||||
|
*
|
||||||
|
* Sets the internal config cache and fires the config callback.
|
||||||
|
* Only non-NULL parameters are updated.
|
||||||
|
*/
|
||||||
|
void TcpLogic_InjectTestConfig(const ConfigCommon_t *common,
|
||||||
|
const Config2D_t *cfg2d,
|
||||||
|
const Config1D_t *cfg1d);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
709
prj/TCPClient/User/main.c.bak
Normal file
709
prj/TCPClient/User/main.c.bak
Normal file
@ -0,0 +1,709 @@
|
|||||||
|
#include "ch32v30x.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "eth_driver.h"
|
||||||
|
#include "dvp.h"
|
||||||
|
#include "mini212g2.h"
|
||||||
|
#include "qdx_port.h"
|
||||||
|
#include "qdx_preprocess.h"
|
||||||
|
#include "qdx_tcp_logic.h"
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* TEST MODE: Generate simulated sensor data without real hardware.
|
||||||
|
* Set to 1 to enable test pattern generation (no sensor/DVP needed).
|
||||||
|
* Set to 0 for normal operation with real Mini212G2 sensor.
|
||||||
|
* ============================================================ */
|
||||||
|
#define TEST_PATTERN_MODE 1
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Feature Test Switches — set to 1 to enable, 0 to disable.
|
||||||
|
* Allows testing each feature independently.
|
||||||
|
* ============================================================ */
|
||||||
|
#define TEST_ENABLE_HEARTBEAT 1 /* Periodic heartbeat debug print task */
|
||||||
|
#define TEST_ENABLE_TRIGGER 0 /* Internal trigger + burst upload */
|
||||||
|
#define TEST_ENABLE_NG_GPIO 0 /* NG GPIO pulse output on detection */
|
||||||
|
#define TEST_ENABLE_TEMP_REQ 1 /* On-demand frame upload from server */
|
||||||
|
#define TEST_ENABLE_TCP_SEND 1 /* TCP data channel sending */
|
||||||
|
|
||||||
|
/* Default burst parameters — used when server hasn't sent config yet */
|
||||||
|
#define DEFAULT_BURST_COUNT 3
|
||||||
|
#define DEFAULT_BURST_INTERVAL_MS 200
|
||||||
|
|
||||||
|
#define MAX_TCP_PAYLOAD_SIZE 10240
|
||||||
|
uint8_t g_TxNetBuffer_A_Mem[MAX_TCP_PAYLOAD_SIZE];
|
||||||
|
uint8_t g_TxNetBuffer_B_Mem[MAX_TCP_PAYLOAD_SIZE];
|
||||||
|
|
||||||
|
TcpTxBuffer_t g_TxNetBuffer_A = {
|
||||||
|
.pBuffer = g_TxNetBuffer_A_Mem,
|
||||||
|
.TotalCapacity = MAX_TCP_PAYLOAD_SIZE,
|
||||||
|
.HeadOffset = 64,
|
||||||
|
.ValidPayloadLen = 0
|
||||||
|
};
|
||||||
|
TcpTxBuffer_t g_TxNetBuffer_B = {
|
||||||
|
.pBuffer = g_TxNetBuffer_B_Mem,
|
||||||
|
.TotalCapacity = MAX_TCP_PAYLOAD_SIZE,
|
||||||
|
.HeadOffset = 64,
|
||||||
|
.ValidPayloadLen = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void OnConfigUpdate(const ConfigCommon_t *common, const Config2D_t *cfg2d, const Config1D_t *cfg1d)
|
||||||
|
{
|
||||||
|
Preprocess_Settings_Change(cfg2d, cfg1d, common);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern volatile uint32_t sys_tick_ms;
|
||||||
|
|
||||||
|
#if TEST_ENABLE_NG_GPIO
|
||||||
|
/* NG output GPIO: PA8 push-pull, active-high when NG detected */
|
||||||
|
#define NG_GPIO_PORT GPIOA
|
||||||
|
#define NG_GPIO_PIN GPIO_Pin_8
|
||||||
|
#define NG_GPIO_CLK RCC_APB2Periph_GPIOA
|
||||||
|
#define NG_PULSE_MS 200 /* default NG pulse width */
|
||||||
|
|
||||||
|
static volatile uint32_t g_ng_off_time = 0;
|
||||||
|
|
||||||
|
static void NG_GPIO_Init(void)
|
||||||
|
{
|
||||||
|
GPIO_InitTypeDef gpio = {0};
|
||||||
|
RCC_APB2PeriphClockCmd(NG_GPIO_CLK, ENABLE);
|
||||||
|
gpio.GPIO_Pin = NG_GPIO_PIN;
|
||||||
|
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||||
|
gpio.GPIO_Speed = GPIO_Speed_2MHz;
|
||||||
|
GPIO_Init(NG_GPIO_PORT, &gpio);
|
||||||
|
GPIO_ResetBits(NG_GPIO_PORT, NG_GPIO_PIN);
|
||||||
|
}
|
||||||
|
#endif /* TEST_ENABLE_NG_GPIO */
|
||||||
|
|
||||||
|
#if TEST_ENABLE_TEMP_REQ
|
||||||
|
/* Flag set by TempFrameRequest callback; consumed by business task */
|
||||||
|
static volatile uint8_t g_temp_req_pending = 0;
|
||||||
|
static volatile uint8_t g_temp_req_is2d = 1;
|
||||||
|
|
||||||
|
void OnTempFrameRequest(uint8_t is2dRequest)
|
||||||
|
{
|
||||||
|
g_temp_req_is2d = is2dRequest;
|
||||||
|
g_temp_req_pending = 1;
|
||||||
|
DBG_APP("TempFrameReq is2d=%d\r\n", (int)is2dRequest);
|
||||||
|
}
|
||||||
|
#endif /* TEST_ENABLE_TEMP_REQ */
|
||||||
|
|
||||||
|
#if TEST_ENABLE_NG_GPIO
|
||||||
|
void OnDetectionResult(uint32_t frameNumber, uint8_t resultStatus)
|
||||||
|
{
|
||||||
|
(void)frameNumber;
|
||||||
|
/* resultStatus: 0 = NG, 1 = OK */
|
||||||
|
if (resultStatus == 0) {
|
||||||
|
ConfigCommon_t tmp_common;
|
||||||
|
Config2D_t tmp_cfg2d;
|
||||||
|
Config1D_t tmp_cfg1d;
|
||||||
|
uint32_t pulse_ms = NG_PULSE_MS; /* fallback default */
|
||||||
|
if (TcpLogic_GetLatestConfig(&tmp_common, &tmp_cfg2d, &tmp_cfg1d) == 0
|
||||||
|
&& tmp_cfg2d.NGioDelay > 0) {
|
||||||
|
pulse_ms = tmp_cfg2d.NGioDelay;
|
||||||
|
}
|
||||||
|
GPIO_SetBits(NG_GPIO_PORT, NG_GPIO_PIN);
|
||||||
|
g_ng_off_time = sys_tick_ms + pulse_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TEST_ENABLE_NG_GPIO */
|
||||||
|
|
||||||
|
#define KEEPALIVE_ENABLE 1
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* FLASH / SRAM 分配配置 (Option Bytes RAM_CODE_MOD[2:0])
|
||||||
|
* 修改后需复位才生效
|
||||||
|
* ============================================================ */
|
||||||
|
typedef enum {
|
||||||
|
FLASH_192_SRAM_128 = 0, /* 00x 默认 */
|
||||||
|
FLASH_224_SRAM_96, /* 01x */
|
||||||
|
FLASH_256_SRAM_64, /* 10x */
|
||||||
|
FLASH_128_SRAM_192, /* 110 */
|
||||||
|
FLASH_288_SRAM_32 /* 111 */
|
||||||
|
} FLASH_SRAM_DEFIN;
|
||||||
|
|
||||||
|
static void Config_Flash_SRAM(FLASH_SRAM_DEFIN mode)
|
||||||
|
{
|
||||||
|
uint8_t UserByte = FLASH_GetUserOptionByte() & 0xFF;
|
||||||
|
uint8_t newByte = UserByte & ~0xE0; /* clear bits [7:5] */
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case FLASH_192_SRAM_128: break; /* 000 */
|
||||||
|
case FLASH_224_SRAM_96: newByte |= 0x40; break; /* 010 */
|
||||||
|
case FLASH_256_SRAM_64: newByte |= 0x80; break; /* 100 */
|
||||||
|
case FLASH_128_SRAM_192: newByte |= 0xC0; break; /* 110 */
|
||||||
|
case FLASH_288_SRAM_32: newByte |= 0xE0; break; /* 111 */
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newByte == UserByte) return; /* already configured */
|
||||||
|
|
||||||
|
FLASH_Unlock();
|
||||||
|
FLASH_ProgramOptionByteData(0x1FFFF802, newByte);
|
||||||
|
FLASH_Lock();
|
||||||
|
printf("Flash/SRAM config changed to %d, resetting...\r\n", mode);
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 MACAddr[6];
|
||||||
|
u8 IPAddr[4] = {192, 168, 7, 10};
|
||||||
|
u8 GWIPAddr[4] = {192, 168, 7, 1};
|
||||||
|
u8 IPMask[4] = {255, 255, 255, 0};
|
||||||
|
u8 DESIP[4] = {192, 168, 7, 50};
|
||||||
|
u16 desport = 5512;
|
||||||
|
u16 srcport = 5511;
|
||||||
|
|
||||||
|
u8 SocketId;
|
||||||
|
u8 SocketRecvBuf[WCHNET_MAX_SOCKET_NUM][RECE_BUF_LEN];
|
||||||
|
|
||||||
|
void mStopIfError(u8 iError)
|
||||||
|
{
|
||||||
|
if (iError == WCHNET_ERR_SUCCESS) return;
|
||||||
|
printf("Error: %02X\r\n", (u16)iError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TIM2_Init(void)
|
||||||
|
{
|
||||||
|
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
|
||||||
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||||
|
TIM_TimeBaseStructure.TIM_Period = WCHNETTIMERPERIOD * 1000 - 1;
|
||||||
|
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1;
|
||||||
|
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
|
||||||
|
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
||||||
|
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
|
||||||
|
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
|
||||||
|
TIM_Cmd(TIM2, ENABLE);
|
||||||
|
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
|
||||||
|
NVIC_EnableIRQ(TIM2_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* qdx_port.c notification hooks */
|
||||||
|
extern void qdx_port_sock_recv_notify(uint8_t sockid);
|
||||||
|
extern void qdx_port_sock_connect_notify(uint8_t sockid);
|
||||||
|
extern void qdx_port_sock_disconnect_notify(uint8_t sockid);
|
||||||
|
extern void qdx_port_init(void);
|
||||||
|
extern void qdx_port_net_lock(void);
|
||||||
|
extern void qdx_port_net_unlock(void);
|
||||||
|
|
||||||
|
|
||||||
|
void WCHNET_HandleSockInt(u8 socketid, u8 intstat)
|
||||||
|
{
|
||||||
|
DBG_APP("SockInt: id=%d stat=0x%02X\r\n", socketid, intstat);
|
||||||
|
if (intstat & SINT_STAT_RECV)
|
||||||
|
{
|
||||||
|
qdx_port_sock_recv_notify(socketid);
|
||||||
|
}
|
||||||
|
if (intstat & SINT_STAT_CONNECT)
|
||||||
|
{
|
||||||
|
WCHNET_ModifyRecvBuf(socketid, (u32)SocketRecvBuf[socketid], RECE_BUF_LEN);
|
||||||
|
qdx_port_sock_connect_notify(socketid);
|
||||||
|
DBG_APP("TCP Connected, socket %d\r\n", socketid);
|
||||||
|
}
|
||||||
|
if (intstat & SINT_STAT_DISCONNECT)
|
||||||
|
{
|
||||||
|
qdx_port_sock_disconnect_notify(socketid);
|
||||||
|
DBG_APP("TCP Disconnected, socket %d\r\n", socketid);
|
||||||
|
}
|
||||||
|
if (intstat & SINT_STAT_TIM_OUT)
|
||||||
|
{
|
||||||
|
qdx_port_sock_disconnect_notify(socketid);
|
||||||
|
DBG_APP("TCP Timeout, socket %d\r\n", socketid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WCHNET_HandleGlobalInt(void)
|
||||||
|
{
|
||||||
|
u8 intstat;
|
||||||
|
u16 i;
|
||||||
|
u8 socketint;
|
||||||
|
intstat = WCHNET_GetGlobalInt();
|
||||||
|
DBG_APP("GlobalInt: 0x%02X\r\n", intstat);
|
||||||
|
if (intstat & GINT_STAT_UNREACH) DBG_APP("GINT_STAT_UNREACH\r\n");
|
||||||
|
if (intstat & GINT_STAT_IP_CONFLI) DBG_APP("GINT_STAT_IP_CONFLI\r\n");
|
||||||
|
if (intstat & GINT_STAT_PHY_CHANGE) {
|
||||||
|
i = WCHNET_GetPHYStatus();
|
||||||
|
DBG_APP("PHY_CHANGE: status=0x%04X %s\r\n", i,
|
||||||
|
(i & PHY_Linked_Status) ? "LINK_UP" : "LINK_DOWN");
|
||||||
|
}
|
||||||
|
if (intstat & GINT_STAT_SOCKET) {
|
||||||
|
for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {
|
||||||
|
socketint = WCHNET_GetSocketInt(i);
|
||||||
|
if (socketint) WCHNET_HandleSockInt(i, socketint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TEST_ENABLE_HEARTBEAT
|
||||||
|
/* ============================================================
|
||||||
|
* RTOS Task: Heartbeat (periodic print for debug)
|
||||||
|
* ============================================================ */
|
||||||
|
static void task_heartbeat_entry(void *pvParameters)
|
||||||
|
{
|
||||||
|
(void)pvParameters;
|
||||||
|
uint32_t cnt = 0;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
printf("[HB] %d tick=%d TEST_MODE frm=%d\r\n",
|
||||||
|
(int)cnt++, (int)xTaskGetTickCount(),
|
||||||
|
(int)dvp_frame_count);
|
||||||
|
#else
|
||||||
|
printf("[HB] %d tick=%d dvp_frm=%d row_irq=%d\r\n",
|
||||||
|
(int)cnt++, (int)xTaskGetTickCount(),
|
||||||
|
(int)dvp_frame_count, (int)dvp_row_irq_cnt);
|
||||||
|
#endif
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TEST_ENABLE_HEARTBEAT */
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* RTOS Task: WCHNET protocol stack driver (highest priority)
|
||||||
|
* ============================================================ */
|
||||||
|
static void task_wchnet_entry(void *pvParameters)
|
||||||
|
{
|
||||||
|
(void)pvParameters;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
qdx_port_net_lock();
|
||||||
|
WCHNET_MainTask();
|
||||||
|
if (WCHNET_QueryGlobalInt())
|
||||||
|
WCHNET_HandleGlobalInt();
|
||||||
|
qdx_port_net_unlock();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* RTOS Task: Business logic (DVP + preprocess + send)
|
||||||
|
* ============================================================ */
|
||||||
|
#if TEST_ENABLE_TRIGGER
|
||||||
|
/* ============================================================
|
||||||
|
* Burst capture state machine
|
||||||
|
* ============================================================ */
|
||||||
|
static uint8_t burst_active = 0; /* 1 = currently in burst */
|
||||||
|
static uint8_t burst_remaining = 0; /* frames left to capture */
|
||||||
|
static uint32_t burst_next_time_ms = 0; /* next burst frame time */
|
||||||
|
#endif /* TEST_ENABLE_TRIGGER */
|
||||||
|
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
/* ============================================================
|
||||||
|
* Test Pattern Generator
|
||||||
|
* Generates simulated 256x192 Y16 thermal images at ~10 FPS.
|
||||||
|
* Pattern cycles through several types so all pipeline paths
|
||||||
|
* can be exercised.
|
||||||
|
*
|
||||||
|
* Pattern 0: Gradient (25.00~45.00°C) — baseline, no trigger
|
||||||
|
* Pattern 1: Hot center spot (90.00°C) — should trigger alarm
|
||||||
|
* Pattern 2: Uniform warm (35.00°C) — no trigger
|
||||||
|
* Pattern 3: Checkerboard with hot cells — tests ROI search
|
||||||
|
* ============================================================ */
|
||||||
|
#define TEST_FPS_DELAY_MS 100 /* ~10 FPS */
|
||||||
|
#define TEMP_RAW(deg_c) ((uint16_t)((deg_c) * 10)) /* e.g. 35.0°C → 350, 0.1°C/LSB */
|
||||||
|
|
||||||
|
static void test_fill_gradient(uint16_t *buf, uint16_t w, uint16_t h)
|
||||||
|
{
|
||||||
|
/* Diagonal gradient: 25°C at top-left → 45°C at bottom-right
|
||||||
|
* Both X and Y contribute so 1D center-row also shows variation */
|
||||||
|
for (uint16_t y = 0; y < h; y++) {
|
||||||
|
for (uint16_t x = 0; x < w; x++) {
|
||||||
|
uint32_t pos = (uint32_t)y * w + (uint32_t)x * h; /* combined weight */
|
||||||
|
uint16_t temp = TEMP_RAW(25.0) + (uint16_t)(pos * TEMP_RAW(20.0) / ((uint32_t)w * h));
|
||||||
|
buf[y * w + x] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fill_hotspot(uint16_t *buf, uint16_t w, uint16_t h)
|
||||||
|
{
|
||||||
|
/* Background 30°C with slight X variation, center 32x32 block at 90°C */
|
||||||
|
for (uint16_t y = 0; y < h; y++)
|
||||||
|
for (uint16_t x = 0; x < w; x++)
|
||||||
|
buf[y * w + x] = TEMP_RAW(28.0) + (uint16_t)((uint32_t)x * TEMP_RAW(4.0) / w);
|
||||||
|
uint16_t cx = w / 2, cy = h / 2;
|
||||||
|
for (uint16_t y = cy - 16; y < cy + 16; y++)
|
||||||
|
for (uint16_t x = cx - 16; x < cx + 16; x++)
|
||||||
|
buf[y * w + x] = TEMP_RAW(90.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fill_uniform(uint16_t *buf, uint16_t w, uint16_t h)
|
||||||
|
{
|
||||||
|
for (uint16_t y = 0; y < h; y++)
|
||||||
|
for (uint16_t x = 0; x < w; x++)
|
||||||
|
buf[y * w + x] = TEMP_RAW(35.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fill_checker(uint16_t *buf, uint16_t w, uint16_t h)
|
||||||
|
{
|
||||||
|
/* 32x32 checkerboard: alternating 30°C and 85°C blocks */
|
||||||
|
for (uint16_t y = 0; y < h; y++)
|
||||||
|
for (uint16_t x = 0; x < w; x++) {
|
||||||
|
uint8_t cell = ((y / 32) + (x / 32)) & 1;
|
||||||
|
buf[y * w + x] = cell ? TEMP_RAW(85.0) : TEMP_RAW(28.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void task_test_pattern_entry(void *pvParameters)
|
||||||
|
{
|
||||||
|
(void)pvParameters;
|
||||||
|
uint32_t frame_num = 0;
|
||||||
|
uint8_t pattern_idx = 0;
|
||||||
|
/* Cycle: gradient x20, hotspot x5, uniform x10, checker x5 */
|
||||||
|
const uint8_t pattern_seq[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
1,1,1,1,1,
|
||||||
|
2,2,2,2,2,2,2,2,2,2,
|
||||||
|
3,3,3,3,3};
|
||||||
|
const uint8_t seq_len = sizeof(pattern_seq);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
uint16_t *buf = (uint16_t *)FrameBuffer;
|
||||||
|
switch (pattern_seq[pattern_idx % seq_len]) {
|
||||||
|
case 0: test_fill_gradient(buf, SENSOR_WIDTH, SENSOR_HEIGHT); break;
|
||||||
|
case 1: test_fill_hotspot(buf, SENSOR_WIDTH, SENSOR_HEIGHT); break;
|
||||||
|
case 2: test_fill_uniform(buf, SENSOR_WIDTH, SENSOR_HEIGHT); break;
|
||||||
|
case 3: test_fill_checker(buf, SENSOR_WIDTH, SENSOR_HEIGHT); break;
|
||||||
|
}
|
||||||
|
pattern_idx++;
|
||||||
|
if (pattern_idx >= seq_len) pattern_idx = 0;
|
||||||
|
|
||||||
|
frame_num++;
|
||||||
|
dvp_frame_count = frame_num;
|
||||||
|
Ready_Frame_Count = frame_num;
|
||||||
|
Frame_Ready_Flag = 1;
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(TEST_FPS_DELAY_MS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TEST_PATTERN_MODE */
|
||||||
|
|
||||||
|
#if TEST_ENABLE_TEMP_REQ
|
||||||
|
/* ============================================================
|
||||||
|
* 1D mode: build and send a single 1D temperature frame from
|
||||||
|
* the current raw image. Scans the center row and packs
|
||||||
|
* TempPoint1D_t (2B time_offset + 2B temp, little-endian).
|
||||||
|
* ============================================================ */
|
||||||
|
#define TEST_1D_POINTS 30 /* 1D test: sample 30 points from center row */
|
||||||
|
|
||||||
|
static void send_1d_frame_from_raw(const RawImageBuffer_t *raw, TcpTxBuffer_t *tx_buf)
|
||||||
|
{
|
||||||
|
uint16_t w = raw->Width;
|
||||||
|
uint16_t h = raw->Height;
|
||||||
|
uint16_t *src = raw->pData;
|
||||||
|
uint16_t row = h / 2; /* center row */
|
||||||
|
|
||||||
|
tx_buf->ValidPayloadLen = 0;
|
||||||
|
uint8_t *dest = tx_buf->pBuffer + tx_buf->HeadOffset;
|
||||||
|
uint32_t capacity = tx_buf->TotalCapacity - tx_buf->HeadOffset;
|
||||||
|
uint16_t points = TEST_1D_POINTS;
|
||||||
|
if (points > w) points = w;
|
||||||
|
if ((uint32_t)points * 4 > capacity)
|
||||||
|
points = (uint16_t)(capacity / 4);
|
||||||
|
|
||||||
|
int16_t min_t = 32767, max_t = -32768;
|
||||||
|
int32_t sum_t = 0;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < points; i++) {
|
||||||
|
/* Evenly sample across the full row width */
|
||||||
|
uint16_t col = (uint16_t)((uint32_t)i * (w - 1) / (points > 1 ? points - 1 : 1));
|
||||||
|
uint16_t temp = src[row * w + col];
|
||||||
|
uint16_t time_offset = (uint16_t)((uint32_t)i * 600 / (points > 1 ? points - 1 : 1));
|
||||||
|
int16_t t = (int16_t)temp;
|
||||||
|
if (t < min_t) min_t = t;
|
||||||
|
if (t > max_t) max_t = t;
|
||||||
|
sum_t += t;
|
||||||
|
dest[0] = (uint8_t)(time_offset & 0xFF);
|
||||||
|
dest[1] = (uint8_t)((time_offset >> 8) & 0xFF);
|
||||||
|
dest[2] = (uint8_t)(temp & 0xFF);
|
||||||
|
dest[3] = (uint8_t)((temp >> 8) & 0xFF);
|
||||||
|
dest += 4;
|
||||||
|
}
|
||||||
|
tx_buf->ValidPayloadLen = (uint32_t)points * 4;
|
||||||
|
|
||||||
|
PreprocessResult_t meta;
|
||||||
|
memset(&meta, 0, sizeof(meta));
|
||||||
|
meta.pValidData = tx_buf->pBuffer + tx_buf->HeadOffset;
|
||||||
|
meta.DataLength = tx_buf->ValidPayloadLen;
|
||||||
|
meta.ValidWidth = points;
|
||||||
|
meta.ValidHeight = 1;
|
||||||
|
meta.MinTemp = min_t;
|
||||||
|
meta.MaxTemp = max_t;
|
||||||
|
meta.AvgTemp = (int16_t)(sum_t / (points > 0 ? points : 1));
|
||||||
|
meta.RoiTemp = meta.AvgTemp;
|
||||||
|
meta.FrameNumber = raw->FrameNumber;
|
||||||
|
|
||||||
|
TcpLogic_BuildAndSendTemperatureFrame(tx_buf, &meta, 0x01, 0 /* IS_1D */);
|
||||||
|
}
|
||||||
|
#endif /* TEST_ENABLE_TEMP_REQ */
|
||||||
|
|
||||||
|
static void task_business_entry(void *pvParameters)
|
||||||
|
{
|
||||||
|
(void)pvParameters;
|
||||||
|
static uint8_t use_buffer_A = 1;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
#if TEST_ENABLE_NG_GPIO
|
||||||
|
/* NG pulse off check */
|
||||||
|
if (g_ng_off_time && sys_tick_ms >= g_ng_off_time) {
|
||||||
|
GPIO_ResetBits(NG_GPIO_PORT, NG_GPIO_PIN);
|
||||||
|
g_ng_off_time = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !TEST_PATTERN_MODE
|
||||||
|
DVP_Task();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TEST_ENABLE_TEMP_REQ
|
||||||
|
/* Handle on-demand frame request from server */
|
||||||
|
if (g_temp_req_pending)
|
||||||
|
{
|
||||||
|
g_temp_req_pending = 0;
|
||||||
|
|
||||||
|
/* 检查对应模式是否已由服务器使能 */
|
||||||
|
ConfigCommon_t tc; Config2D_t t2; Config1D_t t1;
|
||||||
|
uint8_t mode_enabled = 0;
|
||||||
|
if (TcpLogic_GetLatestConfig(&tc, &t2, &t1) == 0) {
|
||||||
|
if (g_temp_req_is2d && t2.Enabled)
|
||||||
|
mode_enabled = 1;
|
||||||
|
else if (!g_temp_req_is2d && t1.Enabled)
|
||||||
|
mode_enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mode_enabled) {
|
||||||
|
DBG_APP("TEMP_REQ ignored: %s not enabled\r\n",
|
||||||
|
g_temp_req_is2d ? "2D" : "1D");
|
||||||
|
} else {
|
||||||
|
RawImageBuffer_t raw_img;
|
||||||
|
raw_img.pData = (uint16_t *)FrameBuffer;
|
||||||
|
raw_img.Width = SENSOR_WIDTH;
|
||||||
|
raw_img.Height = SENSOR_HEIGHT;
|
||||||
|
raw_img.FrameNumber = Ready_Frame_Count;
|
||||||
|
|
||||||
|
TcpTxBuffer_t *tx_buf = use_buffer_A ? &g_TxNetBuffer_A : &g_TxNetBuffer_B;
|
||||||
|
use_buffer_A = !use_buffer_A;
|
||||||
|
tx_buf->ValidPayloadLen = 0;
|
||||||
|
|
||||||
|
if (g_temp_req_is2d) {
|
||||||
|
PreprocessResult_t meta;
|
||||||
|
int8_t pp_ret = Preprocess_Execute(&raw_img, tx_buf, &meta);
|
||||||
|
if (pp_ret == 0) {
|
||||||
|
int8_t tx_ret = TcpLogic_BuildAndSendTemperatureFrame(tx_buf, &meta, 0x02 /* REQUEST */, 1);
|
||||||
|
DBG_APP("TEMP_REQ SEND 2D frm=%d %dx%d ret=%d\r\n",
|
||||||
|
(int)meta.FrameNumber, (int)meta.ValidWidth,
|
||||||
|
(int)meta.ValidHeight, (int)tx_ret);
|
||||||
|
} else {
|
||||||
|
DBG_APP("TEMP_REQ PP fail ret=%d\r\n", (int)pp_ret);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
send_1d_frame_from_raw(&raw_img, tx_buf);
|
||||||
|
DBG_APP("TEMP_REQ SEND 1D frm=%d\r\n", (int)raw_img.FrameNumber);
|
||||||
|
}
|
||||||
|
} /* mode_enabled */
|
||||||
|
}
|
||||||
|
#endif /* TEST_ENABLE_TEMP_REQ */
|
||||||
|
|
||||||
|
if (Frame_Ready_Flag)
|
||||||
|
{
|
||||||
|
Frame_Ready_Flag = 0;
|
||||||
|
|
||||||
|
RawImageBuffer_t raw_img;
|
||||||
|
raw_img.pData = (uint16_t *)FrameBuffer;
|
||||||
|
raw_img.Width = SENSOR_WIDTH;
|
||||||
|
raw_img.Height = SENSOR_HEIGHT;
|
||||||
|
raw_img.FrameNumber = Ready_Frame_Count;
|
||||||
|
|
||||||
|
#if TEST_ENABLE_TRIGGER
|
||||||
|
/* 触发逻辑仅在服务器下发 Config2D 且 Enabled=1 后才激活 */
|
||||||
|
{
|
||||||
|
ConfigCommon_t tc; Config2D_t t2; Config1D_t t1;
|
||||||
|
uint8_t trigger_armed = 0;
|
||||||
|
if (TcpLogic_GetLatestConfig(&tc, &t2, &t1) == 0 && t2.Enabled)
|
||||||
|
trigger_armed = 1;
|
||||||
|
|
||||||
|
if (trigger_armed)
|
||||||
|
{
|
||||||
|
if (burst_active)
|
||||||
|
{
|
||||||
|
/* Burst mode: wait for interval, then capture & send */
|
||||||
|
if (sys_tick_ms >= burst_next_time_ms)
|
||||||
|
{
|
||||||
|
PreprocessResult_t meta;
|
||||||
|
TcpTxBuffer_t *tx_buf = use_buffer_A ? &g_TxNetBuffer_A : &g_TxNetBuffer_B;
|
||||||
|
use_buffer_A = !use_buffer_A;
|
||||||
|
tx_buf->ValidPayloadLen = 0;
|
||||||
|
|
||||||
|
if (Preprocess_Execute(&raw_img, tx_buf, &meta) == 0)
|
||||||
|
{
|
||||||
|
TcpLogic_BuildAndSendTemperatureFrame(tx_buf, &meta, 0x01, 1);
|
||||||
|
}
|
||||||
|
burst_remaining--;
|
||||||
|
if (burst_remaining == 0) {
|
||||||
|
burst_active = 0;
|
||||||
|
DBG_APP("Burst complete\r\n");
|
||||||
|
} else {
|
||||||
|
ConfigCommon_t tc; Config2D_t t2; Config1D_t t1;
|
||||||
|
uint16_t interval_ms = DEFAULT_BURST_INTERVAL_MS;
|
||||||
|
if (TcpLogic_GetLatestConfig(&tc, &t2, &t1) == 0
|
||||||
|
&& t2.TriggerInternalIntervalMs > 0)
|
||||||
|
interval_ms = t2.TriggerInternalIntervalMs;
|
||||||
|
burst_next_time_ms = sys_tick_ms + interval_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Preprocess_CheckInternalTrigger2D(&raw_img) == 1)
|
||||||
|
{
|
||||||
|
/* Trigger hit! Start burst: this frame is frame #0 */
|
||||||
|
DBG_APP("TRIGGER frm=%d\r\n", (int)raw_img.FrameNumber);
|
||||||
|
PreprocessResult_t meta;
|
||||||
|
TcpTxBuffer_t *tx_buf = use_buffer_A ? &g_TxNetBuffer_A : &g_TxNetBuffer_B;
|
||||||
|
use_buffer_A = !use_buffer_A;
|
||||||
|
tx_buf->ValidPayloadLen = 0;
|
||||||
|
|
||||||
|
int8_t pp_ret = Preprocess_Execute(&raw_img, tx_buf, &meta);
|
||||||
|
if (pp_ret == 0)
|
||||||
|
{
|
||||||
|
int8_t tx_ret = TcpLogic_BuildAndSendTemperatureFrame(tx_buf, &meta, 0x01, 1);
|
||||||
|
DBG_APP("SEND frm=%d %dx%d ret=%d\r\n",
|
||||||
|
(int)meta.FrameNumber, (int)meta.ValidWidth,
|
||||||
|
(int)meta.ValidHeight, (int)tx_ret);
|
||||||
|
} else {
|
||||||
|
DBG_APP("PP fail ret=%d\r\n", (int)pp_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine burst count from config (fall back to defaults) */
|
||||||
|
ConfigCommon_t tc; Config2D_t t2; Config1D_t t1;
|
||||||
|
uint8_t total_burst = DEFAULT_BURST_COUNT;
|
||||||
|
uint16_t interval_ms = DEFAULT_BURST_INTERVAL_MS;
|
||||||
|
if (TcpLogic_GetLatestConfig(&tc, &t2, &t1) == 0) {
|
||||||
|
if (t2.TriggerBurstCount >= 1)
|
||||||
|
total_burst = t2.TriggerBurstCount;
|
||||||
|
if (t2.TriggerInternalIntervalMs > 0)
|
||||||
|
interval_ms = t2.TriggerInternalIntervalMs;
|
||||||
|
}
|
||||||
|
if (total_burst > 1) {
|
||||||
|
burst_active = 1;
|
||||||
|
burst_remaining = total_burst - 1; /* frame #0 already sent */
|
||||||
|
burst_next_time_ms = sys_tick_ms + interval_ms;
|
||||||
|
DBG_APP("Burst start: %d frames, interval=%d ms\r\n",
|
||||||
|
(int)total_burst, (int)interval_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /* if trigger_armed */
|
||||||
|
} /* trigger scope */
|
||||||
|
#endif /* TEST_ENABLE_TRIGGER */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
u8 i;
|
||||||
|
SystemCoreClockUpdate();
|
||||||
|
Delay_Init();
|
||||||
|
USART_Printf_Init(921600);
|
||||||
|
printf("TCPClient Test\r\nSystemClk:%d\r\n", SystemCoreClock);
|
||||||
|
printf("=== Feature Switches ===\r\n");
|
||||||
|
printf(" PATTERN=%d TRIGGER=%d NG_GPIO=%d TEMP_REQ=%d TCP_SEND=%d HB=%d\r\n",
|
||||||
|
TEST_PATTERN_MODE, TEST_ENABLE_TRIGGER, TEST_ENABLE_NG_GPIO,
|
||||||
|
TEST_ENABLE_TEMP_REQ, TEST_ENABLE_TCP_SEND, TEST_ENABLE_HEARTBEAT);
|
||||||
|
printf("UserByte: %02x\r\n", FLASH_GetUserOptionByte() & 0xFF);
|
||||||
|
Config_Flash_SRAM(FLASH_128_SRAM_192);
|
||||||
|
printf("net version:%x\n", WCHNET_GetVer());
|
||||||
|
if (WCHNET_LIB_VER != WCHNET_GetVer()) printf("version error.\n");
|
||||||
|
|
||||||
|
WCHNET_GetMacAddr(MACAddr);
|
||||||
|
printf("mac addr:");
|
||||||
|
for (i = 0; i < 6; i++) printf("%x ", MACAddr[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
printf("=== TEST PATTERN MODE === No sensor/DVP hardware needed\r\n");
|
||||||
|
/* Skip Mini212G2_Init() and DVP_Init() */
|
||||||
|
#else
|
||||||
|
/* 模组已通过 USB 预配置,无需 MCU UART 配置,直接初始化 DVP */
|
||||||
|
DVP_Init();
|
||||||
|
#endif
|
||||||
|
TIM2_Init();
|
||||||
|
#if TEST_ENABLE_NG_GPIO
|
||||||
|
NG_GPIO_Init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
i = ETH_LibInit(IPAddr, GWIPAddr, IPMask, MACAddr);
|
||||||
|
mStopIfError(i);
|
||||||
|
if (i == WCHNET_ERR_SUCCESS) printf("WCHNET_LibInit Success\r\n");
|
||||||
|
|
||||||
|
#if KEEPALIVE_ENABLE
|
||||||
|
{
|
||||||
|
struct _KEEP_CFG cfg = {20000, 15000, 9};
|
||||||
|
WCHNET_ConfigKeepLive(&cfg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
qdx_port_init();
|
||||||
|
|
||||||
|
Preprocess_Init(SENSOR_WIDTH, SENSOR_HEIGHT);
|
||||||
|
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
/* Set default preprocess config so trigger fires with test patterns.
|
||||||
|
* Without this, all Config2D_t fields are 0 and trigger never fires
|
||||||
|
* (roi_w==0 → immediate return 0). */
|
||||||
|
{
|
||||||
|
Config2D_t test_cfg2d;
|
||||||
|
memset(&test_cfg2d, 0, sizeof(test_cfg2d));
|
||||||
|
test_cfg2d.TargetWidth = 64; /* ROI: 64x64 = 8KB, fits in 10KB buffer */
|
||||||
|
test_cfg2d.TargetHeight = 64;
|
||||||
|
test_cfg2d.TriggerRoiX = 0; /* Full-frame trigger area */
|
||||||
|
test_cfg2d.TriggerRoiY = 0;
|
||||||
|
test_cfg2d.TriggerRoiW = SENSOR_WIDTH;
|
||||||
|
test_cfg2d.TriggerRoiH = SENSOR_HEIGHT;
|
||||||
|
test_cfg2d.TriggerCondition = 1; /* Max temperature */
|
||||||
|
test_cfg2d.TriggerTemperatureThreshold = 800; /* 80.0°C (0.1°C/LSB) */
|
||||||
|
test_cfg2d.TriggerBurstCount = 3;
|
||||||
|
test_cfg2d.TriggerInternalIntervalMs = 200;
|
||||||
|
test_cfg2d.NGioDelay = 200;
|
||||||
|
Preprocess_Settings_Change(&test_cfg2d, NULL, NULL);
|
||||||
|
printf("Test default config loaded: trigger thresh=800 ROI=full burst=3\r\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TcpLogic_Init(MACAddr, NULL);
|
||||||
|
TcpLogic_RegisterConfigCallback(OnConfigUpdate);
|
||||||
|
#if TEST_ENABLE_NG_GPIO
|
||||||
|
TcpLogic_RegisterDetectionCallback(OnDetectionResult);
|
||||||
|
#endif
|
||||||
|
#if TEST_ENABLE_TEMP_REQ
|
||||||
|
TcpLogic_RegisterTempFrameRequestCallback(OnTempFrameRequest);
|
||||||
|
#endif
|
||||||
|
DBG_APP("TcpLogic_Start...\r\n");
|
||||||
|
TcpLogic_Start();
|
||||||
|
|
||||||
|
DBG_APP("Creating RTOS tasks...\r\n");
|
||||||
|
xTaskCreate(task_wchnet_entry, "wchnet", 512, NULL, 6, NULL);
|
||||||
|
#if TEST_ENABLE_TCP_SEND
|
||||||
|
xTaskCreate(task_business_entry, "business", 512, NULL, 5, NULL);
|
||||||
|
#endif
|
||||||
|
#if TEST_ENABLE_HEARTBEAT
|
||||||
|
xTaskCreate(task_heartbeat_entry, "hb", 256, NULL, 3, NULL);
|
||||||
|
#endif
|
||||||
|
#if TEST_PATTERN_MODE
|
||||||
|
xTaskCreate(task_test_pattern_entry, "testpat", 256, NULL, 4, NULL);
|
||||||
|
#endif
|
||||||
|
DBG_APP("Starting scheduler\r\n");
|
||||||
|
vTaskStartScheduler();
|
||||||
|
|
||||||
|
/* Should never reach here */
|
||||||
|
while (1) {}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user