Files
TCP2UART/项目技术实现.md
T
gaoro-xiao eb57a564ef fix: 将MCU型号从STM32F103RCT6修正为STM32F103R8T6
- 修改ioc文件中的芯片型号配置
- 调整HeapSize从10KB减至4KB以适应20KB RAM限制
- 更新项目技术实现文档中的MCU规格和内存配置
- 更新MDK-ARM工程文件的芯片定义(STM32F103xB)
- 添加新的启动文件startup_stm32f103xb.s
2026-03-29 17:42:25 +08:00

414 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TCP2UART 项目技术实现
## 一、系统架构
```
+-------------------+ +-------------------+
| TCP Server | | TCP Client |
| (监听端口) | | (连接远程服务器) |
+--------+----------+ +--------+----------+
| |
| CH390D |
+------------------------------+
|
+------v------+
| STM32 |
| F103R8T6 |
+------+------+
|
+-------------+-------------+
| | |
+---v---+ +---v---+ +---v---+
| UART1 | | UART2 | | UART3 |
| 配置口 | | 透传口 | | 透传口 |
+-------+ +-------+ +-------+
```
## 二、硬件配置
### 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 中断优先级分配(建议)
| 外设 | 建议优先级 | 说明 |
|------|------------|------|
| EXTI0CH390D INT | 2 | 网络数据接收最紧急 |
| SPI1 | 3 | 网络数据传输 |
| USART2/3 DMA | 4 | 透传串口 |
| USART1 DMA | 5 | 配置口,非实时 |
| PendSV | 15 | FreeRTOS 任务切换 |
| SysTick | 15 | FreeRTOS 系统节拍 |
### 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 堆和任务栈。
## 三、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. 复位 CH390DRST 引脚)
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
/* 使用 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); // 加载默认配置
}
}
```
## 八、关键配置
### 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 4096 // 4KB(适配R8T6的20KB RAM
#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")
```