From aca8cd3980d7cec64725ed1daebadcf99a768dd7 Mon Sep 17 00:00:00 2001 From: xiao Date: Mon, 23 Mar 2026 14:53:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=8A=80?= =?UTF-8?q?=E6=9C=AF=E5=AE=9E=E7=8E=B0=E6=96=87=E6=A1=A3=EF=BC=9A=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=9E=B6=E6=9E=84=E3=80=81=E4=BB=BB=E5=8A=A1=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E3=80=81=E9=A9=B1=E5=8A=A8=E5=B1=82=E3=80=81=E9=80=8F?= =?UTF-8?q?=E4=BC=A0=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 项目技术实现.md | 356 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 项目技术实现.md diff --git a/项目技术实现.md b/项目技术实现.md new file mode 100644 index 0000000..395e0e7 --- /dev/null +++ b/项目技术实现.md @@ -0,0 +1,356 @@ +# TCP2UART 项目技术实现 + +## 一、系统架构 + +``` ++-------------------+ +-------------------+ +| TCP Server | | TCP Client | +| (监听端口) | | (连接远程服务器) | ++--------+----------+ +--------+----------+ + | | + | CH390D | + +------------------------------+ + | + +------v------+ + | STM32 | + | F103R8T6 | + +------+------+ + | + +-------------+-------------+ + | | | + +---v---+ +---v---+ +---v---+ + | UART1 | | UART2 | | UART3 | + | 配置口 | | 透传口 | | 透传口 | + +-------+ +-------+ +-------+ +``` + +## 二、外设分配 + +| 外设 | 功能 | 对应链路 | +|------|------|----------| +| SPI1 | CH390D 通信接口 | - | +| UART1 | 参数配置串口 | 配置通道 | +| UART2 | 数据透传 | Server 链路 | +| UART3 | 数据透传 | Client 链路 | +| GPIO | CH390D INT/RST | - | +| TIM2 | FreeRTOS Tick | - | + +## 三、FreeRTOS 任务设计 + +### 3.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 | + +### 3.2 任务间通信 + +``` +NetServerTask <--Queue1--> Uart2Task +NetClientTask <--Queue2--> Uart3Task +ConfigTask <--独立运行--> (读写参数Flash) +``` + +- **Queue1**: Server 网络数据 <-> UART2 双向队列(128 bytes) +- **Queue2**: Client 网络数据 <-> UART3 双向队列(128 bytes) + +### 3.3 数据流向 + +``` +Server 链路: + TCP Client → CH390D → NetServerTask → Queue1 → Uart2Task → UART2 + UART2 → Uart2Task → Queue1 → NetServerTask → CH390D → TCP Client + +Client 链路: + 远程服务器 → CH390D → NetClientTask → Queue2 → Uart3Task → UART3 + UART3 → Uart3Task → Queue2 → NetClientTask → CH390D → 远程服务器 +``` + +## 四、CH390D 驱动层 + +### 4.1 接口设计 + +```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); +``` + +### 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) + +```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)); + } +} +``` + +### 5.2 Client 链路(Socket1) + +```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)); + } +} +``` + +## 六、串口透传层 + +### 6.1 UART DMA 配置 + +```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; + + // 使能 DMA 接收 + HAL_UART_Receive_DMA(&huart2, uart2_rx_buf, UART_BUF_SIZE); + __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); +} +``` + +### 6.2 透传数据流 + +```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); + } +} + +/* 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); + } + } +} +``` + +## 七、参数配置 + +### 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 +/* 使用 STM32F103 内部 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); // 加载默认配置 + } +} +``` + +## 八、关键配置 + +### 8.1 FreeRTOS 配置 + +```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 10240 // 10KB +#define configUSE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +``` + +### 8.2 缓冲区配置 + +```c +#define UART_BUF_SIZE 256 // 串口接收缓冲区 +#define NET_BUF_SIZE 256 // 网络接收缓冲区 +#define QUEUE_ITEM_SIZE 128 // 队列单次传输大小 +#define QUEUE_LENGTH 4 // 队列深度 +``` + +## 九、丢包测试方案 + +### 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") +```