refactor: 基于EVT示例重构技术实现,采用LwIP协议栈架构

- 引入LwIP协议栈替代直接操作CH390D硬件Socket
- CH390D驱动层直接移植官方EVT代码(CH390.c/h + CH390_Interface.c/h)
- 重构FreeRTOS任务:LwIPTask统一处理协议栈,透传任务专注数据搬运
- 使用StreamBuffer替代Queue,更适合流式数据传输
- 更新中断优先级配置,符合FreeRTOS要求
- 添加内存使用估算(18KB/20KB)
- 完善SPI配置和LwIP配置说明
This commit is contained in:
2026-03-29 17:51:47 +08:00
parent eb57a564ef
commit d5803ca7dd
+695 -171
View File
@@ -8,10 +8,15 @@
| (监听端口) | | (连接远程服务器) | | (监听端口) | | (连接远程服务器) |
+--------+----------+ +--------+----------+ +--------+----------+ +--------+----------+
| | | |
| CH390D | | LwIP TCP/IP Stack |
+------------------------------+ +------------------------------+
| |
+------v------+ +------v------+
| CH390D |
| (SPI接口) |
+------+------+
|
+------v------+
| STM32 | | STM32 |
| F103R8T6 | | F103R8T6 |
+------+------+ +------+------+
@@ -24,6 +29,8 @@
+-------+ +-------+ +-------+ +-------+ +-------+ +-------+
``` ```
> **架构说明**:基于官方 EVT 示例代码,采用 LwIP 轻量级 TCP/IP 协议栈,而非直接操作 CH390D 的硬件 Socket。CH390D 作为以太网 MAC+PHY 芯片,通过 SPI 接口与 STM32 通信。
## 二、硬件配置 ## 二、硬件配置
### 2.1 MCU 型号 ### 2.1 MCU 型号
@@ -62,16 +69,19 @@ STM32F103R8T6LQFP6464KB Flash20KB RAM
| DMA1_Ch6 | USART2_RX | 外设→内存 | | DMA1_Ch6 | USART2_RX | 外设→内存 |
| DMA1_Ch7 | USART2_TX | 内存→外设 | | DMA1_Ch7 | USART2_TX | 内存→外设 |
### 2.4 中断优先级分配(建议) ### 2.4 中断优先级分配
| 外设 | 建议优先级 | 说明 | 基于 FreeRTOS 的 NVIC 优先级分组配置(使用 4 位抢占优先级):
|------|------------|------|
| EXTI0CH390D INT | 2 | 网络数据接收最紧急 | | 外设 | 抢占优先级 | 子优先级 | 说明 |
| SPI1 | 3 | 网络数据传输 | |------|------------|----------|------|
| USART2/3 DMA | 4 | 透传串口 | | EXTI0CH390D INT | 5 | 0 | 网络数据接收,需高于 FreeRTOS 临界区 |
| USART1 DMA | 5 | 配置口,非实时 | | DMA1_Ch2/3 (USART3) | 6 | 0 | 透传串口 DMA |
| PendSV | 15 | FreeRTOS 任务切换 | | DMA1_Ch6/7 (USART2) | 6 | 1 | 透传串口 DMA |
| SysTick | 15 | FreeRTOS 系统节拍 | | 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 时钟配置 ### 2.5 时钟配置
@@ -92,196 +102,661 @@ STM32F103R8T6LQFP6464KB Flash20KB RAM
> **注意**STM32F103R8T6 仅有 20KB RAM,需合理分配 FreeRTOS 堆和任务栈。 > **注意**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 | | LwIPTask | osPriorityHigh | LwIP 协议栈处理(定时器 + 网卡输入) | 512 words |
| NetClientTask | High | TCP Client 管理 + 数据收发 | 512 words | | ServerTransTask | osPriorityAboveNormal | TCP Server ↔ UART2 双向透传 | 384 words |
| Uart2Task | Normal | UART2 数据收发(Server 透传 | 256 words | | ClientTransTask | osPriorityAboveNormal | TCP Client ↔ UART3 双向透传 | 384 words |
| Uart3Task | Normal | UART3 数据收发(Client 透传) | 256 words | | ConfigTask | osPriorityNormal | UART1 AT 命令解析 | 256 words |
| ConfigTask | Low | UART1 配置命令解析 | 256 words |
### 3.2 任务间通信 > **说明**:相比原设计减少任务数,将网络任务合并为 LwIPTask 统一处理,透传任务专注数据搬运。
### 4.2 任务间通信
``` ```
NetServerTask <--Queue1--> Uart2Task +-------------+
NetClientTask <--Queue2--> Uart3Task | LwIPTask |
ConfigTask <--独立运行--> (读写参数Flash) | (协议栈处理) |
+------+------+
|
+------------------+------------------+
| |
+-------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 ### 4.3 同步机制
- **Queue2**: Client 网络数据 <-> UART3 双向队列(128 bytes
### 3.3 数据流向 | 机制 | 用途 | 说明 |
|------|------|------|
| xStreamBuffer | UART DMA → TCP 发送 | 零拷贝流式缓冲,适合变长数据 |
| xSemaphore | TCP 接收 → UART 发送 | 通知信号量,TCP 收到数据后释放 |
| xMutex | CH390 SPI 访问保护 | LwIP 输出 + 中断输入共享 SPI |
### 4.4 数据流向
``` ```
Server 链路: Server 链路UART2 ↔ TCP Server:
TCP Client → CH390DNetServerTask → Queue1 → Uart2Task → UART2 [外部TCP Client] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ServerTransTask → UART2_TX(DMA)
UART2 → Uart2Task → Queue1 → NetServerTask → CH390D → TCP Client UART2_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [外部TCP Client]
Client 链路: Client 链路UART3 ↔ TCP Client:
远程服务器 → CH390DNetClientTask → Queue2 → Uart3Task → UART3 [远程服务器] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ClientTransTask → UART3_TX(DMA)
UART3 → Uart3Task → Queue2 → NetClientTask → CH390D → 远程服务器 UART3_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [远程服务器]
``` ```
## 、CH390D 驱动层 ## 、CH390D 驱动层(基于官方 EVT
### 4.1 接口设计 ### 5.1 驱动文件结构
驱动代码直接移植自 `Reference/EVT/EXAM/PUB/`,需修改接口宏定义:
```c ```c
/* ch390d.h */ /* CH390.h 中启用 SPI 接口 */
typedef struct { #define CH390_INTERFACE_SPI // CH390H/CH390D 使用 SPI
SPI_HandleTypeDef *hspi; // #define CH390_INTERFACE_8_BIT // CH390L/CH390F 8-bit mode
GPIO_TypeDef *int_port; // #define CH390_INTERFACE_16_BIT // CH390L 16-bit mode
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);
``` ```
### 4.2 初始化流程 ### 5.2 SPI 接口层(CH390_Interface.c 适配 HAL
```
1. 复位 CH390DRST 引脚)
2. 配置 SPI 通信参数
3. 设置 MAC 地址
4. 设置 IP/Mask/Gateway
5. 配置 Socket0 为 TCP Server 模式
6. 配置 Socket1 为 TCP Client 模式
7. 使能中断
```
## 五、TCP 链路管理
### 5.1 Server 链路(Socket0
```c ```c
void NetServerTask(void *param) { /* 引脚定义适配本项目 */
while(1) { #define CH390_SPI_HANDLE hspi1
switch(server_state) { #define CH390_CS_PORT GPIOA
case STATE_LISTEN: #define CH390_CS_PIN GPIO_PIN_4
// 等待客户端连接 #define CH390_RST_PORT GPIOB
if(CH390D_CheckConnect(&hch, 0)) { #define CH390_RST_PIN GPIO_PIN_1
server_state = STATE_CONNECTED; #define CH390_INT_PORT GPIOB
} #define CH390_INT_PIN GPIO_PIN_0
break;
/* HAL SPI 适配 */
case STATE_CONNECTED: static uint8_t ch390_spi_exchange_byte(uint8_t byte)
// 接收数据 {
if(CH390D_TCP_Recv(&hch, 0, rx_buf, &len) == HAL_OK) { uint8_t rx;
xQueueSend(uart2_queue, rx_buf, portMAX_DELAY); HAL_SPI_TransmitReceive(&CH390_SPI_HANDLE, &byte, &rx, 1, HAL_MAX_DELAY);
} return rx;
}
// 发送数据
if(xQueueReceive(server_tx_queue, tx_buf, 0) == pdTRUE) { /* FreeRTOS 延时适配 */
CH390D_TCP_Send(&hch, 0, tx_buf, len); void ch390_delay_us(uint32_t time)
} {
/* 微秒级延时,可用 DWT 或简单循环 */
// 检测断开 uint32_t cycles = (SystemCoreClock / 1000000) * time;
if(CH390D_CheckDisconnect(&hch, 0)) { while(cycles--) __NOP();
server_state = STATE_DISCONNECTED; }
}
break; void ch390_delay_ms(uint32_t time)
{
case STATE_DISCONNECTED: /* FreeRTOS 环境下使用 vTaskDelay */
// 重新监听 if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
CH390D_TCP_Listen(&hch, 0, SERVER_PORT); vTaskDelay(pdMS_TO_TICKS(time));
server_state = STATE_LISTEN; } else {
break; HAL_Delay(time);
}
vTaskDelay(pdMS_TO_TICKS(10));
} }
} }
``` ```
### 5.2 Client 链路(Socket1 ### 5.3 协议层主要 API
```c ```c
void NetClientTask(void *param) { /* 从 CH390.h 提取的关键函数 */
while(1) {
switch(client_state) { /* 初始化与配置 */
case STATE_DISCONNECTED: void ch390_hardware_reset(void); // 硬件复位(拉低 RST 引脚)
// 尝试连接远程服务器 void ch390_software_reset(void); // 软件复位(写 NCR 寄存器)
if(CH390D_TCP_Connect(&hch, 1, remote_ip, remote_port) == HAL_OK) { void ch390_default_config(void); // 默认配置:LED模式、校验和、使能RX/中断
client_state = STATE_CONNECTING; void ch390_set_phy_mode(enum ch390_phy_mode mode); // CH390_AUTO / CH390_100MFD / CH390_10MFD
} else { void ch390_set_mac_address(uint8_t *mac_addr); // 设置 MAC 地址
vTaskDelay(pdMS_TO_TICKS(3000)); // 3秒后重试
} /* 数据收发 */
break; uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status); // 接收以太网帧
void ch390_send_packet(uint8_t *buff, uint16_t length); // 发送以太网帧
case STATE_CONNECTING: void ch390_drop_packet(uint16_t len); // 丢弃当前包
if(CH390D_CheckConnect(&hch, 1)) {
client_state = STATE_CONNECTED; /* 状态查询 */
} else if(CH390D_CheckTimeout(&hch, 1)) { int ch390_get_link_status(void); // 0: 断开, 1: 连接
client_state = STATE_DISCONNECTED; int ch390_get_phy_speed(void); // 0: 100Mbps, 1: 10Mbps
} uint8_t ch390_get_int_status(void); // 获取并清除中断状态
break;
/* 中断配置 */
case STATE_CONNECTED: void ch390_interrupt_config(uint8_t mask); // IMR_PRI | IMR_PTI | IMR_LNKCHGI 等
// 数据收发同 Server 链路 uint16_t ch390_get_int_pin(void); // 读取 INT 引脚电平
// 断线后自动回到 STATE_DISCONNECTED
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
``` ```
## 六、串口透传层 ### 5.4 初始化流程
### 6.1 UART DMA 配置
```c ```c
/* 使用 DMA + 空闲中断接收,提高效率 */ void CH390_Init(void)
void UART2_Init(void) { {
// 波特率可配置(9600/115200/230400/460800 /* 1. GPIO 初始化(由 CubeMX 完成) */
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
// 使能 DMA 接收 /* 2. SPI 初始化(由 CubeMX 完成)
HAL_UART_Receive_DMA(&huart2, uart2_rx_buf, UART_BUF_SIZE); * Mode: Master, Full-Duplex
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); * 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 ```c
/* UART2 空闲中断回调 */ /* 基础配置 */
void UART2_IdleCallback(void) { #define NO_SYS 0 /* 使用 OS */
uint16_t len = UART_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart2.hdmarx); #define LWIP_NETCONN 0 /* 不使用 Netconn API */
if(len > 0) { #define LWIP_SOCKET 0 /* 不使用 Socket API */
xQueueSendFromISR(server_tx_queue, uart2_rx_buf, len);
HAL_UART_Receive_DMA(&huart2, uart2_rx_buf, UART_BUF_SIZE); /* 内存配置(适配 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 发送任务 */ /* LwIP 任务中的中断处理 */
void Uart2Task(void *param) { void ch390_int_handler(void)
uint8_t buf[128]; {
while(1) { uint8_t int_status = ch390_get_int_status();
if(xQueueReceive(uart2_queue, buf, portMAX_DELAY) == pdTRUE) {
HAL_UART_Transmit(&huart2, buf, len, 100); /* 链路状态变化 */
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 ### 7.1 配置命令格式(UART1
@@ -336,32 +811,81 @@ void Config_Load(ConfigTypeDef *cfg) {
} }
``` ```
## 、关键配置 ## 十一、关键配置
### 8.1 FreeRTOS 配置 ### 11.1 FreeRTOS 配置FreeRTOSConfig.h
```c ```c
/* FreeRTOSConfig.h 关键参数 */ /* 基础配置 */
#define configUSE_PREEMPTION 1 #define configUSE_PREEMPTION 1
#define configCPU_CLOCK_HZ 72000000 #define configCPU_CLOCK_HZ 72000000
#define configTICK_RATE_HZ 1000 #define configTICK_RATE_HZ 1000
#define configMAX_PRIORITIES 5 #define configMAX_PRIORITIES 7
#define configMINIMAL_STACK_SIZE 128 #define configMINIMAL_STACK_SIZE 128
#define configTOTAL_HEAP_SIZE 4096 // 4KB(适配R8T6的20KB RAM #define configTOTAL_HEAP_SIZE (8*1024) /* 8KBLwIP 需要更多堆 */
#define configUSE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1 /* 中断优先级配置 */
#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 ```c
#define UART_BUF_SIZE 256 // 串口接收缓冲区 /* 串口缓冲区 */
#define NET_BUF_SIZE 256 // 网络接收缓冲区 #define UART_RX_BUF_SIZE 256 /* DMA 接收缓冲区 */
#define QUEUE_ITEM_SIZE 128 // 队列单次传输大小
#define QUEUE_LENGTH 4 // 队列深度 /* 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 测试方法 ### 9.1 测试方法