From d5803ca7dd6c18363ed7765207eb749d1bc86b4c Mon Sep 17 00:00:00 2001 From: xiao Date: Sun, 29 Mar 2026 17:51:47 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=9F=BA=E4=BA=8EEVT=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E9=87=8D=E6=9E=84=E6=8A=80=E6=9C=AF=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=EF=BC=8C=E9=87=87=E7=94=A8LwIP=E5=8D=8F=E8=AE=AE=E6=A0=88?= =?UTF-8?q?=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入LwIP协议栈替代直接操作CH390D硬件Socket - CH390D驱动层直接移植官方EVT代码(CH390.c/h + CH390_Interface.c/h) - 重构FreeRTOS任务:LwIPTask统一处理协议栈,透传任务专注数据搬运 - 使用StreamBuffer替代Queue,更适合流式数据传输 - 更新中断优先级配置,符合FreeRTOS要求 - 添加内存使用估算(18KB/20KB) - 完善SPI配置和LwIP配置说明 --- 项目技术实现.md | 866 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 695 insertions(+), 171 deletions(-) diff --git a/项目技术实现.md b/项目技术实现.md index fca6516..fec017c 100644 --- a/项目技术实现.md +++ b/项目技术实现.md @@ -8,10 +8,15 @@ | (监听端口) | | (连接远程服务器) | +--------+----------+ +--------+----------+ | | - | CH390D | + | LwIP TCP/IP Stack | +------------------------------+ | +------v------+ + | CH390D | + | (SPI接口) | + +------+------+ + | + +------v------+ | STM32 | | F103R8T6 | +------+------+ @@ -24,6 +29,8 @@ +-------+ +-------+ +-------+ ``` +> **架构说明**:基于官方 EVT 示例代码,采用 LwIP 轻量级 TCP/IP 协议栈,而非直接操作 CH390D 的硬件 Socket。CH390D 作为以太网 MAC+PHY 芯片,通过 SPI 接口与 STM32 通信。 + ## 二、硬件配置 ### 2.1 MCU 型号 @@ -62,16 +69,19 @@ STM32F103R8T6(LQFP64,64KB Flash,20KB RAM) | DMA1_Ch6 | USART2_RX | 外设→内存 | | DMA1_Ch7 | USART2_TX | 内存→外设 | -### 2.4 中断优先级分配(建议) +### 2.4 中断优先级分配 -| 外设 | 建议优先级 | 说明 | -|------|------------|------| -| EXTI0(CH390D INT) | 2 | 网络数据接收最紧急 | -| SPI1 | 3 | 网络数据传输 | -| USART2/3 DMA | 4 | 透传串口 | -| USART1 DMA | 5 | 配置口,非实时 | -| PendSV | 15 | FreeRTOS 任务切换 | -| SysTick | 15 | FreeRTOS 系统节拍 | +基于 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 时钟配置 @@ -92,196 +102,661 @@ STM32F103R8T6(LQFP64,64KB Flash,20KB RAM) > **注意**:STM32F103R8T6 仅有 20KB RAM,需合理分配 FreeRTOS 堆和任务栈。 -## 三、FreeRTOS 任务设计 +## 三、软件架构分层 -### 3.1 任务划分 +### 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 任务划分 | 任务名 | 优先级 | 功能 | 栈大小 | |--------|--------|------|--------| -| NetServerTask | High | TCP Server 管理 + 数据收发 | 512 words | -| NetClientTask | High | TCP Client 管理 + 数据收发 | 512 words | -| Uart2Task | Normal | UART2 数据收发(Server 透传) | 256 words | -| Uart3Task | Normal | UART3 数据收发(Client 透传) | 256 words | -| ConfigTask | Low | UART1 配置命令解析 | 256 words | +| 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 | -### 3.2 任务间通信 +> **说明**:相比原设计减少任务数,将网络任务合并为 LwIPTask 统一处理,透传任务专注数据搬运。 + +### 4.2 任务间通信 ``` -NetServerTask <--Queue1--> Uart2Task -NetClientTask <--Queue2--> Uart3Task -ConfigTask <--独立运行--> (读写参数Flash) + +-------------+ + | LwIPTask | + | (协议栈处理) | + +------+------+ + | + +------------------+------------------+ + | | ++-------v-------+ +-------v-------+ +| tcp_server_pcb| | tcp_client_pcb| +| (LwIP PCB) | | (LwIP PCB) | ++-------+-------+ +-------+-------+ + | | ++-------v-------+ +-------v-------+ +|ServerTransTask| |ClientTransTask| +| StreamBuffer1 | | StreamBuffer2 | ++-------+-------+ +-------+-------+ + | | + +---v---+ +---v---+ + | UART2 | | UART3 | + +-------+ +-------+ ``` -- **Queue1**: Server 网络数据 <-> UART2 双向队列(128 bytes) -- **Queue2**: Client 网络数据 <-> UART3 双向队列(128 bytes) +### 4.3 同步机制 -### 3.3 数据流向 +| 机制 | 用途 | 说明 | +|------|------|------| +| xStreamBuffer | UART DMA → TCP 发送 | 零拷贝流式缓冲,适合变长数据 | +| xSemaphore | TCP 接收 → UART 发送 | 通知信号量,TCP 收到数据后释放 | +| xMutex | CH390 SPI 访问保护 | LwIP 输出 + 中断输入共享 SPI | + +### 4.4 数据流向 ``` -Server 链路: - TCP Client → CH390D → NetServerTask → Queue1 → Uart2Task → UART2 - UART2 → Uart2Task → Queue1 → NetServerTask → CH390D → TCP Client +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 链路: - 远程服务器 → CH390D → NetClientTask → Queue2 → Uart3Task → UART3 - UART3 → Uart3Task → Queue2 → NetClientTask → CH390D → 远程服务器 +Client 链路(UART3 ↔ TCP Client): + [远程服务器] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ClientTransTask → UART3_TX(DMA) + UART3_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [远程服务器] ``` -## 四、CH390D 驱动层 +## 五、CH390D 驱动层(基于官方 EVT) -### 4.1 接口设计 +### 5.1 驱动文件结构 + +驱动代码直接移植自 `Reference/EVT/EXAM/PUB/`,需修改接口宏定义: ```c -/* ch390d.h */ -typedef struct { - SPI_HandleTypeDef *hspi; - GPIO_TypeDef *int_port; - uint16_t int_pin; - GPIO_TypeDef *rst_port; - uint16_t rst_pin; -} CH390D_HandleTypeDef; - -HAL_StatusTypeDef CH390D_Init(CH390D_HandleTypeDef *hch); -HAL_StatusTypeDef CH390D_SetMAC(CH390D_HandleTypeDef *hch, uint8_t *mac); -HAL_StatusTypeDef CH390D_SetIP(CH390D_HandleTypeDef *hch, uint8_t *ip, uint8_t *mask, uint8_t *gw); -HAL_StatusTypeDef CH390D_TCP_Listen(CH390D_HandleTypeDef *hch, uint8_t sock, uint16_t port); -HAL_StatusTypeDef CH390D_TCP_Connect(CH390D_HandleTypeDef *hch, uint8_t sock, uint8_t *ip, uint16_t port); -HAL_StatusTypeDef CH390D_TCP_Send(CH390D_HandleTypeDef *hch, uint8_t sock, uint8_t *data, uint16_t len); -HAL_StatusTypeDef CH390D_TCP_Recv(CH390D_HandleTypeDef *hch, uint8_t sock, uint8_t *buf, uint16_t *len); -HAL_StatusTypeDef CH390D_GetSocketStatus(CH390D_HandleTypeDef *hch, uint8_t sock, uint8_t *status); +/* 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 ``` -### 4.2 初始化流程 - -``` -1. 复位 CH390D(RST 引脚) -2. 配置 SPI 通信参数 -3. 设置 MAC 地址 -4. 设置 IP/Mask/Gateway -5. 配置 Socket0 为 TCP Server 模式 -6. 配置 Socket1 为 TCP Client 模式 -7. 使能中断 -``` - -## 五、TCP 链路管理 - -### 5.1 Server 链路(Socket0) +### 5.2 SPI 接口层(CH390_Interface.c 适配 HAL) ```c -void NetServerTask(void *param) { - while(1) { - switch(server_state) { - case STATE_LISTEN: - // 等待客户端连接 - if(CH390D_CheckConnect(&hch, 0)) { - server_state = STATE_CONNECTED; - } - break; - - case STATE_CONNECTED: - // 接收数据 - if(CH390D_TCP_Recv(&hch, 0, rx_buf, &len) == HAL_OK) { - xQueueSend(uart2_queue, rx_buf, portMAX_DELAY); - } - - // 发送数据 - if(xQueueReceive(server_tx_queue, tx_buf, 0) == pdTRUE) { - CH390D_TCP_Send(&hch, 0, tx_buf, len); - } - - // 检测断开 - if(CH390D_CheckDisconnect(&hch, 0)) { - server_state = STATE_DISCONNECTED; - } - break; - - case STATE_DISCONNECTED: - // 重新监听 - CH390D_TCP_Listen(&hch, 0, SERVER_PORT); - server_state = STATE_LISTEN; - break; - } - vTaskDelay(pdMS_TO_TICKS(10)); +/* 引脚定义适配本项目 */ +#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.2 Client 链路(Socket1) +### 5.3 协议层主要 API ```c -void NetClientTask(void *param) { - while(1) { - switch(client_state) { - case STATE_DISCONNECTED: - // 尝试连接远程服务器 - if(CH390D_TCP_Connect(&hch, 1, remote_ip, remote_port) == HAL_OK) { - client_state = STATE_CONNECTING; - } else { - vTaskDelay(pdMS_TO_TICKS(3000)); // 3秒后重试 - } - break; - - case STATE_CONNECTING: - if(CH390D_CheckConnect(&hch, 1)) { - client_state = STATE_CONNECTED; - } else if(CH390D_CheckTimeout(&hch, 1)) { - client_state = STATE_DISCONNECTED; - } - break; - - case STATE_CONNECTED: - // 数据收发同 Server 链路 - // 断线后自动回到 STATE_DISCONNECTED - break; - } - vTaskDelay(pdMS_TO_TICKS(10)); - } -} +/* 从 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 引脚电平 ``` -## 六、串口透传层 - -### 6.1 UART DMA 配置 +### 5.4 初始化流程 ```c -/* 使用 DMA + 空闲中断接收,提高效率 */ -void UART2_Init(void) { - // 波特率可配置(9600/115200/230400/460800) - huart2.Init.BaudRate = 115200; - huart2.Init.WordLength = UART_WORDLENGTH_8B; - huart2.Init.StopBits = UART_STOPBITS_1; - huart2.Init.Parity = UART_PARITY_NONE; +void CH390_Init(void) +{ + /* 1. GPIO 初始化(由 CubeMX 完成) */ - // 使能 DMA 接收 - HAL_UART_Receive_DMA(&huart2, uart2_rx_buf, UART_BUF_SIZE); - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); + /* 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); + } } ``` -### 6.2 透传数据流 +## 六、LwIP 协议栈集成 + +### 6.1 LwIP 配置(lwipopts.h) ```c -/* UART2 空闲中断回调 */ -void UART2_IdleCallback(void) { - uint16_t len = UART_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart2.hdmarx); - if(len > 0) { - xQueueSendFromISR(server_tx_queue, uart2_rx_buf, len); - HAL_UART_Receive_DMA(&huart2, uart2_rx_buf, UART_BUF_SIZE); +/* 基础配置 */ +#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); } } -/* UART2 发送任务 */ -void Uart2Task(void *param) { - uint8_t buf[128]; - while(1) { - if(xQueueReceive(uart2_queue, buf, portMAX_DELAY) == pdTRUE) { - HAL_UART_Transmit(&huart2, buf, len, 100); +/* 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) @@ -336,32 +811,81 @@ void Config_Load(ConfigTypeDef *cfg) { } ``` -## 八、关键配置 +## 十一、关键配置 -### 8.1 FreeRTOS 配置 +### 11.1 FreeRTOS 配置(FreeRTOSConfig.h) ```c -/* FreeRTOSConfig.h 关键参数 */ -#define configUSE_PREEMPTION 1 -#define configCPU_CLOCK_HZ 72000000 -#define configTICK_RATE_HZ 1000 -#define configMAX_PRIORITIES 5 -#define configMINIMAL_STACK_SIZE 128 -#define configTOTAL_HEAP_SIZE 4096 // 4KB(适配R8T6的20KB RAM) -#define configUSE_MUTEXES 1 -#define configUSE_COUNTING_SEMAPHORES 1 +/* 基础配置 */ +#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 ``` -### 8.2 缓冲区配置 +### 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_BUF_SIZE 256 // 串口接收缓冲区 -#define NET_BUF_SIZE 256 // 网络接收缓冲区 -#define QUEUE_ITEM_SIZE 128 // 队列单次传输大小 -#define QUEUE_LENGTH 4 // 队列深度 +/* 串口缓冲区 */ +#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 测试方法