# 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 型号 STM32F103R8T6(LQFP64,64KB Flash,20KB 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 位抢占优先级): | 外设 | 抢占优先级 | 子优先级 | 说明 | |------|------------|----------|------| | EXTI0(CH390D 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` 设为 5(0x50),所有使用 FreeRTOS API 的 ISR 优先级数值必须 >= 5。 ### 2.5 时钟配置 | 参数 | 值 | |------|-----| | SYSCLK | 72MHz(PLL × 9) | | HCLK | 72MHz | | APB1 | 36MHz(分频 2) | | APB2 | 72MHz | | SPI1 | 18MHz(预分频 4) | ### 2.6 内存配置 | 参数 | 值 | |------|-----| | Heap | 0x1000(4KB) | | Stack | 0x800(2KB) | > **注意**: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/`,需修改接口宏定义: ```c /* 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) ```c /* 引脚定义适配本项目 */ #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 ```c /* 从 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 初始化流程 ```c 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) ```c /* 基础配置 */ #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` 修改: ```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 中断处理 ```c /* 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 链路(监听端口) ```c 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 链路(主动连接) ```c 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 + 空闲中断配置 ```c /* 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 空闲中断回调(接收完成) ```c /* 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 透传任务 ```c /* 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 主任务 ```c /* 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 参数存储结构 ```c 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 存储 ```c /* 使用 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) ```c /* 基础配置 */ #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) /* 8KB,LwIP 需要更多堆 */ /* 中断优先级配置 */ #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 缓冲区配置 ```c /* 串口缓冲区 */ #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_SIZE` 和 `StreamBuffer` 大小 > - 使用 `configSUPPORT_STATIC_ALLOCATION` 静态分配任务栈 ## 十二、丢包测试方案 ### 9.1 测试方法 1. **TCP 发送端**:连续发送递增序号数据包(10000包 × 100字节) 2. **串口接收端**:统计接收数据包数量及序号连续性 3. **反向测试**:串口发送 → TCP 接收 ### 9.2 测试指标 | 指标 | 要求 | |------|------| | 丢包率 | < 0.01% | | 延迟 | < 10ms | | 吞吐量 | > 90% 理论值 | ### 9.3 测试脚本(PC端) ```python 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") ```