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:
+40
-4
@@ -46,6 +46,14 @@
|
||||
* ⚠ L1-5 通道标定未实现(缺标准源)→ 暂以**原始码值**
|
||||
* 充作工程量(系数 = 1),故相量读数只证明**链路通**,
|
||||
* 不可作幅值精度判据(同 L1-3 的替代口径)。
|
||||
* v2.5 2026-10-10 王建锋 Task #32 的 P2 测量埋点:单拍加常开耗时探针
|
||||
* (task_prof ADC_BEAT),用于判定本任务是否超拍
|
||||
* (名义节拍 1 ms = 168000 周期)而挤占低优先级任务
|
||||
* v2.6 2026-10-10 王建锋 Task #32 的 P0 修复(两处):
|
||||
* §5 无新帧仍压环 → 改为**仅 r == 1 时**压环 + 重算
|
||||
* (消除重复采样污染 20 点 DFT 窗的系统性误差);
|
||||
* §4.3 osDelayUntil 返回值被丢弃 → 落后时一次性重同步
|
||||
* (原写法会永久落后 → 不再阻塞 → 空转抢占 uiTask)
|
||||
*/
|
||||
|
||||
#include "adc_task.h"
|
||||
@@ -56,6 +64,7 @@
|
||||
#include "tpafe5160.h"
|
||||
#include "task_watchdog.h"
|
||||
#include "console_log.h"
|
||||
#include "task_prof.h" /* P2 常开探针:单拍耗时(Task #32) */
|
||||
|
||||
#define DBG_TAG "[ADC_TASK]"
|
||||
#include "dbg_log.h"
|
||||
@@ -134,10 +143,19 @@ void adc_task_func(void *arg) {
|
||||
for (;;) {
|
||||
int r;
|
||||
|
||||
/* P2 探针:单拍起点。名义节拍 1 ms = 168000 周期;若实测远超,
|
||||
* 本任务(osPriorityNormal)会持续抢占 uiTask(osPriorityBelowNormal),
|
||||
* 是"UI 心跳缺席"的候选占用者之一 */
|
||||
task_prof_enter(TASK_PROF_ADC_BEAT);
|
||||
|
||||
/* 1) 采集一拍:L1 层 adc_input_poll() = TPAFE 原子快照 + STM32 ADC1 顺序扫描
|
||||
* (仅任务上下文;TPAFE 侧 1=有新帧 0=无新帧 -1=参数错)。
|
||||
* 采样节拍由 TIM6 更新中断提供(ISR 内触发 CONVST),本任务不触发。 */
|
||||
* 采样节拍由 TIM6 更新中断提供(ISR 内触发 CONVST),本任务不触发。
|
||||
* P2 探针:本段单独计时——ADC 超时是 2ms 不让出的忙等(HAL_ADC_
|
||||
* PollForConversion),是单拍耗时的主要来源,必须与压环/相量分开看 */
|
||||
task_prof_enter(TASK_PROF_ADC_POLL);
|
||||
r = adc_input_poll();
|
||||
task_prof_exit(TASK_PROF_ADC_POLL);
|
||||
if (r == 1) {
|
||||
s_frames_consumed++;
|
||||
} else if (r == 0) {
|
||||
@@ -148,9 +166,18 @@ void adc_task_func(void *arg) {
|
||||
|
||||
/* 2) L2 输入 + 相量重算:把本拍 9 路语义值转浮点帧压入采样环,再重算全部
|
||||
* 相量实例(L2-6 滑动窗:每拍以"当前时刻为终止的前一周波"整窗重算)。
|
||||
*
|
||||
* ⚠ P0 修复(2026-10-10,Task #32 §5):**仅在本拍确有新帧(r == 1)时**才压环。
|
||||
* 原实现在 r == 0(TPAFE 无新帧)时**仍然**压环 + phasor_run(),只有 r == -1
|
||||
* 才跳过 —— 于是把**上一拍的重复采样值**塞进 20 点 DFT 窗,等效采样间隔被拉长。
|
||||
* 证据是日志里的恒等式 `ring = frames + idle`(09:36:21: 9957+44=10001 ✓;
|
||||
* 09:36:31: 19352+649=20001 ✓)。本例 idle 占 3.2% → 窗长从 20.0 ms 漂到约
|
||||
* 20.6 ms → 对 50 Hz 信号相当于约 3% 的频率失配 → 幅值/相位**系统性**误差
|
||||
* (不是随机噪声)。修好后 `ring` 只在真实新帧上增长(ring == frames)。
|
||||
*
|
||||
* 任一通道读不到(尚未首次成功)→ 本拍**整帧跳过**:不向窗内注入 0 点
|
||||
* (单个 0 点会显著污染 20 点窗),并计入 skip 观测。 */
|
||||
{
|
||||
if (r == 1) {
|
||||
float frame[ADC_SEM_COUNT];
|
||||
uint8_t s;
|
||||
uint8_t all_ok = 1u;
|
||||
@@ -239,8 +266,17 @@ void adc_task_func(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 7) 等下一个节拍(固定周期;落后则立即补齐,不累积漂移) */
|
||||
/* 7) 等下一个节拍(固定周期;落后则立即补齐,不累积漂移)
|
||||
* ⚠ P0 修复(2026-10-10,Task #32 §4.3):osDelayUntil 在**目标时刻已过**
|
||||
* 时返回 osErrorParameter 并立即返回(cmsis_os2.c 的 "No delay or already
|
||||
* expired" 分支)。若像原实现那样只写 `(void)` 丢弃返回值,一次超拍会让
|
||||
* next 相对当前时刻**永久落后** → 本任务从此不再阻塞、以 Normal 优先级
|
||||
* 空转,直到某个"快拍"把差额追平(连续慢拍则锁死,全程抢占低优先级的
|
||||
* uiTask)。返回值是现成的重同步钩子:落后就一次性把基准拉回当前时刻。 */
|
||||
task_prof_exit(TASK_PROF_ADC_BEAT); /* P2 探针:单拍耗时结算(不含睡眠) */
|
||||
next += ADC_TASK_BEAT_MS;
|
||||
(void)osDelayUntil(next);
|
||||
if (osDelayUntil(next) != osOK) {
|
||||
next = osKernelGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user