d64bbdc57f
数据面(App/algo,纯逻辑,双端编译) - wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask 唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃 兜底——下一次真故障无波可录比丢一段更贵 - wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带 锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度 - 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式 - 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样 纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉 - test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条 自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合 - 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定) 接线(任务层) - adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图), 段头按最低有效位精确记账,不经浮点回算 - adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实; 台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行 - defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到 关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms 补一条告警并记历史峰值(喂狗余量可核) - Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留 超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的, 半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进 - Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到 上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数; 段文件字节布局零改动,三份合成段哈希逐字节不变 - protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体 零改动 人机(160×160 单色屏) - 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器 得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30 - 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与 垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、 单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据 - ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、 手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口 - 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有 60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形 - 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变 - 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏 22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、 仪器用已在跑的单轮工作耗时探针,不新加埋点 台账与文档 - 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936 字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名; 散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建 重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置) - 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据); §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径 风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」 - 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项 - 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划 索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动) - 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件 形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记 「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场 - 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行 227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退 为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与 体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账 同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道 编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串 生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过
488 lines
22 KiB
C
488 lines
22 KiB
C
/*
|
||
* 模块名称:通道标定与量纲折算(标定折算)——实现
|
||
* 模块功能:见 adc_cal.h。折算模型(工程量 = (code + b) × k × 1e-6,k 为 Q16
|
||
* µV/码)与两段 cfg 布局([adc_cal]:k_* + valid 位图;[adc_cal_ofs]:
|
||
* b_*)照 docs/任务规划/数据可信.md §4.1 / §4.4 落地;系数口径
|
||
* (ROM 默认 = 量程口径冻结定死的引脚侧 LSB)见 docs/硬件平台/ADC通道映射说明.md
|
||
* §7.1。
|
||
* 并发契约:cfg RAM 副本属主 = 配置引擎(defaultTask 的 load/poll 原地
|
||
* 改写),本层逐点折算现读——int32 对齐字在 Cortex-M4 上读写原子,
|
||
* poll 重载「复位默认 + 逐键解析」存在 µs 级窗口,撞上则该拍该通道
|
||
* 为默认值/半新值(相量 20 点窗摊薄,且触发面 = 用户改文件后 4 s
|
||
* 沉降一次性事件)——「数据可信」§4.4 明载此设计(不挂回调)。
|
||
* 合法带哨兵(防损坏文件/错误标定把假电压送进下游):k ∉ ROM×[0.5,2.0]
|
||
* 或 |b| > ADC_CAL_B_MAX → 该通道折算回退 ROM + b=0,flags 不报
|
||
* VALID,每通道每次开机首见沿打一条 [ERR](adc_cal_init 重置留痕)。
|
||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||
* 作者:DTU 项目组
|
||
* 创建日期:2026-10-25(道A 标定折算与落盘整定)
|
||
* 修改记录:
|
||
* v1.0 2026-10-25 DTU 项目组 创建:Q16 系数/零点两段落盘 + 折算 + 三态 flags
|
||
* v1.1 2026-10-25 DTU 项目组 运行参数段(道D):新增 adc_cal_b_codes() 生效零点
|
||
* 读取口;set() 授权写者扩至 iec104Task 直通路径
|
||
* (run_par 0x8040~/0x8048~,契约同 §4.4)
|
||
* + 合法带哨兵回退 + 两点标定公式(台架注入待跑)
|
||
* v1.2 2026-10-26 DTU 项目组 道F task-16:新增 adc_cal_prot_blocked()——未标定
|
||
* 闭锁(UA/UN/IA 三路 VALID 全齐才放行)收成本层单点,
|
||
* DTU_BENCH 旁路同时收成全仓唯一一处;此前该判据在
|
||
* protect_task.c 与 ui_data_feed.c 各写一份,两处一旦
|
||
* 漂移,屏上「未标定」与判据实际停走就不是同一件事
|
||
* v1.3 2026-10-29 DTU 项目组 故障录波 task-2:新增 adc_cal_k_q16() int32 原值直读口
|
||
* (段头 k 字段 LSB 精确,float 反推在 5.28e7 掉尾数,
|
||
* 口径见 docs/子系统设计/故障录波设计说明.md §6.3)
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <stddef.h>
|
||
#include <string.h>
|
||
|
||
#include "adc_cal.h"
|
||
|
||
#include "cfg_file.h"
|
||
|
||
#define DBG_TAG "[CAL]"
|
||
#include "dbg_log.h"
|
||
|
||
/* 常量定义区 */
|
||
|
||
/* 段名(≤ CFG_FILE_MAX_NAME_LEN)与键名(k_ua…b_in,均 4/5 字符)
|
||
* ——「数据可信」§4.4 布局;键的书写顺序按该表,存储下标按语义枚举 */
|
||
#define ADC_CAL_SEC_K "adc_cal"
|
||
#define ADC_CAL_SEC_B "adc_cal_ofs"
|
||
|
||
/* Q16 → V 的合并比例因子:1e-6 / 65536(float 常量,精确到 1 ulp 以内即可:
|
||
* 系数本身 Q16 舍入误差 < 0.001 %,见「数据可信」§4.1) */
|
||
#define ADC_CAL_K_SCALE (1.52587890625e-11f)
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
/* [adc_cal] 段 RAM 副本:k 按语义下标 0..7,valid 位图 bit=语义下标。
|
||
* data_size 取 offsetof(valid)+1 = 33(尾随对齐填充不属于引擎视图,
|
||
* 引擎「复位默认」按 data_size memcpy,必须覆盖 valid,见单测 TC-ADCCAL-001) */
|
||
typedef struct {
|
||
int32_t k[8];
|
||
uint8_t valid;
|
||
} adc_cal_kcfg_t;
|
||
|
||
/* [adc_cal_ofs] 段 RAM 副本:b 按语义下标 0..7(int32 有符号,有符号零点类型
|
||
* CFG_VAL_INT 是负零点偏移能干净落盘的前提) */
|
||
typedef struct {
|
||
int32_t b[8];
|
||
} adc_cal_bcfg_t;
|
||
|
||
static adc_cal_kcfg_t s_kcfg; /* 生效系数副本(引擎原地更新) */
|
||
static adc_cal_bcfg_t s_bcfg; /* 生效零点副本 */
|
||
|
||
/* 工厂默认(ROM 理论系数,量程口径冻结定死,「数据可信」§4.4):**下标 = 语义枚举**
|
||
* (UA0 / UB1 / UC2 / UN3 / IA4 / IB5 / IC6 / IN7)——UA/UN/IA 走 TPAFE
|
||
* (16 位 ±5 V 档),UB/UC/IB/IC/IN 走 STM32 ADC1;逐路对过
|
||
* docs/硬件平台/ADC通道映射说明.md §3(IN=ADC11/STM32,勿再按分组顺序误写)。
|
||
* ROM 表按**当前 V0.1 来源排布**显式书写(不查 desc 运行期派生),因为它是
|
||
* 量程口径冻结定稿的存档事实;V0.2 改版(消除双转换器无共同采样触发)时本表与 adc_input 映射表同步改,
|
||
* 上层语义与 API 不动。CLIPPED 位则**运行期按来源派生**(见 adc_cal_flags) */
|
||
static const adc_cal_kcfg_t s_kcfg_rom = {
|
||
/* idx0 UA / idx1 UB / idx2 UC / idx3 UN */
|
||
{ ADC_CAL_K_Q16_TPAFE, ADC_CAL_K_Q16_STM32, ADC_CAL_K_Q16_STM32,
|
||
ADC_CAL_K_Q16_TPAFE,
|
||
/* idx4 IA / idx5 IB / idx6 IC / idx7 IN */
|
||
ADC_CAL_K_Q16_TPAFE, ADC_CAL_K_Q16_STM32, ADC_CAL_K_Q16_STM32,
|
||
ADC_CAL_K_Q16_STM32 },
|
||
0u /* 出厂未标定:VALID 全 0 */
|
||
};
|
||
static const adc_cal_bcfg_t s_bcfg_rom = { { 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L } };
|
||
|
||
/* 合法带哨兵留痕位(bit=语义下标;每通道每次开机只打一条 [ERR],
|
||
* adc_cal_init 清零 = 新一轮观测窗) */
|
||
static uint8_t s_err_latched;
|
||
|
||
/*
|
||
* 观测计数(三态语义唯一落点在 adc_cal.h,这里只写"怎么自增"):与上面那个留痕位
|
||
* 不同,这三个数一个 [ERR] 都不打、也不参与任何判定,因此 **adc_cal_init() 刻意不清**
|
||
* ——清了水位与计数就失配,会凭空造出一段"假未落盘"差额。
|
||
*/
|
||
static volatile uint32_t s_st_set_ok; /* 成功改写 RAM 副本的次数 */
|
||
static volatile uint32_t s_st_set_reject; /* 被拒次数(非法通道 / 越出合法带) */
|
||
static volatile uint32_t s_st_persist_water; /* 落盘水位:盖写,绝不自增 */
|
||
|
||
/* 键表:offset 用 offsetof 取真值(k[0]@0 / k[1]@4 … valid@32) */
|
||
static const cfg_key_def_t s_k_keys_tbl[9] = {
|
||
{ "k_ua", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[0]), 4u },
|
||
{ "k_un", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[3]), 4u },
|
||
{ "k_ia", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[4]), 4u },
|
||
{ "k_ub", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[1]), 4u },
|
||
{ "k_uc", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[2]), 4u },
|
||
{ "k_ib", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[5]), 4u },
|
||
{ "k_ic", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[6]), 4u },
|
||
{ "k_in", CFG_VAL_INT, offsetof(adc_cal_kcfg_t, k[7]), 4u },
|
||
{ "valid", CFG_VAL_UINT, offsetof(adc_cal_kcfg_t, valid), 1u },
|
||
};
|
||
static const cfg_key_def_t s_b_keys_tbl[8] = {
|
||
{ "b_ua", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[0]), 4u },
|
||
{ "b_un", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[3]), 4u },
|
||
{ "b_ia", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[4]), 4u },
|
||
{ "b_ub", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[1]), 4u },
|
||
{ "b_uc", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[2]), 4u },
|
||
{ "b_ib", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[5]), 4u },
|
||
{ "b_ic", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[6]), 4u },
|
||
{ "b_in", CFG_VAL_INT, offsetof(adc_cal_bcfg_t, b[7]), 4u },
|
||
};
|
||
|
||
/* 函数定义区 */
|
||
|
||
/*
|
||
* 函数功能:判定语义通道是否 STM32 源(经 adc_input 权威映射表,不另立第二份表)
|
||
* 入口参数:sem - 语义通道 unsigned
|
||
* 返 回 值:1 - STM32;0 - TPAFE(含 desc 取不到时的保守分支) int
|
||
* 限定条件:无
|
||
*/
|
||
static int s_rom_k_stm32(unsigned sem) {
|
||
const adc_channel_desc_t *d = adc_input_desc((adc_semantic_t)sem);
|
||
|
||
return ((d != NULL) && (d->source == ADC_SRC_STM32)) ? 1 : 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取语义通道的 ROM 默认系数(合法带/回退参照)
|
||
* 入口参数:sem - 语义下标(0..7) unsigned
|
||
* 返 回 值:Q16 系数 int32_t
|
||
* 限定条件:sem < 8(调用方已先行拦截 TEMP/越界)
|
||
* 函数说明:直接读出厂表本体——保证「哨兵合法带基准」与「引擎工厂默认」
|
||
* 永远同一份数(两者若靠重复表达式各算各的,改一处漏一处就是暗雷)
|
||
*/
|
||
static int32_t s_rom_k(unsigned sem) {
|
||
return s_kcfg_rom.k[sem];
|
||
}
|
||
|
||
/*
|
||
* 函数功能:k/b 合法带检查(ROM×[0.5,2.0] / |b| ≤ ADC_CAL_B_MAX)
|
||
* 入口参数:sem - 语义下标(0..7) unsigned
|
||
* k - 待检系数 Q16 int32_t
|
||
* b - 待检零点 码值 int32_t
|
||
* 返 回 值:1 - 合法;0 - 越带 int
|
||
* 限定条件:sem < 8
|
||
*/
|
||
static int s_kband_ok(unsigned sem, int32_t k, int32_t b) {
|
||
const int32_t rom = s_rom_k(sem);
|
||
|
||
if (k < (rom >> 1) || k > (rom << 1)) {
|
||
return 0;
|
||
}
|
||
if (b > ADC_CAL_B_MAX || b < -ADC_CAL_B_MAX) {
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:越带留痕(每通道每次开机首见沿一条 [ERR],init 重置)
|
||
* 入口参数:sem - 语义下标 0..7;k/b - 现场值
|
||
* 返 回 值:无
|
||
* 限定条件:仅 adc_cal_apply/set 内部
|
||
*/
|
||
static void s_note_bad(unsigned sem, int32_t k, int32_t b) {
|
||
const uint8_t bit = (uint8_t)(1u << sem);
|
||
|
||
if ((s_err_latched & bit) == 0u) {
|
||
s_err_latched |= bit;
|
||
DBG_ERROR("ch=%u k_q16=%ld b=%ld out of ROM band [0.5x,2x]/|b|<=%ld -> ROM fallback",
|
||
(unsigned)sem, (long)k, (long)b, (long)ADC_CAL_B_MAX);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道「当前生效且合法」的 k/b(越带就地回退 ROM)
|
||
* 入口参数:sem - 语义下标 0..7;p_k/p_b - 输出
|
||
* 返 回 值:1 - 用了合法值;0 - 已回退 ROM
|
||
* 限定条件:sem < 8
|
||
*/
|
||
static int s_eff_kb(unsigned sem, int32_t *p_k, int32_t *p_b) {
|
||
const int32_t k = s_kcfg.k[sem];
|
||
const int32_t b = s_bcfg.b[sem];
|
||
|
||
if (s_kband_ok(sem, k, b) != 0) {
|
||
*p_k = k;
|
||
*p_b = b;
|
||
return 1;
|
||
}
|
||
s_note_bad(sem, k, b);
|
||
*p_k = s_rom_k(sem);
|
||
*p_b = 0L;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:标定层初始化——RAM 副本置 ROM 默认 + 注册两段进 cfg 引擎(幂等)
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
* 限定条件:内核启动前调用(app_main_init);注册必须先于 cfg_file_load
|
||
* 函数说明:完整契约见 adc_cal_init() 同名声明(adc_cal.h);注册失败(段名重复/
|
||
* 段数超限)打 [ERR] 后继续——ROM 默认系数照常生效,仅文件整定失效
|
||
*/
|
||
void adc_cal_init(void) {
|
||
cfg_section_reg_t reg;
|
||
|
||
(void)memcpy(&s_kcfg, &s_kcfg_rom, sizeof(s_kcfg));
|
||
(void)memcpy(&s_bcfg, &s_bcfg_rom, sizeof(s_bcfg));
|
||
s_err_latched = 0u;
|
||
|
||
(void)memset(®, 0, sizeof(reg));
|
||
reg.name = ADC_CAL_SEC_K;
|
||
reg.p_data = &s_kcfg;
|
||
reg.p_defaults = &s_kcfg_rom;
|
||
reg.data_size = (offsetof(adc_cal_kcfg_t, valid) + 1u); /* 33:覆盖到 valid */
|
||
reg.p_keys = s_k_keys_tbl;
|
||
reg.key_count = 9u;
|
||
reg.p_cb = NULL; /* 不挂回调:RAM 副本即生效值(「数据可信」§4.4) */
|
||
if (cfg_file_register(®) != 0) {
|
||
DBG_ERROR("register '%s' FAIL (dup/full?) - calibration file values inactive",
|
||
ADC_CAL_SEC_K);
|
||
}
|
||
|
||
(void)memset(®, 0, sizeof(reg));
|
||
reg.name = ADC_CAL_SEC_B;
|
||
reg.p_data = &s_bcfg;
|
||
reg.p_defaults = &s_bcfg_rom;
|
||
reg.data_size = sizeof(s_bcfg); /* 32 */
|
||
reg.p_keys = s_b_keys_tbl;
|
||
reg.key_count = 8u;
|
||
reg.p_cb = NULL;
|
||
if (cfg_file_register(®) != 0) {
|
||
DBG_ERROR("register '%s' FAIL (dup/full?) - zero-offset file values inactive",
|
||
ADC_CAL_SEC_B);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:码值 → 工程量折算(「数据可信」§4.1 模型)
|
||
* 入口参数:sem - 语义通道;code - 原始码值 adc_semantic_t / int32_t
|
||
* 返 回 值:工程量浮点(未标定 = ADC 引脚电压 V;TEMP/越界 = 原样透传码值) float
|
||
* 限定条件:无(任意上下文,纯读)
|
||
* 函数说明:见 adc_cal.h 同名声明;k/b 经 s_eff_kb 合法带哨兵(越带回退 ROM)
|
||
*/
|
||
float adc_cal_apply(adc_semantic_t sem, int32_t code) {
|
||
int32_t k;
|
||
int32_t b;
|
||
|
||
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||
return (float)code; /* TEMP/越界:不纳入线性标定(决策 5),原样透传 */
|
||
}
|
||
(void)s_eff_kb((unsigned)sem, &k, &b);
|
||
return ((float)(code + b)) * ((float)k) * ADC_CAL_K_SCALE;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道可用性标志(消费者判「能不能信」的唯一入口)
|
||
* 入口参数:sem - 语义通道 adc_semantic_t
|
||
* 返 回 值:ADC_CAL_F_VALID / ADC_CAL_F_CLIPPED 组合;TEMP/越界恒 0 uint32_t
|
||
* 限定条件:无
|
||
* 函数说明:VALID 需文件位图与 k 合法带**同时**成立(哨兵压制说谎位图,见文件头)
|
||
*/
|
||
uint32_t adc_cal_flags(adc_semantic_t sem) {
|
||
uint32_t f = 0u;
|
||
|
||
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||
return 0u;
|
||
}
|
||
/* VALID = 文件位图置位 且 k 合法(哨兵不落共享状态,随重载自愈) */
|
||
if (((s_kcfg.valid & (uint8_t)(1u << (unsigned)sem)) != 0u) &&
|
||
(s_kband_ok((unsigned)sem, s_kcfg.k[sem], s_bcfg.b[sem]) != 0)) {
|
||
f |= ADC_CAL_F_VALID;
|
||
}
|
||
/* CLIPPED 由**来源**派生(R5:STM32 侧单极性无偏置 → 负半周削顶)——
|
||
* V0.2 改版(8 路全接 TPAFE,消除双转换器无共同采样触发)后本位自动恒 0,无需改码,
|
||
* 对应「数据可信」§10「页面与 104 绑定零改动」的要求 */
|
||
if (s_rom_k_stm32((unsigned)sem) != 0) {
|
||
f |= ADC_CAL_F_CLIPPED;
|
||
}
|
||
return f;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:未标定闭锁判定(判据所需的 UA/UN/IA 三路是否齐)
|
||
* 返 回 值:1 - 闭锁;0 - 放行 uint8_t(契约、三路集合的理由、判据停走的四路
|
||
* 来源、两个同族谓词的边界都写在 adc_cal.h,本处不重述——重述就是第二真源)
|
||
* 函数说明:**现查 flags(),本函数不留任何状态**。刻意不复用保护任务里那份
|
||
* 「上拍闭锁态」——那是给边沿日志用的,开机首拍前恒 0;拿它当判据等于在
|
||
* 「还没判过」的时候谎称「已标定」,而这正是本谓词最贵的失效(屏上
|
||
* 「未标定」徽标一消失,现场读到的就是「改了定值就会动作」)。
|
||
* DTU_BENCH 旁路留在这里、且全仓只此一处;用 #ifndef 把产品默认路径留在
|
||
* 主分支——#ifdef 双分支会让 cppcheck 对未定义宏取真分支、把静态分析引到
|
||
* 旁路上报 knownConditionTrueFalse 假信号(实测 2026-10-25,这条形态说明原先
|
||
* 写在 protect_task.c 的实现处,随判据一起搬到这里)
|
||
*/
|
||
uint8_t adc_cal_prot_blocked(void) {
|
||
#ifndef DTU_BENCH
|
||
if (((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) == 0u) ||
|
||
((adc_cal_flags(ADC_SEM_UN) & ADC_CAL_F_VALID) == 0u) ||
|
||
((adc_cal_flags(ADC_SEM_IA) & ADC_CAL_F_VALID) == 0u)) {
|
||
return 1u;
|
||
}
|
||
#endif
|
||
return 0u;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道当前生效系数(µV/码,回退后口径)
|
||
* 入口参数:sem - 语义通道 adc_semantic_t
|
||
* 返 回 值:k[µV/码];TEMP/越界返 0 float
|
||
* 限定条件:无
|
||
* 函数说明:Q16 现值 ÷ 65536 浮点还原;勿据其反推二次值(可用性看 flags)
|
||
*/
|
||
float adc_cal_k_uv(adc_semantic_t sem) {
|
||
int32_t k;
|
||
int32_t b;
|
||
|
||
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||
return 0.0f;
|
||
}
|
||
(void)s_eff_kb((unsigned)sem, &k, &b);
|
||
return ((float)k) / 65536.0f;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道当前生效系数的 int32 Q16 原值(adc_cal_k_uv 孪生,见 .h 声明)
|
||
* 入口参数:sem - 语义通道 adc_semantic_t
|
||
* 返 回 值:k[Q16];TEMP/越界返 0 int32_t
|
||
* 限定条件:无(任意上下文,纯读)
|
||
* 函数说明:与 adc_cal_k_uv() 共用 s_eff_kb 生效视图(越带回退 ROM 口径一致),
|
||
* 差别只在不做浮点还原——录波段头按 LSB 精确记账必须走本口(§6.3)
|
||
*/
|
||
int32_t adc_cal_k_q16(adc_semantic_t sem) {
|
||
int32_t k;
|
||
int32_t b;
|
||
|
||
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||
return 0;
|
||
}
|
||
(void)s_eff_kb((unsigned)sem, &k, &b);
|
||
return k;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道当前生效零点偏移(码值,浮点)——adc_cal_k_uv 孪生入口
|
||
* 入口参数:sem - 语义通道 adc_semantic_t
|
||
* 返 回 值:b[码值](越带时 s_eff_kb 已回 0);TEMP/越界返 0 float
|
||
* 限定条件:无(任意上下文,纯读)
|
||
* 函数说明:运行参数段的 104 b 覆盖回显 + k 单侧改写 RMW 读侧(App/algo/run_par.c)
|
||
*/
|
||
float adc_cal_b_codes(adc_semantic_t sem) {
|
||
int32_t k;
|
||
int32_t b;
|
||
|
||
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||
return 0.0f;
|
||
}
|
||
(void)s_eff_kb((unsigned)sem, &k, &b);
|
||
return ((float)b);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:整定入口——写通道系数与零点,并置位 VALID(「数据可信」标定落盘与整定入口)
|
||
* 入口参数:sem - 语义通道;k_q16 - 系数(µV/码×65536);b_codes - 零点(码值)
|
||
* 返 回 值:0 - 成功;-1 - 参数非法/越出合法带(RAM 不动,[ERR] 留痕) int
|
||
* 限定条件:整定路径上下文(defaultTask 整定/标定链;iec104Task 经 run_par
|
||
* 直通为运行参数段授权写者,单字原子契约同 §4.4);落盘由属主经 cfg_file_save
|
||
* 函数说明:见 adc_cal.h 同名声明
|
||
*/
|
||
int adc_cal_set(adc_semantic_t sem, int32_t k_q16, int32_t b_codes) {
|
||
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||
s_st_set_reject++; /* 观测:连通道号都非法,RAM 未动 */
|
||
return -1;
|
||
}
|
||
if (s_kband_ok((unsigned)sem, k_q16, b_codes) == 0) {
|
||
DBG_ERROR("set ch=%u rejected: k_q16=%ld b=%ld out of ROM band [0.5x,2x]/|b|<=%ld",
|
||
(unsigned)sem, (long)k_q16, (long)b_codes, (long)ADC_CAL_B_MAX);
|
||
s_st_set_reject++; /* 观测:越出合法带,RAM 未动([ERR] 由上一行打) */
|
||
return -1;
|
||
}
|
||
s_kcfg.k[sem] = k_q16;
|
||
s_bcfg.b[sem] = b_codes;
|
||
s_kcfg.valid |= (uint8_t)(1u << (unsigned)sem);
|
||
s_st_set_ok++; /* 观测:RAM 副本确实被改写了(与落盘无关) */
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读标定层观测计数(不取锁、不取时刻、任意上下文可读)
|
||
* 返 回 值:无 void
|
||
* 限定条件:无;adc_cal_init() 之前可读(全 0 = 本层还没被改写过)
|
||
* 函数说明:见 adc_cal.h 同名声明(三态语义与那张成对读表都在那里,本处只取数)
|
||
*/
|
||
void adc_cal_get_stats(adc_cal_stats_t *p_out) {
|
||
if (p_out == NULL) {
|
||
return; /* 观测件不得反过来制造故障 */
|
||
}
|
||
p_out->set_ok = s_st_set_ok;
|
||
p_out->set_reject = s_st_set_reject;
|
||
p_out->persisted_set_ok = s_st_persist_water;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:落盘成功回执——把水位盖写为当前的 set_ok(不是自增)
|
||
* 返 回 值:无 void
|
||
* 限定条件:仅"整档保存已成功"那一条路径(当前唯一调用点 run_par_store_save())
|
||
* 函数说明:盖写而非自增的理由与回归用例见 adc_cal.h 同名声明。水位直接取 set_ok 的
|
||
* 当前值,因此它**在结构上不可能越过** set_ok——差额是 uint32 相减,一旦越过
|
||
* 就绕回成一个巨大的假未落盘数,这里不留那种可能
|
||
*/
|
||
void adc_cal_note_persisted(void) {
|
||
s_st_persist_water = s_st_set_ok; /* 水位只会追平,不会越过 */
|
||
}
|
||
|
||
/*
|
||
* 函数功能:两点标定公式(标定项的算术核,纯函数无状态)
|
||
* 入口参数:c_lo/c_hi、v_lo_uv/v_hi_uv、p_k_q16/p_b 见 adc_cal.h 同名声明
|
||
* 返 回 值:0 成功 / -1 参数或斜率非法 / -2 零点越 ±B_MAX int
|
||
* 限定条件:无(int64 中间量,主机可测;台架注入待跑)
|
||
*/
|
||
int adc_cal_two_point(int32_t c_lo, int32_t c_hi, int32_t v_lo_uv, int32_t v_hi_uv,
|
||
int32_t *p_k_q16, int32_t *p_b) {
|
||
long long dc;
|
||
long long dv;
|
||
long long kq;
|
||
long long bb;
|
||
|
||
if ((p_k_q16 == NULL) || (p_b == NULL)) {
|
||
return -1;
|
||
}
|
||
dc = (long long)c_hi - (long long)c_lo;
|
||
dv = (long long)v_hi_uv - (long long)v_lo_uv;
|
||
if ((dc <= 0LL) || (dv <= 0LL)) {
|
||
return -1; /* 斜率必须为正:码与电压同向,反向 = 映射/接线错 */
|
||
}
|
||
kq = ((dv << 16) + (dc >> 1)) / dc; /* (v_hi−v_lo)×65536/dc,四舍五入(dc>0) */
|
||
if ((kq <= 0LL) || (kq > 100000000LL)) {
|
||
return -1; /* 绝对合理域上界 1526 µV/code:越界 = 单位/量级错 */
|
||
}
|
||
bb = (((long long)v_lo_uv) << 16) / kq - (long long)c_lo;
|
||
if ((bb > (long long)ADC_CAL_B_MAX) || (bb < -(long long)ADC_CAL_B_MAX)) {
|
||
return -2; /* 零点异常 → 先查前端,别用系数凑(§8 风险表) */
|
||
}
|
||
*p_k_q16 = (int32_t)kq;
|
||
*p_b = (int32_t)bb;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:通道码值域贴轨带门限(采样通道自检 判定的门限来源,按 FS 百分比)
|
||
*(保护功能清单见 docs/算法/继电保护功能复现规划.md)
|
||
* 入口参数:sem - 语义通道;p_lo_th/p_hi_th - 输出下/上轨带界 int32_t *
|
||
* 返 回 值:0 - 已给出门限;-1 - TEMP/越界/参数空(不做贴轨监视) int
|
||
* 限定条件:无
|
||
* 函数说明:状态机在 adc_health_rail_watch();本函数只供门限,无状态
|
||
*/
|
||
int adc_cal_rail_ths(adc_semantic_t sem, int32_t *p_lo_th, int32_t *p_hi_th) {
|
||
if ((p_lo_th == NULL) || (p_hi_th == NULL) ||
|
||
((unsigned)sem >= (unsigned)ADC_SEM_TEMP)) {
|
||
return -1; /* TEMP(热敏非线性,不判「轨」)与越界不做监视 */
|
||
}
|
||
if (s_rom_k_stm32((unsigned)sem) != 0) {
|
||
*p_hi_th = (int32_t)((4095L * 98L) / 100L); /* 4013 = 98 % FS */
|
||
*p_lo_th = (int32_t)((4095L * 2L) / 100L); /* 81 = 触底带(削顶签名) */
|
||
} else {
|
||
*p_hi_th = 32112L; /* +98 % 半量程 */
|
||
*p_lo_th = -32112L; /* -98 % 半量程 */
|
||
}
|
||
return 0;
|
||
}
|