feat(V4B): V4.23 — 红灯呼吸对齐vd960Loop(≈2.6s) + 上电自检基准判稳

现场测试 DLD154V4 反馈两问题修复:

1. 红灯呼吸太快 → 借鉴 vd960Loop 效果 (main.c poll_red_pwm)
   - 参数按 665/6800 缩放取整: 大步 100→39(≈5.9%), 近顶 20→10(≈1.5%),
     分界 585(88%), 峰值保持 660×3 步
   - 周期 ≈1.6s → ≈2.6s (仿真 42 步×60ms=2520ms)

2. 绿灯上电自检闪到"稳定基准值"才停 (TaskLoop.c 稳定期判稳)
   - 根因: 固定 128 样本即判稳, 与基准是否稳定无关; static 计数不清零,
     二次稳定期(安全复位)1 样本瞬间判稳
   - 修复: 全局 g_stable_cnt/g_settle_cnt, 每窗(100样本≈1s)Origin 均值
     漂移 ≤0.1% → settle++, 连续 2 窗 + 最少 128 样本判稳; 硬兜底 500
     样本(5s); 计数判稳退出/INIT_VD 清零

验证: tests/test_power_on_stable.c (200/300/500/200 样本 PASS),
      tests/test_red_breath.c (周期 2520ms/峰值保持/写值≤ARR PASS),
      gcc -fsyntax-only 两文件 0 error (借 vd960Loop AT32F421 库头)
文档: devlog 置顶 + 修订表回填 V2.12~V2.20 + V2.21; product-manual §5.1/
      核心特性; technical-spec V2.2 (§5.2 参数表/§7 判稳语义/§15 修订记录)

待现场验证: 呼吸观感、绿灯自检时长 (无车 2~3s / 首窗尖峰 3s / 5s 兜底)
This commit is contained in:
wangfq
2026-09-02 16:26:03 +08:00
parent 47898fd16e
commit cd0fd90770
8 changed files with 405 additions and 51 deletions
+140
View File
@@ -0,0 +1,140 @@
/*
* test_power_on_stable.c — 上电自检: 基准值稳定后才结束自检 (V4.23)
*
* 背景: 原逻辑 _stable_cnt 达 128 样本(≈1.3s)即判稳, 与"基准值是否真稳定"无关;
* 且 static 计数不清零, 二次稳定期(安全复位)1 个样本即判稳(绿灯自检形同虚设)。
* 修复: 稳定期累计样本 g_stable_cnt + 连续窗口 Origin 均值漂移 ≤0.1% 的
* g_settle_cnt; 两者达标才 g_loop_stable=1; 硬兜底 500 样本(5s)。
*
* 验证:
* 1. 干净常量值 → 第 200 样本判稳 (窗口100/200 两次漂移 0)
* 2. 首窗 Origin 尖峰(1.3×) → 多等一窗, 第 300 样本判稳
* 3. 持续漂移(每窗 +500, >0.1% 带) → 永不 settle, 第 500 样本硬兜底判稳
* 4. 判稳退出后计数清零, 二次稳定期重新从 0 累计(不瞬间判稳)
*
* 编译: gcc -Wall -o test_power_on_stable test_power_on_stable.c && ./test_power_on_stable
*===========================================================================*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#define WIN 100 /* 稳定期小窗口 */
#define STABLE_SAMPLES 128 /* 稳定期最少样本数 */
#define STABLE_ORIGIN_PPT 1 /* 0.1% */
#define STABLE_SETTLE_WINDOWS 2
#define STABLE_MAX_SAMPLES 500
static uint32_t origin;
static uint32_t g_origin_prev;
static uint16_t g_stable_cnt;
static uint16_t g_settle_cnt;
static uint8_t g_loop_stable;
/* 仿真 vd1_task 稳定期单样本处理; 返回 1=本样本判稳 */
static int stable_step(uint32_t value)
{
static uint32_t sum = 0;
static uint16_t cnt = 0;
uint32_t prev_origin;
int win_done = 0;
if (g_loop_stable) return 1;
if (origin == 0) return 0;
prev_origin = origin;
sum += value; cnt++;
g_stable_cnt++;
if (cnt >= WIN) {
win_done = 1;
origin = sum / WIN;
sum = 0; cnt = 0;
}
if (win_done) {
uint32_t drift = (origin > prev_origin) ? (origin - prev_origin)
: (prev_origin - origin);
uint32_t band = (uint32_t)(origin * STABLE_ORIGIN_PPT / 1000);
if (drift <= band) {
if (g_settle_cnt < 0xFFFF) g_settle_cnt++;
} else {
g_settle_cnt = 0;
}
}
if ((g_stable_cnt >= STABLE_SAMPLES &&
g_settle_cnt >= STABLE_SETTLE_WINDOWS) ||
g_stable_cnt >= STABLE_MAX_SAMPLES) {
g_loop_stable = 1;
g_stable_cnt = 0;
g_settle_cnt = 0;
return 1;
}
return 0;
}
static void reset(uint32_t initial_origin)
{
origin = initial_origin;
g_origin_prev = 0;
g_stable_cnt = 0;
g_settle_cnt = 0;
g_loop_stable = 0;
}
int main(void)
{
const uint32_t BASE = 128844UL;
int i, done;
/* --- 1. 干净常量值: 第 200 样本判稳 --- */
reset(BASE);
done = 0;
for (i = 1; i <= 600 && !done; i++) {
done = stable_step(BASE);
if (done) break;
}
printf("1. 常量值 判稳样本=%d (期望 200)\n", i);
assert(i == 200);
assert(g_stable_cnt == 0 && g_settle_cnt == 0);
/* --- 2. 首窗 Origin 尖峰(1.3×, 实际值正常): 第 300 样本判稳 --- */
reset((uint32_t)(BASE * 1.3));
done = 0;
for (i = 1; i <= 600 && !done; i++) {
done = stable_step(BASE);
if (done) break;
}
printf("2. 首窗尖峰 判稳样本=%d (期望 300)\n", i);
assert(i == 300);
/* --- 3. 持续漂移: 每窗均值 +500 (>0.1% 带) → 硬兜底 500 --- */
reset(BASE);
done = 0;
for (i = 1; i <= 600 && !done; i++) {
uint32_t w = (uint32_t)((i - 1) / WIN);
done = stable_step(BASE + 500UL * w);
if (done) break;
}
printf("3. 持续漂移 判稳样本=%d (期望 500 硬兜底)\n", i);
assert(i == 500);
/* --- 4. 二次稳定期: 计数清零, 重新累计 → 第 200 样本判稳(非 1 样本) --- */
reset(BASE);
done = 0;
for (i = 1; i <= 600 && !done; i++) {
done = stable_step(BASE);
if (done) break;
}
assert(i == 200); /* 第一次完成 */
/* 模拟安全复位: 仅清 g_loop_stable, 计数应已是 0 */
g_loop_stable = 0;
done = 0;
for (i = 1; i <= 600 && !done; i++) {
done = stable_step(BASE);
if (done) break;
}
printf("4. 二次稳定期 判稳样本=%d (期望 200, 修掉原 1 样本瞬间判稳)\n", i);
assert(i == 200);
printf("ALL PASS (test_power_on_stable)\n");
return 0;
}