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