Files
TCP2UART/项目技术实现.md
T

8.2 KiB
Raw Blame History

TCP2UART 项目技术实现

一、文档目的

本文档描述 TCP2UART 项目的最终内部实现口径。

本文档只围绕最终协议模型展开:

  • MUX:串口承载层
  • NET:全局网络配置层
  • LINK[idx]:实例配置与连接管理层

不再保留历史 S1... / C1... 外部字段模型。

二、当前工程基础

当前工程基础约束如下:

  1. MCUSTM32F103R8T6
  2. 网络芯片:CH390D
  3. 软件架构:bare-metal main loop
  4. 协议栈:lwIP RAW API + NO_SYS=1
  5. 调试输出:SEGGER RTT
  6. 不使用 FreeRTOS
  7. 不实现 DHCP

三、总体架构

+--------------------------------------------------+
| AT / Control Plane                               |
| USART1 AT parser + MUX control frame parser      |
+--------------------------------------------------+
| Configuration Model                              |
| MUX / NET / LINK[idx]                            |
+--------------------------------------------------+
| Routing & Session Layer                          |
| TCP instance scheduling + UART dispatch          |
+--------------------------------------------------+
| Transport Poll Loop                              |
| ethernetif_poll / sys_check_timeouts / uart poll |
+--------------------------------------------------+
| Driver Layer                                     |
| CH390 / lwIP netif / UART DMA+IDLE / HAL         |
+--------------------------------------------------+

四、最终协议实现模型

4.1 MUX 帧承载层

数据口启用 MUX 后,统一处理如下帧:

SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL

实现职责:

  1. 识别帧边界
  2. 解析长度字段
  3. 提取 SRCID
  4. 解析 DSTMASK
  5. 按控制帧或数据帧分流

4.2 控制帧与数据帧分离

控制规则固定如下:

  • DSTMASK = 0x00:系统控制帧
  • DSTMASK != 0x00:业务数据帧

系统控制帧处理要求:

  1. PAYLOAD 解释为 AT 文本
  2. AT 文本必须以 \r\n 结束
  3. 控制帧进入 AT 命令处理链路

业务数据帧处理要求:

  1. SRCID 表示单一源端点
  2. DSTMASK 表示目标端点集合
  3. 路由层根据 DSTMASK 做多目标分发

4.3 统一端点编码

内部与外部文档统一使用以下端点编码:

端点 编码
C1 0x01
C2 0x02
UART2 0x04
UART3 0x08
S1 0x10
S2 0x20

实现要求:

  • SRCID 为单值
  • DSTMASK 为位图
  • DSTMASK=0x00 仅保留为控制帧

五、配置层设计

5.1 MUX 记录

MUX 为全局记录,仅控制设备数据口是否进入 MUX 承载模式。

取值:

  • 0:普通透传
  • 1MUX 透传

5.2 NET 记录

NET 为全局静态网络记录:

IP,MASK,GW,MAC

说明:

  • 设备只有一张网卡,因此不为每个实例单独配置本地 IP
  • 当前实现目标中不包含 DHCP

LINK[idx] 为统一实例记录:

EN,LPORT,RIP,RPORT,UART

固定索引映射:

  • 0 = S1
  • 1 = S2
  • 2 = C1
  • 3 = C2

字段职责:

  • EN:实例启用状态
  • LPORT:本地端口
  • RIP / RPORT:对端地址与端口
  • UART:对应业务数据口

说明:

  • ServerClient 共享同一记录结构
  • ServerRIP / RPORT 可作为对端约束或预设
  • ClientRIP / RPORT 表示远端目标

六、模块职责调整

6.1 配置模块 config.c/.h

最终职责:

  1. 解析 AT+MUX
  2. 解析 AT+NET
  3. 解析 AT+LINK
  4. 加载与保存配置
  5. 处理 SAVE / RESET / DEFAULT

不再以历史展开式字段作为外部接口模型。

6.2 UART 透传模块 uart_trans.c/.h

最终职责:

  1. 保持 USART2 / USART3DMA + IDLE 接收发送基线
  2. MUX=0 时执行普通透传
  3. MUX=1 时执行 MUX 帧收发
  4. 将控制帧与业务数据帧分流

6.3 TCP Server / Client 模块

最终职责:

  1. 不再从外部协议角度区分不同字段模型
  2. 统一受 LINK[idx] 配置驱动
  3. 由调度层决定实例与 UART 的数据交换路径

6.4 v1.1.0 低 RAM TCP 背压修复

v1.1.0 起,TCP -> UART 路径补充如下实现约束,用于解决“TCP 接收过快、UART 发送过慢时本地缓存被冲垮”的问题,同时尽量不新增静态 RAM:

  1. 继续复用 tcp_server / tcp_client 现有 RX ring,不为每个连接新增独立的大块 pending payload 缓冲。
  2. tcp_server_on_recv() / tcp_client_on_recv() 不再在回调内立即 tcp_recved()
  3. lwIP 交来的 pbuf 在回调中通过 pbuf_ref() 转为应用持有,再释放回调上下文的原始引用;后续由应用在主循环中继续把数据泵入 RX ring,最终在消费完成后释放。
  4. RX ring 暂时装不下时,剩余数据保留在 hold_pbuf + hold_offset 中,等待主循环下一轮继续搬运。
  5. 只有当数据真正从 TCP RX ringdrop 掉,也就是已经被下游 UART 接收进入发送路径时,才调用 tcp_recved() 释放 TCP 接收窗口。

这样做的效果是:

  1. UART 慢时,TCP 窗口不会继续无条件放大。
  2. 对端发送速度会被 lwIP 接收窗口自然压制。
  3. 修复点建立在已有 ring 与主循环调度之上,不引入 FreeRTOS 或新的大块静态缓存。

RAW 与 MUX 的分流规则

v1.1.0 中,TCP 侧拿到的都是纯 payload,因此 TCP 背压逻辑在 RAWMUX 两种模式下共用到 UART commit 之前:

  1. RAW 模式:
    • 主循环先查看 uart_trans_tx_free()
    • 再按 min(tcp_available, tx_free, APP_TCP_TO_UART_CHUNK_SIZE) 从 TCP ring peek
    • uart_trans_write() 实际写入多少,就 drop + tcp_recved 多少
  2. MUX 模式:
    • TCP payload 本身不带帧头尾
    • 只有当 UART TX free >= payload_len + 6 时,才在栈上临时编码一帧并一次性写入 UART TX ring
    • 只有整帧成功入队后,才按原始 payload 长度执行 drop + tcp_recved

该设计保证:

  1. RAW 模式允许流式逐步提交
  2. MUX 模式保持“单个 UART 输出帧必须完整入队”的语义
  3. TCP 接收窗口始终以真实下游消费进度为准,而不是以“回调里已经 memcpy 到本地”作为提交点

RAM 与 chunk 策略

为给新增的 hold_pbuf / hold_offset 状态字段让位,并进一步降低单轮转发压力,v1.1.0 同步采用以下策略:

  1. 新增 APP_TCP_TO_UART_CHUNK_SIZE = 128
  2. TCP_SERVER_RX_BUFFER_SIZE512 调整为 480
  3. TCP_CLIENT_RX_BUFFER_SIZE512 调整为 480

设计意图:

  1. 利用更小的单次转发块提升主循环调度颗粒度
  2. MUX 模式下 payload + 6 更容易完整进入 UART TX ring
  3. 在静态 RAM 已接近上限时,为少量新状态字段回收空间

构建基线

v1.1.0MDK-ARM/TCP2UART.uvprojxTCP2UART Target 为构建验收基线。

当前一次通过的参考结果:

  1. errors = 0
  2. warnings = 0
  3. flash_bytes = 56544
  4. ram_bytes = 20376

该结果说明修复后工程仍满足 STM32F103R8T620KB RAM 上限,但余量已经很小;后续若继续增加功能,应优先考虑复用现有缓冲与状态,而不是增加新的静态大数组。

七、主循环实现方向

主循环仍保持裸机轮询风格:

while (1)
{
    ethernetif_poll();
    ethernetif_check_link();
    sys_check_timeouts();
    tcp_link_poll();
    uart_mux_poll();
    config_poll();

    route_dispatch();

    if (reset_requested) {
        NVIC_SystemReset();
    }
}

下一阶段实现要求:

  1. 统一由 LINK[idx] 驱动实例状态
  2. 统一由 MUX 决定数据口承载模式
  3. 统一由 route_dispatch()SRCID / DSTMASK 分发

八、实现边界

  1. 保持单网卡静态网络模型
  2. 不实现 DHCP
  3. 不实现旧 S1... / C1... 外部协议字段
  4. 不在文档中保留兼容层描述
  5. 所有 AT 文本控制统一要求 \r\n 结束

九、文档一致性要求

后续实现、联调、测试与代码注释必须遵守以下统一口径:

  1. 对外协议只使用 MUX / NET / LINK
  2. 控制帧只使用 DSTMASK=0x00
  3. MUX 帧格式固定为 SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
  4. AT 手册、需求说明、技术实现三份文档不得再出现历史展开式字段