diff --git a/docs/0.PD31双向电源协议-和上位机通讯_解读版本V6.15.xlsx b/docs/0.PD31双向电源协议-和上位机通讯_解读版本V6.15.xlsx deleted file mode 100644 index 88feac3..0000000 Binary files a/docs/0.PD31双向电源协议-和上位机通讯_解读版本V6.15.xlsx and /dev/null differ diff --git a/docs/1.双向电源sink板协议解读-20250807.docx b/docs/1.双向电源sink板协议解读-20250807.docx deleted file mode 100644 index 8c21200..0000000 Binary files a/docs/1.双向电源sink板协议解读-20250807.docx and /dev/null differ diff --git a/docs/2.双向电源source板协议解读-20250807.docx b/docs/2.双向电源source板协议解读-20250807.docx deleted file mode 100644 index e868be3..0000000 Binary files a/docs/2.双向电源source板协议解读-20250807.docx and /dev/null differ diff --git a/docs/20250810092400.jpg b/docs/20250810092400.jpg deleted file mode 100644 index c597aa2..0000000 Binary files a/docs/20250810092400.jpg and /dev/null differ diff --git a/docs/20250810093635.jpg b/docs/20250810093635.jpg deleted file mode 100644 index d9e4dde..0000000 Binary files a/docs/20250810093635.jpg and /dev/null differ diff --git a/docs/2026-07-17-apps-chrg-deadlock-remediation.md b/docs/2026-07-17-apps-chrg-deadlock-remediation.md deleted file mode 100644 index e7fa109..0000000 --- a/docs/2026-07-17-apps-chrg-deadlock-remediation.md +++ /dev/null @@ -1,1186 +0,0 @@ -# apps/chrg 长期运行锁死修复 Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 消除 `apps/chrg` 长期运行后的帧组装越界、内存泄漏、南向状态机永久重试和共享状态竞态,并让内部 IWDG 只在全部关键业务线程健康时喂狗。 - -**Architecture:** 先用可观测指标固定故障证据,再将串口分帧、南向命令队列、请求应答关联和线程健康监控拆成边界清晰的模块。业务线程不再通过共享单槽状态传递命令;所有事务必须有队列、截止时间、重试上限和响应匹配,最终由主线程根据六个关键线程的心跳集中决定是否刷新 IWDG。 - -**Tech Stack:** STM32F405RG、RT-Thread 5.2.2、C99、SCons/Keil MDK、RT-Thread UTest、FinSH/MSH、FAL、STM32 HAL IWDG。 - ---- - -## 1. 执行前提 - -当前工作区相对 `HEAD` 存在大量实质性未提交改动,`git diff --ignore-space-at-eol --stat -- apps/chrg` 显示约 5752 行新增、812 行删除。南向批处理逻辑和北向线程第二个喂狗点均位于这些当前改动中。 - -实施本计划前必须满足: - -- 当前工作区由代码所有者创建可恢复的检查点;执行者不得自行回退、暂存或提交不属于本计划的改动。 -- 在包含当前工作区业务改动的基线上实施,不能直接从 `HEAD` 重新开始,否则会遗漏正在现场运行的逻辑。 -- 每次提交只暂存本任务明确列出的文件,禁止使用 `git add -A`、`git add .` 或全仓格式化。 -- 保持现有文件换行格式,不进行 LF/CRLF 批量转换。 -- 必须在具备 SCons + Arm GCC 或 Keil MDK 的构建机上执行编译;当前 Codex 环境没有 `scons` 和 `arm-none-eabi-gcc`。 - -建议的执行顺序严格如下: - -1. 保存故障现场和构建基线。 -2. 建立测试入口。 -3. 修复帧组装内存安全问题。 -4. 重构南向命令队列和有限重试。 -5. 增加请求应答关联。 -6. 收紧互斥量范围和线程初始化。 -7. 引入线程健康监控并调整喂狗。 -8. 完成故障注入和 72 小时稳定性验证。 - -## 2. 文件规划 - -### 新建文件 - -| 文件 | 职责 | -| --- | --- | -| `apps/chrg/applications/Kconfig` | 项目诊断、测试和故障注入配置 | -| `apps/chrg/applications/utils/chrg_frame.h` | 通用分帧状态和所有权接口 | -| `apps/chrg/applications/utils/chrg_frame.c` | 分帧追加、超时、释放和消息移交实现 | -| `apps/chrg/applications/thread/chrg_sou_queue.h` | 南向固定长度命令队列、批次和事务定义 | -| `apps/chrg/applications/thread/chrg_sou_queue.c` | 原子批量入队、出队、批次取消和队列统计 | -| `apps/chrg/applications/utils/chrg_health.h` | 关键线程心跳、错误计数和喂狗判定接口 | -| `apps/chrg/applications/utils/chrg_health.c` | 心跳状态、MSH 健康快照和故障注入实现 | -| `apps/chrg/applications/tests/SConscript` | 仅测试配置下编译 UTest 用例 | -| `apps/chrg/applications/tests/test_chrg_frame.c` | 分包、超时、越界和所有权测试 | -| `apps/chrg/applications/tests/test_chrg_sou_queue.c` | 队列容量、批次原子性、重试和取消测试 | -| `apps/chrg/applications/tests/test_chrg_health.c` | 心跳时限、启动宽限和喂狗判定测试 | -| `docs/debug/apps-chrg-soak-test.md` | 板上故障注入、长期运行和验收记录模板 | - -### 修改文件 - -| 文件 | 修改内容 | -| --- | --- | -| `apps/chrg/Kconfig` | 引入 `applications/Kconfig` | -| `apps/chrg/applications/utils/chrg_def.h` | 邮箱消息增加来源、通道、接收时刻和事务字段 | -| `apps/chrg/applications/thread/chrg_north.c` | 接入安全分帧、检查 RingBuffer 写入、记录 RX 心跳 | -| `apps/chrg/applications/thread/chrg_south.c` | 接入安全分帧、检查 RingBuffer 写入、记录 RX 心跳 | -| `apps/chrg/applications/thread/chrg_roll_nor.c` | 响应关联、缩短锁范围、移除直接喂狗、记录心跳 | -| `apps/chrg/applications/thread/chrg_roll_sou.c` | 使用命令队列、有限重试、响应匹配和自动轮询恢复 | -| `apps/chrg/applications/thread/chrg_roll_sou.h` | 删除共享批处理数组和单槽命令字段,导出提交接口 | -| `apps/chrg/applications/thread/chrg_comm.c` | 构造原子南向批次、检查提交结果、记录线程心跳 | -| `apps/chrg/applications/thread/chrg_lcd.c` | 使用队列提交命令、处理队列满和记录线程心跳 | -| `apps/chrg/applications/utils/chrg_sink.c` | 将分散的 `batch_add` 调用改为一次性批次提交 | -| `apps/chrg/applications/utils/chrg_eload.c` | 提供南向响应匹配和命令编码接口 | -| `apps/chrg/applications/bsp/chrg_tty.c` | 有界获取 TTY 锁、消除事件丢失窗口、返回明确错误 | -| `apps/chrg/applications/bsp/chrg_tty.h` | 锁超时常量和错误语义 | -| `apps/chrg/applications/thread/chrg_thread.c` | 两阶段创建/启动线程、失败回滚和邮箱所有权修正 | -| `apps/chrg/applications/thread/chrg_thread.h` | 线程准备/启动接口和健康标识 | -| `apps/chrg/applications/bsp/chrg_wdt.c` | 记录启动状态、返回喂狗错误、输出复位原因 | -| `apps/chrg/applications/bsp/chrg_wdt.h` | `chrg_wdt_feed()` 改为有返回值并增加 ready 查询 | -| `apps/chrg/applications/main.c` | 集中健康检查和条件喂狗 | -| `apps/chrg/defconfig` | 保留生产健康监控,默认关闭 UTest 和故障注入 | -| `README.md` | 补充线程健康、恢复策略和稳定性验证方法 | - -## 3. 验收标准 - -全部完成必须同时满足: - -- 分帧数据分 1、2、3、N 次到达时,不越界、不泄漏、不重复等待已收齐的数据。 -- 丢半帧后即使不再收到新数据,100 ms 超时仍能释放该帧全部资源。 -- RingBuffer 写不完整时有计数、丢弃当前帧并重新同步,不把截断数据继续交给解析器。 -- 南向单条和批量命令由固定队列传递,多生产者不能交叉覆盖 `ch/reg/value`。 -- 南向事务最多重试 3 次;失败后取消当前批次、记录错误并恢复自动轮询。 -- 延迟旧响应不能推进当前事务;写响应必须匹配地址、功能码、寄存器和值。 -- 业务互斥量不再永久等待;任何获取失败必须返回错误并计数。 -- IWDG 仅在全部六个关键业务线程心跳新鲜且初始化成功时刷新。 -- 暂停任一关键线程后,在最后一次喂狗约 3 秒后发生 IWDG 复位,并在重启日志中看到 IWDG reset cause。 -- 连续 72 小时压力运行中,空闲堆最低值稳定、队列无永久 pending、线程栈余量不持续下降、无异常复位。 -- 主应用和 Bootloader 的链接地址与 Flash 分区不发生变化。 - ---- - -### Task 1: 固定当前基线和现场证据 - -**Files:** -- Create during execution: `docs/debug/apps-chrg-baseline.md` -- Do not modify application source in this task. - -- [ ] **Step 1: 记录工作区基线** - -Run: - -```powershell -git status --short --branch -git diff --ignore-space-at-eol --stat -- apps/chrg -git log -5 --oneline --decorate -``` - -Expected: 输出被保存到 `docs/debug/apps-chrg-baseline.md`,并明确当前业务改动属于哪个检查点。不得执行 reset、checkout 或自动清理。 - -- [ ] **Step 2: 完成修复前构建** - -Run: - -```powershell -scons -C apps/chrg -j4 -``` - -Expected: `chrg.elf` 和 `rtthread.bin` 生成成功。记录 text/data/bss、镜像大小和所有 warning;若失败,先修复构建环境,不开始功能修改。 - -- [ ] **Step 3: 保存故障现场快照** - -在问题出现但 Shell 尚可用时依次执行: - -```text -list_thread -list_mutex -list_mailbox -list_event -free -list_device -``` - -Expected: 记录每个线程状态、栈最大使用、互斥量 owner/suspend、邮箱 entry 数和 heap total/used/max-used。 - -- [ ] **Step 4: 记录故障特征** - -在基线文档中固定以下字段: - -```markdown -- 首次异常运行时长: -- 异常时仍工作的接口:USB Shell / Modbus / LCD / North / South -- 最后 100 行日志: -- `com batch timeout` 是否连续出现:是/否 -- `free` 的 total/used/max-used: -- `thr.rollnor` 和 `thr.rollsou` 状态: -- `thr.nor` 和 `thr.sou` 状态: -- 最近一次复位原因:POR / PIN / SOFTWARE / IWDG / UNKNOWN -``` - -Expected: 不留空字段;无法取得的数据填写 `无法取得:<原因>`。 - -- [ ] **Step 5: 提交仅包含基线文档的检查点** - -```powershell -git add docs/debug/apps-chrg-baseline.md -git commit -m "docs: capture chrg long-run failure baseline" -``` - -Expected: 提交中只有基线文档。 - ---- - -### Task 2: 建立项目级 UTest 测试入口 - -**Files:** -- Create: `apps/chrg/applications/Kconfig` -- Create: `apps/chrg/applications/tests/SConscript` -- Modify: `apps/chrg/Kconfig` -- Modify: `apps/chrg/defconfig` - -- [ ] **Step 1: 在项目 Kconfig 中引入应用配置** - -在 `apps/chrg/Kconfig` 的 RT-Thread 与 board 配置之后加入: - -```kconfig -rsource "applications/Kconfig" -``` - -- [ ] **Step 2: 定义测试和故障注入选项** - -创建 `apps/chrg/applications/Kconfig`: - -```kconfig -menu "Charger application options" - -config CHRG_USING_HEALTH_MONITOR - bool "Enable charger thread health monitor" - default y - -config CHRG_USING_SELFTEST - bool "Build charger UTest cases" - select RT_USING_UTEST - default n - -config CHRG_USING_FAULT_INJECTION - bool "Enable charger fault injection commands" - depends on CHRG_USING_HEALTH_MONITOR - default n - -endmenu -``` - -- [ ] **Step 3: 创建测试构建入口** - -创建 `apps/chrg/applications/tests/SConscript`: - -```python -from building import * - -cwd = GetCurrentDir() -src = Glob('*.c') -group = DefineGroup( - 'Applications/tests', - src, - depend=['CHRG_USING_SELFTEST'], - CPPPATH=[cwd] -) - -Return('group') -``` - -- [ ] **Step 4: 保持生产配置关闭测试功能** - -在 `apps/chrg/defconfig` 中加入: - -```text -CONFIG_CHRG_USING_HEALTH_MONITOR=y -# CONFIG_CHRG_USING_SELFTEST is not set -# CONFIG_CHRG_USING_FAULT_INJECTION is not set -``` - -- [ ] **Step 5: 生成测试配置并构建** - -在测试构建配置中启用 `CHRG_USING_SELFTEST`,然后运行: - -```powershell -scons -C apps/chrg --menuconfig -scons -C apps/chrg -j4 -``` - -Expected: `rtconfig.h` 中存在 `RT_USING_UTEST` 和 `CHRG_USING_SELFTEST`;主应用构建成功。 - -- [ ] **Step 6: 提交测试入口** - -```powershell -git add apps/chrg/Kconfig apps/chrg/applications/Kconfig apps/chrg/applications/tests/SConscript apps/chrg/defconfig -git commit -m "test: add chrg application utest configuration" -``` - ---- - -### Task 3: 用测试锁定分帧越界和泄漏 - -**Files:** -- Create: `apps/chrg/applications/utils/chrg_frame.h` -- Create: `apps/chrg/applications/utils/chrg_frame.c` -- Create: `apps/chrg/applications/tests/test_chrg_frame.c` -- Modify: `apps/chrg/applications/utils/chrg_def.h` - -- [ ] **Step 1: 定义分帧所有权接口** - -`chrg_frame.h` 必须导出以下完整接口: - -```c -#ifndef __CHRG_FRAME_H__ -#define __CHRG_FRAME_H__ - -#include -#include -#include "chrg_def.h" - -enum chrg_frame_status { - CHRG_FRAME_ERROR = -1, - CHRG_FRAME_WAIT = 0, - CHRG_FRAME_COMPLETE = 1, -}; - -int chrg_frame_begin(struct pickup_info_t *frame, - const rt_uint8_t *head, - rt_size_t head_len, - rt_size_t frame_len, - rt_uint8_t origin, - rt_uint8_t channel); -int chrg_frame_append(struct pickup_info_t *frame, - struct rt_ringbuffer *rb); -rt_bool_t chrg_frame_expired(const struct pickup_info_t *frame, - rt_tick_t now, - rt_tick_t timeout); -struct mb_msg_t *chrg_frame_take(struct pickup_info_t *frame); -void chrg_frame_abort(struct pickup_info_t *frame); - -#endif -``` - -扩展 `struct mb_msg_t`: - -```c -struct mb_msg_t { - rt_uint16_t length; - rt_uint8_t *payload; - rt_uint8_t origin; - rt_uint8_t channel; - rt_uint32_t transaction; - rt_tick_t rx_tick; -}; -``` - -- [ ] **Step 2: 先写失败用例:三段分包不能越界** - -`test_chrg_frame.c` 至少包含: - -```c -static void test_frame_three_chunks(void) -{ - struct pickup_info_t frame = {0}; - struct rt_ringbuffer rb; - rt_uint8_t rb_mem[32]; - const rt_uint8_t head[3] = {21, 0, 1}; - const rt_uint8_t body_a[5] = {1, 2, 3, 4, 5}; - const rt_uint8_t body_b[7] = {6, 7, 8, 9, 10, 11, 12}; - const rt_uint8_t body_c[6] = {13, 14, 15, 16, 17, 18}; - - rt_ringbuffer_init(&rb, rb_mem, sizeof(rb_mem)); - uassert_int_equal(chrg_frame_begin(&frame, head, 3, 21, 1, 0), RT_EOK); - - rt_ringbuffer_put(&rb, body_a, sizeof(body_a)); - uassert_int_equal(chrg_frame_append(&frame, &rb), CHRG_FRAME_WAIT); - uassert_int_equal(frame.length, 8); - - rt_ringbuffer_put(&rb, body_b, sizeof(body_b)); - uassert_int_equal(chrg_frame_append(&frame, &rb), CHRG_FRAME_WAIT); - uassert_int_equal(frame.length, 15); - - rt_ringbuffer_put(&rb, body_c, sizeof(body_c)); - uassert_int_equal(chrg_frame_append(&frame, &rb), CHRG_FRAME_COMPLETE); - uassert_int_equal(frame.length, 21); - - chrg_frame_abort(&frame); -} -``` - -- [ ] **Step 3: 增加所有权和超时失败用例** - -必须覆盖: - -```c -static void test_frame_timeout_wrap_safe(void); -static void test_frame_abort_frees_all_state(void); -static void test_frame_take_transfers_message_ownership(void); -static void test_frame_append_never_exceeds_frame_length(void); -static void test_frame_rejects_header_longer_than_frame(void); -static void test_frame_rejects_frame_larger_than_size_buf_tty(void); -``` - -所有用例通过以下入口导出: - -```c -static void test_frame_all(void) -{ - UTEST_UNIT_RUN(test_frame_three_chunks); - UTEST_UNIT_RUN(test_frame_timeout_wrap_safe); - UTEST_UNIT_RUN(test_frame_abort_frees_all_state); - UTEST_UNIT_RUN(test_frame_take_transfers_message_ownership); - UTEST_UNIT_RUN(test_frame_append_never_exceeds_frame_length); - UTEST_UNIT_RUN(test_frame_rejects_header_longer_than_frame); - UTEST_UNIT_RUN(test_frame_rejects_frame_larger_than_size_buf_tty); -} - -UTEST_TC_EXPORT(test_frame_all, "chrg.frame", RT_NULL, RT_NULL, 1000); -``` - -- [ ] **Step 4: 运行测试确认当前实现失败** - -Run on target: - -```text -utest_run chrg.frame -``` - -Expected: 因接口尚无实现或三段分包长度错误而 FAIL。保存失败输出。 - -- [ ] **Step 5: 实现严格的剩余长度和所有权规则** - -`chrg_frame_append()` 的核心必须是: - -```c -int chrg_frame_append(struct pickup_info_t *frame, - struct rt_ringbuffer *rb) -{ - rt_size_t remaining; - rt_size_t available; - rt_size_t copied; - - if (frame == RT_NULL || rb == RT_NULL || frame->pMB == RT_NULL || - frame->pMB->payload == RT_NULL || frame->length > frame->len_msg) { - return CHRG_FRAME_ERROR; - } - - remaining = (rt_size_t)frame->len_msg - frame->length; - if (remaining == 0) { - return CHRG_FRAME_COMPLETE; - } - - available = rt_ringbuffer_data_len(rb); - copied = available < remaining ? available : remaining; - if (copied > 0) { - copied = rt_ringbuffer_get(rb, - &frame->pMB->payload[frame->length], copied); - frame->length += copied; - frame->last_tick = rt_tick_get(); - } - - return frame->length == frame->len_msg - ? CHRG_FRAME_COMPLETE - : CHRG_FRAME_WAIT; -} -``` - -`chrg_frame_expired()` 必须使用无符号差值,不能使用 `now >= last + timeout`: - -```c -return frame->findhead && - (rt_tick_t)(now - frame->last_tick) >= timeout; -``` - -`chrg_frame_abort()` 必须释放 payload、消息对象并清空 `length/findhead/len_msg/pMB/last_tick`。`chrg_frame_take()` 必须只转移消息指针,随后清空 frame 状态但不能释放消息。 - -- [ ] **Step 6: 运行测试确认全部通过** - -```text -utest_run chrg.frame -``` - -Expected: 7 个分帧用例全部 PASS,测试前后 `free` 的 used/max-used 没有持续增长。 - -- [ ] **Step 7: 提交分帧核心和测试** - -```powershell -git add apps/chrg/applications/utils/chrg_frame.h apps/chrg/applications/utils/chrg_frame.c apps/chrg/applications/utils/chrg_def.h apps/chrg/applications/tests/test_chrg_frame.c -git commit -m "fix: make chrg frame assembly memory safe" -``` - ---- - -### Task 4: 将安全分帧接入 North/South 接收线程 - -**Files:** -- Modify: `apps/chrg/applications/thread/chrg_north.c` -- Modify: `apps/chrg/applications/thread/chrg_south.c` -- Modify: `apps/chrg/applications/thread/chrg_thread.c` -- Modify: `apps/chrg/applications/thread/chrg_thread.h` - -- [ ] **Step 1: 在无新数据时也执行分帧超时** - -North 和 South 循环必须先检查: - -```c -if (chrg_frame_expired(&ctx->frame, rt_tick_get(), - rt_tick_from_millisecond(100))) { - chrg_frame_abort(&ctx->frame); - LOG_W("partial frame timeout"); -} -``` - -该检查必须位于 `len <= 0` 的 `continue` 之前。 - -- [ ] **Step 2: 检查 RingBuffer 写入结果** - -替换忽略返回值的 `rt_ringbuffer_put()`: - -```c -written = rt_ringbuffer_put(ctx->rb, ctx->rx_buf, len); -if (written != (rt_size_t)len) { - rt_ringbuffer_reset(ctx->rb); - chrg_frame_abort(&ctx->frame); - LOG_W("receive ringbuffer overflow: %d/%d", written, len); - continue; -} -``` - -- [ ] **Step 3: 用 begin/append/take 替换重复分包逻辑** - -完成帧头识别后调用 `chrg_frame_begin()`;每轮调用 `chrg_frame_append()`;返回 COMPLETE 时调用: - -```c -struct mb_msg_t *msg = chrg_frame_take(&ctx->frame); -if (msg != RT_NULL) { - chrg_thread_mb_send_owned(target, msg); -} -``` - -禁止在 North/South 中直接计算 `len_msg - HEAD_SIZE` 后向 payload 续写。 - -- [ ] **Step 4: 明确邮箱消息所有权** - -新增: - -```c -rt_err_t chrg_thread_mb_send_owned(eIDX_THRS idx, struct mb_msg_t *msg); -``` - -语义固定为:无论发送成功、邮箱为空、邮箱满或其他错误,调用结束后调用者都不再拥有 `msg`。发送失败时函数负责释放 payload 和 msg;发送成功时接收方负责释放。 - -- [ ] **Step 5: 增加分包板上测试** - -通过 UART 测试工具发送: - -1. 完整帧一次发送。 -2. 头 3 字节 + 剩余数据两段发送。 -3. 每字节间隔 1 ms。 -4. 发送半帧后静默 500 ms。 -5. 连续发送 10000 个 CRC 错误帧和截断帧。 - -Expected: 接收线程持续运行;`free` 的 used 值回到稳定区间;frame timeout/overflow 计数按注入增长;无 HardFault 和互斥量异常。 - -- [ ] **Step 6: 构建和提交** - -```powershell -scons -C apps/chrg -j4 -git diff --check -- apps/chrg/applications/thread/chrg_north.c apps/chrg/applications/thread/chrg_south.c apps/chrg/applications/thread/chrg_thread.c apps/chrg/applications/thread/chrg_thread.h -git add apps/chrg/applications/thread/chrg_north.c apps/chrg/applications/thread/chrg_south.c apps/chrg/applications/thread/chrg_thread.c apps/chrg/applications/thread/chrg_thread.h -git commit -m "fix: harden north and south frame receive paths" -``` - ---- - -### Task 5: 用固定队列替换南向共享单槽和无锁批处理 - -**Files:** -- Create: `apps/chrg/applications/thread/chrg_sou_queue.h` -- Create: `apps/chrg/applications/thread/chrg_sou_queue.c` -- Create: `apps/chrg/applications/tests/test_chrg_sou_queue.c` -- Modify: `apps/chrg/applications/thread/chrg_roll_sou.h` - -- [ ] **Step 1: 定义命令和批次模型** - -```c -#define CHRG_SOU_QUEUE_DEPTH 32 -#define CHRG_SOU_RETRY_MAX 3 - -enum chrg_sou_cmd_source { - CHRG_SOU_CMD_AUTO, - CHRG_SOU_CMD_LCD, - CHRG_SOU_CMD_MODBUS, - CHRG_SOU_CMD_INTERNAL, -}; - -struct chrg_sou_cmd { - rt_uint32_t sequence; - rt_uint32_t batch; - rt_uint8_t source; - rt_uint8_t channel; - rt_uint8_t function; - rt_uint8_t attempt; - rt_uint16_t reg; - rt_uint16_t value; - rt_bool_t batch_last; -}; - -struct chrg_sou_queue { - struct chrg_sou_cmd items[CHRG_SOU_QUEUE_DEPTH]; - rt_uint8_t head; - rt_uint8_t tail; - rt_uint8_t count; - rt_uint32_t next_sequence; - rt_uint32_t next_batch; - rt_mutex_t lock; -}; -``` - -导出 `init`、`push`、`push_batch`、`pop`、`drop_batch`、`count`。`push_batch` 必须先检查剩余槽位,空间不足时整批拒绝,不允许部分入队。 - -- [ ] **Step 2: 先写失败的队列测试** - -覆盖: - -```c -static void test_queue_fifo(void); -static void test_queue_batch_is_atomic(void); -static void test_queue_full_rejects_without_overwrite(void); -static void test_queue_drop_batch_keeps_other_batches(void); -static void test_queue_sequence_is_monotonic(void); -static void test_queue_concurrent_producers(void); -``` - -导出: - -```c -UTEST_TC_EXPORT(test_sou_queue_all, "chrg.sou_queue", RT_NULL, RT_NULL, 2000); -``` - -- [ ] **Step 3: 运行测试确认失败** - -```text -utest_run chrg.sou_queue -``` - -Expected: 队列接口尚未实现导致 FAIL。 - -- [ ] **Step 4: 实现固定队列** - -所有读写必须在队列自身 mutex 内完成,mutex 获取超时固定为 100 ms。所有接口返回 `RT_EOK`、`-RT_EFULL`、`-RT_ETIMEOUT` 或 `-RT_EINVAL`,调用者必须处理返回值。 - -禁止: - -- 在锁外修改 head/tail/count。 -- 通过全局 `pROLL->ch/reg/value` 传递命令。 -- 队列满时覆盖旧命令。 -- 为每条命令动态分配内存。 - -- [ ] **Step 5: 运行测试确认通过** - -```text -utest_run chrg.sou_queue -``` - -Expected: 6 个队列用例全部 PASS,队列 count 始终在 0..32 范围内。 - -- [ ] **Step 6: 提交队列模块** - -```powershell -git add apps/chrg/applications/thread/chrg_sou_queue.h apps/chrg/applications/thread/chrg_sou_queue.c apps/chrg/applications/thread/chrg_roll_sou.h apps/chrg/applications/tests/test_chrg_sou_queue.c -git commit -m "feat: add bounded south command queue" -``` - ---- - -### Task 6: 将所有南向命令生产者迁移到队列 - -**Files:** -- Modify: `apps/chrg/applications/thread/chrg_roll_sou.c` -- Modify: `apps/chrg/applications/thread/chrg_roll_sou.h` -- Modify: `apps/chrg/applications/thread/chrg_comm.c` -- Modify: `apps/chrg/applications/thread/chrg_lcd.c` -- Modify: `apps/chrg/applications/utils/chrg_sink.c` - -- [ ] **Step 1: 用返回错误的提交接口替换 void 接口** - -导出: - -```c -int chrg_sou_submit(const struct chrg_sou_cmd *cmd); -int chrg_sou_submit_batch(const struct chrg_sou_cmd *cmds, - rt_size_t count); -``` - -删除 `g_sou_com_batch`、`chrg_sou_com_batch_add_reg()`、`chrg_sou_com_sou_set()` 和 `chrg_set_sou_reg_com()`。`chrg_set_sou_reg()` 改为构造单条命令并返回提交结果。 - -- [ ] **Step 2: 将 Sink 状态切换改为一次原子批次** - -`chrg_sink_work_init()`、`chrg_sink_change_ON()`、`chrg_sink_SHOUT_ON()` 等函数先在栈上构造完整命令数组,再一次调用 `chrg_sou_submit_batch()`。 - -只有提交成功后才更新 `last_state[idx]`: - -```c -ret = chrg_sou_submit_batch(cmds, cmd_count); -if (ret == RT_EOK) { - last_state[idx] = work_mode; -} else { - LOG_E("south batch enqueue failed: %d", ret); -} -``` - -- [ ] **Step 3: 修正 `last_state` 初始化** - -不能使用 `{100}` 期望四个元素都为 100。显式初始化: - -```c -static rt_uint8_t last_state[TOTAL_SOU_CHS] = { - 100, 100, 100, 100 -}; -``` - -- [ ] **Step 4: 迁移 LCD 和 Modbus 调用者** - -每个调用点必须检查返回值: - -```c -ret = chrg_set_sou_reg(channel, reg, value); -if (ret != RT_EOK) { - LOG_E("south command enqueue failed: %d", ret); - return ret; -} -``` - -删除 LCD 中连续 10 次覆盖同一单槽命令的循环,单条设置只入队一次。 - -- [ ] **Step 5: 删除 `RUN_COM` 对共享批处理数组的依赖** - -正常模式下 `thr.rollsou` 每轮先非阻塞检查命令队列:有命令则执行一个事务,无命令才执行自动轮询。`run` 仅保留升级/校准等互斥模式,不再代表单条命令是否存在。 - -- [ ] **Step 6: 构建并验证多生产者顺序** - -同时从 LCD 和 Modbus 连续提交不同通道的 1000 条命令。 - -Expected: 执行顺序与 sequence 一致;`ch/reg/value` 不交叉;queue full 返回错误但不破坏已有命令;自动轮询在队列清空后恢复。 - -- [ ] **Step 7: 提交生产者迁移** - -```powershell -git add apps/chrg/applications/thread/chrg_roll_sou.c apps/chrg/applications/thread/chrg_roll_sou.h apps/chrg/applications/thread/chrg_comm.c apps/chrg/applications/thread/chrg_lcd.c apps/chrg/applications/utils/chrg_sink.c -git commit -m "refactor: serialize south commands through bounded queue" -``` - ---- - -### Task 7: 增加南向请求应答匹配和有限重试 - -**Files:** -- Modify: `apps/chrg/applications/utils/chrg_eload.c` -- Modify: `apps/chrg/applications/utils/chrg_eload.h` -- Modify: `apps/chrg/applications/thread/chrg_roll_sou.c` -- Modify: `apps/chrg/applications/tests/test_chrg_sou_queue.c` - -- [ ] **Step 1: 定义响应匹配接口** - -```c -rt_bool_t chrg_eload_response_matches( - const struct chrg_sou_cmd *cmd, - const rt_uint8_t *frame, - rt_size_t length); -``` - -匹配规则: - -- 地址必须等于 `ID_ELOAD`。 -- CRC 必须通过。 -- 写单寄存器 `0x06` 必须回显相同寄存器和值。 -- 读寄存器必须匹配功能码和预期字节数。 -- Modbus 异常响应必须作为事务失败,不得推进批次。 - -- [ ] **Step 2: 增加延迟旧响应测试** - -测试必须证明:事务 A 超时后,A 的延迟响应不能让事务 B 完成;通道和寄存器不匹配的合法 CRC 帧必须被丢弃并计数。 - -- [ ] **Step 3: 实现单事务截止时间** - -`thr.rollsou` 对每条命令执行: - -```text -send -> wait matching response until 50 ms deadline - matched: complete command - unrelated: free message and continue waiting within same deadline - timeout/error: attempt++ - attempt < 3: wait 20 ms and retry - attempt == 3: drop remaining commands in same batch, report error, resume queue/auto poll -``` - -不能在 CRC 错误或超时后无限保持当前命令。 - -- [ ] **Step 4: 保证消息在所有路径释放** - -匹配成功、无关帧、CRC 错误、异常响应和超时后收到的延迟帧都必须有明确释放路径。用 `free` 的 max-used 和消息分配/释放计数验证收支一致。 - -- [ ] **Step 5: 板上断线恢复测试** - -1. 正常执行一个 6 条命令批次。 -2. 第 3 条命令前断开南向设备。 -3. 确认最多重试 3 次后取消该批次。 -4. 恢复设备。 -5. 确认自动轮询恢复,后续新命令可执行。 - -Expected: 不再连续无限打印 `com batch timeout`;queue count 最终回到 0;业务无需整机复位即可恢复。 - -- [ ] **Step 6: 提交事务匹配和有限重试** - -```powershell -git add apps/chrg/applications/utils/chrg_eload.c apps/chrg/applications/utils/chrg_eload.h apps/chrg/applications/thread/chrg_roll_sou.c apps/chrg/applications/tests/test_chrg_sou_queue.c -git commit -m "fix: bound south retries and match responses" -``` - ---- - -### Task 8: 增加 North 响应元数据和过期帧过滤 - -**Files:** -- Modify: `apps/chrg/applications/utils/chrg_def.h` -- Modify: `apps/chrg/applications/thread/chrg_north.c` -- Modify: `apps/chrg/applications/thread/chrg_roll_nor.c` - -- [ ] **Step 1: 在帧开始时快照通道和接收时刻** - -调用 `chrg_frame_begin()` 时把当前物理通道写入消息 `channel`,完成时保留首字节接收时刻 `rx_tick`。通道不能在消息消费时从全局 `chrgnorth.idx` 重新读取。 - -- [ ] **Step 2: 为每次 North 请求记录事务开始信息** - -在发送前记录: - -```c -active.transaction++; -active.channel = idx; -active.started_at = rt_tick_get(); -active.expected_id = pSW->id; -``` - -- [ ] **Step 3: 等待窗口内过滤不匹配消息** - -North 响应必须满足: - -- `msg->channel == active.channel`。 -- `(rt_tick_t)(msg->rx_tick - active.started_at) < RT_TICK_MAX / 2`。 -- 协议 ID、长度和 CRC 符合当前状态步骤。 - -不匹配消息释放后继续等待,直到 200 ms 总截止时间;不能因为一条旧帧提前结束当前事务。 - -- [ ] **Step 4: 不再用 `pROLL->ch` 解析自动轮询响应** - -自动轮询响应使用消息携带的 channel 或 `active.channel`。`pROLL->ch` 只允许表示人工插入命令目标,不得作为自动轮询当前通道。 - -- [ ] **Step 5: 四通道延迟响应测试** - -人为延迟 CH1 响应,轮询切到 CH2 后再注入 CH1 响应。 - -Expected: CH1 延迟帧被丢弃或归入 CH1,不修改 CH2 状态;CH2 当前事务不会被错误推进。 - -- [ ] **Step 6: 提交 North 事务关联** - -```powershell -git add apps/chrg/applications/utils/chrg_def.h apps/chrg/applications/thread/chrg_north.c apps/chrg/applications/thread/chrg_roll_nor.c -git commit -m "fix: correlate north responses with active transaction" -``` - ---- - -### Task 9: 收紧互斥量范围并修正线程启动顺序 - -**Files:** -- Modify: `apps/chrg/applications/bsp/chrg_tty.c` -- Modify: `apps/chrg/applications/bsp/chrg_tty.h` -- Modify: `apps/chrg/applications/thread/chrg_comm.c` -- Modify: `apps/chrg/applications/thread/chrg_roll_nor.c` -- Modify: `apps/chrg/applications/thread/chrg_thread.c` -- Modify: `apps/chrg/applications/thread/chrg_thread.h` - -- [ ] **Step 1: 为锁定义有界等待** - -```c -#define CHRG_STATE_LOCK_TIMEOUT_MS 100 -#define CHRG_TTY_LOCK_TIMEOUT_MS 250 -``` - -所有 `rt_mutex_take(..., RT_WAITING_FOREVER)` 必须替换为 `rt_tick_from_millisecond()` 转换后的有界值,并检查返回结果。 - -- [ ] **Step 2: 禁止持有状态锁执行 I/O 或延时** - -检查锁内调用,确保锁内只做结构体快照、状态提交或索引更新。以下操作必须在释放状态锁后执行: - -- `chrg_tty_send()` / `chrg_tty_recv()`。 -- `rt_event_recv()` / `rt_mb_recv()`。 -- `rt_thread_mdelay()`。 -- `rt_kprintf()` / `LOG_*()`。 -- Source/Sink/Eload 实际报文发送。 - -- [ ] **Step 3: 缩短 Modbus 参数生效临界区** - -`chrg_comm.c` 的 `SYS_REG_PARAM_EFFECT` 路径在锁内只完成实际结构体更新和动作描述生成;释放锁后再提交南向批次或触发后续状态机。任何错误路径都通过单一 cleanup 出口释放锁。 - -- [ ] **Step 4: 消除 TTY 事件丢失窗口** - -当前“先读为空、再 RESET event、再等待”可能清除刚到达的 RX 事件。调整为: - -1. 清事件。 -2. 再次读取设备。 -3. 第二次仍为空才等待事件。 - -接收等待仍有 `timeout/byte_tmo` 上限;发送锁获取超过 250 ms 返回 `-RT_ETIMEOUT` 并计数。 - -- [ ] **Step 5: 线程初始化改为两阶段** - -第一阶段为六个线程全部创建 mutex/mailbox/event;第二阶段才创建和启动线程。禁止创建一个线程后立即启动、再继续准备后续线程。 - -拆分接口: - -```c -static int chrg_thread_prepare(eIDX_THRS idx); -static int chrg_thread_start(eIDX_THRS idx); -static void chrg_thread_cleanup(eIDX_THRS idx); -``` - -任一准备或启动失败必须返回错误,清理已创建但未启动的对象,并输出具体失败线程和错误码。`rt_thread_create()` 返回 NULL 时不能继续返回 `RT_EOK`。健康错误计数在 Task 10 接入。 - -- [ ] **Step 6: 锁超时和启动失败测试** - -测试构建中故意占用状态 mutex 500 ms,确认调用者在 100 ms 返回错误而不是永久等待。故意让一个线程分配失败,确认初始化报告具体线程且不会访问尚未创建的后续 mutex。 - -- [ ] **Step 7: 构建和提交** - -```powershell -scons -C apps/chrg -j4 -git diff --check -- apps/chrg/applications/bsp/chrg_tty.c apps/chrg/applications/bsp/chrg_tty.h apps/chrg/applications/thread/chrg_comm.c apps/chrg/applications/thread/chrg_roll_nor.c apps/chrg/applications/thread/chrg_thread.c apps/chrg/applications/thread/chrg_thread.h -git add apps/chrg/applications/bsp/chrg_tty.c apps/chrg/applications/bsp/chrg_tty.h apps/chrg/applications/thread/chrg_comm.c apps/chrg/applications/thread/chrg_roll_nor.c apps/chrg/applications/thread/chrg_thread.c apps/chrg/applications/thread/chrg_thread.h -git commit -m "fix: bound chrg locks and start threads in two phases" -``` - ---- - -### Task 10: 增加关键线程健康监控 - -**Files:** -- Create: `apps/chrg/applications/utils/chrg_health.h` -- Create: `apps/chrg/applications/utils/chrg_health.c` -- Create: `apps/chrg/applications/tests/test_chrg_health.c` -- Modify: all six thread entry files under `apps/chrg/applications/thread/` -- Modify: `apps/chrg/applications/bsp/chrg_tty.c` -- Modify: `apps/chrg/applications/thread/chrg_thread.c` - -- [ ] **Step 1: 定义六个关键健康节点** - -```c -enum chrg_health_node { - CHRG_HEALTH_NORTH_RX, - CHRG_HEALTH_SOUTH_RX, - CHRG_HEALTH_COMM, - CHRG_HEALTH_LCD, - CHRG_HEALTH_ROLL_NORTH, - CHRG_HEALTH_ROLL_SOUTH, - CHRG_HEALTH_NODE_COUNT -}; - -enum chrg_health_error { - CHRG_HEALTH_ERR_FRAME_TIMEOUT, - CHRG_HEALTH_ERR_RING_OVERFLOW, - CHRG_HEALTH_ERR_SOU_QUEUE_FULL, - CHRG_HEALTH_ERR_SOU_RETRY_DROP, - CHRG_HEALTH_ERR_MAILBOX_DROP, - CHRG_HEALTH_ERR_MUTEX_TIMEOUT, - CHRG_HEALTH_ERR_THREAD_INIT, - CHRG_HEALTH_ERR_WDT_FEED, - CHRG_HEALTH_ERR_COUNT -}; - -#define CHRG_HEALTH_STARTUP_GRACE_MS 10000 -#define CHRG_HEALTH_STALE_MS 2000 - -void chrg_health_init(void); -void chrg_health_beat(enum chrg_health_node node); -void chrg_health_error(enum chrg_health_error error); -rt_bool_t chrg_health_all_fresh(rt_tick_t now); -rt_uint32_t chrg_health_stale_mask(rt_tick_t now); -``` - -- [ ] **Step 2: 写健康判定失败测试** - -覆盖:全部新鲜、单线程过期、tick 回绕、启动宽限、初始化失败、故障注入冻结和错误计数饱和。 - -```c -UTEST_TC_EXPORT(test_health_all, "chrg.health", RT_NULL, RT_NULL, 1000); -``` - -- [ ] **Step 3: 实现无阻塞心跳更新** - -心跳只写入对齐的 `rt_tick_t` 和循环计数。用短临界区保护快照,不允许在健康模块中分配内存、等待 mutex 或输出日志。 - -- [ ] **Step 4: 在正确的进度点打心跳** - -- RX 线程:每次 `chrg_tty_recv()` 返回并完成超时/数据处理后。 -- Comm:每次接收超时或完成一帧解析后。 -- LCD:每次接收超时或完成一帧解析后。 -- Roll North:每次事务完成、超时处理或空闲轮询后。 -- Roll South:每次队列事务完成/失败或自动轮询后。 - -不能在进入可能永久阻塞的调用之前打心跳。 - -- [ ] **Step 5: 将前序任务的错误路径接入统一计数** - -将 Task 4、6、7、9 中已经具有明确错误返回的路径分别映射到 `FRAME_TIMEOUT`、`RING_OVERFLOW`、`SOU_QUEUE_FULL`、`SOU_RETRY_DROP`、`MAILBOX_DROP`、`MUTEX_TIMEOUT` 和 `THREAD_INIT`。计数失败不能改变原错误路径,也不能在中断中输出日志。 - -- [ ] **Step 6: 导出 `chrg_health` MSH 命令** - -输出固定字段:node、age_ms、loop_count、stale、last_error、frame_timeout、ring_overflow、queue_full、retry_drop、mailbox_drop、mutex_timeout、heap_used/max-used。 - -- [ ] **Step 7: 增加测试构建故障注入** - -仅在 `CHRG_USING_FAULT_INJECTION` 下导出: - -```text -chrg_health_freeze -chrg_health_unfreeze -``` - -冻结只停止对应节点的心跳更新,不关闭中断或破坏内存,用于验证看门狗决策。 - -- [ ] **Step 8: 运行健康测试** - -```text -utest_run chrg.health -chrg_health -``` - -Expected: UTest 全部 PASS;正常运行时 stale mask 为 0。 - -- [ ] **Step 9: 提交健康模块** - -```powershell -git add apps/chrg/applications/utils/chrg_health.h apps/chrg/applications/utils/chrg_health.c apps/chrg/applications/tests/test_chrg_health.c apps/chrg/applications/thread/chrg_north.c apps/chrg/applications/thread/chrg_south.c apps/chrg/applications/thread/chrg_comm.c apps/chrg/applications/thread/chrg_lcd.c apps/chrg/applications/thread/chrg_roll_nor.c apps/chrg/applications/thread/chrg_roll_sou.c apps/chrg/applications/bsp/chrg_tty.c apps/chrg/applications/thread/chrg_thread.c -git commit -m "feat: monitor chrg business thread health" -``` - ---- - -### Task 11: 改为集中条件喂狗并记录复位原因 - -**Files:** -- Modify: `apps/chrg/applications/bsp/chrg_wdt.c` -- Modify: `apps/chrg/applications/bsp/chrg_wdt.h` -- Modify: `apps/chrg/applications/main.c` -- Modify: `apps/chrg/applications/thread/chrg_roll_nor.c` -- Modify: `apps/chrg/applications/utils/chrg_health.c` - -- [ ] **Step 1: 让 WDT API 可验证** - -接口改为: - -```c -int chrg_wdt_init(void); -rt_err_t chrg_wdt_feed(void); -rt_bool_t chrg_wdt_is_ready(void); -rt_uint32_t chrg_wdt_reset_cause(void); -``` - -`chrg_wdt_feed()` 必须检查 `dev` 和 ready 状态并返回 `rt_device_control()` 结果,不能静默忽略失败。 - -- [ ] **Step 2: 启动时读取并清除 RCC reset flags** - -记录至少:Power-on、Pin、Software、IWDG、WWDG、Brownout。先保存标志,再调用 RCC clear reset flags。启动日志输出一次上次复位原因。 - -- [ ] **Step 3: 删除所有分散喂狗点** - -删除 `chrg_roll_nor_thread_entry()` 内的 `chrg_wdt_feed()`。全仓搜索必须只剩主线程调用: - -```powershell -rg -n "chrg_wdt_feed\(" apps/chrg -``` - -Expected: 一个实现、一个声明、一个主线程调用。 - -- [ ] **Step 4: 主线程根据健康状态喂狗** - -```c -while (1) { - rt_thread_mdelay(250); - if (chrg_wdt_is_ready() && chrg_health_all_fresh(rt_tick_get())) { - if (chrg_wdt_feed() != RT_EOK) { - chrg_health_error(CHRG_HEALTH_ERR_WDT_FEED); - } - } -} -``` - -健康过期后只记录一次 stale mask,随后停止喂狗,等待 IWDG 硬复位。禁止业务线程自行恢复喂狗。 - -- [ ] **Step 5: 验证单线程停滞触发复位** - -测试构建执行: - -```text -chrg_health_freeze roll_south -``` - -Expected: 2 秒后 health 判定 stale,停止刷新 IWDG;最后一次刷新约 3 秒后复位;重启日志显示 IWDG reset cause。 - -分别对六个节点执行一次,均必须复位。 - -- [ ] **Step 6: 验证正常长操作不会误复位** - -执行正常 Modbus、LCD、North/South 轮询、队列批量操作和 USB 断连。Expected: 所有节点 age 小于 2 秒,IWDG 不误触发。 - -- [ ] **Step 7: 确认外部看门狗边界** - -当前代码和 GPIO 定义中没有外部 WDI 驱动。本计划只修正 STM32 内部 IWDG。硬件若确有外部看门狗,必须由硬件负责人从原理图确认 WDI 引脚、有效沿、最短/最长喂狗窗口和复位连接后单独立项;在这些参数未确认前不得复用任意 GPIO 模拟外部喂狗。 - -- [ ] **Step 8: 构建和提交** - -```powershell -scons -C apps/chrg -j4 -git diff --check -- apps/chrg/applications/bsp/chrg_wdt.c apps/chrg/applications/bsp/chrg_wdt.h apps/chrg/applications/main.c apps/chrg/applications/thread/chrg_roll_nor.c apps/chrg/applications/utils/chrg_health.c -git add apps/chrg/applications/bsp/chrg_wdt.c apps/chrg/applications/bsp/chrg_wdt.h apps/chrg/applications/main.c apps/chrg/applications/thread/chrg_roll_nor.c apps/chrg/applications/utils/chrg_health.c -git commit -m "fix: feed iwdg only when all chrg threads are healthy" -``` - ---- - -### Task 12: 完成故障注入和 72 小时稳定性验证 - -**Files:** -- Create: `docs/debug/apps-chrg-soak-test.md` -- Modify: `README.md` - -- [ ] **Step 1: 建立测试记录表** - -记录项必须包含:固件 commit、编译器版本、镜像大小、板卡编号、外设固件版本、开始/结束时间、复位次数、reset cause、heap used/max-used、各线程最小剩余栈、各错误计数和最终结论。 - -- [ ] **Step 2: 执行 2 小时通信压力测试** - -并发执行: - -- UART1 持续 Modbus 读写,覆盖四通道。 -- LCD 持续状态刷新和人工设置。 -- North/South 四通道自动轮询。 -- 每分钟注入一次 CRC 错误、截断帧和延迟帧。 - -Expected: 无队列永久 pending;heap used 不呈单调增长;frame abort 后分配/释放计数平衡。 - -- [ ] **Step 3: 执行南向断线恢复矩阵** - -分别在单条命令、批次第 1 条、第 3 条和最后 1 条时断开南向设备。每种情况持续 10 秒后恢复。 - -Expected: 每个失败事务最多重试 3 次;当前批次取消;设备恢复后自动轮询和新命令恢复。 - -- [ ] **Step 4: 执行看门狗故障矩阵** - -逐一冻结六个健康节点并确认 IWDG 复位。随后关闭故障注入配置重新构建生产固件,确认生产镜像不导出 freeze 命令。 - -- [ ] **Step 5: 执行 72 小时稳定运行** - -每小时采集: - -```text -chrg_health -list_thread -list_mutex -list_mailbox -free -``` - -通过条件: - -- 无未解释复位。 -- heap used 在热身后保持有界波动,不单调增长。 -- max-used 可增长到稳定峰值,但剩余 heap 始终高于基线定义的安全下限。 -- 六个线程栈余量不低于各自栈的 20%。 -- queue count 能回到 0,任何批次都能完成或在 3 次重试后取消。 -- mutex 没有长期 owner/waiter 不变化。 - -- [ ] **Step 6: 更新 README** - -写明: - -- `chrg_health` 输出解释。 -- 内部 IWDG 的集中喂狗策略。 -- 南向队列深度、重试上限和批次失败语义。 -- 分帧 timeout/overflow 计数含义。 -- 外部看门狗当前不在软件实现范围内。 - -- [ ] **Step 7: 最终构建、检查和提交** - -```powershell -scons -C apps/chrg -c -scons -C apps/chrg -j4 -git diff --check -- README.md docs/debug/apps-chrg-soak-test.md -git status --short -git add README.md docs/debug/apps-chrg-soak-test.md -git commit -m "docs: document chrg health and soak verification" -``` - -Expected: 主应用构建成功、镜像不超过 256 KiB、文档记录完整、提交中没有 IDE 状态或无关换行变化。 - ---- - -## 4. 回退策略 - -每个任务必须是独立提交,回退只能按提交逆序进行: - -1. 文档和测试记录可单独回退,不影响固件行为。 -2. 看门狗健康策略回退时,必须同时恢复唯一喂狗点,禁止出现无喂狗或多个无条件喂狗点。 -3. 南向事务匹配必须与命令队列一起回退,不能保留新消费者配旧共享批处理数组。 -4. 分帧核心与 North/South 接入必须一起回退,避免消息所有权语义不一致。 -5. 禁止通过恢复 `RT_WAITING_FOREVER` 或无限重试作为临时绕过方案。 - -如某阶段板测失败,先保留失败日志和对应 commit,回退该阶段后重新验证上一阶段仍稳定,再根据单一失败假设继续。 - -## 5. 计划自检 - -- 覆盖了已识别的分帧越界、超时泄漏、payload 分配失败泄漏和 RingBuffer 静默丢数据。 -- 覆盖了南向共享单槽、无锁批处理、无限重试和旧响应错配。 -- 覆盖了 North 通道错配和延迟响应问题。 -- 覆盖了永久 mutex 等待、锁内 I/O 和线程依赖对象尚未创建即启动的问题。 -- 覆盖了 IWDG 多点无条件喂狗、启动失败不可见和复位原因缺失。 -- 明确区分内部 IWDG 与尚无软件证据的外部看门狗。 -- 所有生产行为变更均有对应 UTest、板测和长期运行验收项。 diff --git a/docs/2026-07-21-source-state-machine-remediation-plan.md b/docs/2026-07-21-source-state-machine-remediation-plan.md deleted file mode 100644 index 43f98d0..0000000 --- a/docs/2026-07-21-source-state-machine-remediation-plan.md +++ /dev/null @@ -1,121 +0,0 @@ -# Source 状态机改造与角色切换维护计划 - -## 1. 改造目标 - -将电源(Source)控制整理为与负载(Sink)类似的独立状态机,但保留两者业务差异: - -- 北向协议板只负责角色/CC 线选择、协议设置及周期读取协议板电压电流。 -- 功率板角色、模式、电压、电流、继电器和启停全部由 `chrg_source.c` 管理。 -- 所有 Source 南向寄存器命令统一使用 `chrg_sou_com_batch_submit()`,不再散落调用 `chrg_set_sou_reg()`。 -- Source 不判断带载电流。协议板返回电压有效则进入 `RUNNING`;启动后持续无电压则回到 `WAIT`。 -- 协议、协议电压或协议电流发生变化时,重新进入 `INIT`。 -- Source/Sink 不停机切换时,必须先停止旧角色,确认完成后再初始化新角色。 - -## 2. 当前已完成的修改 - -当前工作区已完成第一阶段,尚未形成完整状态机: - -1. `apps/chrg/applications/utils/chrg_source.c` - - 新增 `chrg_source_init_submit(rt_uint8_t idx, struct chrg_switch_t *pSW)`。 - - 通过一个批次下发 `REG_ELOAD`、`REG_MODE`、`REG_VOLTAGE_OUT + 1`、`REG_CURRENT_OUT + 1` 和 `REG_WORK = WORK_STOP`。 -2. `apps/chrg/applications/utils/chrg_source.h` - - 增加上述函数声明。 -3. `apps/chrg/applications/thread/chrg_roll_nor.c` - - Source 协议设置流程进入 `IDX_GET_SOURCE_VC` 后提交南向初始化批次。 - - 仅当批次提交成功时设置 `source.On_Flag = 1`。 - -注意:当前仍有旧 Source 路径在 `chrg_north.c`、`chrg_roll_nor.c` 和 `chrg_lcd.c` 中直接调用 `chrg_set_sou_reg()`。在完成后续步骤前,新旧逻辑会同时存在,不能作为最终版本发布。 - -## 3. 目标状态机 - -在 `chrg_source.h` 中增加明确的 Source 状态: - -```c -typedef enum { - SOURCE_WORK_STOP = 0, - SOURCE_WORK_WAIT, - SOURCE_WORK_INIT, - SOURCE_WORK_START, - SOURCE_WORK_RUNNING, - SOURCE_WORK_STOPPING, - SOURCE_WORK_FAULT -} source_work_mode_t; -``` - -在 `chrg_src_t` 中增加每通道状态及参数快照:`work_mode`、`last_protocol`、`last_voltage`、`last_current`、`zero_volt_count`、`retry_count`。参数快照只能在对应命令确认完成后更新。 - -状态动作: - -| 状态 | 动作 | 后续状态 | -|---|---|---| -| `INIT` | 批量设置 Source 角色、模式、电压、电流及继电器,保持停止 | `START` | -| `START` | 批量下发 `REG_WORK = WORK_START` | `WAIT` | -| `WAIT` | 北向周期读取协议板 VC | 有电压进入 `RUNNING` | -| `RUNNING` | 更新 VC;检测协议/电压/电流变化 | 变化进入 `INIT` | -| `STOPPING` | 批量停止输出、关闭功率板继电器 | `STOP` | -| `STOP` | 清理使能并断开北向继电器 | 保持 | - -建议电压大于 `WORK_MIN_VOLT` 即视为有效。电压为零连续 3 次再由 `RUNNING` 回到 `WAIT`,避免单帧异常造成状态抖动。 - -## 4. COM 端改造 - -修改 `chrg_mb_write_sys()`: - -- 当目标角色为 Source 且当前未工作时,只设置 `pSW->sub = IDX_SET_SOURCE_CC_SET`,由北向依次完成 CC 和协议设置;协议应答完成后才进入 `SOURCE_WORK_INIT`。 -- 同角色下,比较协议、协议电压、协议电流。任一变化则进入 `SOURCE_WORK_INIT`;相同参数不得重复初始化。 -- 不要在收到 `SYS_REG_ROLE_SWITCH` 时立即覆盖实际角色。应保存 `target_role`,在参数生效时启动切换流程。 -- 切换期间返回 `MB_EX_SLAVE_BUSY`,或者只更新影子参数并等待本次切换完成。 - -角色切换顺序: - -```text -Source -> Sink: Source STOPPING -> STOP -> 切换角色/继电器 -> Sink INIT -Sink -> Source: Sink STOP -> 切换角色/继电器 -> 北向 CC/协议 -> Source INIT -``` - -禁止 Source 和 Sink 的功率板继电器、工作命令在同一通道同时有效。 - -## 5. 北向流程改造 - -精简 `chrg_roll_nor_set()` 的 Source 分支,只保留: - -1. `IDX_SET_SOURCE_CC_SET`:设置 CC 线,成功后进入 `IDX_SET_SOURCE_PD`。 -2. `IDX_SET_SOURCE_PD`:设置协议,应答成功后设置 `SOURCE_WORK_INIT`。 -3. `IDX_GET_SOURCE_VC`:读取协议板电压电流。 - -删除 Source 分支中的 `IDX_SET_SOURCE_ID`、`IDX_SET_SOURCE_CV`、`IDX_SER_SOURCE_VOLT`、`IDX_SER_SOURCE_CURR`、`IDX_SET_SOURCE_WORK` 南向设置动作。校准、控制台和 Sink 分支不在本次范围内。 - -`chrg_north.c` 只解析 Source VC:更新 `source.vc`、`Now_voltage`、`Now_current`,并根据电压推进 `WAIT/RUNNING`。移除 `chrg_source_standard_output()`、`chrg_source_fast_output()` 和旧的分步停机控制,避免解析函数直接下发南向命令。 - -## 6. 南向完成确认 - -`chrg_sou_com_batch_submit()` 返回成功仅表示入队成功,不表示功率板已经执行。需要为每通道增加批次完成序号或完成事件: - -- `INIT` 批次全部应答匹配后才能进入 `START`。 -- `START` 应答成功后才能进入 `WAIT`。 -- `STOPPING` 批次完成后才能断开北向继电器和修改实际角色。 -- CRC 错误、寄存器不匹配或超时不得推进状态;达到重试上限后进入 `FAULT`。 - -## 7. 推荐实施顺序 - -1. 增加 Source 状态枚举、参数快照及 `chrg_source_work()`。 -2. 将 INIT、START、STOPPING 的南向动作全部迁移到 `chrg_source.c`。 -3. 增加南向批次完成通知,再改为应答驱动状态转换。 -4. 精简北向 Source 子状态,只保留 CC、协议和 VC。 -5. 修改 COM 同角色参数差异及跨角色切换。 -6. 清理 `chrg_lcd.c` 中重复的 Source 启停和角色寄存器写入。 -7. 编译并在硬件验证后删除旧字段和旧函数。 - -## 8. 验证清单 - -- Keil 编译 `apps/chrg/chrg.uvprojx`,确认无未定义符号和头文件循环依赖。 -- 单通道验证 Source 首次启动、停止、相同参数重复生效。 -- 分别改变协议、协议电压和协议电流,确认只执行一次重新 INIT。 -- 模拟协议板电压持续为零,确认进入 WAIT 且不会反复提交相同批次。 -- 验证 Source -> Sink、Sink -> Source,示波器确认切换期间无输出重叠。 -- 四通道同时运行并交叉切换,检查南向队列饥饿、响应错配、超时和缓存串通道。 -- 回归 Sink 正常负载、动态测试、无电流重启及低压处理。 - -## 9. 接手注意事项 - -开始修改前先执行 `git status --short` 和 `git diff`。当前 `chrg_north.c` 还包含此前 Sink 连续确认次数改为 3 的修改及注释清理,不能在 Source 重构时回退。不要提交 Keil `build/`、`Obj/`、AXF、BIN 或 MAP 等生成文件。 diff --git a/docs/2026-07-30-source-cc-close-protocol-switch.md b/docs/2026-07-30-source-cc-close-protocol-switch.md deleted file mode 100644 index 924f571..0000000 --- a/docs/2026-07-30-source-cc-close-protocol-switch.md +++ /dev/null @@ -1,101 +0,0 @@ -# Source CC_CLOSE 协议切换修改说明 - -## 修改目标 - -修正 Source 北向协议板的命令顺序,避免在 CC 保持 Attached 时直接重配 -`0xD0`。多通道同时切换时采用共享等待屏障,使保护时间并行发生,不按 -通道串行累计。 - -## 本轮实施步骤 - -### 第一步:统一状态命名 - -断开 CC 的命令状态命名为 `IDX_SET_SOURCE_CC_CLOSE`,等待状态命名为 -`IDX_WAIT_SOURCE_CC_CLOSE`。`CC_CLOSE` 固定发送 `0x17=0x03`,不覆盖 -COM 保存的目标 `CC_Set`。 - -### 第二步:调整停止态启动顺序 - -Source 从停止状态启动以及 Sink 切换为 Source 时,不发送 `CC_CLOSE`, -直接按以下顺序执行: - -```text -IDX_SET_SOURCE_PD --> IDX_WAIT_SOURCE_PROTOCOL --> IDX_SET_SOURCE_CC_SET --> IDX_GET_SOURCE_VC -``` - -即先设置协议,收到 `0xD0` ACK 并完成保护等待后,再连接目标 CC。 - -### 第三步:识别必须断开 CC 的切换 - -COM 参数生效时,满足以下任一条件进入 `CC_CLOSE`: - -- 新协议为 PD/PPS 或 PD3.1/AVS,并且协议或 CC 线与上次不同。 -- 上次协议为 PD/PPS 或 PD3.1/AVS,本次切换为非 PD 协议。 - -其它协议配置变化从 `IDX_SET_SOURCE_PD` 开始。标准协议仅改变电压、电流 -时直接更新功率板,不重复下发 `0xD0`。 - -### 第四步:执行 CC_CLOSE 命令链 - -COM先确定北向`sub`,协议或CC发生变化时进入 -`SOURCE_WORK_PROTOCOL_SWITCH`,不在COM中调用发送函数。北向轮询线程 -执行该Source工作状态,负责向南向功率板写入0V;标准协议还会在同一批 -命令中依次写模式和电流。提交成功后清零`set_volt`并进入 -`SOURCE_WORK_WAIT`,随后按已经选定的`sub`继续执行。 - -需要断开 CC 的通道按以下顺序执行: - -```text -IDX_SET_SOURCE_CC_CLOSE 发送 0x17=0x03 --> IDX_WAIT_SOURCE_CC_CLOSE 等待共享保护时间 --> IDX_SET_SOURCE_PD 发送 0xD0 --> IDX_WAIT_SOURCE_PROTOCOL 等待协议 ACK 后保护时间 --> IDX_SET_SOURCE_CC_SET 发送 0x17=目标 CC --> IDX_GET_SOURCE_VC 恢复 0x20 查询 -``` - -每条设置命令仍需收到角色、命令、长度和 CRC 均正确的回码后才能推进 -`sub`;错误回码继续沿用现有丢弃和有限重发逻辑。 - -### 第五步:多通道共享 CC_CLOSE 等待 - -使用 `pending_mask` 和 `acked_mask` 管理本轮参与切换的 Source 通道。 -所有已登记通道都收到 `0x17` ACK 后,只启动一次 300 ms 保护等待。 -一个通道和四个通道的保护等待本身均为 300 ms;四通道只额外产生必要的 -命令收发时间,不会累计为四次 300 ms。 - -### 第六步:握手稳定确认(本轮不修改) - -本轮不增加“连续两次 `0x20` 电压稳定后才进入 RUNNING”的新逻辑,继续 -使用现有 `chrg_source_parse()` 电压误差和重试规则。 - -### 第七步:配置覆盖保护 - -继续使用 `config_version` 防止 COM 新配置被旧回码推进。停止、角色切换 -或不再需要 `CC_CLOSE` 时,会把通道移出共享等待屏障,避免残留状态影响 -下一次启动。 - -### 第八步:完整失败恢复(本轮不修改) - -本轮不增加握手总超时、再次 detach、整套流程最多重试两轮以及故障上报。 -单条北向命令仍使用现有的三次发送机制。完整恢复流程待单独验证后实施。 - -## 时间参数 - -| 参数 | 当前值 | 说明 | -|---|---:|---| -| `SOURCE_CC_CLOSE_WAIT_MS` | 300 ms | 从本轮最后一个有效 `CC_CLOSE` ACK 开始 | -| `SOURCE_PROTOCOL_WAIT_MS` | 100 ms | 每通道收到 `0xD0` ACK 后并行等待 | -| `NORTH_RESPONSE_TIMEOUT_MS` | 50 ms | 单次北向回码等待 | -| `NORTH_REQUEST_MAX_RETRIES` | 2 | 首次发送外最多重发两次 | - -## 建议验证顺序 - -1. 单通道停止态启动 PD,确认发送顺序为 `D0 -> 17(目标CC) -> 20`。 -2. 单通道 PD 改协议或 CC,确认顺序为 `17(03) -> 等待 -> D0 -> 17(目标CC) -> 20`。 -3. 单通道 PD 切换 QC/FCP/标准,确认切换前先发送 `17(03)`。 -4. 四通道连续生效,确认四路 `17(03)` 均回码后共享一次约 300 ms 等待。 -5. 连续执行 20 至 30 轮协议切换,检查是否仍出现 Source 芯片 PD 状态异常。 diff --git a/docs/BMP4810300-4_产品说明彩页.pdf b/docs/BMP4810300-4_产品说明彩页.pdf deleted file mode 100644 index 3a1532f..0000000 Binary files a/docs/BMP4810300-4_产品说明彩页.pdf and /dev/null differ diff --git a/docs/CH32L103DS0.PDF b/docs/CH32L103DS0.PDF deleted file mode 100644 index c0e1fd7..0000000 Binary files a/docs/CH32L103DS0.PDF and /dev/null differ diff --git a/docs/HC-DFCD-4310A可编程源载板快速使用说明(含协议)V1.1-20251028.docx b/docs/HC-DFCD-4310A可编程源载板快速使用说明(含协议)V1.1-20251028.docx deleted file mode 100644 index 9e59241..0000000 Binary files a/docs/HC-DFCD-4310A可编程源载板快速使用说明(含协议)V1.1-20251028.docx and /dev/null differ diff --git a/docs/HC-DFCD-HMI.docx b/docs/HC-DFCD-HMI.docx deleted file mode 100644 index 7b0c3f0..0000000 Binary files a/docs/HC-DFCD-HMI.docx and /dev/null differ diff --git a/docs/HC_DFCD_GX20_REL-四路-接口继电器板.pdf b/docs/HC_DFCD_GX20_REL-四路-接口继电器板.pdf deleted file mode 100644 index dea3854..0000000 Binary files a/docs/HC_DFCD_GX20_REL-四路-接口继电器板.pdf and /dev/null differ diff --git a/docs/HC_DFCD_Protocol_V1_0_3.pdf b/docs/HC_DFCD_Protocol_V1_0_3.pdf deleted file mode 100644 index ffd927e..0000000 Binary files a/docs/HC_DFCD_Protocol_V1_0_3.pdf and /dev/null differ diff --git a/docs/HC_DFCD_Protocol_V1_0_silk.pdf b/docs/HC_DFCD_Protocol_V1_0_silk.pdf deleted file mode 100644 index 27c2909..0000000 Binary files a/docs/HC_DFCD_Protocol_V1_0_silk.pdf and /dev/null differ diff --git a/docs/HC_DFCD_Source_Sink-板-四路-每路相同.pdf b/docs/HC_DFCD_Source_Sink-板-四路-每路相同.pdf deleted file mode 100644 index d07da55..0000000 Binary files a/docs/HC_DFCD_Source_Sink-板-四路-每路相同.pdf and /dev/null differ diff --git a/docs/HMI-20251021.zip b/docs/HMI-20251021.zip deleted file mode 100644 index f5eeb93..0000000 Binary files a/docs/HMI-20251021.zip and /dev/null differ diff --git a/docs/Modbus电源电压电流指令.txt b/docs/Modbus电源电压电流指令.txt deleted file mode 100644 index a8995fc..0000000 --- a/docs/Modbus电源电压电流指令.txt +++ /dev/null @@ -1,65 +0,0 @@ -300W电源板Modbus RTU标准通讯协议指令 -角色切换,默认是电子负载 -1.读取当前角色信息 -01 03 00 04 00 01 C5 CB -2.设置电源模式 -01 06 00 04 00 01 09 CB - -3.设置电子负载模式 -01 06 00 04 00 02 49 CA - -1.恒流恒压模式读取 -01 03 00 1F 00 01 B5 CC - -2.设置恒压模式 -01 06 00 1F 00 01 79 CC - -3.设置恒流模式 -01 06 00 1F 00 02 39 CD - -输入输出控制 -启动: 01 06 00 1E 00 01 28 0C -停止: 01 06 00 1E 00 02 68 0D -故障复位:01 06 00 1E 00 03 A9 CD - -先发送:01 06 00 29 00 00 58 02 -//1V,最小被限制在3.0V,显示:3.287V; -01 06 00 2C 03 E8 A8 BC -//5V,实际显示:5.280-----------------标准电源 -01 06 00 2A 13 88 A5 54 -//6V,实际显示:6.280 -电压指令,先发送: 电流指令:先发送:01 06 00 2B 00 00 F9 C2 -01 06 00 2A 17 70 A6 16 :保护电流12A -//9V------------------------------------QC标准 //默认1A -01 06 00 2A 23 28 B1 2C //01 06 00 2C 03 E8 48 BD -//10V //2A -01 06 00 2A 27 10 B2 3E //01 06 00 2C 07 D0 4B AF -//11V //3A -01 06 00 2A 2E E0 B4 2A //01 06 00 2C 0B B8 4F 41 -//12V-----------------------------------QC标准 //5A -01 06 00 2A 2E E0 B4 2A //01 06 00 2C 13 88 45 55 -//15V //10A -01 06 00 2A 3A 98 BB 08 //01 06 00 2C 27 10 52 3F -//20V-----------------------------------PD标准 //11A -01 06 00 2A 4E 20 9C 7A //01 06 00 2C 2A F8 56 E1 -//21V -01 06 00 2A 52 08 94 A4 -//24V -01 06 00 2A 5D C0 90 C2 -//28V -01 06 00 2A 6D 60 84 BA -//32V -01 06 00 2A 7D 00 89 52 -//36V -01 06 00 2A 8C A0 CC BA -//42V -01 06 00 2A A4 10 D3 0E -//45V -01 06 00 2A AF C8 D4 64 -//48V -01 06 00 2A BB 80 DB 52 -//48.5V -01 06 00 2A BD 74 D9 75 - -电流: - diff --git a/docs/PD31双向电源协议_V6.15.xlsx b/docs/PD31双向电源协议_V6.15.xlsx deleted file mode 100644 index 09bfebc..0000000 Binary files a/docs/PD31双向电源协议_V6.15.xlsx and /dev/null differ diff --git a/docs/PPEC-86CA3C+Buck-Boost半桥应用手册.pdf b/docs/PPEC-86CA3C+Buck-Boost半桥应用手册.pdf deleted file mode 100644 index a22d411..0000000 Binary files a/docs/PPEC-86CA3C+Buck-Boost半桥应用手册.pdf and /dev/null differ diff --git a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.0.docx b/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.0.docx deleted file mode 100644 index 51c6cc0..0000000 Binary files a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.0.docx and /dev/null differ diff --git a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.1.docx b/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.1.docx deleted file mode 100644 index 4073851..0000000 Binary files a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.1.docx and /dev/null differ diff --git a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.1.pdf b/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.1.pdf deleted file mode 100644 index 3e36075..0000000 Binary files a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页-V1.1.pdf and /dev/null differ diff --git a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页.docx b/docs/QMP4810-S多协议可编程双向电源-产品说明彩页.docx deleted file mode 100644 index eb23f01..0000000 Binary files a/docs/QMP4810-S多协议可编程双向电源-产品说明彩页.docx and /dev/null differ diff --git a/docs/QMP4810300-4多协议可编程双向电源通信协议V_1.1.xlsx b/docs/QMP4810300-4多协议可编程双向电源通信协议V_1.1.xlsx deleted file mode 100644 index 7c05d2f..0000000 Binary files a/docs/QMP4810300-4多协议可编程双向电源通信协议V_1.1.xlsx and /dev/null differ diff --git a/docs/debug/apps-chrg-baseline.md b/docs/debug/apps-chrg-baseline.md deleted file mode 100644 index 35457f8..0000000 --- a/docs/debug/apps-chrg-baseline.md +++ /dev/null @@ -1,108 +0,0 @@ -# apps/chrg 长期运行修复基线 - -## 记录信息 - -- 记录时间:2026-07-18 15:53:36 +08:00 -- 当前分支:`feature` -- 当前 HEAD:`5ffc85077ee7c4ebd52178021d9ac99eed223cd6` -- 业务改动检查点:`c820aad feat: 完成项目多模块功能迭代与配置更新` -- 检查点说明:`c820aad` 包含当前 `apps/chrg` 多模块业务改动;后续 `5ffc850` 仅新增仓库 README 和死锁修复计划。当前 `apps/chrg` 相对 HEAD 无未提交差异。 -- 保留的无关工作区文件:`docs/修改计划.md` 为未跟踪文件,本任务未修改、暂存或清理该文件。 - -## Git 工作区基线 - -执行命令: - -```powershell -git status --short --branch -``` - -输出: - -```text -## feature -?? "docs/\344\277\256\346\224\271\350\256\241\345\210\222.md" -``` - -执行命令: - -```powershell -git diff --ignore-space-at-eol --stat -- apps/chrg -``` - -输出:无输出,表示 `apps/chrg` 相对当前 HEAD 没有未提交差异。 - -执行命令: - -```powershell -git log -5 --oneline --decorate -``` - -输出: - -```text -5ffc850 (HEAD -> feature) docs: 初始化HCDF MID 405充放电控制器固件仓库 -c820aad feat: 完成项目多模块功能迭代与配置更新 -250864c (origin/feature) debug north -3a413f5 (origin/master, origin/HEAD, master, agents/add-comments-to-sinkc) nor / sou roll -7e463e5 add more comments -``` - -## 修复前构建 - -执行命令: - -```powershell -scons -C apps/chrg -j4 -``` - -结果:**失败,构建环境缺失。** - -```text -scons : 无法将“scons”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 -CategoryInfo : ObjectNotFound: (scons:String) [], CommandNotFoundException -FullyQualifiedErrorId : CommandNotFoundException -``` - -环境复核结果: - -- `scons`:无法取得,未安装或未加入 `PATH`。 -- Python:系统 `PATH` 中无法取得;Codex 自带 Python 可运行,但未安装 `SCons` 模块。 -- `arm-none-eabi-gcc`:无法取得,未安装或未加入 `PATH`。 -- Keil UV4:常用路径 `C:\Keil_v5\UV4\UV4.exe` 及检查的常见安装目录中未找到。 -- 既有输出:未找到可复用的 `chrg.elf`、`rtthread.bin` 或 `chrg.axf`。 -- text/data/bss:无法取得,原因是构建未启动。 -- 镜像大小:无法取得,原因是未生成镜像。 -- 编译 warning:无法取得,原因是编译器未运行。 - -结论:Task 1 的修复前构建验收尚未通过。在具备 SCons 与 Arm GCC 或 Keil MDK 的构建机上补跑同一命令并补录产物尺寸、段大小和全部 warning 前,不应开始功能修改。 - -## 故障现场 Shell 快照 - -当前会话只有离线源码工作区,没有连接发生故障的 STM32F405 设备、串口或 USB Shell,因此不能执行板上命令。以下项目均显式记录为无法取得,避免将“未采集”误认为正常状态。 - -| 命令 | 结果 | -| --- | --- | -| `list_thread` | 无法取得:当前未连接目标板 Shell,线程状态和栈使用量未知。 | -| `list_mutex` | 无法取得:当前未连接目标板 Shell,mutex owner/suspend 未采集。 | -| `list_mailbox` | 无法取得:当前未连接目标板 Shell,邮箱 entry 数未采集。 | -| `list_event` | 无法取得:当前未连接目标板 Shell,事件状态未采集。 | -| `free` | 无法取得:当前未连接目标板 Shell,heap total/used/max-used 未采集。 | -| `list_device` | 无法取得:当前未连接目标板 Shell,设备状态未采集。 | - -## 故障特征 - -- 首次异常运行时长:无法取得:未提供对应现场运行记录。 -- 异常时仍工作的接口:无法取得:当前没有现场设备或故障时间点数据。 -- 最后 100 行日志:无法取得:仓库中未提供本次故障对应的运行日志,当前未连接目标板。 -- `com batch timeout` 是否连续出现:无法取得:没有现场串口日志。 -- `free` 的 total/used/max-used:无法取得:当前未连接目标板 Shell。 -- `thr.rollnor` 和 `thr.rollsou` 状态:无法取得:当前未连接目标板 Shell。 -- `thr.nor` 和 `thr.sou` 状态:无法取得:当前未连接目标板 Shell。 -- 最近一次复位原因:无法取得:没有启动日志或 RCC 复位标志快照。 - -## 后续补录要求 - -1. 在配置完整的构建机上重新执行 `scons -C apps/chrg -j4`,补录工具链版本、text/data/bss、镜像大小和 warning。 -2. 下次现场复现且 Shell 尚可用时,立即按上述顺序保存六条诊断命令的原始输出和最后 100 行日志。 -3. 所有后续修复必须基于 `c820aad` 已包含的业务逻辑和当前 HEAD,不得从 `origin/feature` 或 `master` 重新开始。 diff --git a/docs/pcf8574-I2C扩展8路GPIO.pdf b/docs/pcf8574-I2C扩展8路GPIO.pdf deleted file mode 100644 index a1f45b9..0000000 Binary files a/docs/pcf8574-I2C扩展8路GPIO.pdf and /dev/null differ diff --git a/docs/pins.xlsx b/docs/pins.xlsx deleted file mode 100644 index 90234f1..0000000 Binary files a/docs/pins.xlsx and /dev/null differ diff --git a/docs/power_board_upgrade_issues_and_plan.md b/docs/power_board_upgrade_issues_and_plan.md deleted file mode 100644 index 1063714..0000000 --- a/docs/power_board_upgrade_issues_and_plan.md +++ /dev/null @@ -1,167 +0,0 @@ -# 功率板升级问题与修改计划 - -## 1. 升级链路 - -```text -上位机 - ↓ -协议板 App - ↓ 进入升级指令 -协议板南向 UART - ↓ -功率板 Boot - ↓ 0x06、0x43 -协议板 App - ↓ -上位机 -进入升级控制帧: -03 06 00 0F 00 FF F8 6B -跳转 App 控制帧: -03 06 00 0F 00 0F F8 2F -功率板 Boot 使用裸 BIN + YModem 接收升级数据。 -2. 已确认的问题 -2.1 协议板发送日志含义不准确 -协议板日志: -[UPG] CTRL ENTER try=1 tx=0 -tx=0 只表示发送请求已经提交到南向升级发送流程,不能证明 UART 已经在物理线上发送成功。 -需要通过串口逻辑分析仪确认实际线路是否出现完整控制帧。 -2.2 功率板 Boot 控制帧接收依赖一次完整 DMA 回调 -当前 Boot 主要在以下条件同时满足时识别进入升级帧: -received_len == 8 -usart1_dma_rx_buf[0] == 0x03 -UPDATE_FRAME_MATCH -如果控制帧被 DMA 空闲中断拆成多次接收,当前逻辑可能无法在拼接完成后重新判断控制帧。 -2.3 功率板 Boot 只有擦除成功后才发送应答 -处理流程: -收到完整控制帧 - ↓ -Flash_Erase_App_Region() - ↓ -擦除成功:发送 0x06、0x43 -擦除失败:发送 0x15 -因此: -- 完全没有响应:优先检查 UART 接收、帧匹配、通道切换和线路; -- 返回 0x15:重点检查 Flash 擦除; -- 返回 0x06 但协议板没有收到:检查南向返回线路和协议板接收线程。 -2.4 Flash 分区注释与实际大小不一致 -当前配置: -APP_FLASH_BASE_ADDR = 0x08003800 -APP_FLASH_SIZE = 0x0000B000 -0xB000 实际为 44 KB,而代码注释写成了 40 KB。 -同时擦除配置为: -NbPages = 22 -PAGE_SIZE = 0x800 -22 页也是 44 KB。需要确认这是否符合实际功率板 Flash 分区规划。 -2.5 南向通道和收发方向需要单独确认 -协议板升级前会执行: -chrg_south_switch(ch) -串口发送前后还会切换 RS485 收发方向。通道选择错误、方向控制延迟不足或线路未接通,都可能导致功率板无法收到控制帧或协议板收不到 0x06。 -3. 修改计划 -3.1 完善功率板 Boot 控制帧接收 -在 DMA 接收回调中: -1. 累积多次接收到的数据; -2. 达到 8 字节后统一匹配进入升级帧和跳转帧; -3. 匹配成功后设置对应 pending 标志; -4. 匹配失败时清理缓存并等待下一帧; -5. 增加接收长度和帧内容调试输出,或使用 GPIO 翻转辅助定位。 -3.2 完善协议板发送状态 -协议板需要区分: -- 发送请求提交成功; -- UART 实际发送完成; -- 功率板返回 0x06; -- 功率板返回 0x15; -- 等待超时。 -日志中应明确打印实际发送长度和发送帧内容。 -3.3 完善应答重发机制 -控制帧和 YModem 数据帧均应保留最近一次发送内容: -- 等待 ACK 超时后重发; -- 收到 NAK 后重发; -- 超过最大重试次数后退出升级; -- 收到 CAN 后立即中止; -- 半帧超时后清理接收缓存并等待下一帧。 -3.4 核对 Flash 配置 -需要确认: -- 功率板 MCU 型号; -- Flash 页面大小; -- Boot 起始地址; -- App 起始地址; -- App 最大容量; -- Boot 工程链接地址; -- App 工程链接地址; -- YModem 文件最大长度。 -确认后统一修正宏定义、擦除页数和注释。 -3.5 保持升级状态期间停止普通南向轮询 -进入升级模式后: -- 停止普通 Modbus 轮询; -- 锁定目标功率板通道; -- 只转发控制帧、YModem 数据帧和单字节应答; -- 升级结束或失败后恢复普通轮询。 -4. 待验证项目 -4.1 控制帧发送 -使用逻辑分析仪确认协议板实际发送: -03 06 00 0F 00 FF F8 6B -确认: -- 波特率为 115200; -- 数据格式为 8-N-1; -- 通道选择正确; -- RS485 发送方向正确; -- 帧没有被截断。 -4.2 功率板接收 -在功率板 Boot 的: -HAL_UARTEx_RxEventCallback() -检查: -Size -usart1_dma_rx_buf[0..7] -期望: -Size = 8 -03 06 00 0F 00 FF F8 6B -4.3 功率板帧处理 -确认以下变量和流程: -g_update_prepare_pending = 1 -Check_Updata() -Ymodem_HandleControlFrame() -Flash_Erase_App_Region() -记录 Flash 擦除返回值。 -4.4 功率板应答 -确认功率板 UART 线上依次发送: -06 43 -其中: -- 0x06 表示控制帧处理成功; -- 0x43 表示进入 YModem 接收等待。 -4.5 协议板接收 -确认协议板南向接收线程能识别: -0x06 -0x15 -0x43 -0x18 -并将应答正确放入 YModem 应答队列。 -4.6 YModem 数据传输 -确认: -- 上位机发送的是裸 BIN; -- 首包为 YModem 元数据包; -- 数据包号连续; -- 功率板每包返回 0x06; -- 丢包或 CRC 错误时返回 0x15; -- 重发后包号状态正确; -- EOT 流程能够正常结束。 -4.7 异常场景 -需要测试: -- 控制帧拆包; -- 控制帧丢失; -- 0x06 丢失; -- YModem 数据包丢失; -- YModem 数据包 CRC 错误; -- 升级中途断电; -- 升级中途复位; -- BIN 文件超过 App 分区; -- Flash 擦除失败; -- 跳转帧发送失败。 -5. 暂停调试时的结论 -当前暂不进行硬件调试。后续恢复调试时,优先使用逻辑分析仪确认以下最小链路: -协议板发送控制帧 -→ 功率板 DMA 收到 8 字节 -→ 功率板识别控制帧 -→ Flash 擦除成功 -→ 功率板发送 0x06 -→ 协议板接收到 0x06 -只有这条链路确认成功后,再继续验证 YModem 数据包传输。 \ No newline at end of file diff --git a/docs/修改计划.md b/docs/修改计划.md deleted file mode 100644 index 15d3a41..0000000 --- a/docs/修改计划.md +++ /dev/null @@ -1,111 +0,0 @@ -静态排查后,暂未看到明确的 ABBA 型互斥锁环。当前现象更像“内存破坏/资源泄漏后线程停滞”或“状态机无限重试”,而不是整个 RTOS 调度器死锁。 - -另外,代码中使用的是 STM32 内部 IWDG,不是外部独立看门狗。 - -**可疑点** - -1. **高风险:南北向分包接收存在确定的越界写和泄漏路径** - -[north](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:548) 和 [south](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:309) 在续接分包时计算: - -```text -len_need = len_msg - 帧头长度 -``` - -但此时 `pFrame->length` 已经包含之前收到的数据,正确的剩余长度应基于 `len_msg - pFrame->length`。当前代码却从 `payload[pFrame->length]` 开始写入整个“帧头后的总长度”。 - -例如总长 21 字节,首批已存 8 字节,下一次仍可能从偏移 8 写入 18 字节,最终写到偏移 26,越过 21 字节堆块。这会破坏相邻堆块、邮箱消息甚至 RT-Thread 内核对象,最终表现为随机线程卡死、互斥量异常或堆分配失败。 - -同时,分包超时路径只清除了 `findhead`: - -- [chrg_north.c:627](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:627) -- [chrg_south.c:399](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:399) - -没有释放 `pMB/payload`,也没有清除 `length/len_msg`。而且没有新数据时会提前 `continue`,超时检查根本不会执行。串口噪声、丢字节或正常分包都可能不断积累泄漏。 - -此外,payload 分配失败时只返回,没有释放刚分配的 `pMB`: - -- [chrg_north.c:518](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:518) -- [chrg_south.c:280](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:280) - -这是目前最符合“运行时间越长越容易出现”的可疑点。 - -2. **高风险:看门狗喂法无法覆盖业务线程死锁** - -主线程每秒无条件喂狗:[main.c:36](D:/proj/char/chrg_v/apps/chrg/applications/main.c:36)。 - -北向轮询线程又在每轮无条件喂同一只狗:[chrg_roll_nor.c:421](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_nor.c:421)。 - -因此即使 `thr.comm`、`thr.sou`、`thr.rollsou`、LCD 线程全部停止工作,只要主线程或北向轮询线程仍能调度,IWDG 就永远不会复位。这只是在检测“CPU/调度器是否完全停止”,没有检测业务线程是否健康。 - -驱动最终调用的是 `HAL_IWDG_Refresh()`:[drv_wdt.c:40](D:/proj/char/chrg_v/rt-thread/bsp/stm32/libraries/HAL_Drivers/drivers/drv_wdt.c:40)。代码中没有找到外部 WDI 引脚翻转或外部看门狗驱动。 - -3. **高风险:当前新增的南向批处理可能永久停在 `RUN_COM`** - -南向批处理遇到 CRC 错误或应答超时后,不增加重试次数、不设置截止时间,也不清除任务: - -- CRC 错误:[chrg_roll_sou.c:608](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:608) -- 应答超时:[chrg_roll_sou.c:627](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:627) - -线程会一直重发同一个寄存器,`pending` 永远不清除,`run` 永远保持 `RUN_COM`,正常南向轮询无法恢复。这属于业务状态机死锁,调度器仍正常运行,所以看门狗不会复位。 - -这套批处理逻辑是当前工作区相对 `HEAD` 新增的,优先级很高。 - -4. **高风险:南向共享状态的互斥保护实际无效** - -[chrg_set_sou_reg](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:435) 先在锁外写入: - -```text -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](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:394)。 - -5. **中高风险:先切换 `RUN_COM`,再填充批处理数据** - -[chrg_sink_diff_send_reg](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_comm.c:2338) 先调用 `chrg_sou_com_sou_set()` 将状态切到 `RUN_COM`,然后才追加寄存器。 - -`chrg_sink_work()` 也是相同顺序:[chrg_sink.c:541](D:/proj/char/chrg_v/apps/chrg/applications/utils/chrg_sink.c:541)。 - -如果消费者在状态切换后看到尚未填充的批次,会认为没有任务并切回自动模式;随后加入的 `pending` 数据可能一直无人处理。当前线程优先级降低了部分窗口概率,但没有从同步机制上消除竞态。 - -6. **中高风险:应答邮箱没有请求、通道或序号关联** - -邮箱消息只有 `length + payload`:[chrg_def.h:95](D:/proj/char/chrg_v/apps/chrg/applications/utils/chrg_def.h:95)。 - -南北轮询线程默认“收到的下一帧就是刚才请求的响应”。南向批处理只检查 CRC,任何延迟到达的旧响应都可能让当前批次 `cur_idx++`:[chrg_roll_sou.c:574](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:574)。 - -在超时、重发、通道切换或邮箱积压后,请求与响应很容易错位,最终造成状态机停在错误步骤或无限重试。 - -7. **中风险:所有业务互斥量都是永久等待** - -TTY、南北轮询互斥量统一使用 `RT_WAITING_FOREVER`。例如: - -- [chrg_tty.c:457](D:/proj/char/chrg_v/apps/chrg/applications/bsp/chrg_tty.c:457) -- [chrg_comm.c:2246](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_comm.c:2246) -- [chrg_roll_nor.c:439](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_nor.c:439) - -目前没有发现确定的反向加锁环,但一旦锁所有者因堆破坏、状态机卡死或设备写阻塞而不再释放,其他线程会永久挂起,没有超时和故障上报。 - -8. **中风险:串口 RingBuffer 写入结果被忽略** - -[chrg_north.c:623](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:623) 和 [chrg_south.c:383](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:383) 没有检查 `rt_ringbuffer_put()` 实际写入长度。 - -缓冲区满时会静默丢字节,随后触发分包超时、泄漏和上述越界路径,是长期运行后出现问题的重要放大器。 - -**优先判断** - -最可能的两条故障链是: - -1. 串口分包 → 长度计算错误/超时泄漏 → 堆损坏或耗尽 → 某些业务线程永久等待;主线程继续喂狗。 -2. 南向某条命令超时或响应错位 → `RUN_COM` 无限重发 → 南向业务永久停止;主线程和北向线程继续喂狗。 - -现场故障尚能进入 Shell 时,优先保存 `list_thread`、`list_mutex`、`list_mailbox`、`free` 输出,并检查是否持续打印 `com batch timeout, resend current reg`。这几项可以直接区分锁等待、堆耗尽和状态机无限重试。 - -未修改任何文件。 \ No newline at end of file diff --git a/docs/绿联X527-45瓦三口-测试表格.docx b/docs/绿联X527-45瓦三口-测试表格.docx deleted file mode 100644 index 44b6134..0000000 Binary files a/docs/绿联X527-45瓦三口-测试表格.docx and /dev/null differ diff --git a/docs/绿联X527-45瓦三口-测试表格功能测试报告.docx b/docs/绿联X527-45瓦三口-测试表格功能测试报告.docx deleted file mode 100644 index 0d71598..0000000 Binary files a/docs/绿联X527-45瓦三口-测试表格功能测试报告.docx and /dev/null differ diff --git a/docs/绿联X527-45瓦三口-测试表格功能测试用例.docx b/docs/绿联X527-45瓦三口-测试表格功能测试用例.docx deleted file mode 100644 index 27fecd8..0000000 Binary files a/docs/绿联X527-45瓦三口-测试表格功能测试用例.docx and /dev/null differ