10 KiB
10 KiB
TCP2UART 项目技术实现(裸机迁移基线)
一、目标
当前分支 baremetal-r8 的目标不是一次性完成全部业务逻辑重写,而是先把工程基线切换到适合 STM32F103R8T6 的裸机方向,为后续继续开发提供统一入口。
本阶段已经完成或约束如下:
- MCU 目标统一为
STM32F103R8T6 / STM32F103xB CubeMX IOC中已移除FreeRTOS中间件声明- 工程规划转为裸机轮询 + 中断驱动模型
- 暂不在本阶段重写 TCP/串口业务逻辑
- 保留现有源码作为迁移参考,后续由其他 Agent/开发者继续接力实现
二、硬件与资源约束
2.1 MCU
- 型号:
STM32F103R8T6 - Flash:
64 KB - SRAM:
20 KB - 主频:
72 MHz
2.2 主要外设
SPI1:连接CH390DUSART1:配置串口USART2:Server 透传串口USART3:Client 透传串口DMA1:3 路 UART 收发 DMAEXTI0:CH390 中断输入IWDG:独立看门狗
2.3 当前引脚分配
| 引脚 | 功能 | 用途 |
|---|---|---|
| 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 | 配置串口 |
| PB0 | EXTI0 | CH390D INT |
| PB1 | GPIO_Output | CH390D RESET |
| PB10 | USART3_TX | Client 透传串口 |
| PB11 | USART3_RX | Client 透传串口 |
| PC13 | GPIO_Output | 状态 LED |
| PD0/PD1 | HSE | 8MHz 外部晶振 |
三、为何从 FreeRTOS 迁移到裸机
STM32F103R8T6 的 RAM 只有 20KB。当前工程在引入如下组件后,链接空间明显不足:
FreeRTOS内核CMSIS-RTOS V2包装层- 多任务栈
lwIP + socket/netconnOS 抽象层- 多路
StreamBuffer / Semaphore / Mutex
此前编译已经证明:
- 源码层报错可修复
- 统一
R8型号后仍然在链接阶段失败 - 主要瓶颈是
RAM + Flash同时偏紧
因此本项目后续建议的主方向为:
- 去掉
FreeRTOS - 去掉
CMSIS-RTOS V2 - 避免依赖
lwIP socket/netconn - 采用裸机主循环 + DMA/IDLE/EXTI 中断驱动
- 网络侧改为更贴近资源受限场景的实现模型
四、裸机架构目标
4.1 总体分层
+--------------------------------------------------+
| Application State Machine |
| config / server link / client link / watchdog |
+--------------------------------------------------+
| Transport Scheduler |
| main loop polling + event flags + timeout scan |
+--------------------------------------------------+
| Network Interface |
| CH390 event polling / packet rx-tx dispatch |
+--------------------------------------------------+
| Peripheral Drivers |
| UART DMA+IDLE / SPI / GPIO / EXTI / Flash |
+--------------------------------------------------+
| STM32 HAL / CMSIS |
+--------------------------------------------------+
4.2 执行模型
不再使用任务调度器,改为以下模型:
ISR只做最小事件置位与 DMA 状态更新- 主循环统一处理事件、超时、状态机推进
- UART RX 继续依赖
DMA + IDLE - UART TX 可保留
DMA - CH390 中断只置位
netif_pending - 网络协议处理在主循环中推进
4.3 事件源
建议保留以下裸机事件位:
typedef enum {
EVENT_NONE = 0x00000000u,
EVENT_CH390_INT = 0x00000001u,
EVENT_UART1_RX_IDLE = 0x00000002u,
EVENT_UART2_RX_IDLE = 0x00000004u,
EVENT_UART3_RX_IDLE = 0x00000008u,
EVENT_UART2_TX_DONE = 0x00000010u,
EVENT_UART3_TX_DONE = 0x00000020u,
EVENT_NET_TIMER_1MS = 0x00000040u,
EVENT_LINK_RETRY = 0x00000080u,
EVENT_CONFIG_PENDING = 0x00000100u,
} app_event_t;
这些事件位建议通过 volatile uint32_t g_app_events 或一个轻量原子位图维护。
五、建议的软件模块拆分
5.1 保留模块
以下模块可继续保留,但需要去除 RTOS 依赖:
App/config.cApp/flash_param.cApp/tcp_server.*App/tcp_client.*App/uart_trans.*Drivers/CH390/*Drivers/LwIP/*或后续替代网络栈
5.2 需要改造的核心模块
Core/Src/main.cCore/Src/stm32f1xx_it.cCore/Src/usart.cCore/Src/dma.cCore/Src/freertos.c:后续应移除出构建或清空为兼容壳Core/Inc/FreeRTOSConfig.h:后续可移除出工程Drivers/LwIP/port/sys_arch.c:若完全去 OS,应停止使用该移植层Drivers/LwIP/src/include/arch/sys_arch.h
5.3 建议新增的裸机模块
建议后续新增:
-
App/app_scheduler.c/.h- 统一处理事件位
- 驱动周期任务
- 执行状态机推进
-
App/app_net.c/.h- 网络初始化
- CH390 事件分发
- 链路保活/重连
-
App/app_uart.c/.h- UART DMA/IDLE 收发整合
- 与网络方向的缓冲协调
-
App/app_time.c/.h- 基于
SysTick的毫秒计时 - 软件超时管理
- 基于
六、裸机下的关键技术决策
6.1 延时与时间基准
建议统一使用:
SysTick 1ms全局时基- 禁止在主业务路径使用长阻塞
HAL_Delay - 超时逻辑统一基于
tick_now - tick_start
示例:
uint32_t app_now_ms(void);
bool app_is_timeout(uint32_t start, uint32_t timeout_ms);
6.2 UART 接收模型
保持 DMA + IDLE 是合理的,原因:
- 可降低 CPU 占用
- 适合串口透传场景
- 便于在裸机下维持较高吞吐
建议 UART RX 流程:
- DMA 持续接收至环形或双缓冲
- IDLE 中断触发“本帧结束”
- ISR 只记录长度与事件位
- 主循环消费数据并决定转发方向
6.3 UART 发送模型
建议继续使用 DMA TX:
- 发送启动在主循环中执行
- DMA 完成中断只清 busy 标志并置
TX_DONE事件 - 发送缓冲统一由应用层管理
6.4 CH390 访问模型
裸机下不再需要 mutex,但仍需保证上下文一致性:
- ISR 内不要执行复杂 SPI 事务
- EXTI 仅置位
EVENT_CH390_INT - 所有 CH390 SPI 读写都在主循环中完成
- 若必须与 DMA 回调共享状态,使用短临界区保护
6.5 网络栈路线
这里需要后续 Agent 决定最终实现路线,建议二选一:
-
lwIP RAW API + NO_SYS=1- 优点:仍保留成熟 TCP/IP 栈
- 缺点:迁移复杂度较高,需要重写当前
socket/netconn依赖
-
基于现有 CH390 资料,评估是否存在更轻的直接 socket/简化协议接口
- 优点:可能进一步减小资源占用
- 缺点:功能边界和维护成本需重新评估
当前更推荐的长期路线是:
lwIP RAW API + NO_SYS=1
因为这条路线与现有 CH390 + 以太网驱动结构更连续。
七、主循环设计建议
建议采用固定骨架:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_IWDG_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_SPI1_Init();
app_time_init();
app_uart_init();
app_net_init();
app_config_init();
while (1)
{
app_poll_events();
app_process_config();
app_process_uart_links();
app_process_network();
app_process_timeouts();
app_feed_watchdog();
}
}
设计原则:
- 所有步骤可重入或幂等
- 每轮主循环不可长时间阻塞
- 网络与串口处理都要支持“分段推进”
八、建议的状态机拆分
8.1 TCP Server 链路
IDLE -> LISTEN -> CONNECTED -> CLOSING -> LISTEN
8.2 TCP Client 链路
IDLE -> RESOLVE/CONFIG -> CONNECTING -> CONNECTED -> RETRY_WAIT -> CONNECTING
8.3 配置口
IDLE -> RX_FRAME_READY -> PARSE -> EXECUTE -> RESPOND -> IDLE
8.4 CH390 网口
RESET -> INIT -> LINK_CHECK -> RUNNING -> ERROR_RECOVER -> INIT
九、内存预算建议
以 STM32F103R8T6 为目标,后续实现应尽量遵循:
9.1 推荐 RAM 预算
| 项目 | 建议值 | 说明 |
|---|---|---|
| 启动栈 | 1 KB | startup 保留 |
| C Heap | 0 | 默认关闭 |
| UART1 RX/TX | 384 B | 配置口 |
| UART2 RX/TX | 1 KB | 透传链路 |
| UART3 RX/TX | 1 KB | 透传链路 |
| 网络收发缓存 | 2-4 KB | 视协议栈路线而定 |
| 参数/状态结构 | < 2 KB | 控制状态 |
9.2 原则
- 避免动态分配
- 优先静态缓冲 + 明确大小
- 单向链路缓冲优先复用
- 所有大缓冲区要在文档中登记
十、当前已完成的工程侧修改
本分支当前已经完成:
R8/xB型号统一- MDK 启动文件切换到
startup_stm32f103xb.s IOC中移除FREERTOS中间件声明PendSV/SVCall不再作为 RTOS 中断入口保留Heap/Stack的工程默认值收缩到更适合R8
十一、后续 Agent 接手时应优先处理的事项
11.1 工程配置层
- 从
MDK-ARM/TCP2UART.uvprojx中移除FreeRTOS源文件组 - 从包含路径中移除
CMSIS_RTOS_V2和FreeRTOS路径 - 视需要移除
Core/Src/freertos.c - 视需要移除
Core/Inc/FreeRTOSConfig.h
11.2 代码层
- 将所有
osThreadNew、vTaskDelay、xStreamBuffer*、xSemaphore*、xMutex*调用替换为裸机实现 - 将
lwIP从NO_SYS=0路线迁移到裸机可用路线 - 重写
CH390事件处理为主循环驱动 - 重写 UART 透传调度逻辑为状态机
11.3 风险点
- 当前
socket/netconn方案不能直接脱离 OS 使用 sys_arch相关移植层最终应被清退- 原来依赖任务切换“自然解耦”的路径,迁移到裸机后必须明确状态和时序边界
十二、交接说明
当前分支适合作为“裸机迁移起点”,但不是最终可编译成品。它的价值在于:
- 目标器件与工程元数据已经统一
CubeMX方向已经从FreeRTOS转向裸机- 技术实现文档已明确后续重构路线
接下来的工作重点应由后续 Agent 在此基线上继续完成逻辑代码迁移。