## ADDED Requirements

### Requirement: 系统时间获取
`qdx_port_get_tick_ms()` 必须返回自系统启动以来的毫秒级单调递增时间戳，精度不低于 FreeRTOS tick 周期（2ms）。

#### Scenario: 获取系统运行时间
- **WHEN** 任意任务或模块调用 `qdx_port_get_tick_ms()`
- **THEN** 返回值为基于 `xTaskGetTickCount()` 转换的毫秒数，且数值不会回绕至 0（在 uint32_t 溢回之前）

### Requirement: 任务级阻塞延时
`qdx_port_delay_ms()` 必须让当前 FreeRTOS 任务挂起指定毫秒数，期间让出 CPU 给其他任务。

#### Scenario: 延时期间 CPU 让出
- **WHEN** 后台线程调用 `qdx_port_delay_ms(100)`
- **THEN** 该任务进入阻塞态约 100ms，其余就绪任务在此期间获得调度

### Requirement: 互斥锁创建与操作
`qdx_port_mutex_create/lock/unlock/delete` 必须基于 FreeRTOS 互斥信号量实现，支持跨任务的临界区保护和优先级继承。

#### Scenario: 并发访问配置结构体
- **WHEN** `tcp_rx_c` 线程正在更新 `ConfigCommon_t` 且 `task_business` 同时读取该结构体
- **THEN** 互斥锁确保同一时刻仅一个任务可访问，防止数据撕裂

#### Scenario: 互斥锁删除
- **WHEN** 调用 `qdx_port_mutex_delete` 并传入有效句柄
- **THEN** FreeRTOS 信号量资源被释放，句柄失效

### Requirement: 线程（任务）创建
`qdx_port_thread_create` 必须通过 `xTaskCreate` 创建 FreeRTOS 任务，并正确映射名称、入口函数、栈大小和优先级参数。

#### Scenario: TcpLogic_Start 创建三个后台任务
- **WHEN** `TcpLogic_Start()` 依次调用 `qdx_port_thread_create` 创建 `tcp_mgr`、`tcp_rx_c`、`tcp_rx_d`
- **THEN** 三个 FreeRTOS 任务被成功创建并开始调度执行

### Requirement: TCP Socket 连接建立
`qdx_port_tcp_connect` 必须创建 WCHNET TCP socket、配置目标 IP/端口，发起连接并阻塞等待三次握手完成（或超时 5 秒返回 NULL）。

#### Scenario: 成功连接至上位机
- **WHEN** 调用 `qdx_port_tcp_connect("192.168.1.50", 5511)` 且上位机正在监听
- **THEN** WCHNET 完成 TCP 三次握手后，`SINT_STAT_CONNECT` 中断释放连接信号量，函数返回有效的 `qdx_socket_t` 句柄

#### Scenario: 连接超时
- **WHEN** 调用 `qdx_port_tcp_connect` 但目标主机不可达
- **THEN** 阻塞等待 5 秒后超时，释放已分配的 WCHNET socket 资源，返回 NULL

### Requirement: TCP 数据发送
`qdx_port_tcp_send` 必须将指定缓冲区数据通过 `WCHNET_SocketSend` 写入 TCP 发送队列，并返回实际发送的字节数。

#### Scenario: 发送温度帧数据
- **WHEN** `TcpLogic_BuildAndSendTemperatureFrame` 组装完成后调用 `qdx_port_tcp_send` 发送到数据流 socket
- **THEN** 数据被提交至 WCHNET 发送缓冲，函数返回已发送的字节数

#### Scenario: 连接已断开时发送
- **WHEN** 对一个已断开的 socket 调用 `qdx_port_tcp_send`
- **THEN** 函数返回 < 0 表示错误

### Requirement: TCP 数据接收（信号量阻塞模式）
`qdx_port_tcp_recv` 必须从 socket 关联的接收环形缓冲中读取数据。若缓冲为空，则阻塞等待信号量通知（最长 100ms 超时），超时返回 0。

#### Scenario: 接收上位机配置指令
- **WHEN** 上位机通过 5511 端口发送配置报文，WCHNET 中断触发将数据写入环形缓冲并释放信号量
- **THEN** `tcp_rx_c` 线程从阻塞中唤醒，`qdx_port_tcp_recv` 返回实际读取的字节数和数据

#### Scenario: 无数据超时
- **WHEN** 100ms 内未收到任何数据
- **THEN** 函数返回 0，调用方继续执行心跳或重连检查逻辑

#### Scenario: 对端关闭连接
- **WHEN** WCHNET 检测到对端断开（`SINT_STAT_DISCONNECT`）
- **THEN** 函数返回 < 0，通知调用方连接已失效

### Requirement: TCP Socket 关闭
`qdx_port_tcp_close` 必须调用 `WCHNET_SocketClose` 释放 WCHNET socket 资源，清空关联的环形缓冲，并将 `SocketCtx_t` 标记为空闲。

#### Scenario: 正常关闭
- **WHEN** 连接管理线程检测到超时并调用 `qdx_port_tcp_close`
- **THEN** WCHNET socket 被关闭，环形缓冲清零，`SocketCtx_t.connected = 0`，该槽位可被后续连接复用

### Requirement: WCHNET 协议栈周期驱动
系统必须在独立的高优先级 FreeRTOS 任务 `task_wchnet` 中周期调用 `WCHNET_MainTask()` 和 `WCHNET_HandleGlobalInt()`，确保以太网帧收发和 TCP 状态机正常运转。

#### Scenario: 协议栈持续驱动
- **WHEN** FreeRTOS 调度器启动后
- **THEN** `task_wchnet` 以约 5~10ms 周期持续驱动 WCHNET 协议栈，socket 中断事件被及时处理

### Requirement: Socket 映射与上下文管理
系统必须维护静态 `SocketCtx_t` 数组（容量 = 2），管理 WCHNET socket ID 到 `qdx_socket_t` 不透明句柄的映射，以及每个 socket 关联的环形接收缓冲和信号量。

#### Scenario: 双流同时活跃
- **WHEN** 控制流 (5511) 和数据流 (5512) 同时建立连接
- **THEN** 两个 `SocketCtx_t` 槽位被分别占用，各自拥有独立的 WCHNET socket ID、接收缓冲和信号量

### Requirement: net_config.h 双 Socket 配置
`WCHNET_NUM_TCP` 必须从 1 修改为 2，以支持控制流和数据流两个并发 TCP 连接，相关宏（`WCHNET_MAX_SOCKET_NUM`、`WCHNET_NUM_POOL_BUF`、`WCHNET_MEM_HEAP_SIZE` 等）随之联动更新。

#### Scenario: 两个 TCP socket 可同时创建
- **WHEN** 系统初始化后依次创建控制流和数据流 socket
- **THEN** 两个 `WCHNET_SocketCreat` 调用均成功返回不同的 socket ID

### Requirement: TIM2 共享 FreeRTOS Tick 与 WCHNET Timer
TIM2 必须配置为 2ms 周期，ISR 中每次调用 FreeRTOS tick handler，并以软件计数器每 5 次（10ms）调用一次 `WCHNET_TimeIsr()`。

#### Scenario: 双定时源正常工作
- **WHEN** 系统运行中
- **THEN** FreeRTOS 以 500Hz 频率获得 tick 中断，WCHNET 以 100Hz 频率获得定时服务，两者互不干扰

### Requirement: main 函数重构为 RTOS 启动模型
`main()` 在完成硬件初始化和 WCHNET 初始化后，必须创建 `task_wchnet` 和 `task_business` 两个初始任务，然后调用 `vTaskStartScheduler()` 启动调度器，不再使用裸机 `while(1)` 主循环。

#### Scenario: 调度器启动
- **WHEN** `main()` 完成 ETH_LibInit、DVP_Init、Preprocess_Init、TcpLogic_Init 后
- **THEN** 调用 `vTaskStartScheduler()` 进入 RTOS 调度，所有业务逻辑在任务上下文中执行

### Requirement: FreeRTOS 内核集成
TCPClient 工程必须链接 FreeRTOS 内核源文件（tasks.c、queue.c、list.c、timers.c、port.c、heap_4.c）和 RISC-V 移植文件，并配置 `FreeRTOSConfig.h` 适配 CH32V307 时钟和中断。

#### Scenario: 工程编译通过
- **WHEN** 将 FreeRTOS 源文件和 include 路径加入 TCPClient 构建配置
- **THEN** 工程编译无错误，FreeRTOS API 可正常调用
