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