Files
STM32F4-Base/App/algo/meas.c
T
wandering1 8c72698666 feat(运行参数): 卡10 run_par真源收编+104段0x8020~0x8058读写映射(道D M6-①)
新增 App/algo/run_par.c/h(「数据可信」卡 10,lead 四裁落地):
- [run_par] cfg 段:遥测死区 9 键 int32 毫单位(db_ua…db_freq),工厂默认 =
  卡 6 额定 0.5% 档(500mV×4 / 5mA×4 / 500mHz);注册走 adc_cal 同款内核前
  契约(app_main_init),不挂回调 = 引擎副本即生效值;键数 9≤16 不拆段
- 真源收编(裁③):pts 层 s_yc_db 运行时副本删除,COV 每拍实时读 run_par
  (协议→算法向下依赖,同 meas 先例;无投递无缓存 = 顺序/幂等平凡成立);
  iec104_pts_yc_set_deadband()/deadband() 保留为转发壳,壳内保留"成功清
  半截确认计数"语义并由 TC-IEC104-027 追加断言钉住(裁⑤)
- 104 地址映射(裁①):0x8030~0x8038 死区 9 点 / 0x8040~0x8047 k 覆盖 /
  0x8048~0x804F b 覆盖(逐项地址本仓自定——复现规划 §5.4 权威表不在仓,
  送检点表到位后替换 run_par.c 顶部常量段一处;已在 IEC104 §3.4 表头挂账);
  路由顺序 = run_par 表先、未命中落 prot_set——0x8027~0x802A 属 prot_set
  告警段永不覆盖,TC-IEC104-029 双向往返钉死;空隙地址自然 COT14/PN
- 通道系数覆盖 = adc_cal_set 代理(裁②a):合法带/越带 [ERR] 留痕全复用,
  run_par 不存第二份系数(防双真源);单侧写 RMW 用另一侧当前生效值;
  回显 = 生效值视图(文件坏值被哨兵回退 ROM 时主站看到的就是下游吃到的);
  adc_cal 配套:+adc_cal_b_codes() 读取口(回显/RMW 用)+ set() 授权写者
  注记扩至 iec104Task 直通路径(单字原子契约同「数据可信」§4.4)
- 持久化(裁②b):新事件位 SYS_EVT_RUN_PAR_SAVE(1<<8);iec104Task 主循环
  peek 投递 → defaultTask run_par_store_save() 一条事件一趟 cfg_file_save
  整档外科(覆盖 [run_par] 9 键 + 联动 [adc_cal] 两段);失败脏保留 = 天然
  重试(B7 prot_set 同款)。C_SE 现行无"暂停/测试态"门控——文档现行如此
  不造新语义,送检若要求接线点已写进 §3.4 注(裁④)

测试:新建 test/host/run_par(TC-RUNPAR-001~008,67 断言,真实 cfg 引擎 +
真实 adc_cal + 内存虚拟盘同 adc_cal 套件先例;[run_par] 注册契约/落盘往返/
外改文件即副本生效/两段式不提交不置脏/合法带拒+RMW+生效值回显/init 幂等
重注册留痕);iec104 套扩真链 TU(cfg_file+adc_cal+run_par 非桩,141 断言:
+TC-IEC104-029 路由 + 壳清确认计数 3 断言);meas 套静态更名 s_snap*→
s_ms_snap*(与 cfg_file.c s_snap 同 TU 相撞,D1 同款改名法,226 断言不变);
iec104_serv 套补 run_par_dirty_peek 假件。id_registry 建族 TC-HOST-RUNPAR
(游标 008)+ TC-HOST-IEC104 游标 028→029;新钩子 run-par-unit-test +
iec104 钩子触发面随真链扩;uvprojx +run_par.c(合法入库)+ SIM_EXEMPT 登记
(模拟器不编 104 协议,已核实 CMake 面)。

文档:IEC104 §3.4 运行参数段表(含送检替换账/门控未来条件/代理声明)、§7、
§8 第 4 条、变更记录;网络配置说明新增 §3.5 [run_par] 键表;复现规划 §5.4
补运行参数段落实进度。**遗留(交 lead)**:数据可信 §4.4 "引擎 RAM 副本"
一节当时的措辞对象含 pts 侧副本,s_yc_db 删除后该句过时(裁③b 点名,§4.4
在写域外未动)。

gate:@build 0/0(Code=219620 RO=22036 RW=1280 ZI=132864);vs 0cbae7d:
Flash +1912B、RAM(RW+ZI) +8B(37B 新静态被链接器并洞吸收);iec104Task 栈
1456B/margin 43%、defaultTask 3032B 不变;id-check/doc-links/cppcheck/
coding-standard 实跑过;--all-files 全量 45 钩子实跑(43 绿 + 仅 mem-budget /
mem-numbers 两红 = 台账受管区 vs .map 漂移,回填属 lead 单写者区,批 1/道D-1
/D2 同口径):gate: only mem-budget/mem-numbers pending lead ledger backfill
BOM/CR/U+FFFD/字面量 ASCII 逐文件自查通过。
2026-10-25 13:15:41 +08:00

171 lines
6.8 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.
/*
* 模块名称:遥测快照发布层(L2 派生·发布侧,「数据可信」卡 8)
* 模块功能:实现见 meas.h——200 ms 基波有效值 + 品质快照,双区无锁读。
* 本文件纯算法:输入 = sample_ring 取窗 + 调用方注入(频率/削顶位图),
* 无 RTOS / 无寄存器 / 无日志(「数据可信」层内禁令:本目录不写日志)。
* 双区不变量(docs/任务规划/数据可信.md §4.5,L2-7 同型,先例 = App/algo/
* phasor.c 的双区注释):写侧只碰**非使用区**,全部字段填完后一次
* volatile 单字节 store 翻转使用区;读者先取索引再整体拷贝 → 拷贝期
* 间写者不可能改读者所在区(两帧间隔 200 ms 远大于 72 B 拷贝)。
* 单核顺序执行,无需内存屏障;volatile 阻编译器把字段写入挪到翻转后。
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
* 作者:DTU 项目组
* 创建日期:2026-10-25(道D 卡 8)
* 修改记录:
* v1.0 2026-10-25 DTU 项目组 创建:200 ms 遥测快照 + 双区无锁读
*/
#include "meas.h"
#include <stddef.h>
#include "phasor.h" /* phasor_calc():基波 DFT 纯函数核(与使用区同核) */
#include "sample_ring.h" /* sample_ring_window():以本拍为终止的一周波窗 */
/* ===== 双区(计算区 / 使用区),口径同 App/algo/phasor.c 的 L2-7 ===== */
static meas_snap_t s_ms_snap[2];
static volatile uint8_t s_ms_use;
static uint8_t s_ms_ready; /* 使用区是否已产出过有效结果 */
static uint32_t s_ms_seq; /* 只增帧号(写者单写者,读者原子单字读) */
/*
* 函数功能:清零一份快照(含 rsv 与全部数组元素)
* 入口参数:p - 目标快照
* 返 回 值:无
* 限定条件:p 非空
* 函数说明:静态辅助;逐字段清零(不依赖 memset,行为与本目录 phasor_zero 同型)。
*/
static void meas_zero(meas_snap_t *p) {
uint8_t i;
p->seq = 0u;
p->tick_ms = 0u;
p->valid = 0u;
p->rsv[0] = 0u;
p->rsv[1] = 0u;
p->rsv[2] = 0u;
p->freq_valid = 0u;
p->freq_hz = 0.0f;
for (i = 0u; i < (uint8_t)ADC_SEM_COUNT; i++) {
p->yc[i] = 0.0f;
p->qds[i] = 0u;
}
}
/*
* 函数功能:遥测快照层初始化
* 入口参数:无
* 返 回 值:0 - 成功
* 限定条件:内核启动前或任务上下文皆可;幂等,可重复调用
* 函数说明:清双区 + 索引/就绪/序号复位。init 后到首次发布前,meas_get() 恒
* 返 -2(out 清零)、meas_seq() 恒返 0。
*/
int meas_init(void) {
meas_zero(&s_ms_snap[0]);
meas_zero(&s_ms_snap[1]);
s_ms_use = 0u;
s_ms_ready = 0u;
s_ms_seq = 0u;
return 0;
}
/*
* 函数功能:一拍遥测发布(被调即发布,节拍把关在调用方)
* 入口参数:tick_ms - 本拍时基(毫秒) uint32_t
* freq_valid_in - 频率读数新鲜度(freq_valid() 值) uint8_t
* freq_hz_in - 频率读数(freq_last() 值) float
* clip_mask - 削顶通道位图(bit s = 语义通道 s,来源唯一 =
* adc_cal_flags(),编排方注入) uint32_t
* 返 回 值:无
* 限定条件:仅 adcTask 上下文、全仓唯一调用点(meas.h 发布纪律);须在
* phasor_run() 之后调用(本拍窗含最新点,与使用区同窗同核)
* 函数说明:电气 8 路(语义序中 TEMP 之前的全部通道,映射表属主 =
* docs/硬件平台/ADC通道映射说明.md)逐路一周波基波 DFT;任一路取窗
* 不足(开机未满一周波)→ 该路 IV|NT + 整帧 valid=0,但照常发布
* (品质节拍性刷新,见 meas_tick 头注)。TEMP 只取最新一点透传 +
* NT(「数据可信」决策 5)。品质值不因 IV 清零——"值在场但不可信"
* 与"值缺失"分开,消费者按 qds 裁决。
*/
void meas_tick(uint32_t tick_ms, uint8_t freq_valid_in, float freq_hz_in,
uint32_t clip_mask) {
meas_snap_t *z = &s_ms_snap[1u - s_ms_use]; /* 计算区 = 非使用区 */
float win[PHASOR_N];
phasor_t p;
uint8_t all_ok = 1u;
uint8_t s;
meas_zero(z);
z->seq = s_ms_seq + 1u;
z->tick_ms = tick_ms;
for (s = 0u; s < (uint8_t)ADC_SEM_TEMP; s++) {
if (sample_ring_window((adc_semantic_t)s, (uint8_t)PHASOR_N, win) == 0) {
phasor_calc(win, (uint8_t)PHASOR_N, 1u, &p);
z->yc[s] = p.fAmSQ; /* 有效值口径(系数已折算,同 phasor 层) */
if ((clip_mask & (1uL << s)) != 0u) {
z->qds[s] |= (uint16_t)MEAS_QDS_CLIPPED;
}
} else {
/* 本路窗不足:值缺位(yc 保持 0)+ IV|NT;整帧判不可信 */
z->qds[s] = (uint16_t)(MEAS_QDS_IV | MEAS_QDS_NT);
all_ok = 0u;
}
}
/* TEMP:非交流量语义(「数据可信」决策 5),最新一点原始帧值透传 + NT;
* clip_mask 位 8 不参与(adc_cal_flags(TEMP) 恒 0,无削顶语义) */
{
float tv = 0.0f;
if (sample_ring_window(ADC_SEM_TEMP, 1u, &tv) == 0) {
z->yc[ADC_SEM_TEMP] = tv;
z->qds[ADC_SEM_TEMP] = (uint16_t)MEAS_QDS_NT;
} else {
z->qds[ADC_SEM_TEMP] = (uint16_t)(MEAS_QDS_IV | MEAS_QDS_NT);
}
}
z->valid = all_ok;
z->freq_valid = (freq_valid_in != 0u) ? 1u : 0u;
z->freq_hz = freq_hz_in;
/* 全部字段就绪后才翻索引 + 置就绪(顺序即协议,勿交换) */
s_ms_seq = z->seq;
s_ms_use = (uint8_t)(1u - s_ms_use);
s_ms_ready = 1u;
}
/*
* 函数功能:读当前使用区快照(无锁整体拷贝)
* 入口参数:out - 输出快照;失败路径同样清零填充
* 返 回 值:0 - 成功;-1 - 参数错;-2 - 从未发布过 int
* 限定条件:任意上下文;无(读 volatile 索引 + 普通拷贝)
* 函数说明:先取索引再索引拷贝,避免拷贝中途索引被翻转导致"半旧半新"。
* 返回 0 时帧内部自洽(同一发布拍的 seq/tick_ms/yc/qds 成套)。
*/
int meas_get(meas_snap_t *out) {
uint8_t z;
if (out == NULL) {
return -1;
}
if (s_ms_ready == 0u) {
meas_zero(out);
return -2;
}
z = s_ms_use;
*out = s_ms_snap[z];
return 0;
}
/*
* 函数功能:读已发布帧数(只增)
* 入口参数:无
* 返 回 值:seq,0 = 从未发布(单字读,原子) uint32_t
* 限定条件:任意上下文
* 函数说明:轻量"是否换帧"探针,正式取数以 meas_get() 为准。
*/
uint32_t meas_seq(void) {
return s_ms_seq;
}