Files
chrg/docs/修改计划.md
yhf dd66c421dd fix: 修复南向分包、线程通信和批量下发逻辑
1.  apps/chrg/debug_hot.ini: 增加调试断点配置
2.  apps/chrg/applications/thread/chrg_north.h: 开启北向调试日志
3.  修正多个头文件函数返回值类型,从void改为int
4.  重构chrg_thread.c: 新增mb消息释放函数,优化邮箱发送逻辑
5.  重写chrg_south.c: 修复分包越界和内存泄漏,增加帧超时处理
6.  重构chrg_sink.c: 统一批量下发接口,修正初始化和运行逻辑
7.  重写chrg_comm.c: 修复系统寄存器写入逻辑,增加通道激活校验
8.  重构chrg_north.c: 修复北向分包越界和内存泄漏,增加超时处理和诊断命令
9.  重构chrg_roll_sou.c: 重构批量下发逻辑,增加重试机制和错误处理
2026-07-18 18:24:42 +08:00

6.9 KiB
Raw Permalink Blame History

静态排查后,暂未看到明确的 ABBA 型互斥锁环。当前现象更像“内存破坏/资源泄漏后线程停滞”或“状态机无限重试”,而不是整个 RTOS 调度器死锁。

另外,代码中使用的是 STM32 内部 IWDG,不是外部独立看门狗。

可疑点

  1. 高风险:南北向分包接收存在确定的越界写和泄漏路径

northsouth 在续接分包时计算:

len_need = len_msg - 帧头长度

但此时 pFrame->length 已经包含之前收到的数据,正确的剩余长度应基于 len_msg - pFrame->length。当前代码却从 payload[pFrame->length] 开始写入整个“帧头后的总长度”。

例如总长 21 字节,首批已存 8 字节,下一次仍可能从偏移 8 写入 18 字节,最终写到偏移 26,越过 21 字节堆块。这会破坏相邻堆块、邮箱消息甚至 RT-Thread 内核对象,最终表现为随机线程卡死、互斥量异常或堆分配失败。

同时,分包超时路径只清除了 findhead

没有释放 pMB/payload,也没有清除 length/len_msg。而且没有新数据时会提前 continue,超时检查根本不会执行。串口噪声、丢字节或正常分包都可能不断积累泄漏。

此外,payload 分配失败时只返回,没有释放刚分配的 pMB

这是目前最符合“运行时间越长越容易出现”的可疑点。

  1. 高风险:看门狗喂法无法覆盖业务线程死锁

主线程每秒无条件喂狗:main.c:36

北向轮询线程又在每轮无条件喂同一只狗:chrg_roll_nor.c:421

因此即使 thr.commthr.southr.rollsou、LCD 线程全部停止工作,只要主线程或北向轮询线程仍能调度,IWDG 就永远不会复位。这只是在检测“CPU/调度器是否完全停止”,没有检测业务线程是否健康。

驱动最终调用的是 HAL_IWDG_Refresh()drv_wdt.c:40。代码中没有找到外部 WDI 引脚翻转或外部看门狗驱动。

  1. 高风险:当前新增的南向批处理可能永久停在 RUN_COM

南向批处理遇到 CRC 错误或应答超时后,不增加重试次数、不设置截止时间,也不清除任务:

线程会一直重发同一个寄存器,pending 永远不清除,run 永远保持 RUN_COM,正常南向轮询无法恢复。这属于业务状态机死锁,调度器仍正常运行,所以看门狗不会复位。

这套批处理逻辑是当前工作区相对 HEAD 新增的,优先级很高。

  1. 高风险:南向共享状态的互斥保护实际无效

chrg_set_sou_reg 先在锁外写入:

pROLL->ch
pROLL->reg
pROLL->value

随后互斥量只保护 pROLL->run。消费者也只在锁内读取 run,实际使用 ch/reg/value 时已经离开锁。

多个生产者包括 LCD、Modbus 和状态机可能交叉覆盖这些字段,形成“通道来自命令 A、寄存器来自命令 B、值来自命令 C”的组合。

批处理数组 g_sou_com_batch 同样由生产者和 thr.rollsou 同时读写,却没有共同的锁:chrg_roll_sou.c:394

  1. 中高风险:先切换 RUN_COM,再填充批处理数据

chrg_sink_diff_send_reg 先调用 chrg_sou_com_sou_set() 将状态切到 RUN_COM,然后才追加寄存器。

chrg_sink_work() 也是相同顺序:chrg_sink.c:541

如果消费者在状态切换后看到尚未填充的批次,会认为没有任务并切回自动模式;随后加入的 pending 数据可能一直无人处理。当前线程优先级降低了部分窗口概率,但没有从同步机制上消除竞态。

  1. 中高风险:应答邮箱没有请求、通道或序号关联

邮箱消息只有 length + payloadchrg_def.h:95

南北轮询线程默认“收到的下一帧就是刚才请求的响应”。南向批处理只检查 CRC,任何延迟到达的旧响应都可能让当前批次 cur_idx++chrg_roll_sou.c:574

在超时、重发、通道切换或邮箱积压后,请求与响应很容易错位,最终造成状态机停在错误步骤或无限重试。

  1. 中风险:所有业务互斥量都是永久等待

TTY、南北轮询互斥量统一使用 RT_WAITING_FOREVER。例如:

目前没有发现确定的反向加锁环,但一旦锁所有者因堆破坏、状态机卡死或设备写阻塞而不再释放,其他线程会永久挂起,没有超时和故障上报。

  1. 中风险:串口 RingBuffer 写入结果被忽略

chrg_north.c:623chrg_south.c:383 没有检查 rt_ringbuffer_put() 实际写入长度。

缓冲区满时会静默丢字节,随后触发分包超时、泄漏和上述越界路径,是长期运行后出现问题的重要放大器。

优先判断

最可能的两条故障链是:

  1. 串口分包 → 长度计算错误/超时泄漏 → 堆损坏或耗尽 → 某些业务线程永久等待;主线程继续喂狗。
  2. 南向某条命令超时或响应错位 → RUN_COM 无限重发 → 南向业务永久停止;主线程和北向线程继续喂狗。

现场故障尚能进入 Shell 时,优先保存 list_threadlist_mutexlist_mailboxfree 输出,并检查是否持续打印 com batch timeout, resend current reg。这几项可以直接区分锁等待、堆耗尽和状态机无限重试。

未修改任何文件。