# TCP2UART 调试指导 ## 1. 适用范围 本指导面向当前 `TCP2UART` 工程,覆盖以下四类调试场景: 1. `STM32F103RCT6 + CH390D` 的基础 bring-up 2. `SEGGER RTT`、异常陷阱与 FreeRTOS 任务运行状态确认 3. `USART1` 配置口、`USART2/USART3` 数据口与 `MUX / NET / LINK[idx]` 协议联调 4. `TCP Server / TCP Client / UART` 三层数据通路联调与问题隔离 本指导默认基线如下: 1. 当前工程采用 `FreeRTOS` 任务调度架构 2. `CH390` 运行时访问由 `xSpiMutex` 保护,`NetworkTask` 持有主要访问权 3. 调试输出统一使用 `SEGGER RTT` 4. 当前应用层协议模型已经收敛到 `MUX / NET / LINK[idx]` 5. 当前代码应以 `MDK-ARM` 工程构建结果为准 --- ## 2. 当前工程边界与真实状态 在进入现场调试前,先统一以下工程边界: 1. 当前项目的主要软件路径已经切换为: - `NET`:网络基础参数 - `LINK[idx]`:链路配置记录 - `MUX`:数据口承载模式 2. 对外 AT 配置面应只围绕以下命令展开: - `AT` / `AT+?` / `AT+QUERY` - `AT+MUX` / `AT+NET` / `AT+LINK` - `AT+SAVE` / `AT+RESET` / `AT+DEFAULT` 3. 已有结论表明: - MCU 启动、RTT、FreeRTOS 调度、TIM4 心跳路径可工作 - `CH390D` 基础寄存器读写与 `lwIP netif` 基本链路已经打通过一次 - 真实硬件侧曾定位到 `CH390D` 供电滤波电容虚焊问题 4. 当前调试重点是: - FreeRTOS 任务是否正常创建与调度 - `MUX / NET / LINK[idx]` 协议是否与代码一致 - UART / TCP / CH390 三层通路是否协同稳定 - 参数保存、复位和恢复流程是否可靠 --- ## 3. 代码入口与调试责任边界 ### 3.1 启动与 FreeRTOS 入口 以下代码路径是 bring-up 的第一现场: 1. `Core/Src/main.c` - `main()`:总启动入口 - `SystemClock_Config()`:时钟初始化 - `MX_FREERTOS_Init()`:FreeRTOS 任务创建(在 `freertos.c` 中实现) 2. `Core/Src/freertos.c` - `StartDefaultTask()`:默认任务(LED 心跳 + 看门狗) - `MX_FREERTOS_Init()`:用户任务创建入口 3. `Core/Src/stm32f1xx_it.c` - 故障与中断入口 - `TIM4_IRQHandler`:HAL 时间基准 - `USART1/2/3`、`EXTI0`、DMA 回调等联调关键入口 ### 3.2 CH390 责任边界 当前 CH390 调试必须遵守以下责任边界: 1. `Drivers/CH390/CH390_Interface.c`:GPIO / SPI / 寄存器与内存事务 2. `Drivers/CH390/CH390.c`:芯片级 helper 3. `Drivers/CH390/ch390_runtime.c`:唯一的运行时拥有者 4. `Drivers/LwIP/src/netif/ethernetif.c`:netif glue 与轮询桥接 5. SPI 访问由 `xSpiMutex` 保护,避免多任务竞争 ### 3.3 配置口与业务口边界 1. `USART1`:AT 配置口,接收 `AT` 命令 2. `USART2 / USART3`:数据口,普通透传或 MUX 承载 --- ## 4. 当前硬件与调试工具基线 ### 4.1 核心硬件对象 1. MCU:`STM32F103RCT6`(256KB Flash / 48KB SRAM) 2. 以太网芯片:`CH390D` 3. 配置串口:`USART1` 4. 数据串口:`USART2 / USART3` 5. 调试输出:`SEGGER RTT` ### 4.2 构建与下载基线 1. `MDK-ARM/TCP2UART.uvprojx` 2. 启动文件:`startup_stm32f103xe.s` 3. 目标器件:`STM32F103RC` 4. 预处理器宏:`USE_HAL_DRIVER, STM32F103xE` ### 4.3 常用调试工具 1. `Keil MDK-ARM` 2. `ST-Link / J-Link` 3. `SEGGER RTT Viewer` 4. `PowerShell` 5. `tools/start_tcp_debug_server.ps1` 6. `tools/tcp_debug_server.py` --- ## 5. FreeRTOS 专项调试 ### 5.1 任务状态检查 使用 `vTaskList` 获取所有任务运行状态: ```c char buf[512]; vTaskList(buf); SEGGER_RTT_WriteString(0, buf); ``` 输出格式: ```text 任务名 状态 优先级 剩余栈 编号 NetworkTask R 4 120 1 UartTask B 4 200 2 ConfigTask B 3 150 3 RouteTask R 3 180 4 DefaultTask B 1 80 5 IDLE R 0 100 6 Tmr Svc B 2 90 7 ``` 状态码:`R`=Ready, `B`=Blocked, `S`=Suspended, `D`=Deleted, `I`=Invalid ### 5.2 栈溢出检测 已启用 `configCHECK_FOR_STACK_OVERFLOW = 2`,溢出时自动调用: ```c void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { SEGGER_RTT_printf(0, "STACK OVERFLOW: %s\n", pcTaskName); __BKPT(0); } ``` ### 5.3 堆内存失败检测 已启用 `configUSE_MALLOC_FAILED_HOOK`,分配失败时自动调用: ```c void vApplicationMallocFailedHook(void) { SEGGER_RTT_printf(0, "MALLOC FAILED: Free heap = %u\n", xPortGetFreeHeapSize()); __BKPT(0); } ``` ### 5.4 常见 FreeRTOS 调试陷阱 1. **优先级反转**:使用 Mutex(含优先级继承)而非 Binary Semaphore 保护共享资源 2. **死锁**:多 Mutex 场景确保所有任务按相同顺序获取 3. **中断优先级**:FreeRTOS 可管理的 ISR 优先级必须 >= `configMAX_SYSCALL_INTERRUPT_PRIORITY`(本工程 5) 4. **栈不足**:每个任务定期调用 `uxTaskGetStackHighWaterMark(NULL)` 检查剩余栈 5. **禁止在中断中调用阻塞 API**:必须使用 `FromISR` 后缀版本 --- ## 6. 启动阶段调试顺序 建议按 P0 ~ P5 顺序推进,不要跳层。 ### 6.1 P0:确认最小基础条件 1. `MDK-ARM` 可构建并产出新的 `axf/hex/map` 2. 板卡可正常下载与复位 3. RTT 可连接并看到启动输出 4. FreeRTOS 任务创建成功,`DefaultTask` LED 心跳可工作 5. `TIM4` 1ms tick 正常运行 ### 6.2 P1:确认 FreeRTOS 调度正常 上电或复位后,优先确认: 1. `StartDefaultTask` 是否进入运行 2. `vTaskList` 输出是否显示所有预期任务 3. `xPortGetFreeHeapSize()` 返回值是否合理 4. 无 `STACK OVERFLOW` 或 `MALLOC FAILED` 输出 ### 6.3 P2:确认 CH390 初始化链路 启动阶段应重点关注 `NetworkTask` 中初始化日志: 1. `ETH init: gpio` 2. `ETH init: spi` 3. `ETH init: reset` 4. `ETH init: probe` 5. `ETH init: default` 6. `ETH init: mac` 7. `ETH init: done` ### 6.4 P3:确认 TCP 链路 1. lwIP `tcpip_thread` 是否正常运行 2. TCP Server 是否在指定端口监听 3. TCP Client 是否成功连接远端 ### 6.5 P3.5:确认 ARP / ICMP 基础网络可达 在继续 TCP 联调前,建议先把 `ARP + ping(ICMP)` 跑通。对于当前 `CH390 + lwIP + FreeRTOS` 架构,这一步不是可选项,而是 TCP 可达之前必须成立的网络基线。 #### 6.5.1 推荐最小验证顺序 1. 先确认板卡 IP、掩码、MAC 与 PC 所在网段一致 2. 上电后先观察 RTT,确认 `ETH init: done` 已出现 3. 在 PC 侧执行一次 `ping <板卡IP>`,同时开启 Wireshark 抓包 4. 先看是否出现发往板卡 IP 的 ARP request,再看设备是否回 ARP reply 5. ARP 正常后,再看是否出现 ICMP echo request / echo reply 成对出现 #### 6.5.2 当前工程推荐观察点 如果网络基础链路有疑问,建议按以下分层观察,不要只看某一层: 1. **raw RX 层**:CH390 是否确实收到了以太网帧 2. **Ethernet demux 层**:`ethernet_input()` 是否识别到 `ETHTYPE_ARP` / `ETHTYPE_IP` 3. **协议处理层**:`etharp_input()` / `ip_input()` 是否真正进入 4. **协议发包层**:lwIP 是否已经生成待发送的 ARP reply / ICMP reply 5. **驱动发送层**:`low_level_output()` / CH390 TX 是否真正把帧送出 这次 bring-up 证明,`raw RX 正常` 并不等于 `lwIP 已真正处理该帧`。如果只看到底层收到了包,就直接假设协议栈一定会回复,通常会把排查方向带偏。 #### 6.5.3 这次 ARP / ICMP bring-up 的关键结论 本轮调试中,最终根因位于: - `Drivers/LwIP/src/netif/ethernet.c` 问题本质是: 1. `ethernet_input()` 在 `ETHTYPE_ARP` 分支中,曾直接调用 `etharp_input(p, netif)` 2. 但 `etharp_input()` 要求 `p->payload` 从 **ARP 头** 开始,而不是从 Ethernet 头开始 3. 因此如果没有先执行: ```c pbuf_remove_header(p, SIZEOF_ETH_HDR) ``` 则 ARP 包虽然被收到了,但会在 `etharp_input()` 的早期校验中被静默丢弃,最终表现为: 1. Wireshark 能看到 PC 发来的 ARP request 2. 板子侧底层收包计数在增长 3. 但设备始终不回 ARP reply 4. ping 也自然不会成功 当前应保留的正确处理方式如下: ```c case ETHTYPE_ARP: if (netif->flags & NETIF_FLAG_ETHARP) { if (pbuf_remove_header(p, SIZEOF_ETH_HDR)) { pbuf_free(p); return ERR_OK; } etharp_input(p, netif); } else { pbuf_free(p); } return ERR_OK; ``` #### 6.5.4 为什么这类问题容易漏看 这类问题常见但隐蔽,原因通常有三点: 1. 根因非常小,外在表现却像“整个发送链路都坏了” 2. 多个低层信号可能同时正常,容易误导为 SPI / TX / CH390 初始化问题 3. `rx ok`、`ARP 帧计数在涨`、`链路已 up` 都不代表协议层一定接受了该帧 因此,后续遇到“收得到包但就是不回”的问题时,优先检查: 1. 传给上层协议处理函数时,`pbuf->payload` 是否已经对齐到正确协议头 2. glue-layer 是否和 lwIP 原生调用约定一致 3. 观察点是否已经覆盖到 `demux -> protocol handler -> linkoutput` 这一整条链 #### 6.5.5 建议保留的最小验收标准 在认定网络基线“已经打通”之前,至少应满足: 1. Keil 工程可稳定构建通过 2. 上电后可稳定看到网络初始化完成日志 3. Wireshark 中能看到设备对本机 ARP request 做出 reply 4. PC 对设备 `ping` 时,能看到 ICMP echo request / reply 成对出现 5. RTT 中无 `STACK OVERFLOW`、`MALLOC FAILED`、异常 trap 等故障信号 --- ## 7. MUX / NET / LINK[idx] 联调指导 ### 7.1 协议总则 与裸机版本完全一致,参见 `AT固件使用手册.md`。 ### 7.2 推荐最小 MUX 联调顺序 1. 先在 `MUX=0` 下跑通原始透传 2. 再切换 `MUX=1` 3. 先发一个控制帧,确认 `DSTMASK=0x00` 路径可通 4. 再发一个单目标数据帧 5. 最后验证多目标位图转发 --- ## 8. 异常、卡死与假死排查 ### 8.1 看到 `TRAP:` 时怎么做 1. 先记录 RTT 中的 trap 标签 2. 立刻用调试器查看当前 PC / LR / 调用栈 3. 结合 `Core/Src/stm32f1xx_it.c` 中对应 handler 定位异常类型 ### 8.2 FreeRTOS 任务卡死时怎么做 1. 使用 `vTaskList` 检查各任务状态 2. 如果某个任务始终 `B`(Blocked),检查其等待的队列/信号量 3. 检查是否有 Mutex 被持有但从未释放 4. 使用调试器暂停,查看各任务的调用栈 ### 8.3 常见 FreeRTOS 陷阱 1. 在 ISR 中误调用阻塞 API(如 `xQueueSend` 而非 `xQueueSendFromISR`) 2. 中断优先级低于 `configMAX_SYSCALL_INTERRUPT_PRIORITY` 但调用了 FreeRTOS API 3. Mutex 持有期间任务被删除导致 Mutex 永不释放 4. 栈溢出导致邻近变量被破坏 --- ## 9. 常见误区 1. 不要继续沿用"CH390 恒为全 `0xFF`"过时结论 2. 不要在多个任务中直接访问 CH390 SPI(必须通过 Mutex 保护) 3. 不要在没有芯片脚侧证据前,只凭 GPIO 判断总线正常 4. 不要在基础寄存器读写尚不可信时,直接调高层业务 5. 不要在 ISR 中执行复杂 SPI 事务或调用阻塞 API 6. 不要忽视 `configCHECK_FOR_STACK_OVERFLOW` 报告 7. 不要把“底层已经收到 ARP 包”等同于“lwIP 一定已经正确处理 ARP 包” 8. 不要忽略 glue-layer 对 `pbuf->payload` 起始位置的约定,特别是 `Ethernet header -> ARP/IP header` 的切换 9. 不要在 ARP / ICMP 还没闭环前,就直接怀疑 TCP Server / TCP Client 逻辑 10. 不要在没有抓包和分层观测点的情况下,只凭单一日志就断言故障位于 TX 或 SPI 层 --- ## 10. 推荐配套阅读 1. `AT固件使用手册.md` 2. `项目技术实现.md` 3. `项目需求说明.md` 4. `Keil工程配置说明.txt`