Files
STM32F4-Base/App/cfg/prot_set_store.c
T
wandering1 0264b49489 feat(algo): B8 频率测量层 + 过频/低频保护(L3-5)
新增 App/algo/freq.c/h:由 L2 基波相量导出系统频率。相邻两拍共轭
乘积 P=Z(m)·conj(Z(m-1)) 取角 = 每拍相位增量(免显式解缠绕),再按
自适应窗长 K_eff=round(8·Fs/(2·f_hat))(45~55 Hz 段 73~89 拍)做滑动
平均。定长窗受 20 点矩形窗负序泄漏纹波限制(实测扫频最坏 0.317 Hz;
60 拍 Kay 复数和 0.197 Hz,交叉项和角不按 1/K^2 衰减),自适应窗使
窗长恰为纹波周期 fs/(2f) 整数倍 -> 最坏 0.0101 Hz。推导与两轮弯路
写在 freq.c 文件头。

假值钳制带取 35~65 Hz,刻意宽于系统合理带 40.5~60.5:单拍相位差本身
带 ±5 Hz 泄漏纹波,贴边钳制会非对称丢弃纹波半周 -> 均值偏置(实测
45 Hz 点 +0.16 Hz)。系统合理带由 OF/UF 定值范围在消费侧把关。
幅值门 FREQ_AMP_MIN=50 码(拟定,待 L1-5 标定复核):低于门判无有效
波形,停更新、valid 清零、读数保持 -> 浮空/PT 断线不发散。源优先级
UA(C02)->IA(C01),按名前缀解析、每拍动态回退,切换即废相位基准。

prot_set:尾追加 8 项(PS_ID_COUNT 32->40,编号不回收)
OF_EN/OF_HZ/OF_DLY/UF_EN/UF_HZ/UF_DLY/RET_OF/RET_UF。OF/UF 逐项通讯
地址不在本仓库 -> 全部 comm_addr=0 本地项(104 C_SE 不可寻址,待对照
参考表补)。频率返回系数独立(0.99/1.01),勿与电气量 0.95/1.05 共用。
bad_mask 随项数扩为 uint64_t。PROT_SET_BLOB_VER 1->2。

prot_set_store:换代与损坏分流。load 返回 2 = 版本/项数换代(本文件
打 INFO "prot set blob version change, defaults in use"),-1 = 真损坏
(defaultTask 按 CORRUPT 报 ERROR + 自检留痕)。B7 旧档(ver=1/32 项)
升级首启属预期路径,不得报成损坏。语义分流收在属主文件内,
default_task.c 不动。

prot_elm:实例表加 src 字段(0=phasor rms,1=freq),PE_OF/PE_UF 尾
追加(PE_COUNT 7->9)。freq 无有效读数按方向填"必不启动"值(过量 0 /
欠量 1e30 -> 滞回比较恒假 -> 元件复位),不整拍跳过——频率源缺失不得
饿死电流/电压类元件(B5 行为不回归)。OF/UF 暂未映射独立 104 遥信点,
动作只经 YX15 保护总聚合可见(送检点表 = B10)。

板端接线:adcTask 在 phasor_run() 之后同拍调 freq_run();心跳/首拍日志
追加 freq(<tag>): f=%ld valid=%u (x0.01)(MicroLIB 无 %f)。app_main
内核前 freq_init()。

主机单测:freq 46 用例(J1 扫频 45~55 Hz <=0.02 Hz 实测最坏 0.0101;
J2 噪声 1% <=0.10 实测 0.0160;J3 启动收敛 <=40 ms 实测 20;J4 阶跃
50->51 <=120 ms 实测 98;J5 浮空 valid=0 不发散;J6 跳变 60 ms 内回
±0.05;另直流 8000 码 / 3-5 次谐波 20%/10% / UA->IA 回退)。
prot_set 90(+TC-PS-011 OF/UF 行 + 64 位 bad_mask bit39 + 旧档拒绝)、
prot_elm 47(+OF/UF 滞回与方向隔离 + run() 测频接线)。回归:phasor
57、iec104 94 全绿。

J4=120 ms 是"精度优先于响应"的显式取舍(Lead 批准):4 周波窗换
0.317->0.0101 Hz,测频响应 98 ms << OF/UF 定时限默认 1000 ms(占
总动作时间 <10%),暂态另有 J6 <=60 ms 兜底。

注册:freq.c 入 uvprojx algo 组 + check_registration.py SIM_EXEMPT
(纯算法、模拟器无消费路径,照 prot_set.c 先例)。

@build 0 错误 0 警告(Code=207468 RO=18304 RW=1068 ZI=153084)。
RAM:RW_IRAM1 96096->96728 B(+632 B,73.3%->73.8%)、RW_CCM 不变
57376 B(87.9%)、Flash 224596->226840 B(+2244 B,21.4%->21.6%);
台账随合并由 Lead 回填。stack_report:adcTask 静态最坏链 1320 B /
余量 1240 B(48%)改前后完全不变(freq_run 帧未进最坏链,无需抬栈);
protectTask 链 872 B / 余 1176 B(57%)同样不变。

gate: only mem-budget/mem-numbers pending lead ledger backfill
--------------------------------------------------------------
门禁实跑(pre-commit run --all-files,Lead 批准 --no-verify 提交):
28 Passed / 2 Failed,失败仅 mem-numbers 与 mem-budget 两条——均为
docs/内存管理 受管快照与本 .map 不一致(台账由 Lead 每次合并后
--write 回填,避免多分支同改受管表冲突)。其余含 doc-links /
coding-standard / no-mojibake / cppcheck / registration-check /
13 条主机单测钩子全 Passed(gitlint 属 commit-msg 阶段,不在该次
--all-files 内)。ci_local L0.5 全绿(freq 46/46 等)。
字节卫生自查(--no-verify 跳过卫生钩子,故手工核):17 个暂存文件
全部 BOM=无 / CR=0(纯 LF)/ U+FFFD=0;自研 .c/.h 去注释后字符串
字面量 100% ASCII(唯一非 ASCII 字面量在 test/host/iec104/iec104_test.c
的既有用例名,非本次改动行;test/ 不在 coding-standard 钩子范围)。
2026-10-12 19:16:37 +08:00

103 lines
3.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:prot_set_store(B7 定值持久化)——实现
* 模块功能:见 prot_set_store.h。blob 格式/校验/原子装载全在 prot_set.c
* (纯逻辑,主机单测覆盖),本文件只做"读文件→parse、
* serialize→原子写"的平台胶水。
* 文件口径:"/prot_set.bin"(卷根,8.3 合规,FF_USE_LFN=0 约束);
* 原子写临时名由 cfg_store_write_atomic 按目标目录推导
* (固定 config.TMP),与 /config.ini 的保存同在 defaultTask
* 串行执行,无并发竞争。
* 创建日期:2026-10-12(B7)
*/
#include "prot_set_store.h"
#include <stddef.h>
#define DBG_TAG "[CFG]"
#include "dbg_log.h"
#include "cfg_file_store.h"
#include "prot_set.h"
#define PROT_SET_STORE_PATH "/prot_set.bin"
/* blob 收发缓冲(静态:defaultTask 栈深已是全项目最深链,勿再压局部 blob;
* B8 定值表 32→40 项 → blob 144→176 B,随 PROT_SET_BLOB_SZ 自动适配) */
static uint8_t s_blob[PROT_SET_BLOB_SZ];
/*
* 函数功能:从小端缓冲读 16 位值(仅头部判别用,与 prot_set.c 内部同字节序)
* 入口参数:p - 读位置
* 返 回 值:值 uint16_t
*/
static uint16_t s_ld16(const uint8_t *p) {
return (uint16_t)((uint32_t)p[0] | ((uint32_t)p[1] << 8));
}
/*
* 函数功能:从小端缓冲读 32 位值
* 入口参数:p - 读位置
* 返 回 值:值 uint32_t
*/
static uint32_t s_ld32(const uint8_t *p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16)
| ((uint32_t)p[3] << 24);
}
/*
* 函数功能:开机装载定值 blob
* 入口参数:无
* 返 回 值:0 已装载 / 1 文件缺失或读失败(保持默认)/
* 2 版本或项数换代(旧档被拒,保持默认;本函数已落 INFO 日志)/
* -1 损坏被拒
* 函数说明:读失败与"文件不存在"同归 1——启动期卷锁无竞争(本任务独占
* bring-up),try-lock 失败即真 I/O 故障,此时保持出厂默认并让
* 调用方按"无档"处理是安全侧(定值仍封存自洽,L5-1 不误锁)。
* **换代与损坏分开**(B8):magic 完好但 ver/cnt 与当前固件不符 =
* 定值表升级(如 B8 32→40 项),是**预期升级路径**不是损坏 →
* 归 2 并打 INFO("version change"措辞),调用方不得按 CORRUPT
* 报 ERROR/自检留痕;magic 或校验对被破坏才是 -1。
*/
int prot_set_store_load(void) {
size_t nread = 0u;
if (cfg_store_read(PROT_SET_STORE_PATH, s_blob, sizeof(s_blob), &nread) != 0) {
return 1;
}
if (prot_set_store_parse(s_blob, (uint32_t)nread) == 0) {
return 0;
}
/* 拒绝原因分流:头部 magic 完好 + 版本/项数与当前固件不符 = 换代 */
if ((nread >= (size_t)PROT_SET_BLOB_HDR_SZ)
&& (s_ld32(s_blob) == (uint32_t)PROT_SET_BLOB_MAGIC)
&& ((s_ld16(s_blob + 4u) != (uint16_t)PROT_SET_BLOB_VER)
|| (s_ld16(s_blob + 6u) != (uint16_t)PS_ID_COUNT))) {
DBG_INFO("prot set blob version change (ver=%u cnt=%u), defaults in use",
(unsigned)s_ld16(s_blob + 4u), (unsigned)s_ld16(s_blob + 6u));
return 2;
}
return -1;
}
/*
* 函数功能:落盘当前定值(消费脏标志;失败回置等重试)
* 入口参数:无
* 返 回 值:0 无待写或成功 / -1 写失败
*/
int prot_set_store_save(void) {
uint32_t len = 0u;
if (prot_set_dirty_take() == 0u) {
return 0;
}
if (prot_set_store_serialize(s_blob, (uint32_t)sizeof(s_blob), &len) != 0) {
prot_set_dirty_mark();
return -1;
}
if (cfg_store_write_atomic(PROT_SET_STORE_PATH, s_blob, (size_t)len) != 0) {
prot_set_dirty_mark(); /* 卷忙/写失败:脏保留,iec104Task 下轮再通知 */
return -1;
}
return 0;
}