ffd462ef3c
卡 5(protect_task.c 第⑤闭锁源,「数据可信」§4.3 决策 1=闭锁): - UA/UN/IA 的 adc_cal VALID 未全置位 → prot_elm_init + 本拍不 eval;每拍现查 flags、无自有状态(卡 12 标定写入后自动解锁);判据链优先级 = 采样可信在前、 标定可用在后(数据都是旧的谈不上标定) - 边沿日志用 DBG_INFO 而非 [ERR]:未标定是出厂常态、非故障,且 @test 禁用 [ERR](「默认构建保护不动作 = 期望」的台架判据同步项已在 MR 报 lead); SOE 分型 SOE_T_CAL_BLOCK/RESUME(soe_rec.h 枚举尾追加,值 6/7——reader 侧 显示标签映射归道B reader,MR 已报) - 台架旁路 = 编译期宏 DTU_BENCH(§4.3:不进 cfg 不进 104;本仓所有提交目标 都不定义它 = 产品恒强制闭锁) 卡 6(下游三常数按工程量重定,依据同源 docs/任务规划/数据可信.md §2.3): - FREQ_AMP_MIN(freq.h):语义从拟定码值域重定为二次值 V——50 V = 0.5×Un (标称 100 V),断保险/PT 空载/浮空必低于之;数值沿用 50.0f 属巧合并在 注释明写;eng=0 过渡期本门恒失效 → OF/UF 按必不启动填充,与卡 5 同向安全 - k_yc[].deadband(iec104_pts.c):9 点按「额定 0.5 %」规则重定:电压类 0.5 %×100 V=0.5、电流类 0.5 %×1 A=0.005、频率维持 0.5 Hz 绝对值(非额定 比,卡 10 运行参数化);标称基准与 prot_set 头部同源,防两处真相 - g_prot_set_meta[](prot_set.c):数值/范围复核结论=不动(0~4×标称自洽), 文件头补量纲口径块:A/V = 二次值、标称 100 V/1 A、OV120=1.2Un/UV20=.2Un/ 3U0 15=.15Un、OC1/2/3=10/5/2×Ir、OL=1.0×Ir;PS_ID_COUNT/blob VER 未动 - iec104 套件 TC-008 死区用例同步重定标(100.4 带内 / 101.25 越带),97/97 卡 7(test/host/prot_dim 新套件,真实链 adc_cal→ring→phasor→prot_elm): - 用例从 ADC 码值出发、显式写出 code↔二次值互逆换算(dim_code vs apply), 折算点被旁路或量纲再错位断言立刻翻转(M4 判据⑥「不该动作时不动作」) - 合成 ROM 覆写(#undef K_Q16 宏)模拟卡 12 标定后板级口径,走 adc_cal_set 正规路径置 VALID;b=-30 码全链对消进 rms 断言 - 38/38:基线静默(含 OL 低定值陷阱)、反错配对照(raw 码值灌环复现历史事故 必假动作 vs 经折算必静默)、OC1/2/3+OL+OV+UV+3U0 七类逐一:越限定值→ 受控启动拍→dly-2 不动作/+5 动作→返回系数带内保持→跌破释放、双件聚合位图 独立性(撤 IA 只回 OC1) 测试与注册:prot-dim-unit-test 钩子 + TC-HOST-PROTDIM 族(游标 010,B 层口径 同 ADCHEALTH/ADCCAL 先例);无新 MCU 源文件(uvprojx/SIM_EXEMPT 零改动) gate:@build 0 错 0 警(Code=215640 RO=21388 RW=1220 ZI=131980 vs 基线 215440/19780/1220/131980 → Flash +1808 B、RAM 零增量);protectTask 静态最坏 链 872 B/margin 1176 B(57%)PASS,adcTask 不变;staged 门禁除 mem-numbers/ mem-budget(台账漂移,回填归 lead)外全绿: gate: only mem-budget/mem-numbers pending lead ledger backfill BOM/CR/U+FFFD/字面量 ASCII 已逐文件自查(本轮未触碰带 BOM 文件)
476 lines
19 KiB
C
476 lines
19 KiB
C
/*
|
||
* 模块名称:量纲回归主机侧单测(「数据可信」卡 7 · 道A)
|
||
* 被测对象:真实源文件(#include 原样纳入,全链路 L1.5→L2→L3):
|
||
* adc_cal.c + prot_set.c + sample_ring.c + phasor.c + freq.c +
|
||
* prot_elm.c
|
||
* 平台依赖桩:dbg_log.h(stub/)、cfg_file_register(捕获计数)、
|
||
* adc_input_desc(V0.1 权威来源表,同 adc_cal 套件)。
|
||
*
|
||
* 本卡的存在理由(短板评测 + 「数据可信」§2.2):台架此前"通过"是因为没有
|
||
* 一条判据检查**不该动作时不动作**——"码值当工程量"的错配曾让 OC 拿 ~11000
|
||
* 码值比 10 A 定值恒假动作。本套件把这条固化成回归:用例从 **ADC 码值** 出发、
|
||
* **显式写出码值↔二次值换算**,走真实 折算→采样环→全周傅氏→元件判据 链;
|
||
* 任何一环把量纲弄丢(比如折算点被旁路),断言立刻翻转。
|
||
*
|
||
* ROM 系数覆写(#undef/#define):把"板级理论系数"替换为合成端子侧口径
|
||
* (10 mV/码,K_DIM),模拟卡 12 标定完成后的 ROM/默认状态——合法带随之
|
||
* 锚定合成值,adc_cal_set 走正规路径置 VALID,测试不白盒改引擎内存。
|
||
*
|
||
* 覆盖点(族 TC-HOST-PROTDIM,属主登记处 = 本目录):
|
||
* TC-PROTDIM-001 标定链基线:0.5 A/100 V/0 V 注入 → 七类元件全静默(含
|
||
* OL:0.5 < 1 A 定值)+ IA 相量有效值 = 0.5 A ±0.5 %
|
||
* TC-PROTDIM-002 反错配对照:同一批码值,raw 直灌 → OC1 假动作(复现历史
|
||
* 事故);经 adc_cal_apply → OC1 静默(折算点不可旁路)
|
||
* TC-PROTDIM-003 OC1(10 A,dly=0):9.995 不动作 / 10.5 启动即动作 /
|
||
* 9.6 滞回保持 / 9.49 释放
|
||
* TC-PROTDIM-004 OC2(5 A,500 ms):6 A 启动,启动后 499 ms 不动作、
|
||
* 501 ms 动作;4.8 保持(ret 4.75)、4.6 释放
|
||
* TC-PROTDIM-005 OC3(2 A,3000 ms):同上时序(2.1 A)
|
||
* TC-PROTDIM-006 OL(1 A,10 s):1.05 A 启动、9999/10001 ms 时序、
|
||
* 0.96 保持(ret 0.95)、0.93 释放
|
||
* TC-PROTDIM-007 OV(120 V,10 s):100 V 静默;121 V 启动、到点动作;
|
||
* 115 保持(ret 114)、113 释放
|
||
* TC-PROTDIM-008 UV(20 V,10 s,欠量方向):21 V 静默;19.9 V 启动、
|
||
* 到点动作;20.5 保持(ret 21)、21.6 释放
|
||
* TC-PROTDIM-009 3U0(15 V,5 s):14 V 静默;15.1 启动到点;
|
||
* 14.4 保持(ret 14.25)、14.1 释放
|
||
* TC-PROTDIM-010 聚合与独立性:OC1+OV 同拍动作位图 = 双位;撤 IA →
|
||
* OC1 返回、OV 保持(互不污染)
|
||
*
|
||
* 通过:全部 TC PASS(main 返回 0)。
|
||
* 创建日期:2026-10-25(道A 卡 7)
|
||
*/
|
||
|
||
#include <math.h>
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
/* 桩头先于生产源码 */
|
||
#include "dbg_log.h"
|
||
int g_dimtest_err_count = 0;
|
||
int g_dimtest_info_count = 0;
|
||
int g_dimtest_debug_count = 0;
|
||
|
||
#include "cfg_file.h"
|
||
|
||
static int s_reg_count = 0;
|
||
|
||
int cfg_file_register(const cfg_section_reg_t *p_reg) {
|
||
(void)p_reg;
|
||
s_reg_count++;
|
||
return 0;
|
||
}
|
||
|
||
/* V0.1 权威来源表(docs/硬件平台/ADC通道映射说明.md §3/§5,同 adc_cal 套件桩) */
|
||
#include "adc_input.h"
|
||
|
||
static const adc_channel_desc_t s_desc_stub[ADC_SEM_COUNT] = {
|
||
{ ADC_SRC_TPAFE, 6u }, { ADC_SRC_STM32, 14u }, { ADC_SRC_STM32, 3u },
|
||
{ ADC_SRC_TPAFE, 5u }, { ADC_SRC_TPAFE, 2u }, { ADC_SRC_STM32, 6u },
|
||
{ ADC_SRC_STM32, 1u }, { ADC_SRC_STM32, 2u }, { ADC_SRC_STM32, 5u },
|
||
};
|
||
|
||
const adc_channel_desc_t *adc_input_desc(adc_semantic_t ch) {
|
||
if ((unsigned)ch >= (unsigned)ADC_SEM_COUNT) {
|
||
return NULL;
|
||
}
|
||
return &s_desc_stub[(unsigned)ch];
|
||
}
|
||
|
||
/* ---- 测试框架(同 prot_elm 套件) ---- */
|
||
static int s_total = 0, s_passed = 0, s_failed = 0;
|
||
|
||
#define CHECK(cond, name) do { \
|
||
s_total++; \
|
||
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
|
||
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
|
||
} while (0)
|
||
|
||
/* ---- 合成"标定后"板级口径(覆写 adc_cal.h 的 ROM 常量,必须在纳入 .c 之前) ----
|
||
* K_DIM = 655360000 Q16 = 10000 µV/码 = 10 mV/码:
|
||
* 电压类 100 V rms → 峰值 141.42 V → 14142 码(TPAFE 半量程内,量级刻意
|
||
* 贴近卡 12 端子变比标定后的真实形态);电流类二次值经变换器落到同一定标。
|
||
* B_IA = -30 码(零点偏移,经 apply 精确对消——验证 b 参与全链而非仅落盘)。 */
|
||
#include "adc_cal.h"
|
||
#undef ADC_CAL_K_Q16_TPAFE
|
||
#undef ADC_CAL_K_Q16_STM32
|
||
#define ADC_CAL_K_Q16_TPAFE (655360000L)
|
||
#define ADC_CAL_K_Q16_STM32 (655360000L)
|
||
#define K_DIM ADC_CAL_K_Q16_TPAFE
|
||
#define B_IA_DIM (-30L)
|
||
|
||
/* ===================== 被测生产源文件 ===================== */
|
||
#include "prot_set.c"
|
||
#include "sample_ring.c"
|
||
#include "phasor.c"
|
||
#include "freq.c"
|
||
#include "prot_elm.c"
|
||
#include "adc_cal.c"
|
||
|
||
/* ===================== 波形/时基(同 prot_elm 套件 run() 级用例口径) ===================== */
|
||
static uint32_t s_t = 0u;
|
||
|
||
static long my_lround(double x) { return (long)((x >= 0.0) ? (x + 0.5) : (x - 0.5)); }
|
||
|
||
/* 显式换算(卡 7 的核心手法,两处方向都要有):
|
||
* code = round(eng[V 或 A] × 65536×1e6 / k_q16) − b (期望 → 码值)
|
||
* eng = (code + b) × k_q16 / 65536 / 1e6 = adc_cal_apply (码值 → 工程量)
|
||
* 上面两式必须互逆——若实现(adc_cal_apply)与期望推导脱钩,全部 TC 翻转。 */
|
||
static int32_t dim_code(double eng, double b_codes) {
|
||
return (int32_t)(my_lround(eng * 65536.0e6 / (double)K_DIM) - b_codes);
|
||
}
|
||
|
||
/* 一拍:三相量按 50 Hz 正弦(峰值 = rms×√2),其余电气量通道给 0 码值,
|
||
* TEMP 透传 0;随后跑真实 L2 链。 */
|
||
static void dim_beat(double ia_rms, double ua_rms, double un_rms) {
|
||
float frame[ADC_SEM_COUNT];
|
||
double s50 = sin(2.0 * 3.14159265358979 * 50.0 * (double)s_t / 1000.0);
|
||
int32_t ci_ia, ci_ua, ci_un;
|
||
uint8_t c;
|
||
|
||
ci_ia = dim_code(ia_rms * 1.4142135623730951 * s50, (double)B_IA_DIM);
|
||
ci_ua = dim_code(ua_rms * 1.4142135623730951 * s50, 0.0);
|
||
ci_un = dim_code(un_rms * 1.4142135623730951 * s50, 0.0);
|
||
|
||
frame[ADC_SEM_IA] = adc_cal_apply(ADC_SEM_IA, ci_ia);
|
||
frame[ADC_SEM_UA] = adc_cal_apply(ADC_SEM_UA, ci_ua);
|
||
frame[ADC_SEM_UN] = adc_cal_apply(ADC_SEM_UN, ci_un);
|
||
for (c = 0u; c < (uint8_t)ADC_SEM_COUNT; c++) {
|
||
if ((c != (uint8_t)ADC_SEM_IA) && (c != (uint8_t)ADC_SEM_UA) &&
|
||
(c != (uint8_t)ADC_SEM_UN)) {
|
||
frame[c] = adc_cal_apply((adc_semantic_t)c, 0L);
|
||
}
|
||
}
|
||
(void)sample_ring_push_frame(frame, (uint8_t)ADC_SEM_COUNT);
|
||
phasor_run();
|
||
freq_run();
|
||
prot_elm_run(s_t);
|
||
s_t++;
|
||
}
|
||
|
||
/* TC 起手:定值/元件全复位 + 标定注入(set 正规路径)+ 中性 45 拍收敛 */
|
||
static void dim_setup(void) {
|
||
prot_set_init();
|
||
adc_cal_init();
|
||
(void)adc_cal_set(ADC_SEM_UA, K_DIM, 0L);
|
||
(void)adc_cal_set(ADC_SEM_UN, K_DIM, 0L);
|
||
(void)adc_cal_set(ADC_SEM_IA, K_DIM, B_IA_DIM);
|
||
prot_elm_init();
|
||
}
|
||
|
||
static void set_en(prot_set_id_t id, int on) {
|
||
(void)prot_set_set(id, PROT_SET_Q10(on ? 1.0 : 0.0));
|
||
}
|
||
|
||
/* 只留一个电气量元件家族:默认全 EN=1,逐个关到只剩目标 */
|
||
static void isolate_oc_family(void) {
|
||
set_en(PS_OC1_EN, 1); set_en(PS_OC2_EN, 0); set_en(PS_OC3_EN, 0);
|
||
set_en(PS_OL_EN, 0); set_en(PS_OV_EN, 0); set_en(PS_UV_EN, 0);
|
||
set_en(PS_3U0_EN, 0);
|
||
}
|
||
|
||
static int pick_of(prot_elm_id_t e) {
|
||
prot_elm_state_t st;
|
||
prot_elm_state(e, &st);
|
||
return (st.picked != 0u) ? 1 : 0;
|
||
}
|
||
|
||
static int assert_of(prot_elm_id_t e) {
|
||
prot_elm_state_t st;
|
||
prot_elm_state(e, &st);
|
||
return (st.asserted != 0u) ? 1 : 0;
|
||
}
|
||
|
||
/* 把 IA 拉到 far_below 收敛 45 拍(确保 picked 干净归零、t0 无效) */
|
||
static void settle_oc(double ia_rms, uint32_t beats) {
|
||
uint32_t i;
|
||
for (i = 0u; i < beats; i++) { dim_beat(ia_rms, 100.0, 0.0); }
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-001 标定链基线 ===================== */
|
||
static void tc_001_baseline(void) {
|
||
phasor_t p;
|
||
double want_rms;
|
||
double got;
|
||
uint32_t i;
|
||
int quiet = 1;
|
||
|
||
printf("[TC-PROTDIM-001] calibrated chain baseline: no element moves\n");
|
||
dim_setup();
|
||
for (i = 0u; i < 60u; i++) { dim_beat(0.5, 100.0, 0.0); }
|
||
|
||
/* 换算链可验证:C01-IA(idx0)有效值 = 注入 0.5 A ±0.5 %
|
||
* (b=-30 码应被 apply 精确对消;若折算丢失,这里会是 ~700 量级码值) */
|
||
want_rms = 0.5;
|
||
CHECK(phasor_get(0u, &p) == 0, "001: C01-IA phasor ready");
|
||
got = (double)p.fAmSQ;
|
||
CHECK((got > want_rms * 0.995) && (got < want_rms * 1.005),
|
||
"001: IA rms = 0.5 A (secondary domain, b cancelled)");
|
||
|
||
for (i = 0u; i < (uint32_t)PE_OC1; i++) {
|
||
if ((pick_of((prot_elm_id_t)i) != 0) || (assert_of((prot_elm_id_t)i) != 0)) {
|
||
quiet = 0;
|
||
}
|
||
}
|
||
CHECK(quiet, "001: OC1/OC2/OC3/OL/OV/UV/3U0 all silent at 0.5A/100V/0V");
|
||
CHECK(prot_elm_active_mask() == 0u, "001: active mask 0");
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-002 反错配对照 ===================== */
|
||
static void tc_002_mismatch_trap(void) {
|
||
float raw[ADC_SEM_COUNT];
|
||
uint32_t i;
|
||
|
||
printf("[TC-PROTDIM-002] raw-codes-as-engineering trap / apply guard\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_OC2_EN, 0);
|
||
|
||
/* (a) 复现历史事故:把"码值本身"当工程量灌环(等价于折算点被旁路)。
|
||
* 3.36 A 物理电流的码值 ~672 → 比 OC1(10 A) → 必假动作 */
|
||
for (i = 0u; i < (uint32_t)ADC_SEM_COUNT; i++) {
|
||
raw[i] = 0.0f;
|
||
}
|
||
for (i = 0u; i < 45u; i++) {
|
||
double s50 = sin(2.0 * 3.14159265358979 * 50.0 * (double)s_t / 1000.0);
|
||
raw[ADC_SEM_IA] = (float)((double)dim_code(3.36 * 1.4142135623730951 * s50,
|
||
(double)B_IA_DIM));
|
||
(void)sample_ring_push_frame(raw, (uint8_t)ADC_SEM_COUNT);
|
||
phasor_run();
|
||
prot_elm_run(s_t);
|
||
s_t++;
|
||
}
|
||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||
"002a: raw code domain falsely trips OC1 (documents the incident)");
|
||
|
||
/* (b) 同一物理量经正规折算链:3.36 A < 10 A → 必须静默。
|
||
* (若生产链有人绕过 adc_cal_apply,本断言与 001 一起翻红) */
|
||
for (i = 0u; i < 45u; i++) { dim_beat(3.36, 100.0, 0.0); }
|
||
CHECK((pick_of(PE_OC1) == 0) && (assert_of(PE_OC1) == 0),
|
||
"002b: same physical current via apply() stays silent");
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-003 OC1:滞回 + dly0 ===================== */
|
||
static void tc_003_oc1(void) {
|
||
printf("[TC-PROTDIM-003] OC1 10A/dly0 hysteresis\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
|
||
settle_oc(3.0, 45u);
|
||
settle_oc(9.995, 45u);
|
||
CHECK(pick_of(PE_OC1) == 0, "003: 9.995 A below set: no pickup");
|
||
|
||
settle_oc(10.5, 45u);
|
||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||
"003: 10.5 A -> picked AND asserted (dly=0 same-beat)");
|
||
|
||
settle_oc(9.6, 45u);
|
||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||
"003: 9.6 A inside hysteresis (ret=9.5): holds");
|
||
|
||
settle_oc(9.49, 45u);
|
||
CHECK((pick_of(PE_OC1) == 0) && (assert_of(PE_OC1) == 0),
|
||
"003: 9.49 A below return threshold: drops out");
|
||
}
|
||
|
||
/* 过量定时限通用核:启动拍受控(先远低收敛),到点前后各验一次 */
|
||
static void timed_over_case(prot_elm_id_t e, prot_set_id_t cur_id, double trip,
|
||
double hold, double drop, float dly_ms,
|
||
const char *tag) {
|
||
uint32_t t_pick = 0u;
|
||
uint32_t i;
|
||
uint32_t settle;
|
||
char nm[96];
|
||
int pre_ok = 1, post_ok = 0, hold_ok = 0, drop_ok = 0, trip_ok = 0;
|
||
|
||
(void)cur_id;
|
||
(void)tag;
|
||
/* 1) 远低收敛;2) 阶跃到 trip,逐拍轮询到 picked(记录启动拍) */
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.1 * trip, 100.0, 0.0); }
|
||
for (i = 0u; i < 400u; i++) {
|
||
dim_beat(trip, 100.0, 0.0);
|
||
if (pick_of(e) != 0) { t_pick = s_t - 1u; trip_ok = 1; break; }
|
||
}
|
||
if (trip_ok == 0) {
|
||
(void)snprintf(nm, sizeof(nm), "%s: step to trip level picks up", tag);
|
||
CHECK(0, nm);
|
||
return;
|
||
}
|
||
/* 3) 到点前 2 ms 不许动作、到点后 2 ms 必须动作(同幅值持续注入) */
|
||
settle = 0u;
|
||
for (i = t_pick; i < (t_pick + (uint32_t)dly_ms - 2u); i++) {
|
||
dim_beat(trip, 100.0, 0.0);
|
||
settle++;
|
||
}
|
||
(void)settle;
|
||
if (assert_of(e) != 0) { pre_ok = 0; }
|
||
for (i = 0u; i < 5u; i++) { dim_beat(trip, 100.0, 0.0); }
|
||
post_ok = assert_of(e);
|
||
|
||
(void)snprintf(nm, sizeof(nm), "%s: no assert before setpoint-2ms", tag);
|
||
CHECK(pre_ok != 0 && post_ok != 0, nm);
|
||
|
||
/* 4) 返回系数滞回带内保持、跌破释放 */
|
||
for (i = 0u; i < 45u; i++) { dim_beat(hold, 100.0, 0.0); }
|
||
hold_ok = (pick_of(e) != 0) && (assert_of(e) != 0);
|
||
for (i = 0u; i < 45u; i++) { dim_beat(drop, 100.0, 0.0); }
|
||
drop_ok = (pick_of(e) == 0) && (assert_of(e) == 0);
|
||
|
||
(void)snprintf(nm, sizeof(nm), "%s: holds inside return band", tag);
|
||
CHECK(hold_ok != 0, nm);
|
||
(void)snprintf(nm, sizeof(nm), "%s: returns below return threshold", tag);
|
||
CHECK(drop_ok != 0, nm);
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-004~006 OC2/OC3/OL ===================== */
|
||
static void tc_004_oc2(void) {
|
||
printf("[TC-PROTDIM-004] OC2 5A/500ms timed+return\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_OC2_EN, 1);
|
||
timed_over_case(PE_OC2, PS_OC2_CUR, 6.0, 4.8, 4.6, 500.0f, "004:OC2");
|
||
}
|
||
|
||
static void tc_005_oc3(void) {
|
||
printf("[TC-PROTDIM-005] OC3 2A/3000ms timed+return\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_OC3_EN, 1);
|
||
timed_over_case(PE_OC3, PS_OC3_CUR, 2.1, 1.95, 1.89, 3000.0f, "005:OC3");
|
||
}
|
||
|
||
static void tc_006_ol(void) {
|
||
printf("[TC-PROTDIM-006] OL 1A/10s timed+return\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_OL_EN, 1);
|
||
timed_over_case(PE_OL, PS_OL_CUR, 1.05, 0.96, 0.93, 10000.0f, "006:OL");
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-007 OV(电压域走 UA) ===================== */
|
||
static void tc_007_ov(void) {
|
||
uint32_t t_pick = 0u;
|
||
uint32_t i;
|
||
int ok;
|
||
|
||
printf("[TC-PROTDIM-007] OV 120V/10s (UA secondary domain)\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_OV_EN, 1);
|
||
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 0.0); }
|
||
CHECK(pick_of(PE_OV) == 0, "007: nominal 100 V silent");
|
||
for (i = 0u; i < 400u; i++) {
|
||
dim_beat(0.0, 121.0, 0.0);
|
||
if (pick_of(PE_OV) != 0) { t_pick = s_t - 1u; break; }
|
||
}
|
||
CHECK(pick_of(PE_OV) != 0, "007: 121 V picks up");
|
||
for (i = t_pick; i < (t_pick + 9998u); i++) { dim_beat(0.0, 121.0, 0.0); }
|
||
ok = (assert_of(PE_OV) == 0);
|
||
for (i = 0u; i < 5u; i++) { dim_beat(0.0, 121.0, 0.0); }
|
||
CHECK((ok != 0) && (assert_of(PE_OV) != 0),
|
||
"007: asserts exactly at +10000ms window (not earlier)");
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 115.0, 0.0); }
|
||
CHECK(assert_of(PE_OV) != 0, "007: 115 V holds (ret=114)");
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 113.0, 0.0); }
|
||
CHECK((pick_of(PE_OV) == 0) && (assert_of(PE_OV) == 0),
|
||
"007: 113 V returns");
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-008 UV(欠量方向) ===================== */
|
||
static void tc_008_uv(void) {
|
||
uint32_t t_pick = 0u;
|
||
uint32_t i;
|
||
int ok;
|
||
|
||
printf("[TC-PROTDIM-008] UV 20V/10s under-direction\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_UV_EN, 1);
|
||
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 21.0, 0.0); }
|
||
CHECK(pick_of(PE_UV) == 0, "008: 21 V above set(20): silent");
|
||
for (i = 0u; i < 400u; i++) {
|
||
dim_beat(0.0, 19.9, 0.0);
|
||
if (pick_of(PE_UV) != 0) { t_pick = s_t - 1u; break; }
|
||
}
|
||
CHECK(pick_of(PE_UV) != 0, "008: 19.9 V picks up");
|
||
for (i = t_pick; i < (t_pick + 9998u); i++) { dim_beat(0.0, 19.9, 0.0); }
|
||
ok = (assert_of(PE_UV) == 0);
|
||
for (i = 0u; i < 5u; i++) { dim_beat(0.0, 19.9, 0.0); }
|
||
CHECK((ok != 0) && (assert_of(PE_UV) != 0), "008: asserts at +10000ms");
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 20.5, 0.0); }
|
||
CHECK(assert_of(PE_UV) != 0, "008: 20.5 V holds under return(21)");
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 21.6, 0.0); }
|
||
CHECK((pick_of(PE_UV) == 0) && (assert_of(PE_UV) == 0), "008: 21.6 V returns");
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-009 3U0(UN 通道) ===================== */
|
||
static void tc_009_3u0(void) {
|
||
uint32_t t_pick = 0u;
|
||
uint32_t i;
|
||
int ok;
|
||
|
||
printf("[TC-PROTDIM-009] 3U0 15V/5s via UN channel\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_3U0_EN, 1);
|
||
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 14.0); }
|
||
CHECK(pick_of(PE_3U0) == 0, "009: 14 V below set(15): silent");
|
||
for (i = 0u; i < 400u; i++) {
|
||
dim_beat(0.0, 100.0, 15.1);
|
||
if (pick_of(PE_3U0) != 0) { t_pick = s_t - 1u; break; }
|
||
}
|
||
CHECK(pick_of(PE_3U0) != 0, "009: 15.1 V picks up");
|
||
for (i = t_pick; i < (t_pick + 4998u); i++) { dim_beat(0.0, 100.0, 15.1); }
|
||
ok = (assert_of(PE_3U0) == 0);
|
||
for (i = 0u; i < 5u; i++) { dim_beat(0.0, 100.0, 15.1); }
|
||
CHECK((ok != 0) && (assert_of(PE_3U0) != 0), "009: asserts at +5000ms");
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 14.4); }
|
||
CHECK(assert_of(PE_3U0) != 0, "009: 14.4 V holds (ret=14.25)");
|
||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 14.1); }
|
||
CHECK((pick_of(PE_3U0) == 0) && (assert_of(PE_3U0) == 0), "009: 14.1 V returns");
|
||
}
|
||
|
||
/* ===================== TC-PROTDIM-010 聚合位图与独立性 ===================== */
|
||
static void tc_010_mask(void) {
|
||
uint32_t i;
|
||
|
||
printf("[TC-PROTDIM-010] dual assert mask independence\n");
|
||
dim_setup();
|
||
isolate_oc_family();
|
||
set_en(PS_OV_EN, 1);
|
||
(void)prot_set_set(PS_OV_DLY, PROT_SET_Q10(0.0)); /* 同拍聚合,聚焦位图 */
|
||
|
||
for (i = 0u; i < 45u; i++) { dim_beat(10.5, 121.0, 0.0); }
|
||
CHECK((assert_of(PE_OC1) != 0) && (assert_of(PE_OV) != 0),
|
||
"010: OC1+OV both asserted");
|
||
CHECK(prot_elm_active_mask() == ((1uL << PE_OC1) | (1uL << PE_OV)),
|
||
"010: active mask = bit0|bit4 exactly");
|
||
|
||
for (i = 0u; i < 45u; i++) { dim_beat(3.0, 121.0, 0.0); }
|
||
CHECK((assert_of(PE_OC1) == 0) && (assert_of(PE_OV) != 0),
|
||
"010: IA withdrawn -> OC1 returns, OV untouched");
|
||
CHECK(prot_elm_active_mask() == (1uL << PE_OV), "010: mask = OV only");
|
||
}
|
||
|
||
int main(void) {
|
||
printf("=== Prot Dim Regression Tests ===\n");
|
||
adc_cal_init(); /* 先把 RAM 系数置成合成 ROM(各 TC 内还会重跑) */
|
||
tc_001_baseline();
|
||
tc_002_mismatch_trap();
|
||
tc_003_oc1();
|
||
tc_004_oc2();
|
||
tc_005_oc3();
|
||
tc_006_ol();
|
||
tc_007_ov();
|
||
tc_008_uv();
|
||
tc_009_3u0();
|
||
tc_010_mask();
|
||
printf("\n=== Prot Dim Regression Tests: %d/%d PASSED (failed=%d) ===\n",
|
||
s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|