# TCP2UART 项目技术实现 ## 一、文档目的 本文档描述 `TCP2UART` 项目基于 `STM32F103RCT6 + FreeRTOS` 的最终内部实现口径。 本文档只围绕最终协议模型展开: - `MUX`:串口承载层 - `NET`:全局网络配置层 - `LINK[idx]`:实例配置与连接管理层 不再保留历史 `S1... / C1...` 外部字段模型。 ## 二、当前工程基础 当前工程基础约束如下: 1. MCU:`STM32F103RCT6`(256KB Flash / 48KB SRAM) 2. 网络芯片:`CH390D` 3. 软件架构:`FreeRTOS + lwIP NO_SYS=0` 4. 协议栈:`lwIP socket/netconn API` 5. 调试输出:`SEGGER RTT` 6. 使用 `FreeRTOS` 任务调度 7. 不实现 DHCP ## 三、总体架构 ```text +--------------------------------------------------+ | AT / Control Plane | | USART1 AT parser + MUX control frame parser | +--------------------------------------------------+ | Configuration Model | | MUX / NET / LINK[idx] | +--------------------------------------------------+ | FreeRTOS Tasks | | NetworkTask / UartTask / ConfigTask / RouteTask | +--------------------------------------------------+ | Inter-Task Communication | | Queue / Semaphore / Mutex / StreamBuffer | +--------------------------------------------------+ | lwIP TCP/IP Stack (NO_SYS=0) | | tcpip_thread + socket/netconn + sys_arch | +--------------------------------------------------+ | Driver Layer | | CH390 / lwIP netif / UART DMA+IDLE / HAL | +--------------------------------------------------+ ``` ## 四、FreeRTOS 任务设计(路径 A:netconn + 多 TCP 任务) ### 4.1 架构路线 本项目采用 **路径 A**:`NO_SYS=0 + netconn API + 每个 TCP 连接独立任务`。 核心决策: 1. lwIP 以 `NO_SYS=0` 模式运行,`tcpip_thread` 由 lwIP 自动创建 2. TCP 连接使用 `netconn` 阻塞 API(`netconn_accept` / `netconn_recv` / `netconn_write`) 3. 每个 TCP Server 和 Client 实例各占一个独立任务 4. 任务间通过 Queue 传递指针 + 元数据描述符,实现零拷贝 ### 4.2 任务列表(共 9 个任务 + 1 个 lwIP 自建) | 任务名 | 优先级 | 栈(words) | 模式 | 职责 | |--------|--------|-----------|------|------| | `tcpip_thread` | 6 (最高) | 512 | 阻塞 | lwIP 内核线程(自动创建) | | `NetPollTask` | 5 | 384 | 事件驱动 | `ethernetif_poll` + 链路检测 | | `TcpSrvTask_S1` | 4 | 384 | 阻塞 | `netconn_accept` + S1 收发 | | `TcpSrvTask_S2` | 4 | 384 | 阻塞 | `netconn_accept` + S2 收发 | | `TcpCliTask_C1` | 4 | 256 | 阻塞 | `netconn_connect` + C1 收发 | | `TcpCliTask_C2` | 4 | 256 | 阻塞 | `netconn_connect` + C2 收发 | | `UartRxTask` | 4 | 384 | 事件驱动 | UART DMA/IDLE 接收 + MUX 帧提取 | | `ConfigTask` | 2 | 256 | 阻塞 | AT 命令解析与响应 | | `DefaultTask` | 1 | 128 | 周期 | LED 心跳 + IWDG 喂狗 | 说明: - `tcpip_thread` 是 lwIP 自建的内核线程,处理所有协议栈内部事件 - 所有 `netconn_*` API 通过 `tcpip_thread` 消息机制实现线程安全 - `LWIP_TCPIP_CORE_LOCKING=1` 允许应用任务直接调用 `netconn_write` 而不经邮箱中转 - `tcpip_thread` 优先级最高(6),确保 TCP ACK 和超时处理不被延迟 ### 4.3 零拷贝路由消息设计 ```c /* 路由消息描述符 - Queue 传递的是此结构的指针,不拷贝负载数据 */ typedef struct { uint8_t src_id; /* 源端点 ID */ uint8_t dst_mask; /* 目标端点位图 */ uint16_t len; /* 数据长度 */ uint8_t conn_type; /* 连接标识:LINK_S1/S2/C1/C2 */ uint8_t *data; /* 指向预分配静态缓冲区 */ } route_msg_t; ``` 静态缓冲池(预分配,避免动态分配): ```c #define ROUTE_BUF_COUNT 4 #define ROUTE_BUF_SIZE 512 static uint8_t g_route_buf_pool[ROUTE_BUF_COUNT][ROUTE_BUF_SIZE]; static volatile uint8_t g_route_buf_used[ROUTE_BUF_COUNT]; ``` - 发送方:从池中获取空闲缓冲区,拷贝数据,填充 `route_msg_t`,Queue 发送指针 - 接收方:从 Queue 取 `route_msg_t*`,处理数据后标记缓冲区为可用 - 无动态分配,无堆碎片 ### 4.4 任务间通信机制 ```text UART ISR ──[TaskNotify]──> UartRxTask ──[Queue*]──> TcpSrvTask / TcpCliTask │ ▲ ├──[Queue*]──────────────>─┘ │ DSTMASK=0 └──[Queue]──> ConfigTask TcpSrvTask / TcpCliTask ──[Queue*]──> UartRxTask (UART TX 方向) EXTI0 ISR ──[BinarySem]──> NetPollTask ──> ethernetif_poll ──> tcpip_thread ``` 通信对象: | 对象 | 类型 | 生产者 | 消费者 | 用途 | |------|------|--------|--------|------| | `xNetSemaphore` | Binary Semaphore | EXTI0 ISR | NetPollTask | CH390 中断通知 | | `xUartRxNotify` | TaskNotification | UART IDLE ISR | UartRxTask | UART 接收通知 | | `xTcpRxQueue` | Queue (16, route_msg_t*) | TCP 任务 | UartRxTask | TCP→UART 数据 | | `xUartTxQueue` | Queue (8, route_msg_t*) | UartRxTask | TCP 任务 | UART→TCP 数据 | | `xConfigQueue` | Queue (8, char*) | UartRxTask | ConfigTask | AT 文本 | 说明: - TCP→UART 方向:TCP 任务调用 `netconn_recv` 获取数据,构造 `route_msg_t` 指针投递到 `xTcpRxQueue`,UartRxTask 取出后写入 UART DMA - UART→TCP 方向:UartRxTask 从 UART DMA 读取数据,构造 `route_msg_t` 指针投递到 `xUartTxQueue`,对应 TCP 任务取出后调用 `netconn_write` 发送 - 路由逻辑内联在 UartRxTask 和各 TCP 任务中,不设独立 RouteTask ### 4.5 NetPollTask 实现 ```c void NetPollTask(void *argument) { /* 初始化 CH390 + lwIP netif */ ethernetif_init(&ch390_netif); /* 等待链路就绪后启动 TCP 任务 */ /* ... */ for (;;) { xSemaphoreTake(xNetSemaphore, pdMS_TO_TICKS(2)); ethernetif_poll(&ch390_netif); ethernetif_check_link(&ch390_netif); /* sys_check_timeouts() 由 tcpip_thread 自动执行,此处不需要调用 */ } } ``` ### 4.6 TcpSrvTask 实现模板 ```c void TcpSrvTask_S1(void *argument) { struct netconn *conn = netconn_new(NETCONN_TCP); netconn_bind(conn, IP_ADDR_ANY, cfg->links[0].lport); netconn_listen(conn); for (;;) { struct netconn *newconn; if (netconn_accept(conn, &newconn) == ERR_OK) { /* 在本任务内处理唯一客户端 */ tcp_server_worker(newconn, LINK_S1); netconn_close(newconn); netconn_delete(newconn); } } } ``` ### 4.7 TcpCliTask 实现模板 ```c void TcpCliTask_C1(void *argument) { for (;;) { struct netconn *conn = netconn_new(NETCONN_TCP); ip_addr_t remote_ip; IP_ADDR4(&remote_ip, cfg->links[2].rip[0], ...); if (netconn_connect(conn, &remote_ip, cfg->links[2].rport) == ERR_OK) { tcp_client_worker(conn, LINK_C1); } netconn_close(conn); netconn_delete(conn); vTaskDelay(pdMS_TO_TICKS(cfg->links[2].reconnect_interval)); } } ``` ### 4.8 UartRxTask 实现 ```c void UartRxTask(void *argument) { for (;;) { ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10)); /* 处理 UART2/UART3 DMA 接收数据 */ /* MUX=0: 构造 route_msg_t 指针投递到 xUartTxQueue */ /* MUX=1: 提取 MUX 帧,DSTMASK=0 投 xConfigQueue,否则投 xUartTxQueue */ /* 从 xTcpRxQueue 取数据,写入 UART DMA 发送 */ } } ``` ### 4.9 ConfigTask 实现 ```c void ConfigTask(void *argument) { for (;;) { char *cmd; xQueueReceive(xConfigQueue, &cmd, portMAX_DELAY); config_process_at_cmd(cmd); /* 通过 UART1 发送响应 */ } } ``` ## 五、最终协议实现模型 ### 5.1 MUX 帧承载层 数据口启用 MUX 后,统一处理如下帧: ```text SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL ``` 实现职责: 1. 识别帧边界 2. 解析长度字段 3. 提取 `SRCID` 4. 解析 `DSTMASK` 5. 按控制帧或数据帧分流 ### 5.2 控制帧与数据帧分离 控制规则固定如下: - `DSTMASK = 0x00`:系统控制帧 - `DSTMASK != 0x00`:业务数据帧 系统控制帧处理要求: 1. `PAYLOAD` 解释为 AT 文本 2. AT 文本必须以 `\r\n` 结束 3. 控制帧投递到 `ConfigTask` 业务数据帧处理要求: 1. `SRCID` 表示单一源端点 2. `DSTMASK` 表示目标端点集合 3. `RouteTask` 根据 `DSTMASK` 做多目标分发 ### 5.3 统一端点编码 内部与外部文档统一使用以下端点编码: | 端点 | 编码 | |------|------| | `C1` | `0x01` | | `C2` | `0x02` | | `UART2` | `0x04` | | `UART3` | `0x08` | | `S1` | `0x10` | | `S2` | `0x20` | 实现要求: - `SRCID` 为单值 - `DSTMASK` 为位图 - `DSTMASK=0x00` 仅保留为控制帧 ## 六、配置层设计 ### 6.1 MUX 记录 `MUX` 为全局记录,仅控制设备数据口是否进入 MUX 承载模式。 取值: - `0`:普通透传 - `1`:MUX 透传 ### 6.2 NET 记录 `NET` 为全局静态网络记录: ```text IP,MASK,GW,MAC ``` 说明: - 设备只有一张网卡,因此不为每个实例单独配置本地 IP - 当前实现目标中不包含 DHCP ### 6.3 LINK 记录 `LINK[idx]` 为统一实例记录: ```text EN,LPORT,RIP,RPORT,UART ``` 固定索引映射: - `0 = S1` - `1 = S2` - `2 = C1` - `3 = C2` 字段职责: - `EN`:实例启用状态 - `LPORT`:本地端口 - `RIP / RPORT`:对端地址与端口 - `UART`:对应业务数据口 说明: - `Server` 与 `Client` 共享同一记录结构 - `Server` 的 `RIP / RPORT` 可作为对端约束或预设 - `Client` 的 `RIP / RPORT` 表示远端目标 ## 七、模块职责 ### 7.1 配置模块 `config.c/.h` 最终职责: 1. 解析 `AT+MUX` 2. 解析 `AT+NET` 3. 解析 `AT+LINK` 4. 加载与保存配置 5. 处理 `SAVE / RESET / DEFAULT` ### 7.2 UART 透传模块 `uart_trans.c/.h` 最终职责: 1. 保持 `USART2 / USART3` 的 `DMA + IDLE` 接收发送基线 2. 在 `MUX=0` 时执行普通透传 3. 在 `MUX=1` 时执行 MUX 帧收发 4. 将控制帧与业务数据帧分流 ### 7.3 TCP Server / Client 模块(需重写) 原 `tcp_server.c` / `tcp_client.c` 基于 lwIP RAW API 回调模式,需重写为 netconn 阻塞模式: 1. 删除所有 `tcp_pcb` / `tcp_recv` / `tcp_accept` 回调代码 2. 改用 `netconn_new` / `netconn_bind` / `netconn_listen` / `netconn_accept`(Server) 3. 改用 `netconn_new` / `netconn_connect`(Client) 4. 收发改为 `netconn_recv` / `netconn_write` 阻塞调用 5. 内部 ring buffer 可取消(netconn 内部已有 pbuf 缓冲) 6. 每个 TCP 任务内直接处理路由,通过 Queue 指针传递数据 ### 7.4 FreeRTOS 初始化 `freertos.c` CubeMX 生成的 FreeRTOS 初始化文件,职责: 1. 定义默认任务 `StartDefaultTask` 2. 用户在 `MX_FREERTOS_Init` 中创建自定义任务 ### 7.5 FreeRTOS 配置 `FreeRTOSConfig.h` 关键配置项: | 配置项 | 值 | 说明 | |--------|-----|------| | `configUSE_PREEMPTION` | 1 | 抢占式调度 | | `configTICK_RATE_HZ` | 1000 | 1ms tick | | `configMINIMAL_STACK_SIZE` | 128 | 最小栈(words) | | `configTOTAL_HEAP_SIZE` | 10240 | FreeRTOS 堆大小 | | `configMAX_PRIORITIES` | 7 | 最大优先级数 | | `configUSE_MUTEXES` | 1 | 启用互斥锁 | | `configUSE_COUNTING_SEMAPHORES` | 1 | 启用计数信号量 | | `configUSE_RECURSIVE_MUTEXES` | 1 | 启用递归互斥锁 | | `configCHECK_FOR_STACK_OVERFLOW` | 2 | 栈溢出检测方式 2 | | `configUSE_MALLOC_FAILED_HOOK` | 1 | 内存分配失败钩子 | | `configSUPPORT_DYNAMIC_ALLOCATION` | 1 | 动态内存分配 | ## 八、lwIP 配置(NO_SYS=0 + netconn) ### 8.1 lwIP 线程模型 由于采用 `NO_SYS=0`,lwIP 将运行以下线程: 1. `tcpip_thread`(优先级 6,栈 512 words):lwIP 核心线程,处理所有协议栈内部事件 2. 应用任务通过 `netconn` API 与 lwIP 交互,由 `tcpip_thread` 消息机制保证线程安全 3. `LWIP_TCPIP_CORE_LOCKING=1`:允许应用任务在持有核心锁时直接调用 `netconn_write`,无需通过邮箱中转 ### 8.2 lwIP 关键配置项(lwipopts.h) | 配置项 | 值 | 说明 | |--------|-----|------| | `NO_SYS` | 0 | 启用 OS 抽象 | | `LWIP_NETCONN` | 1 | 启用 netconn API | | `LWIP_SOCKET` | 0 | 不使用 socket API,节省 RAM | | `LWIP_TCPIP_CORE_LOCKING` | 1 | 允许直接发送,减少延时 | | `MEM_SIZE` | 8192 | lwIP 堆大小 | | `PBUF_POOL_SIZE` | 10 | pbuf 池数量 | | `MEMP_NUM_NETCONN` | 8 | 2监听 + 4连接 + 2余量 | | `MEMP_NUM_NETBUF` | 8 | netconn 缓冲 | | `MEMP_NUM_TCP_PCB` | 4 | TCP 控制块 | | `MEMP_NUM_TCP_PCB_LISTEN` | 2 | TCP 监听 | | `MEMP_NUM_TCP_SEG` | 24 | TCP 段 | | `MEMP_NUM_TCPIP_MSG_API` | 8 | API 消息池 | | `MEMP_NUM_TCPIP_MSG_INPKT` | 8 | 入包消息池 | | `TCP_MSS` | 536 | 保守 MSS | | `TCP_SND_BUF` | 8×MSS=4288 | 发送缓冲 | | `TCP_WND` | 8×MSS=4288 | 接收窗口 | | `TCPIP_THREAD_STACKSIZE` | 512 | tcpip_thread 栈 | | `TCPIP_THREAD_PRIO` | 6 (最高) | tcpip_thread 优先级 | | `LWIP_DHCP` | 0 | 不使用 DHCP | | `LWIP_UDP` | 0 | 不使用 UDP | ### 8.3 sys_arch 移植层 `Drivers/LwIP/port/sys_arch.c` 需实现 lwIP 到 FreeRTOS 的适配: 1. `sys_thread_new`:创建 lwIP 线程(即 `tcpip_thread`) 2. `sys_mbox_*`:消息邮箱(基于 FreeRTOS Queue,容量 `TCPIP_MBOX_SIZE=8`) 3. `sys_sem_*`:信号量(基于 FreeRTOS Semaphore) 4. `sys_mutex_*`:互斥锁(基于 FreeRTOS Mutex) 5. `sys_arch_protect / unprotect`:临界区保护(基于 `vPortEnterCritical / vPortExitCritical`) ### 8.4 lwIP 初始化流程 ```c /* 在 MX_FREERTOS_Init 或 NetPollTask 中调用 */ void lwip_init_task(void) { /* tcpip_thread 在 lwip_init() 或第一个 sys_thread_new 时自动启动 */ tcpip_init(NULL, NULL); /* 等待 tcpip_thread 就绪 */ /* 添加网络接口 */ netif_add(&ch390_netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input); netif_set_default(&ch390_netif); netif_set_up(&ch390_netif); } ``` ## 九、中断与 HAL 时间基准 ### 9.1 HAL 时间基准 FreeRTOS 下 `SysTick` 被 FreeRTOS 占用,HAL 时间基准改用 `TIM4`: - `TIM4` 配置为 1ms 中断(72MHz / (71+1) / (999+1) = 1kHz) - `HAL_InitTick` 使用 `TIM4` 而非 `SysTick` - `uwTick` 在 `TIM4_IRQHandler` 中递增 ### 9.2 中断优先级规划 | 中断 | 优先级 | 说明 | |------|--------|------| | `SysTick` | 15(最低) | FreeRTOS tick | | `PendSV` | 15(最低) | FreeRTOS 上下文切换 | | `SVCall` | 0 | FreeRTOS 服务调用 | | `TIM4` | 0 | HAL 时间基准 | | `EXTI0` | 5 | CH390 中断 | | `DMA1_Ch2~7` | 5 | UART DMA | | `USART1/2/3` | 5 | UART 中断 | | `SPI1` | 5 | SPI 中断 | FreeRTOS 可管理的中断优先级必须 >= `configMAX_SYSCALL_INTERRUPT_PRIORITY`(本工程为 5)。 ## 十、内存预算(路径 A 精确估算) 以 `STM32F103RCT6` 为目标(48KB SRAM): ### 10.1 RAM 预算 | 项目 | 大小 | 说明 | |------|------|------| | 启动栈 (MSP) | 2,048 B | `startup_stm32f103xe.s` 定义 0x800 | | FreeRTOS 堆 (heap_4) | 10,240 B | `configTOTAL_HEAP_SIZE` | | 任务栈 (9 任务) | 13,312 B | 3,328 words × 4 (见上表) | | lwIP 堆 (MEM_SIZE) | 8,192 B | | | PBUF 池 (10 个) | ~6,000 B | 10 × ~600B | | MEMP 池 | ~4,000 B | netconn/netbuf/tcpip_msg/pcb/seg | | UART DMA+Ring 缓冲 | 2,304 B | 2 通道 × (256+256+512+384)/2 | | 路由缓冲池 (4×512B) | 2,048 B | 零拷贝指针传递 | | 配置结构 | 1,024 B | | | **合计** | **~49,168 B** | | | **余量 (RCT6 48KB)** | **~-928 B** | ⚠️ 超出,需优化或换 RDT6 | | **余量 (RDT6 64KB)** | **~15,264 B** | ✅ 充裕 | ### 10.2 优化空间(RCT6 下) 若坚持使用 RCT6,可通过以下措施压缩到 48KB 以内: | 优化项 | 节省 | |--------|------| | 取消 TCP 任务的 ring buffer(netconn 内部有 pbuf 缓冲) | -2,048 B | | `configTOTAL_HEAP_SIZE` 降至 8KB | -2,048 B | | `MEM_SIZE` 降至 6KB | -2,048 B | | TcpCliTask 栈降至 192 words × 2 | -512 B | | **优化后合计** | **~42,504 B** | | **RCT6 余量** | **~5,464 B** | ### 10.3 备选 MCU 当 RAM 最终不够用时,切换为 `STM32F103RDT6`(pin-to-pin 替代): | 项目 | RCT6 | RDT6 | |------|------|------| | Flash | 256 KB | 384 KB | | SRAM | 48 KB | 64 KB | | 引脚 | LQFP64 | LQFP64(完全兼容) | | 启动文件 | `startup_stm32f103xe.s` | `startup_stm32f103xe.s` | | 宏定义 | `STM32F103xE` | `STM32F103xE` | | Flash 算法 | `STM32F10x_HD` | `STM32F10x_HD`(相同) | | SRAM 大小 | `0xC000` | `0x10000` | | Flash 大小 | `0x40000` | `0x60000` | ### 10.4 Flash 预算 | 项目 | 估计值 | 说明 | |------|--------|------| | FreeRTOS 内核 | ~8 KB | 含 CMSIS-RTOS V2 | | HAL 驱动 | ~20 KB | GPIO/UART/SPI/DMA/IWDG/TIM | | lwIP 协议栈 | ~50 KB | core + api + ipv4 + netif + sys_arch | | CH390 驱动 | ~4 KB | | | 应用代码 | ~20 KB | config/uart_trans/tcp_server/tcp_client | | **合计** | ~102 KB | RCT6 预留 154 KB,RDT6 预留 282 KB | ## 十一、硬件资源 ### 11.1 MCU - 型号:`STM32F103RCT6` - Flash:`256 KB` - SRAM:`48 KB` - 主频:`72 MHz` ### 11.2 主要外设 - `SPI1`:连接 `CH390D` - `USART1`:配置串口 - `USART2`:数据透传串口 - `USART3`:数据透传串口 - `DMA1`:3 路 UART 收发 DMA - `EXTI0`:CH390 中断输入 - `IWDG`:独立看门狗 - `TIM4`:HAL 时间基准(替代 SysTick) ### 11.3 引脚分配 | 引脚 | 功能 | 用途 | |------|------|------| | PA2 | USART2_TX | 数据透传串口 | | PA3 | USART2_RX | 数据透传串口 | | PA4 | SPI1_NSS | CH390D 片选 | | PA5 | SPI1_SCK | CH390D SPI 时钟 | | PA6 | SPI1_MISO | CH390D SPI 数据输入 | | PA7 | SPI1_MOSI | CH390D SPI 数据输出 | | PA9 | USART1_TX | 配置串口 | | PA10 | USART1_RX | 配置串口 | | PB0 | EXTI0 | CH390D INT | | PB1 | GPIO_Output | CH390D RESET | | PB10 | USART3_TX | 数据透传串口 | | PB11 | USART3_RX | 数据透传串口 | | PC13 | GPIO_Output | 状态 LED | | PD0/PD1 | HSE | 8MHz 外部晶振 | ## 十二、实现边界 1. 保持单网卡静态网络模型 2. 不实现 DHCP 3. 不实现旧 `S1... / C1...` 外部协议字段 4. 不在文档中保留兼容层描述 5. 所有 AT 文本控制统一要求 `\r\n` 结束 6. FreeRTOS 堆管理使用 `heap_4.c` 7. HAL 时间基准使用 `TIM4` 而非 `SysTick` ## 十三、文档一致性要求 后续实现、联调、测试与代码注释必须遵守以下统一口径: 1. 对外协议只使用 `MUX / NET / LINK` 2. 控制帧只使用 `DSTMASK=0x00` 3. MUX 帧格式固定为 `SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL` 4. AT 手册、需求说明、技术实现三份文档不得再出现历史展开式字段 ## 十四、路径 A 实现清单 ### 14.1 必须重写的模块 | 模块 | 原实现 | 目标实现 | 说明 | |------|--------|----------|------| | `tcp_server.c/.h` | lwIP RAW API 回调 | netconn 阻塞任务 | `tcp_new` → `netconn_new`,回调 → 阻塞循环 | | `tcp_client.c/.h` | lwIP RAW API 回调 | netconn 阻塞任务 | 同上 | | `sys_arch.c/.h` | NO_SYS=1 空壳 | FreeRTOS 移植层 | `sys_mbox`、`sys_sem`、`sys_mutex`、`sys_thread` | | `lwipopts.h` | NO_SYS=1 | NO_SYS=0 + netconn | 已更新 | | `main.c` | while(1) 轮询 | FreeRTOS 任务创建 | `App_Poll()` → 各任务函数 | | `stm32f1xx_it.c` | 裸机 ISR | FreeRTOS ISR | `xxFromISR` API | ### 14.2 可复用(需适配)的模块 | 模块 | 适配内容 | |------|----------| | `uart_trans.c/.h` | 添加 `xTaskNotifyFromISR` 替代 poll 模式 | | `config.c/.h` | 添加 `xQueueReceive` 替代 `config_poll()` | | `flash_param.c/.h` | 无需修改 | | `CH390` 驱动 | `ethernetif_poll` 改为 NetPollTask 调用,SPI 加 Mutex 保护 | | `ethernetif.c` | 添加 `netif_add` 的 `tcpip_input` 回调 | ### 14.3 无需修改的模块 | 模块 | 说明 | |------|------| | `FreeRTOSConfig.h` | 已更新(任务优先级宏、堆大小) | | `startup_stm32f103xe.s` | 已适配 RCT6 | | `TCP2UART.ioc` | 已适配 RCT6 + FreeRTOS + TIM4 | | `MDK-ARM/TCP2UART.uvprojx` | 已适配 RCT6 + xE 宏 | | MUX 帧编解码 | 协议逻辑与 RTOS 无关 | ### 14.4 新增模块 | 模块 | 职责 | |------|------| | `route_msg.c/.h` | 零拷贝路由消息池管理 | | `task_tcp_server.c/.h` | netconn Server 任务模板 | | `task_tcp_client.c/.h` | netconn Client 任务模板 | | `task_net_poll.c/.h` | CH390 poll + link check 任务 |