refactor: 完成R8裸机lwIP移植并更新文档
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
# TCP2UART 项目技术实现(裸机迁移基线)
|
||||
# TCP2UART 项目技术实现
|
||||
|
||||
## 一、目标
|
||||
## 一、当前实现结论
|
||||
|
||||
当前分支 `baremetal-r8` 的目标不是一次性完成全部业务逻辑重写,而是先把工程基线切换到适合 `STM32F103R8T6` 的裸机方向,为后续继续开发提供统一入口。
|
||||
当前工程已经从原先的 `FreeRTOS + lwIP socket/netconn` 方向,重构为适配 `STM32F103R8T6` 的裸机实现。
|
||||
|
||||
本阶段已经完成或约束如下:
|
||||
当前基线特征如下:
|
||||
|
||||
1. MCU 目标统一为 `STM32F103R8T6 / STM32F103xB`
|
||||
2. `CubeMX IOC` 中已移除 `FreeRTOS` 中间件声明
|
||||
3. 工程规划转为裸机轮询 + 中断驱动模型
|
||||
4. 暂不在本阶段重写 TCP/串口业务逻辑
|
||||
5. 保留现有源码作为迁移参考,后续由其他 Agent/开发者继续接力实现
|
||||
1. MCU 目标固定为 `STM32F103R8T6 / STM32F103xB`
|
||||
2. 软件架构改为 `bare-metal main loop + DMA/IDLE + EXTI`
|
||||
3. 网络栈采用 `lwIP RAW API + NO_SYS=1`
|
||||
4. 调试输出采用 `SEGGER RTT`
|
||||
5. 构建目标已通过 `MDK-ARM` 编译,适配 `64KB Flash / 20KB SRAM`
|
||||
|
||||
## 二、硬件与资源约束
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
- `USART1`:配置串口
|
||||
- `USART2`:Server 透传串口
|
||||
- `USART3`:Client 透传串口
|
||||
- `DMA1`:3 路 UART 收发 DMA
|
||||
- `DMA1`:UART 收发 DMA
|
||||
- `EXTI0`:CH390 中断输入
|
||||
- `IWDG`:独立看门狗
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|------|------|------|
|
||||
| PA2 | USART2_TX | Server 透传串口 |
|
||||
| PA3 | USART2_RX | Server 透传串口 |
|
||||
| PA4 | SPI1_NSS | CH390D 片选 |
|
||||
| PA4 | GPIO_Output | CH390D 片选 |
|
||||
| PA5 | SPI1_SCK | CH390D SPI 时钟 |
|
||||
| PA6 | SPI1_MISO | CH390D SPI 数据输入 |
|
||||
| PA7 | SPI1_MOSI | CH390D SPI 数据输出 |
|
||||
@@ -50,326 +50,216 @@
|
||||
| PC13 | GPIO_Output | 状态 LED |
|
||||
| PD0/PD1 | HSE | 8MHz 外部晶振 |
|
||||
|
||||
## 三、为何从 FreeRTOS 迁移到裸机
|
||||
## 三、架构选择原因
|
||||
|
||||
`STM32F103R8T6` 的 RAM 只有 `20KB`。当前工程在引入如下组件后,链接空间明显不足:
|
||||
`STM32F103R8T6` 的资源上限不足以稳定承载原方案中的以下组合:
|
||||
|
||||
1. `FreeRTOS` 内核
|
||||
2. `CMSIS-RTOS V2` 包装层
|
||||
1. `FreeRTOS`
|
||||
2. `CMSIS-RTOS V2`
|
||||
3. 多任务栈
|
||||
4. `lwIP + socket/netconn` OS 抽象层
|
||||
5. 多路 `StreamBuffer / Semaphore / Mutex`
|
||||
4. `lwIP socket/netconn/tcpip` OS 路线
|
||||
5. `StreamBuffer / Semaphore / Mutex`
|
||||
|
||||
此前编译已经证明:
|
||||
|
||||
1. 源码层报错可修复
|
||||
2. 统一 `R8` 型号后仍然在链接阶段失败
|
||||
3. 主要瓶颈是 `RAM + Flash` 同时偏紧
|
||||
|
||||
因此本项目后续建议的主方向为:
|
||||
因此当前实现采用如下组合:
|
||||
|
||||
1. 去掉 `FreeRTOS`
|
||||
2. 去掉 `CMSIS-RTOS V2`
|
||||
3. 避免依赖 `lwIP socket/netconn`
|
||||
4. 采用裸机主循环 + DMA/IDLE/EXTI 中断驱动
|
||||
5. 网络侧改为更贴近资源受限场景的实现模型
|
||||
3. 去掉 `lwIP socket/netconn`
|
||||
4. 改为 `lwIP RAW API + NO_SYS=1`
|
||||
5. 串口与网口统一由主循环推进
|
||||
|
||||
## 四、裸机架构目标
|
||||
## 四、当前软件架构
|
||||
|
||||
### 4.1 总体分层
|
||||
### 4.1 分层
|
||||
|
||||
```text
|
||||
+--------------------------------------------------+
|
||||
| Application State Machine |
|
||||
| config / server link / client link / watchdog |
|
||||
| Application Logic |
|
||||
| config / tcp_server / tcp_client / uart bridge |
|
||||
+--------------------------------------------------+
|
||||
| Transport Scheduler |
|
||||
| main loop polling + event flags + timeout scan |
|
||||
| Main Poll Loop |
|
||||
| ethernetif_poll / sys_check_timeouts / watchdog |
|
||||
+--------------------------------------------------+
|
||||
| Network Interface |
|
||||
| CH390 event polling / packet rx-tx dispatch |
|
||||
| Peripheral/Event Layer |
|
||||
| UART DMA+IDLE / DMA IRQ / EXTI / SysTick |
|
||||
+--------------------------------------------------+
|
||||
| Peripheral Drivers |
|
||||
| UART DMA+IDLE / SPI / GPIO / EXTI / Flash |
|
||||
+--------------------------------------------------+
|
||||
| STM32 HAL / CMSIS |
|
||||
| Drivers |
|
||||
| CH390 / lwIP netif / HAL |
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
### 4.2 执行模型
|
||||
|
||||
不再使用任务调度器,改为以下模型:
|
||||
当前执行模型为:
|
||||
|
||||
1. `ISR` 只做最小事件置位与 DMA 状态更新
|
||||
2. 主循环统一处理事件、超时、状态机推进
|
||||
3. UART RX 继续依赖 `DMA + IDLE`
|
||||
4. UART TX 可保留 `DMA`
|
||||
5. CH390 中断只置位 `netif_pending`
|
||||
6. 网络协议处理在主循环中推进
|
||||
1. `SysTick` 提供全局毫秒时基
|
||||
2. `EXTI0` 只置位 CH390 待处理标志
|
||||
3. `DMA IRQ` 和 `UART IRQ` 只完成回调分发与 IDLE 采样
|
||||
4. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗
|
||||
|
||||
### 4.3 事件源
|
||||
## 五、当前模块实现状态
|
||||
|
||||
建议保留以下裸机事件位:
|
||||
### 5.1 配置模块
|
||||
|
||||
文件:`App/config.c/.h`
|
||||
|
||||
已实现:
|
||||
|
||||
1. 从 Flash 加载配置
|
||||
2. UART1 命令口接收
|
||||
3. 常用网络与串口参数解析
|
||||
4. 参数保存与软复位请求
|
||||
|
||||
当前约束:
|
||||
|
||||
1. 构建已关闭 `DHCP`,因此 `AT+DHCP=1` 会明确返回错误
|
||||
2. 配置损坏时会回退默认值,但默认值不会自动写回 Flash,仍需手动 `AT+SAVE`
|
||||
|
||||
### 5.2 UART 透传模块
|
||||
|
||||
文件:`App/uart_trans.c/.h`
|
||||
|
||||
已实现:
|
||||
|
||||
1. `USART2/USART3` 使用 `DMA + IDLE`
|
||||
2. RX 使用 DMA 缓冲转环形缓冲
|
||||
3. TX 使用 DMA 发送
|
||||
4. TX/RX 完成由 `HAL_UART_*Callback` 驱动
|
||||
|
||||
### 5.3 TCP Server 模块
|
||||
|
||||
文件:`App/tcp_server.c/.h`
|
||||
|
||||
已实现:
|
||||
|
||||
1. `lwIP RAW API` 监听指定端口
|
||||
2. 单连接接入
|
||||
3. 网络数据写入本地环形缓冲
|
||||
4. 主循环中与 UART2 做双向桥接
|
||||
|
||||
### 5.4 TCP Client 模块
|
||||
|
||||
文件:`App/tcp_client.c/.h`
|
||||
|
||||
已实现:
|
||||
|
||||
1. `lwIP RAW API` 主动连接远端地址
|
||||
2. 断链后按周期重连
|
||||
3. 网络数据写入本地环形缓冲
|
||||
4. 主循环中与 UART3 做双向桥接
|
||||
|
||||
### 5.5 CH390 与 netif 模块
|
||||
|
||||
文件:`Drivers/CH390/*`、`Drivers/LwIP/src/netif/*`
|
||||
|
||||
已实现:
|
||||
|
||||
1. `SPI1 + GPIO CS + EXTI0` 驱动 CH390
|
||||
2. `ethernetif.c` 采用 `NO_SYS=1` 路线
|
||||
3. CH390 中断在主循环中轮询处理
|
||||
4. 配置中的 MAC 地址会在初始化时写入 CH390
|
||||
|
||||
### 5.6 RTT 调试输出
|
||||
|
||||
文件:`Middlewares/Third_Party/SEGGER_RTT/*`
|
||||
|
||||
已实现:
|
||||
|
||||
1. 工程内置最小 `SEGGER RTT` 源文件
|
||||
2. `main.c` 中 `printf/fputc` 已重定向到 RTT
|
||||
|
||||
## 六、lwIP 配置策略
|
||||
|
||||
当前 `lwIP` 配置以适配 `R8T6` 资源为原则,核心策略如下:
|
||||
|
||||
1. `NO_SYS = 1`
|
||||
2. `LWIP_SOCKET = 0`
|
||||
3. `LWIP_NETCONN = 0`
|
||||
4. `LWIP_NETIF_API = 0`
|
||||
5. `LWIP_DHCP = 0`
|
||||
6. `LWIP_UDP = 0`
|
||||
7. `LWIP_DNS = 0`
|
||||
8. `LWIP_IGMP = 0`
|
||||
9. 关闭 `lwIP` 平台诊断 `printf`
|
||||
|
||||
同时从 `MDK` 工程中移除了:
|
||||
|
||||
1. `FreeRTOS` 相关源码
|
||||
2. `lwIP api/socket/netconn/tcpip` 路线源码
|
||||
3. `altcp / autoip / acd / dhcp / dns / igmp` 等当前不需要模块
|
||||
|
||||
## 七、主循环实际骨架
|
||||
|
||||
当前主循环逻辑可概括为:
|
||||
|
||||
```c
|
||||
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 依赖:
|
||||
|
||||
1. `App/config.c`
|
||||
2. `App/flash_param.c`
|
||||
3. `App/tcp_server.*`
|
||||
4. `App/tcp_client.*`
|
||||
5. `App/uart_trans.*`
|
||||
6. `Drivers/CH390/*`
|
||||
7. `Drivers/LwIP/*` 或后续替代网络栈
|
||||
|
||||
### 5.2 需要改造的核心模块
|
||||
|
||||
1. `Core/Src/main.c`
|
||||
2. `Core/Src/stm32f1xx_it.c`
|
||||
3. `Core/Src/usart.c`
|
||||
4. `Core/Src/dma.c`
|
||||
5. `Core/Src/freertos.c`:后续应移除出构建或清空为兼容壳
|
||||
6. `Core/Inc/FreeRTOSConfig.h`:后续可移除出工程
|
||||
7. `Drivers/LwIP/port/sys_arch.c`:若完全去 OS,应停止使用该移植层
|
||||
8. `Drivers/LwIP/src/include/arch/sys_arch.h`
|
||||
|
||||
### 5.3 建议新增的裸机模块
|
||||
|
||||
建议后续新增:
|
||||
|
||||
1. `App/app_scheduler.c/.h`
|
||||
- 统一处理事件位
|
||||
- 驱动周期任务
|
||||
- 执行状态机推进
|
||||
|
||||
2. `App/app_net.c/.h`
|
||||
- 网络初始化
|
||||
- CH390 事件分发
|
||||
- 链路保活/重连
|
||||
|
||||
3. `App/app_uart.c/.h`
|
||||
- UART DMA/IDLE 收发整合
|
||||
- 与网络方向的缓冲协调
|
||||
|
||||
4. `App/app_time.c/.h`
|
||||
- 基于 `SysTick` 的毫秒计时
|
||||
- 软件超时管理
|
||||
|
||||
## 六、裸机下的关键技术决策
|
||||
|
||||
### 6.1 延时与时间基准
|
||||
|
||||
建议统一使用:
|
||||
|
||||
1. `SysTick 1ms` 全局时基
|
||||
2. 禁止在主业务路径使用长阻塞 `HAL_Delay`
|
||||
3. 超时逻辑统一基于 `tick_now - tick_start`
|
||||
|
||||
示例:
|
||||
|
||||
```c
|
||||
uint32_t app_now_ms(void);
|
||||
bool app_is_timeout(uint32_t start, uint32_t timeout_ms);
|
||||
```
|
||||
|
||||
### 6.2 UART 接收模型
|
||||
|
||||
保持 `DMA + IDLE` 是合理的,原因:
|
||||
|
||||
1. 可降低 CPU 占用
|
||||
2. 适合串口透传场景
|
||||
3. 便于在裸机下维持较高吞吐
|
||||
|
||||
建议 UART RX 流程:
|
||||
|
||||
1. DMA 持续接收至环形或双缓冲
|
||||
2. IDLE 中断触发“本帧结束”
|
||||
3. ISR 只记录长度与事件位
|
||||
4. 主循环消费数据并决定转发方向
|
||||
|
||||
### 6.3 UART 发送模型
|
||||
|
||||
建议继续使用 `DMA TX`:
|
||||
|
||||
1. 发送启动在主循环中执行
|
||||
2. DMA 完成中断只清 busy 标志并置 `TX_DONE` 事件
|
||||
3. 发送缓冲统一由应用层管理
|
||||
|
||||
### 6.4 CH390 访问模型
|
||||
|
||||
裸机下不再需要 `mutex`,但仍需保证上下文一致性:
|
||||
|
||||
1. ISR 内不要执行复杂 SPI 事务
|
||||
2. EXTI 仅置位 `EVENT_CH390_INT`
|
||||
3. 所有 CH390 SPI 读写都在主循环中完成
|
||||
4. 若必须与 DMA 回调共享状态,使用短临界区保护
|
||||
|
||||
### 6.5 网络栈路线
|
||||
|
||||
这里需要后续 Agent 决定最终实现路线,建议二选一:
|
||||
|
||||
1. `lwIP RAW API + NO_SYS=1`
|
||||
- 优点:仍保留成熟 TCP/IP 栈
|
||||
- 缺点:迁移复杂度较高,需要重写当前 `socket/netconn` 依赖
|
||||
|
||||
2. 基于现有 CH390 资料,评估是否存在更轻的直接 socket/简化协议接口
|
||||
- 优点:可能进一步减小资源占用
|
||||
- 缺点:功能边界和维护成本需重新评估
|
||||
|
||||
当前更推荐的长期路线是:
|
||||
|
||||
`lwIP RAW API + NO_SYS=1`
|
||||
|
||||
因为这条路线与现有 CH390 + 以太网驱动结构更连续。
|
||||
|
||||
## 七、主循环设计建议
|
||||
|
||||
建议采用固定骨架:
|
||||
|
||||
```c
|
||||
int main(void)
|
||||
while (1)
|
||||
{
|
||||
HAL_Init();
|
||||
SystemClock_Config();
|
||||
ethernetif_poll();
|
||||
ethernetif_check_link();
|
||||
sys_check_timeouts();
|
||||
tcp_client_poll();
|
||||
uart_trans_poll();
|
||||
config_poll();
|
||||
|
||||
MX_GPIO_Init();
|
||||
MX_DMA_Init();
|
||||
MX_IWDG_Init();
|
||||
MX_USART1_UART_Init();
|
||||
MX_USART2_UART_Init();
|
||||
MX_USART3_UART_Init();
|
||||
MX_SPI1_Init();
|
||||
tcp_server <-> UART2;
|
||||
tcp_client <-> UART3;
|
||||
|
||||
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();
|
||||
if (reset_requested) {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
}
|
||||
```
|
||||
|
||||
设计原则:
|
||||
## 八、当前构建状态
|
||||
|
||||
1. 所有步骤可重入或幂等
|
||||
2. 每轮主循环不可长时间阻塞
|
||||
3. 网络与串口处理都要支持“分段推进”
|
||||
### 8.1 MDK 构建命令
|
||||
|
||||
## 八、建议的状态机拆分
|
||||
|
||||
### 8.1 TCP Server 链路
|
||||
|
||||
```text
|
||||
IDLE -> LISTEN -> CONNECTED -> CLOSING -> LISTEN
|
||||
```bat
|
||||
"C:\Keil_v5\UV4\UV4.exe" -b "D:\code\STM32Project\TCP2UART\MDK-ARM\TCP2UART.uvprojx" -j0
|
||||
```
|
||||
|
||||
### 8.2 TCP Client 链路
|
||||
### 8.2 当前结果
|
||||
|
||||
```text
|
||||
IDLE -> RESOLVE/CONFIG -> CONNECTING -> CONNECTED -> RETRY_WAIT -> CONNECTING
|
||||
```
|
||||
当前构建结果:
|
||||
|
||||
### 8.3 配置口
|
||||
1. `0 Error(s)`
|
||||
2. `0 Warning(s)`
|
||||
3. `Code=38664`
|
||||
4. `RO-data=1272`
|
||||
5. `RW-data=168`
|
||||
6. `ZI-data=19120`
|
||||
|
||||
```text
|
||||
IDLE -> RX_FRAME_READY -> PARSE -> EXECUTE -> RESPOND -> IDLE
|
||||
```
|
||||
说明当前版本已经满足:
|
||||
|
||||
### 8.4 CH390 网口
|
||||
1. `STM32F103R8T6 64KB Flash` 约束
|
||||
2. `20KB SRAM` 约束
|
||||
3. `MDK-ARM` 可直接编译
|
||||
|
||||
```text
|
||||
RESET -> INIT -> LINK_CHECK -> RUNNING -> ERROR_RECOVER -> INIT
|
||||
```
|
||||
## 九、当前已知限制与待验证项
|
||||
|
||||
## 九、内存预算建议
|
||||
### 9.1 功能限制
|
||||
|
||||
以 `STM32F103R8T6` 为目标,后续实现应尽量遵循:
|
||||
1. 当前使用静态 IP,不支持 DHCP
|
||||
2. 目前未提供上板网络与串口吞吐测试结论
|
||||
3. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受
|
||||
|
||||
### 9.1 推荐 RAM 预算
|
||||
### 9.2 上板验证重点
|
||||
|
||||
| 项目 | 建议值 | 说明 |
|
||||
|------|--------|------|
|
||||
| 启动栈 | 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 | 控制状态 |
|
||||
1. 验证 CH390 INT 极性与 EXTI 触发行为
|
||||
2. 验证 `SPI1` 与 CH390 的稳定性
|
||||
3. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为
|
||||
4. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性
|
||||
5. 验证配置保存、复位、MAC 生效路径
|
||||
|
||||
### 9.2 原则
|
||||
## 十、后续建议
|
||||
|
||||
1. 避免动态分配
|
||||
2. 优先静态缓冲 + 明确大小
|
||||
3. 单向链路缓冲优先复用
|
||||
4. 所有大缓冲区要在文档中登记
|
||||
下一阶段建议按以下顺序推进:
|
||||
|
||||
## 十、当前已完成的工程侧修改
|
||||
|
||||
本分支当前已经完成:
|
||||
|
||||
1. `R8/xB` 型号统一
|
||||
2. MDK 启动文件切换到 `startup_stm32f103xb.s`
|
||||
3. `IOC` 中移除 `FREERTOS` 中间件声明
|
||||
4. `PendSV/SVCall` 不再作为 RTOS 中断入口保留
|
||||
5. `Heap/Stack` 的工程默认值收缩到更适合 `R8`
|
||||
|
||||
## 十一、后续 Agent 接手时应优先处理的事项
|
||||
|
||||
### 11.1 工程配置层
|
||||
|
||||
1. 从 `MDK-ARM/TCP2UART.uvprojx` 中移除 `FreeRTOS` 源文件组
|
||||
2. 从包含路径中移除 `CMSIS_RTOS_V2` 和 `FreeRTOS` 路径
|
||||
3. 视需要移除 `Core/Src/freertos.c`
|
||||
4. 视需要移除 `Core/Inc/FreeRTOSConfig.h`
|
||||
|
||||
### 11.2 代码层
|
||||
|
||||
1. 将所有 `osThreadNew`、`vTaskDelay`、`xStreamBuffer*`、`xSemaphore*`、`xMutex*` 调用替换为裸机实现
|
||||
2. 将 `lwIP` 从 `NO_SYS=0` 路线迁移到裸机可用路线
|
||||
3. 重写 `CH390` 事件处理为主循环驱动
|
||||
4. 重写 UART 透传调度逻辑为状态机
|
||||
|
||||
### 11.3 风险点
|
||||
|
||||
1. 当前 `socket/netconn` 方案不能直接脱离 OS 使用
|
||||
2. `sys_arch` 相关移植层最终应被清退
|
||||
3. 原来依赖任务切换“自然解耦”的路径,迁移到裸机后必须明确状态和时序边界
|
||||
|
||||
## 十二、交接说明
|
||||
|
||||
当前分支适合作为“裸机迁移起点”,但不是最终可编译成品。它的价值在于:
|
||||
|
||||
1. 目标器件与工程元数据已经统一
|
||||
2. `CubeMX` 方向已经从 `FreeRTOS` 转向裸机
|
||||
3. 技术实现文档已明确后续重构路线
|
||||
|
||||
接下来的工作重点应由后续 Agent 在此基线上继续完成逻辑代码迁移。
|
||||
1. 上板联调 CH390 链路与 RTT 输出
|
||||
2. 验证 UART2/3 透传功能
|
||||
3. 补充双向透传稳定性与丢包测试
|
||||
4. 视需要继续优化 `config.c` 的体积与命令集
|
||||
5. 若后续必须支持 DHCP,再单独评估资源预算
|
||||
|
||||
Reference in New Issue
Block a user