Files
TCP2UART/项目技术实现.md
T
gaoro-xiao d5803ca7dd refactor: 基于EVT示例重构技术实现,采用LwIP协议栈架构
- 引入LwIP协议栈替代直接操作CH390D硬件Socket
- CH390D驱动层直接移植官方EVT代码(CH390.c/h + CH390_Interface.c/h)
- 重构FreeRTOS任务:LwIPTask统一处理协议栈,透传任务专注数据搬运
- 使用StreamBuffer替代Queue,更适合流式数据传输
- 更新中断优先级配置,符合FreeRTOS要求
- 添加内存使用估算(18KB/20KB)
- 完善SPI配置和LwIP配置说明
2026-03-29 17:51:47 +08:00

28 KiB
Raw Blame History

TCP2UART 项目技术实现

一、系统架构

+-------------------+        +-------------------+
|   TCP Server      |        |   TCP Client      |
|   (监听端口)       |        |   (连接远程服务器)  |
+--------+----------+        +--------+----------+
         |                              |
         |     LwIP TCP/IP Stack        |
         +------------------------------+
                     |
              +------v------+
              |   CH390D    |
              | (SPI接口)    |
              +------+------+
                     |
              +------v------+
              |   STM32     |
              |  F103R8T6   |
              +------+------+
                     |
       +-------------+-------------+
       |             |             |
   +---v---+    +---v---+    +---v---+
   | UART1 |    | UART2 |    | UART3 |
   | 配置口 |    | 透传口 |    | 透传口 |
   +-------+    +-------+    +-------+

架构说明:基于官方 EVT 示例代码,采用 LwIP 轻量级 TCP/IP 协议栈,而非直接操作 CH390D 的硬件 Socket。CH390D 作为以太网 MAC+PHY 芯片,通过 SPI 接口与 STM32 通信。

二、硬件配置

2.1 MCU 型号

STM32F103R8T6LQFP6464KB Flash20KB RAM

2.2 引脚分配

引脚 功能 用途
PA2 USART2_TX Server 透传串口
PA3 USART2_RX Server 透传串口
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 配置串口
PA13 SWDIO SWD 调试接口
PA14 SWCLK SWD 调试接口
PB0 EXTI0 CH390D 中断输入
PB1 GPIO_Output CH390D 复位
PB10 USART3_TX Client 透传串口
PB11 USART3_RX Client 透传串口
PC13 GPIO_Output 板载 LED(灌电流,系统状态指示)
PD0/PD1 HSE 8MHz 外部晶振

2.3 DMA 通道分配

DMA 通道 外设 方向
DMA1_Ch2 USART3_TX 内存→外设
DMA1_Ch3 USART3_RX 外设→内存
DMA1_Ch4 USART1_TX 内存→外设
DMA1_Ch5 USART1_RX 外设→内存
DMA1_Ch6 USART2_RX 外设→内存
DMA1_Ch7 USART2_TX 内存→外设

2.4 中断优先级分配

基于 FreeRTOS 的 NVIC 优先级分组配置(使用 4 位抢占优先级):

外设 抢占优先级 子优先级 说明
EXTI0CH390D INT 5 0 网络数据接收,需高于 FreeRTOS 临界区
DMA1_Ch2/3 (USART3) 6 0 透传串口 DMA
DMA1_Ch6/7 (USART2) 6 1 透传串口 DMA
DMA1_Ch4/5 (USART1) 7 0 配置口 DMA
SysTick 15 0 FreeRTOS 系统节拍(configKERNEL_INTERRUPT_PRIORITY

注意FreeRTOS 要求 configMAX_SYSCALL_INTERRUPT_PRIORITY 设为 50x50),所有使用 FreeRTOS API 的 ISR 优先级数值必须 >= 5。

2.5 时钟配置

参数
SYSCLK 72MHzPLL × 9
HCLK 72MHz
APB1 36MHz(分频 2
APB2 72MHz
SPI1 18MHz(预分频 4

2.6 内存配置

参数
Heap 0x10004KB
Stack 0x8002KB

注意STM32F103R8T6 仅有 20KB RAM,需合理分配 FreeRTOS 堆和任务栈。

三、软件架构分层

3.1 软件分层结构

+----------------------------------------------------------+
|                    Application Layer                      |
|  (ConfigTask, ServerTransTask, ClientTransTask)          |
+----------------------------------------------------------+
|                    TCP/IP Stack (LwIP)                    |
|  tcp_server / tcp_client / netif / pbuf / timeouts       |
+----------------------------------------------------------+
|                  Network Interface Layer                  |
|  ethernetif (low_level_input / low_level_output)         |
+----------------------------------------------------------+
|                   CH390D Driver Layer                     |
|  CH390.c (协议层) + CH390_Interface.c (硬件抽象层)         |
+----------------------------------------------------------+
|                      HAL/LL Layer                         |
|  SPI + GPIO + DMA + UART                                 |
+----------------------------------------------------------+

3.2 目录结构

Core/
├── Inc/
│   ├── main.h
│   ├── FreeRTOSConfig.h
│   └── lwipopts.h              # LwIP 配置
├── Src/
│   ├── main.c
│   ├── freertos.c              # FreeRTOS 任务创建
│   └── stm32f1xx_it.c          # 中断处理
│
Drivers/
├── CH390/                       # CH390 驱动(从 EVT 移植)
│   ├── CH390.c                  # 协议层:收发包、PHY 配置等
│   ├── CH390.h
│   ├── CH390_Interface.c        # 硬件抽象层:SPI 读写
│   └── CH390_Interface.h
│
├── LwIP/                        # LwIP 协议栈(从 EVT 移植)
│   ├── src/
│   │   ├── core/                # TCP/IP 核心
│   │   ├── netif/               # 网络接口
│   │   │   ├── ethernetif.c     # CH390 网卡驱动适配
│   │   │   └── ethernetif.h
│   │   └── include/
│   └── apps/
│       ├── tcp_server.c         # TCP Server 透传
│       └── tcp_client.c         # TCP Client 透传
│
App/
├── config.c                     # AT 命令解析
├── config.h
├── uart_trans.c                 # UART 透传管理
├── uart_trans.h
├── flash_param.c                # Flash 参数存储
└── flash_param.h

四、FreeRTOS 任务设计

4.1 任务划分

任务名 优先级 功能 栈大小
LwIPTask osPriorityHigh LwIP 协议栈处理(定时器 + 网卡输入) 512 words
ServerTransTask osPriorityAboveNormal TCP Server ↔ UART2 双向透传 384 words
ClientTransTask osPriorityAboveNormal TCP Client ↔ UART3 双向透传 384 words
ConfigTask osPriorityNormal UART1 AT 命令解析 256 words

说明:相比原设计减少任务数,将网络任务合并为 LwIPTask 统一处理,透传任务专注数据搬运。

4.2 任务间通信

                    +-------------+
                    |  LwIPTask   |
                    | (协议栈处理) |
                    +------+------+
                           |
        +------------------+------------------+
        |                                     |
+-------v-------+                     +-------v-------+
| tcp_server_pcb|                     | tcp_client_pcb|
|   (LwIP PCB)  |                     |   (LwIP PCB)  |
+-------+-------+                     +-------+-------+
        |                                     |
+-------v-------+                     +-------v-------+
|ServerTransTask|                     |ClientTransTask|
| StreamBuffer1 |                     | StreamBuffer2 |
+-------+-------+                     +-------+-------+
        |                                     |
    +---v---+                             +---v---+
    | UART2 |                             | UART3 |
    +-------+                             +-------+

4.3 同步机制

机制 用途 说明
xStreamBuffer UART DMA → TCP 发送 零拷贝流式缓冲,适合变长数据
xSemaphore TCP 接收 → UART 发送 通知信号量,TCP 收到数据后释放
xMutex CH390 SPI 访问保护 LwIP 输出 + 中断输入共享 SPI

4.4 数据流向

Server 链路(UART2 ↔ TCP Server:
  [外部TCP Client] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ServerTransTask → UART2_TX(DMA)
  UART2_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [外部TCP Client]

Client 链路(UART3 ↔ TCP Client:
  [远程服务器] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ClientTransTask → UART3_TX(DMA)
  UART3_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [远程服务器]

五、CH390D 驱动层(基于官方 EVT)

5.1 驱动文件结构

驱动代码直接移植自 Reference/EVT/EXAM/PUB/,需修改接口宏定义:

/* CH390.h 中启用 SPI 接口 */
#define CH390_INTERFACE_SPI     // CH390H/CH390D 使用 SPI
// #define CH390_INTERFACE_8_BIT   // CH390L/CH390F 8-bit mode
// #define CH390_INTERFACE_16_BIT  // CH390L 16-bit mode

5.2 SPI 接口层(CH390_Interface.c 适配 HAL

/* 引脚定义适配本项目 */
#define CH390_SPI_HANDLE    hspi1
#define CH390_CS_PORT       GPIOA
#define CH390_CS_PIN        GPIO_PIN_4
#define CH390_RST_PORT      GPIOB
#define CH390_RST_PIN       GPIO_PIN_1
#define CH390_INT_PORT      GPIOB
#define CH390_INT_PIN       GPIO_PIN_0

/* HAL SPI 适配 */
static uint8_t ch390_spi_exchange_byte(uint8_t byte)
{
    uint8_t rx;
    HAL_SPI_TransmitReceive(&CH390_SPI_HANDLE, &byte, &rx, 1, HAL_MAX_DELAY);
    return rx;
}

/* FreeRTOS 延时适配 */
void ch390_delay_us(uint32_t time)
{
    /* 微秒级延时,可用 DWT 或简单循环 */
    uint32_t cycles = (SystemCoreClock / 1000000) * time;
    while(cycles--) __NOP();
}

void ch390_delay_ms(uint32_t time)
{
    /* FreeRTOS 环境下使用 vTaskDelay */
    if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
        vTaskDelay(pdMS_TO_TICKS(time));
    } else {
        HAL_Delay(time);
    }
}

5.3 协议层主要 API

/* 从 CH390.h 提取的关键函数 */

/* 初始化与配置 */
void ch390_hardware_reset(void);              // 硬件复位(拉低 RST 引脚)
void ch390_software_reset(void);              // 软件复位(写 NCR 寄存器)
void ch390_default_config(void);              // 默认配置:LED模式、校验和、使能RX/中断
void ch390_set_phy_mode(enum ch390_phy_mode mode);  // CH390_AUTO / CH390_100MFD / CH390_10MFD
void ch390_set_mac_address(uint8_t *mac_addr);      // 设置 MAC 地址

/* 数据收发 */
uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status);  // 接收以太网帧
void ch390_send_packet(uint8_t *buff, uint16_t length);            // 发送以太网帧
void ch390_drop_packet(uint16_t len);                              // 丢弃当前包

/* 状态查询 */
int ch390_get_link_status(void);              // 0: 断开, 1: 连接
int ch390_get_phy_speed(void);                // 0: 100Mbps, 1: 10Mbps
uint8_t ch390_get_int_status(void);           // 获取并清除中断状态

/* 中断配置 */
void ch390_interrupt_config(uint8_t mask);    // IMR_PRI | IMR_PTI | IMR_LNKCHGI 等
uint16_t ch390_get_int_pin(void);             // 读取 INT 引脚电平

5.4 初始化流程

void CH390_Init(void)
{
    /* 1. GPIO 初始化(由 CubeMX 完成) */
    
    /* 2. SPI 初始化(由 CubeMX 完成)
     *    Mode: Master, Full-Duplex
     *    CPOL: High, CPHA: 2Edge (Mode 3)
     *    NSS: Software
     *    Prescaler: 4 (18MHz @ 72MHz PCLK)
     */
    
    /* 3. 硬件复位 */
    ch390_hardware_reset();
    ch390_delay_ms(10);  // 上电后等待 10ms
    
    /* 4. 默认配置 */
    ch390_default_config();
    
    /* 5. 读取内置 MAC 或设置自定义 MAC */
    uint8_t mac[6];
    ch390_get_mac(mac);  // CH390 内置 MAC
    // ch390_set_mac_address(custom_mac);  // 或使用自定义 MAC
    
    /* 6. 等待 PHY 链路建立 */
    while (!ch390_get_link_status()) {
        ch390_delay_ms(100);
    }
}

六、LwIP 协议栈集成

6.1 LwIP 配置(lwipopts.h

/* 基础配置 */
#define NO_SYS                      0       /* 使用 OS */
#define LWIP_NETCONN                0       /* 不使用 Netconn API */
#define LWIP_SOCKET                 0       /* 不使用 Socket API */

/* 内存配置(适配 20KB RAM */
#define MEM_SIZE                    (4*1024) /* 堆大小 4KB */
#define MEMP_NUM_PBUF               8
#define MEMP_NUM_TCP_PCB            4        /* 同时 TCP 连接数 */
#define MEMP_NUM_TCP_PCB_LISTEN     2        /* Server + Client */
#define PBUF_POOL_SIZE              8
#define PBUF_POOL_BUFSIZE           512

/* TCP 配置 */
#define LWIP_TCP                    1
#define TCP_MSS                     (1460)
#define TCP_SND_BUF                 (2*TCP_MSS)
#define TCP_WND                     (2*TCP_MSS)
#define TCP_SND_QUEUELEN            (4*TCP_SND_BUF/TCP_MSS)

/* DHCP/静态 IP */
#define LWIP_DHCP                   1

/* 校验和由 CH390 硬件计算 */
#define CHECKSUM_GEN_IP             0
#define CHECKSUM_GEN_TCP            0
#define CHECKSUM_GEN_UDP            0
#define CHECKSUM_CHECK_IP           0
#define CHECKSUM_CHECK_TCP          0
#define CHECKSUM_CHECK_UDP          0

6.2 网卡驱动适配(ethernetif.c

基于 Reference/EVT/EXAM/LwIP_Example/lwip-2_2_0/src/netif/ethernetif.c 修改:

/* 发送以太网帧 */
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    struct pbuf *q;
    
    /* 获取 SPI 互斥锁(FreeRTOS 环境) */
    xSemaphoreTake(ch390_spi_mutex, portMAX_DELAY);
    
    for (q = p; q != NULL; q = q->next) {
        ch390_write_mem(q->payload, q->len);
    }
    
    /* 等待上次发送完成 */
    while(ch390_read_reg(CH390_TCR) & TCR_TXREQ);
    
    /* 设置包长度并发起发送请求 */
    ch390_write_reg(CH390_TXPLL, p->tot_len & 0xff);
    ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xff);
    ch390_send_request();
    
    xSemaphoreGive(ch390_spi_mutex);
    
    LINK_STATS_INC(link.xmit);
    return ERR_OK;
}

/* 接收以太网帧 */
static struct pbuf *low_level_input(struct netif *netif)
{
    struct ethernetif *ethernetif = netif->state;
    struct pbuf *p, *q;
    uint8_t rx_ready;
    uint8_t header[4];
    uint16_t len;
    
    /* 检查是否有数据包 */
    ch390_read_reg(CH390_MRCMDX);
    rx_ready = ch390_read_reg(CH390_MRCMDX);
    
    if (rx_ready & CH390_PKT_ERR) {
        /* 复位 RX FIFO */
        ch390_write_reg(CH390_RCR, 0);
        ch390_write_reg(CH390_MPTRCR, 0x01);
        ch390_delay_ms(1);
        ch390_write_reg(CH390_RCR, RCR_RXEN | RCR_DIS_CRC);
        ethernetif->rx_len = 0;
        return NULL;
    }
    
    if (!(rx_ready & CH390_PKT_RDY)) {
        ethernetif->rx_len = 0;
        return NULL;
    }
    
    /* 读取头部:状态 + 长度 */
    ch390_read_mem(header, 4);
    ethernetif->rx_status = header[1];
    len = (header[2] | (header[3] << 8)) - 4;  // 去掉 CRC
    ethernetif->rx_len = len;
    
    /* 分配 pbuf 并读取数据 */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
    if (p != NULL) {
        for (q = p; q != NULL; q = q->next) {
            ch390_read_mem(q->payload, q->len);
        }
        ch390_drop_packet(4);  // 跳过 CRC
        LINK_STATS_INC(link.recv);
    } else {
        ch390_drop_packet(len + 4);
        LINK_STATS_INC(link.memerr);
    }
    
    return p;
}

6.3 中断处理

/* EXTI0 中断服务程序(PB0 连接 CH390D INT */
void EXTI0_IRQHandler(void)
{
    if (__HAL_GPIO_EXTI_GET_IT(CH390_INT_PIN) != RESET) {
        __HAL_GPIO_EXTI_CLEAR_IT(CH390_INT_PIN);
        
        /* 通知 LwIP 任务处理 */
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        vTaskNotifyGiveFromISR(lwipTaskHandle, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

/* LwIP 任务中的中断处理 */
void ch390_int_handler(void)
{
    uint8_t int_status = ch390_get_int_status();
    
    /* 链路状态变化 */
    if (int_status & ISR_LNKCHG) {
        vTaskDelay(pdMS_TO_TICKS(65));  // 等待 PHY 稳定
        if (ch390_get_link_status()) {
            netif_set_link_up(&ch390_netif);
        } else {
            netif_set_link_down(&ch390_netif);
        }
    }
    
    /* 接收溢出 */
    if (int_status & ISR_ROS) {
        struct ethernetif *ethernetif = ch390_netif.state;
        do {
            ethernetif_input(&ch390_netif);
        } while (ethernetif->rx_len != 0);
    }
    
    /* 收到数据包 */
    if (int_status & ISR_PR) {
        struct ethernetif *ethernetif = ch390_netif.state;
        do {
            ethernetif_input(&ch390_netif);
        } while (ethernetif->rx_len != 0);
    }
}

七、TCP 链路管理(基于 LwIP RAW API

7.1 Server 链路(监听端口)

static struct tcp_pcb *tcp_server_pcb;
static struct tcp_pcb *tcp_server_conn;  // 当前连接的客户端

/* 接收回调:TCP 数据 → StreamBuffer → UART2 */
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (p != NULL) {
        tcp_recved(tpcb, p->tot_len);
        
        /* 写入 StreamBuffer,通知 ServerTransTask */
        xStreamBufferSend(uart2_tx_stream, p->payload, p->tot_len, 0);
        
        pbuf_free(p);
        return ERR_OK;
    } else {
        /* 对端关闭连接 */
        tcp_close(tpcb);
        tcp_server_conn = NULL;
        return ERR_OK;
    }
}

/* 连接接受回调 */
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    /* 只接受一个连接 */
    if (tcp_server_conn != NULL) {
        tcp_abort(newpcb);
        return ERR_ABRT;
    }
    
    tcp_server_conn = newpcb;
    tcp_recv(newpcb, tcp_server_recv);
    tcp_err(newpcb, tcp_server_error);
    return ERR_OK;
}

/* 初始化 TCP Server */
void tcp_server_init(uint16_t port)
{
    tcp_server_pcb = tcp_new();
    tcp_bind(tcp_server_pcb, IP_ADDR_ANY, port);
    tcp_server_pcb = tcp_listen(tcp_server_pcb);
    tcp_accept(tcp_server_pcb, tcp_server_accept);
}

/* UART2 数据发送到 TCP */
void tcp_server_send_data(uint8_t *data, uint16_t len)
{
    if (tcp_server_conn != NULL && tcp_sndbuf(tcp_server_conn) >= len) {
        tcp_write(tcp_server_conn, data, len, TCP_WRITE_FLAG_COPY);
        tcp_output(tcp_server_conn);
    }
}

7.2 Client 链路(主动连接)

static struct tcp_pcb *tcp_client_pcb;
static uint8_t client_connected = 0;

/* 接收回调:TCP 数据 → StreamBuffer → UART3 */
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (p != NULL) {
        tcp_recved(tpcb, p->tot_len);
        xStreamBufferSend(uart3_tx_stream, p->payload, p->tot_len, 0);
        pbuf_free(p);
    } else if (err == ERR_OK) {
        /* 服务器断开,延时重连 */
        tcp_close(tpcb);
        client_connected = 0;
        sys_timeout(3000, tcp_client_reconnect, NULL);
    }
    return ERR_OK;
}

/* 连接错误回调 */
static void tcp_client_error(void *arg, err_t err)
{
    client_connected = 0;
    sys_timeout(3000, tcp_client_reconnect, NULL);
}

/* 连接成功回调 */
static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
    client_connected = 1;
    tcp_recv(pcb, tcp_client_recv);
    return ERR_OK;
}

/* 初始化 TCP Client */
void tcp_client_init(ip_addr_t *server_ip, uint16_t server_port)
{
    tcp_client_pcb = tcp_new();
    tcp_err(tcp_client_pcb, tcp_client_error);
    tcp_connect(tcp_client_pcb, server_ip, server_port, tcp_client_connected);
}

/* 重连定时器回调 */
static void tcp_client_reconnect(void *arg)
{
    if (!client_connected) {
        tcp_client_init(&config.remote_ip, config.remote_port);
    }
}

八、串口透传层

8.1 UART DMA + 空闲中断配置

/* CubeMX 配置:
 * USART2/3: 115200, 8N1, DMA TX/RX, 空闲中断
 */

#define UART_RX_BUF_SIZE    256

uint8_t uart2_rx_buf[UART_RX_BUF_SIZE];
uint8_t uart3_rx_buf[UART_RX_BUF_SIZE];

void UART_Trans_Init(void)
{
    /* 启动 DMA 接收 */
    HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart2_rx_buf, UART_RX_BUF_SIZE);
    HAL_UARTEx_ReceiveToIdle_DMA(&huart3, uart3_rx_buf, UART_RX_BUF_SIZE);
    
    /* 禁用半传输中断(减少中断次数) */
    __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT);
    __HAL_DMA_DISABLE_IT(huart3.hdmarx, DMA_IT_HT);
}

8.2 空闲中断回调(接收完成)

/* HAL 空闲中断回调 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if (huart == &huart2) {
        /* UART2 数据 → Server StreamBuffer */
        xStreamBufferSendFromISR(server_rx_stream, uart2_rx_buf, Size, &xHigherPriorityTaskWoken);
        /* 重启 DMA 接收 */
        HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart2_rx_buf, UART_RX_BUF_SIZE);
        __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT);
    }
    else if (huart == &huart3) {
        /* UART3 数据 → Client StreamBuffer */
        xStreamBufferSendFromISR(client_rx_stream, uart3_rx_buf, Size, &xHigherPriorityTaskWoken);
        HAL_UARTEx_ReceiveToIdle_DMA(&huart3, uart3_rx_buf, UART_RX_BUF_SIZE);
        __HAL_DMA_DISABLE_IT(huart3.hdmarx, DMA_IT_HT);
    }
    
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

8.3 透传任务

/* Server 透传任务:UART2 ↔ TCP Server */
void ServerTransTask(void *param)
{
    uint8_t buf[256];
    size_t len;
    
    for (;;) {
        /* TCP → UART2:从 StreamBuffer 读取并 DMA 发送 */
        len = xStreamBufferReceive(uart2_tx_stream, buf, sizeof(buf), pdMS_TO_TICKS(10));
        if (len > 0) {
            HAL_UART_Transmit_DMA(&huart2, buf, len);
            ulTaskNotifyTake(pdTRUE, portMAX_DELAY);  // 等待 DMA 完成
        }
        
        /* UART2 → TCP:从 StreamBuffer 读取并 TCP 发送 */
        len = xStreamBufferReceive(server_rx_stream, buf, sizeof(buf), 0);
        if (len > 0) {
            tcp_server_send_data(buf, len);
        }
    }
}

/* DMA 发送完成回调 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if (huart == &huart2) {
        vTaskNotifyGiveFromISR(serverTransTaskHandle, &xHigherPriorityTaskWoken);
    }
    else if (huart == &huart3) {
        vTaskNotifyGiveFromISR(clientTransTaskHandle, &xHigherPriorityTaskWoken);
    }
    
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

九、LwIP 主任务

/* LwIP 任务:协议栈处理 + 定时器 */
void LwIPTask(void *param)
{
    /* 初始化网卡 */
    ip4_addr_t ipaddr, netmask, gateway;
    IP4_ADDR(&ipaddr, config.ip[0], config.ip[1], config.ip[2], config.ip[3]);
    IP4_ADDR(&netmask, config.mask[0], config.mask[1], config.mask[2], config.mask[3]);
    IP4_ADDR(&gateway, config.gw[0], config.gw[1], config.gw[2], config.gw[3]);
    
    init_lwip_netif(&ipaddr, &netmask, &gateway);
    
    /* 等待 PHY 链路建立 */
    while (!ch390_get_link_status()) {
        vTaskDelay(pdMS_TO_TICKS(100));
    }
    netif_set_link_up(&ch390_netif);
    
    /* 初始化 TCP Server/Client */
    tcp_server_init(config.server_port);
    
    ip_addr_t remote_ip;
    IP4_ADDR(&remote_ip, config.remote_ip[0], config.remote_ip[1], 
             config.remote_ip[2], config.remote_ip[3]);
    tcp_client_init(&remote_ip, config.remote_port);
    
    /* 主循环 */
    for (;;) {
        /* 等待中断通知或超时 */
        uint32_t notify = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10));
        
        if (notify > 0 || ch390_get_int_pin()) {
            xSemaphoreTake(ch390_spi_mutex, portMAX_DELAY);
            ch390_int_handler();
            xSemaphoreGive(ch390_spi_mutex);
        }
        
        /* LwIP 定时器处理 */
        sys_check_timeouts();
    }
}

十、参数配置

7.1 配置命令格式(UART1

AT+IP=192.168.1.100\r\n      // 设置设备IP
AT+MASK=255.255.255.0\r\n    // 设置子网掩码
AT+GW=192.168.1.1\r\n        // 设置网关
AT+PORT=8080\r\n             // 设置Server监听端口
AT+RIP=192.168.1.200\r\n     // 设置Client连接的远程IP
AT+RPORT=9000\r\n            // 设置Client连接的远程端口
AT+BAUD1=115200\r\n          // 设置UART2波特率
AT+BAUD2=115200\r\n          // 设置UART3波特率
AT+SAVE\r\n                  // 保存参数到Flash
AT+RESET\r\n                 // 重启设备
AT+?\r\n                     // 查询当前配置

7.2 参数存储结构

typedef struct {
    uint32_t magic;           // 0x54435055 "TCPU"
    uint8_t  ip[4];
    uint8_t  mask[4];
    uint8_t  gw[4];
    uint16_t server_port;     // Server 监听端口
    uint8_t  remote_ip[4];    // Client 远程IP
    uint16_t remote_port;     // Client 远程端口
    uint32_t uart2_baud;      // UART2 波特率
    uint32_t uart3_baud;      // UART3 波特率
    uint32_t crc;             // CRC32校验
} ConfigTypeDef;

7.3 Flash 存储

/* 使用 STM32F103R8 内部 Flash 最后一页存储配置 */
#define CONFIG_FLASH_ADDR  0x0800FC00  // 64KB Flash最后1KB

void Config_Save(ConfigTypeDef *cfg) {
    cfg->crc = CRC32_Calculate((uint8_t*)cfg, sizeof(ConfigTypeDef)-4);
    FLASH_ErasePage(CONFIG_FLASH_ADDR);
    FLASH_Program(CONFIG_FLASH_ADDR, cfg, sizeof(ConfigTypeDef));
}

void Config_Load(ConfigTypeDef *cfg) {
    memcpy(cfg, (void*)CONFIG_FLASH_ADDR, sizeof(ConfigTypeDef));
    if(cfg->magic != 0x54435055 || CRC32_Check(cfg) != HAL_OK) {
        Config_Default(cfg);  // 加载默认配置
    }
}

十一、关键配置

11.1 FreeRTOS 配置(FreeRTOSConfig.h

/* 基础配置 */
#define configUSE_PREEMPTION                    1
#define configCPU_CLOCK_HZ                      72000000
#define configTICK_RATE_HZ                      1000
#define configMAX_PRIORITIES                    7
#define configMINIMAL_STACK_SIZE                128
#define configTOTAL_HEAP_SIZE                   (8*1024)  /* 8KBLwIP 需要更多堆 */

/* 中断优先级配置 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5
#define configKERNEL_INTERRUPT_PRIORITY         (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4)
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4)

/* 功能启用 */
#define configUSE_MUTEXES                       1
#define configUSE_COUNTING_SEMAPHORES           1
#define configUSE_TASK_NOTIFICATIONS            1
#define configSUPPORT_STATIC_ALLOCATION         0
#define configSUPPORT_DYNAMIC_ALLOCATION        1

/* Stream Buffer 支持 */
#define configUSE_STREAM_BUFFERS                1

11.2 SPI 配置(CubeMX

参数 说明
Mode Full-Duplex Master
NSS Software 软件控制 CS
Data Size 8 Bits
First Bit MSB First
Prescaler 4 72MHz/4 = 18MHz
CPOL High 空闲时 CLK 高电平
CPHA 2 Edge 第二个边沿采样
CRC Disabled

11.3 缓冲区配置

/* 串口缓冲区 */
#define UART_RX_BUF_SIZE        256     /* DMA 接收缓冲区 */

/* StreamBuffer 配置 */
#define STREAM_BUFFER_SIZE      512     /* 每个方向的流缓冲区大小 */
#define STREAM_TRIGGER_LEVEL    1       /* 触发等待任务的最小字节数 */

/* LwIP 缓冲区(见 lwipopts.h */
#define PBUF_POOL_SIZE          8
#define PBUF_POOL_BUFSIZE       512

11.4 内存使用估算(20KB RAM

组件 大小 说明
FreeRTOS Heap 8KB 任务栈 + 队列 + 信号量等
LwIP MEM_SIZE 4KB pbuf/TCP 缓冲区
UART DMA 缓冲区 512B 2 × 256B
StreamBuffer 2KB 4 × 512B
全局变量 ~1KB 配置结构 + 状态变量
栈(main 2KB 启动前使用
总计 ~18KB 预留 2KB 安全余量

优化建议

  • 如内存紧张,可减小 PBUF_POOL_SIZEStreamBuffer 大小
  • 使用 configSUPPORT_STATIC_ALLOCATION 静态分配任务栈

十二、丢包测试方案

9.1 测试方法

  1. TCP 发送端:连续发送递增序号数据包(10000包 × 100字节)
  2. 串口接收端:统计接收数据包数量及序号连续性
  3. 反向测试:串口发送 → TCP 接收

9.2 测试指标

指标 要求
丢包率 < 0.01%
延迟 < 10ms
吞吐量 > 90% 理论值

9.3 测试脚本(PC端)

import socket
import time

def send_test(host, port, count=10000):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    
    for i in range(count):
        data = f"PKT{i:06d}".encode() + b'\x00' * 92  # 100字节
        sock.send(data)
        time.sleep(0.001)  # 1ms间隔
    
    sock.close()
    print(f"Sent {count} packets")

def recv_test(port, timeout=30):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('0.0.0.0', port))
    sock.listen(1)
    conn, addr = sock.accept()
    
    recv_count = 0
    start = time.time()
    while time.time() - start < timeout:
        data = conn.recv(1024)
        if data:
            recv_count += len(data) // 100
    
    conn.close()
    print(f"Received {recv_count} packets")