fix(ui): Task #32 P0/P1/P2 — 解耦 UI 心跳、修 adc 压环与截止期、加测量探针

Task #32 UI 看门狗停摆(docs/问题记录/UI任务看门狗停摆分析.md)

P0 adc_task.c(已上板验证):
- 仅 r==1 才压环+重算;上板印证 ring==frames(修前 ring=frames+idle)
- osDelayUntil 返回值重同步,落后拉回基准,解除永久空转死锁

P1-4a ui_task.c v1.3: task_wd_beat 提到 ui_net_sink_poll 之前
  → 心跳测"UI 活着"而非"netTask 响应",解开 §4.2 心跳依赖
  → 未上 P1-3(提优先级会掩盖 §4.2/§4.4);P1-5 暂缓

P2 新增 App/util/task_prof.c/.h: DWT 常开探针,测各任务周期/耗时/
  推流往返/ADC 轮询/控制台排空/syslog 落盘/net_poll 最坏值;心跳位图
  1→0 边沿裸 UART 直发 [PROF]/[PROF2] 快照;uvprojx 已登记

doc: 分析 doc §7 补良性启动 episode 与复位非诊断项;§8 加处置落地
验证: P0+P1 累计 32 次上板启动 0/32 复现(原 L2 基线 3/10),构建 0/0
This commit is contained in:
2026-10-10 19:06:06 +08:00
parent 28bd391035
commit 3b1d0308d8
9 changed files with 623 additions and 14 deletions
+36
View File
@@ -19,6 +19,10 @@
* FTP 文件操作与 syslog/配置的卷访问自此串行化
* (2026-10-02 台架:并发 FTL 写 → journal 竞争 →
* 本任务主循环喂狗点饥饿 → IWDG 误复位)
* v1.2 2026-10-10 王建锋 Task #32 的 P2 测量接线:主循环周期上报各任务最坏
* 耗时(task_prof_report),并在**心跳位图出现 1→0 边沿**
* 的瞬间留一份现场快照(task_prof_report_raw,裸 UART);
* 另给控制台排空与 syslog 落盘各加一个耗时探针
*/
#include "default_task.h"
@@ -39,6 +43,7 @@
#define DBG_LINE_BUF_MAX 128U
#include "dbg_log.h"
#include "task_watchdog.h"
#include "task_prof.h" /* P2 测量:各任务最坏耗时上报(Task #32) */
#include "vol_lock.h"
/* 任务句柄与属性块(CubeMX 生成全局,定义于 Src/freertos.c):
@@ -185,6 +190,22 @@ void default_task_func(void *argument) {
task_wd_seg(TASK_WD_SEG_POLL);
/* 心跳抽样与喂狗判定(内部节流至 100ms;全员到位才喂,否则累计停喂) */
task_wd_poll();
/* P2 测量(Task #32):某位由"到过"变为"缺席"的瞬间留一份现场快照。
* 走裸 UART 直发——缺席窗口内环形缓冲可能积压,这两行必须出去。
*
* ⚠ 判据用**位图边沿**,不能用 miss 累计数:`task_wd_miss_count()` 返回的是
* 单调累计的 s_miss_total(永不清零),开机 bring-up 阶段任务尚未全部启动时
* 就已经 >0 → 若以"归零"判恢复,则首次缺席之后再也不会触发。
* 位图边沿(某位 1→0)天然"每个 episode 只报一次":此后 prev 也不含该位。 */
{
static uint32_t s_prof_prev_mask;
const uint32_t mask = task_wd_last_mask();
if ((s_prof_prev_mask != 0u) && ((s_prof_prev_mask & ~mask) != 0u)) {
task_prof_report_raw();
}
s_prof_prev_mask = mask;
}
/* 本任务自己的心跳 */
task_wd_beat(TASK_WD_DEFAULT);
/* 等待"保存网络配置"或"控制台待发":任一即醒;无事件 50ms 后返回
@@ -197,7 +218,12 @@ void default_task_func(void *argument) {
}
/* 控制台唯一发送点:把环形缓冲中的日志按块发出(无待发时立即返回) */
task_wd_seg(TASK_WD_SEG_DRAIN);
/* P2 探针:控制台排空单独计时。它内部**忙等** UART TXE(单次预算
* DBG_CON_DRAIN_BUDGET_MS=250ms),且本任务为 osPriorityNormal →
* 是"某个 Normal 任务连续占用 CPU 把 UI 饿死"的头号候选 */
task_prof_enter(TASK_PROF_DEF_DRAIN);
dbg_console_drain();
task_prof_exit(TASK_PROF_DEF_DRAIN);
/* 控制台 TX 故障留痕:由本任务(syslog 唯一写者)在 drain **之外**补记 run.log。
* console_log 内部刻意不调 syslog_post —— 那会构成日志调用图成环,
* 让 armlink 静态栈深在每个任务上都多叠一条日志尾巴(见 console_log.c) */
@@ -252,11 +278,21 @@ void default_task_func(void *argument) {
#if !defined(TEST_SUITE_STORAGE)
/* 排空运行期日志(上电/重启分类与自检故障落盘) */
task_wd_seg(TASK_WD_SEG_SYSLOG);
/* P2 探针:syslog 落盘(NAND/FTL 写,最坏数百 ms)单独计时 */
task_prof_enter(TASK_PROF_DEF_SYSLOG);
syslog_drain();
task_prof_exit(TASK_PROF_DEF_SYSLOG);
#endif
/* 栈水位观测:首次 5s 后报告,此后每 2s 采样、出现更深峰值才打印 */
task_wd_seg(TASK_WD_SEG_STACK);
stack_probe_poll();
/* P2 测量(Task #32):各任务最坏耗时 / 次数 / "仍在段内"快照。
* 内部节流(首次 5s,此后每 2s);判据:
* uisn 持续增长 → uiTask 卡在 ui_net_sink_poll 的往返里(阻塞)
* uipn 持续增长而 uisn 为 0 → 被调度饿死(本轮迟迟跑不完)
* uiw 大 → uiTask 自身渲染拖长
* [PROF2] 的 defd/netp/adcp 谁在同一窗口抬起来 → 谁就是占用者 */
task_prof_report();
}
}