Files
TCP2UART/CODING_PROMPT.md
T

272 lines
11 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 FreeRTOS 编码任务提示词
## 项目位置
`D:\code\STM32Project\TCP2UART`,当前在 `master` 分支。
## 背景
本项目是一个 TCP↔UART 双向透传设备,之前在 `baremetal-r8` 分支上以裸机 + lwIP RAW API 方式实现并已调通。现在需要在 `master` 分支上将其迁移为 **FreeRTOS + lwIP netconn API** 架构。
**工程配置文件(IOC、Keil、启动文件、lwipopts.h、FreeRTOSConfig.h)已全部更新完毕,不需要你改。你的任务是编写/重写 C 代码。**
---
## 必读文档(按优先级)
1. `项目技术实现.md`**首要参考**,第四节"FreeRTOS 任务设计"包含完整的 9+1 任务架构、通信机制、实现模板
2. `项目需求说明.md` — MUX/NET/LINK 协议需求
3. `AT固件使用手册.md` — AT 命令定义
4. `工程调试指南.md` — 调试方法
5. `Keil工程配置说明.txt` — 工程结构
---
## 架构核心决策(不可更改)
1. **lwIP**: `NO_SYS=0`, `netconn API`, `LWIP_SOCKET=0`, `LWIP_TCPIP_CORE_LOCKING=1`
2. **tcpip_thread**: lwIP 自建,优先级 6,栈 512 words
3. **每条 TCP 连接独立任务**: S1/S2 各一个 Server 任务,C1/C2 各一个 Client 任务
4. **NetPollTask**: 独立任务调 `ethernetif_poll()`,不由 tcpip_thread 做
5. **零拷贝 Queue**: 传递 `route_msg_t*` 指针(含 src_id, dst_mask, len, conn_type, *data
6. **路由内联**: 不设独立 RouteTask,路由逻辑分别在 UartRxTask 和各 TCP 任务内
7. **HAL 时间基准**: TIM4(非 SysTickSysTick 被 FreeRTOS 占用)
8. **MCU**: STM32F103RCT6 (256KB/48KB),若 RAM 不够换 RDT6 (384KB/64KB) pin-to-pin
---
## 任务清单(9 个 FreeRTOS 任务)
| 任务 | 优先级 | 栈(words) | 阻塞方式 | 对应文件 |
|------|--------|-----------|----------|----------|
| tcpip_thread | 6 | 512 | mbox | lwIP 自动创建 |
| NetPollTask | 5 | 384 | BinarySem 2ms 超时 | 新建 `task_net_poll.c` |
| TcpSrvTask_S1 | 4 | 384 | netconn_accept 阻塞 | 重写 `tcp_server.c` |
| TcpSrvTask_S2 | 4 | 384 | netconn_accept 阻塞 | 重写 `tcp_server.c` |
| TcpCliTask_C1 | 4 | 256 | netconn_connect 阻塞 | 重写 `tcp_client.c` |
| TcpCliTask_C2 | 4 | 256 | netconn_connect 阻塞 | 重写 `tcp_client.c` |
| UartRxTask | 4 | 384 | TaskNotify 10ms 超时 | 适配 `uart_trans.c` |
| ConfigTask | 2 | 256 | QueueReceive 阻塞 | 适配 `config.c` |
| DefaultTask | 1 | 128 | vTaskDelay 500ms | `freertos.c` CubeMX 生成,LED 心跳 + IWDG 喂狗,保留不可删 |
---
## 文件级操作指南
### 第一优先级:sys_arch.clwIP 移植层)
**文件**: `Drivers/LwIP/port/sys_arch.c`
这是最关键的基础模块,没有它 tcpip_thread 无法启动。必须实现:
- `sys_init()` — 空函数即可
- `sys_thread_new(name, thread, arg, stacksize, prio)` — 调用 `xTaskCreate`
- `sys_mbox_new(mbox, size)` / `sys_mbox_free` / `sys_mbox_post` / `sys_mbox_trypost` / `sys_arch_mbox_fetch` / `sys_arch_mbox_tryfetch` — 基于 FreeRTOS `xQueueCreate` / `xQueueSend` / `xQueueReceive`
- `sys_sem_new` / `sys_sem_free` / `sys_arch_sem_wait` / `sys_sem_signal` — 基于 FreeRTOS `xSemaphoreCreateBinary`
- `sys_mutex_new` / `sys_mutex_free` / `sys_mutex_lock` / `sys_mutex_unlock` — 基于 FreeRTOS `xSemaphoreCreateMutex`
- `sys_arch_protect()` / `sys_arch_unprotect()` — 基于 `vPortEnterCritical()` / `vPortExitCritical()`
- `sys_now()` — 返回 `xTaskGetTickCount()`
- `sys_thread_id()` — 返回 `xTaskGetCurrentTaskHandle()`
参考 FreeRTOSConfig.h 中已定义的兼容宏:
```c
#define sys_arch_protect() vPortEnterCritical()
#define sys_arch_unprotect(x) vPortExitCritical()
#define sys_now() ((uint32_t)xTaskGetTickCount())
```
**参考**: `baremetal-r8` 分支的 `Drivers/LwIP/port/sys_arch.c` 是 NO_SYS=1 的空壳,需要完全重写。
### 第二优先级:main.c + freertos.c
**文件**: `Core/Src/main.c`
当前 main.c 是裸机 while(1) 轮询架构,需要改为:
1. 保留所有 `MX_xxx_Init()` 外设初始化
2. 删除 `App_Init()` / `App_Poll()` 及所有 `App_Route*` 函数
3. 删除 `StackGuard_*`(FreeRTOS 有自己的栈溢出检测)
4. 保留 `SystemClock_Config()``Debug_TrapWithRttHint()``Error_Handler()`
5. `main()` 最后调用 `MX_FREERTOS_Init()` 启动调度器(由 CubeMX 生成的 freertos.c 中实现)
**文件**: `Core/Src/freertos.c`
CubeMX 生成的框架文件,需要在 `MX_FREERTOS_Init()` 中创建所有自定义任务:
- NetPollTask
- TcpSrvTask_S1 / TcpSrvTask_S2
- TcpCliTask_C1 / TcpCliTask_C2
- UartRxTask
- ConfigTask
任务优先级和栈大小使用 FreeRTOSConfig.h 中定义的常量:
```c
#define TASK_PRIORITY_NET_POLL 5
#define TASK_STACK_NET_POLL 384
// ... 等等,见 FreeRTOSConfig.h 底部
```
### 第三优先级:tcp_server.c / tcp_client.c(重写)
**文件**: `App/tcp_server.c` / `App/tcp_server.h`
当前基于 RAW API (`tcp_pcb`, `tcp_recv` 回调),需完全重写为 netconn:
```c
// Server 任务模板(每个实例一个任务)
void TcpSrvTask_S1(void *arg) {
struct netconn *conn = netconn_new(NETCONN_TCP);
netconn_bind(conn, IP_ADDR_ANY, cfg->links[0].lport);
netconn_listen(conn);
for (;;) {
struct netconn *newconn;
if (netconn_accept(conn, &newconn) == ERR_OK) {
server_worker(newconn, LINK_S1);
netconn_close(newconn);
netconn_delete(newconn);
}
}
}
void server_worker(struct netconn *conn, uint8_t link_idx) {
struct netbuf *buf;
while (netconn_recv(conn, &buf) == ERR_OK) {
// 构造 route_msg_t,投递到 xTcpRxQueue
// xQueueSend(xTcpRxQueue, &msg, pdMS_TO_TICKS(10));
netbuf_delete(buf);
}
}
```
**文件**: `App/tcp_client.c` / `App/tcp_client.h`
```c
void TcpCliTask_C1(void *arg) {
for (;;) {
struct netconn *conn = netconn_new(NETCONN_TCP);
ip_addr_t rip;
IP_ADDR4(&rip, cfg->links[2].rip[0], ...);
if (netconn_connect(conn, &rip, cfg->links[2].rport) == ERR_OK) {
client_worker(conn, LINK_C1);
}
netconn_close(conn);
netconn_delete(conn);
vTaskDelay(pdMS_TO_TICKS(reconnect_interval));
}
}
```
**重要**
- 删除内部 ring buffernetconn 内部有 pbuf 缓冲)
- 删除 RAW API 回调函数 (`tcp_recv`, `tcp_sent`, `tcp_err` 等)
- Server 接受的连接在**本任务内**处理(不 spawn 新任务)
- Client 断连后需自动重连(delay 后重试)
- TCP→UART 数据通过 `xTcpRxQueue` 传递 route_msg_t 指针
### 第四优先级:uart_trans.c 适配
**文件**: `App/uart_trans.c` / `App/uart_trans.h`
核心改动:
1. 删除 `uart_trans_poll()` 函数(不再需要轮询)
2. UART IDLE 中断回调中调用 `xTaskNotifyFromISR(xUartRxTaskHandle, 1, eSetBits, &xHigherPriorityTaskWoken)`
3. UART TX 完成中断回调中触发 DMA 继续发送(保持现有逻辑)
4. 保持 MUX 帧编解码 (`uart_mux_try_extract_frame`, `uart_mux_encode_frame`) 不变
### 第五优先级:config.c 适配
**文件**: `App/config.c` / `App/config.h`
改动较小:
1. 删除 `config_poll()` 函数
2. ConfigTask 通过 `xQueueReceive(xConfigQueue, ...)` 阻塞等待 AT 文本
3. AT 命令解析逻辑保持不变
4. `flash_param.c` 无需改动
### 第六优先级:stm32f1xx_it.c 适配
**文件**: `Core/Src/stm32f1xx_it.c`
改动:
1. `EXTI0_IRQHandler`CH390 中断): 调用 `xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken)` + `portYIELD_FROM_ISR`
2. `USART2_IRQHandler` / `USART3_IRQHandler`: IDLE 中断中调用 `xTaskNotifyFromISR` 通知 UartRxTask
3. `TIM4_IRQHandler`: 保持 HAL 时间基准(调用 `HAL_TIM_IRQHandler`
4. DMA 中断保持现有逻辑
5. **删除** `SysTick_Handler`FreeRTOS 接管 SysTickTIM4 做 HAL tick
### 第七优先级:新建路由消息模块
**新建文件**: `App/route_msg.c` / `App/route_msg.h`
```c
typedef struct {
uint8_t src_id;
uint8_t dst_mask;
uint16_t len;
uint8_t conn_type; /* LINK_S1/S2/C1/C2 */
uint8_t *data; /* 指向静态缓冲池 */
} route_msg_t;
#define ROUTE_BUF_COUNT 4
#define ROUTE_BUF_SIZE 512
/* 从池中获取空闲缓冲 */
uint8_t *route_buf_alloc(void);
/* 标记缓冲为可用 */
void route_buf_free(uint8_t *buf);
/* 构造 route_msg_t 并发送到指定 Queue */
void route_send(QueueHandle_t queue, uint8_t src_id, uint8_t dst_mask,
uint8_t conn_type, const uint8_t *data, uint16_t len);
```
---
## 可参考的 baremetal-r8 分支代码
通过 `git show baremetal-r8:<filepath>` 查看裸机版本(已验证可工作):
- `App/config.c` — AT 命令解析(可直接复用大部分逻辑)
- `App/flash_param.c` — Flash 读写(完全不用改)
- `App/uart_trans.c` — UART DMA/IDLE + MUX 帧编解码(复用 + 适配)
- `Core/Src/main.c` — App_Poll / App_RouteRawUartTraffic / App_RouteMuxUartTraffic(路由逻辑参考)
- `Drivers/CH390/*` — CH390 驱动(复用 + 加 Mutex
- `Drivers/LwIP/src/netif/ethernetif.c` — netif glue(复用 + 适配 tcpip_input
---
## 约束
1. **不修改**以下文件:TCP2UART.ioc, MDK-ARM/TCP2UART.uvprojx, startup_stm32f103xe.s, FreeRTOSConfig.h, lwipopts.h
2. **不引入**新依赖,不使用 DHCP、DNS、UDP
3. FreeRTOS 堆管理用 `heap_4.c`(已在 FreeRTOSConfig.h 中配置)
4. 调试输出统一使用 `SEGGER_RTT`
5. ISR 中只调用 `FromISR` 后缀的 FreeRTOS API
6. FreeRTOS 可管理的中断优先级 >= 5configMAX_SYSCALL_INTERRUPT_PRIORITY
7. 所有任务栈用 `uxTaskGetStackHighWaterMark(NULL)` 监控
8. 所有 `.c` 文件使用 `#include "xxx.h"` 而非相对路径
9. Keil 工程文件(uvprojx)中已有所有源文件组,如果新建文件需要在 Keil 中添加
---
## 验证目标
完成编码后,应能通过以下验证:
1. MDK-ARM 编译 0 Error / 0 Warning
2. RTT 输出显示所有 9 个任务成功创建(`vTaskList` 输出)
3. CH390 初始化成功(`ETH init: done`
4. TCP Server 在配置端口监听(外部工具可连接)
5. TCP Client 连接远端成功
6. UART 数据通过 TCP 双向透传
7. MUX 帧模式下路由正确
8. AT 命令通过 UART1 正常响应
9. FreeRTOS 堆剩余 > 1KB`xPortGetFreeHeapSize()`
10. 所有任务栈 highwatermark > 20% 余量
11. DefaultTaskCubeMX 生成)保留,充实为 LED 心跳 + IWDG 喂狗,不可删除
---
## 注意事项
- 如果编译时发现 RAM 溢出(48KB),优先压缩:取消 TCP ring buffernetconn 自带 pbuf)、降低 MEM_SIZE 到 6KB、降低 FreeRTOS 堆到 8KB
- 如果仍然不够,告知用户切换 STM32F103RDT664KB SRAM),仅需改 startup/宏/SRAM 大小
- `baremetal-r8` 分支的代码是已验证可工作的,可以作为逻辑参考,但不要照搬 RAW API 调用
- 路由逻辑参考 `baremetal-r8``App_RouteRawUartTraffic()``App_RouteMuxUartTraffic()`