b623a406ad
这四笔在主树里已经互相纠缠(同一次 @build 编译、同一份 .htm 度量),拆成四笔就不
存在任何一个被真编过的中间态,所以合一笔入库;下面按模块分段陈述,作者与出证归属
逐段写明。
—— 一、崩溃证据路径的整行独占收尾(log-maint-writer,task-26)——
上午那笔 1d4b6a17 之后,stack_report 报 iec104Task 静态最坏链 1,544 / margin 1,016,
低于 1,024 的 ISR 底线。真因不是新增局部缓冲,而是本模块与内核临界区之间新长出
一条环:taskEXIT_CRITICAL ⇒ vPortExitCritical ⇒ configASSERT ⇒ vApplicationAssertFailed
⇒ dbg_console_write_raw ⇒ 回到本模块。armlink 把这一整坨计入**每一个含临界区的
任务链**(vApplicationAssertFailed 的 Max Depth 由约 40 B 涨到 128 B + In Cycle),
所以九条链同涨约 80 B——同涨本身就排除了任何单个调用方。
改法两处:① 本模块三处窗口(取凭证 / 还凭证 / 整行入环)改用 CON_CRIT_ENTER/EXIT
(PRIMASK 保存恢复到局部变量;注释写全为什么不能直接 __enable_irq()——嵌套时会
替别人打开任务级临界区——以及屏蔽面变宽是有意为之);② 异常/终点钩子里那对
自证空转的 begin/end 摘掉(异常态 begin 放行但不持有、end 是幂等空操作,调它只有
成本没有收益),任务态 STALL 行保留 begin/end,这个区分写进两处注释与文档 §4.4.1。
实测四棵树逐个 .htm 解析(身份钉 tree + Linker run 时刻 + 产物来源):A=77779d49
链 1,456;B=1d4b6a17 链 1,464 / margin 1,096 PASS;C=含未提交钩子接线的工作树
1,544 / 1,016 FAIL;D=本轮修复后(Linker run 22:52)回到 **1,464 / 1,096 PASS**,
九条链齐降 80 B。断环图证:vPortExitCritical 由 136 B + In Cycle 变 8 B 且不再
In Cycle,con_tx_release 子树 152→8;Error_Handler 回到 280 + In Cycle = A 树同值
(既有发送环无人拆动)。顺带删掉 Src/freertos.c 崩溃路径里最后一个 HAL_Delay(200)
(该文件真实调用现为 0)。
测试:console_log 152→**153** 条(只增不减);换钉不删防——015 现在钉「PRIMASK
窗口 2 次、深度 1、内核临界区 0 次」,新钉 015c 绝不等待,160 注入点搬到
con_pm_hook,176 强化到未调度路径连 PRIMASK 窗口都不开;红线水位
con_pm_at_tx_max 与 con_uart_crit_at_tx_max 双 0;判据⑦(延后的行与当场直发逐
字节相同)170b/171/178 仍全绿。作者自报并纠正一处:换钉时曾把 TC-CONLOG-015b 改
编号,而 015b 在生产注释与文档里都被引用——已恢复「编号不退役、语义搬过去」。
覆盖率 App/log/console_log.c 199/200 → 256/257 行、21/21 函数(只增)。
—— 二、未标定闭锁判据收成单点访问器(ui-param-writer,task-16)——
「UA/UN/IA 三相 VALID 全置位才允许跑判据」这条门控原先在两处各自镜像实现
(protect_task.c 的 s_cal_locked 与 ui_data_feed.c 的 feed_cal_block),改一处另一处
静默失效且无门禁可照。现新增 adc_cal_prot_blocked() 落在 App/algo/adc_cal,DTU_BENCH
特例收进一处;两个消费者同批改调、本地副本删除。作者还查实并纠正了我先前一句
错判:全仓 App/app→App/task 的 include 实为 0 条,故访问器不能落在 protect_task。
新增 TC-ADCCAL-018 八条(含冷启动即闭锁),adc_cal 套件 99/99。
—— 三、共享卷锁五态可观测性(ui-param-writer,task-17)——
原状比登记簿写的更糟:五个可区分态(未调用 / try 超时 / 分片超时 / fail-open /
未持锁却 release)生产侧零留痕,且 vol_lock.c 的返回值注释与代码相反——
fail-open(锁没建成、互斥已失效却在读写盘)被返 1 **谎报成取到锁**,而那正是
vol_lock.h 记录的台架实证故障(并发写坏 FTL → 喂狗点饥饿 → IWDG 误复位)的前置。
锁层加四个 volatile 计数(16 B 静态,零新局部、零新 callee、零日志、零 FatFs)与
不取锁的 vol_lock_get_stats();13 处 try 调用点一字未动。**刻意不在锁层记时刻**:
记时刻要读 tick,等于给 defaultTask 取锁链添帧——诊断不值那几个字节;消费方若
自盖时间戳,那只能是被观测到的时刻而非发生时刻,.h 里已写明不得写成首次/末次。
四套本地假件各加 s_last_wait_to(字段名逐字一致),把「喂狗点取锁一律 timeout=0」
与「ftpTask 分片一律 100 ms」从注释纪律升级成会 FAIL 的断言;红-绿反证:临时把
sys_log_mcu.c 的 0u 改 50u → TC-SYSLOG-010/012 双红,把 lftpd_io.c 的 100u 改 0u →
TC-LFTPIO-034 红,两处按原串改回并用 SHA-256 证明逐字节还原。TC-VOLLOCK-015~019
共 45/45(无新桩)。
—— 四、UI 死接口清理(ui-maint-writer,task-6 收尾)——
删掉 ui_data_maint_ready():全仓零消费者、只有它自己的用例在读。**更值得记的是
它为什么能活过一整轮评审**:头注释写的是「页面用它决定这页能不能操作」,而页面
从来没用过它——注释陈述的是意图,评审读的是意图,门禁读的是可达路径,两者都不
反驳「意图未实现」。作者对「连用例一起删」做了显式抗命:TC-UIDATA-025 的 13 条里
只有 4 条读那个死口,另 9 条钉的是真防线(六口未注入安全返回 / 残表整表拒绝 /
被拒不覆盖已生效的好表),故删冗余的 025a、把 025i/025k/025m 换成用 reset_req()
的可观察返回值判定,性质不丢。ui_data 套件 244 → 243(净减 1),黄金 40 页与
池 5448 B 未变(该刀零像素影响,参考图无 diff)。
—— 门禁与未跑项(本笔如实留账)——
cubemx-survival 基线已 --write(漂移 6 项全部是上述意图内区域:断言/栈溢出钩子
实现、看门狗模块行等),基线文件同笔入库,随后 --check 通过。
本笔未跑的钩子只有两条:mem-numbers 与 mem-budget——磁盘 .map 出自 22:52 那次
构建、含尚未入库的 UI 与工具改动,与 HEAD 台账必然不同码;按仓库既有约定允许
--no-verify + 正文留证,回填在紧随的台账一笔统一做(含 ram_baseline --update 与
覆盖率基线两条 [NOTE]:ui_data.c 65→316、iec104_comm.c 36/36)。
两条已挂着的账:① id-check 不可判定引用 1,162 / 上限 1,166,**余量只剩 4**,
主要吃进者是我今天推进的 UI 那笔(68 条文本断言的注释引用),账在 Lead;
② 崩溃注入腿(故意复位)台架复验未跑,需先由代码道提交触发器,且判据只判原子性
不判 [ERR]=0——IPSR 路径的主张归主机侧套件,硬件照不出来。
产物身份(按 D 表要求钉死,避免数字与树脱钩):本笔包含的固件树就是那张 D 表对应的
那一棵——MDK-ARM/.../STM32F407-Demo.htm 的 mtime = 2026-10-26 22:52:12(.htm/.map 按
约定不入库,故把时刻写进正文)。提交前在本树复跑 stack_report.py:exit 0,
iec104Task 1,464 / margin 1,096(42%)PASS、defaultTask 3,040 / 2,080(40%)PASS,
与 D 表逐项一致;本次运行不含任何未提交固件源码之外的新改动。
426 lines
19 KiB
C
426 lines
19 KiB
C
/*
|
||
* 模块名称:通道标定与量纲折算(L1.5)——实现
|
||
* 模块功能:见 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 默认 = 卡 1 定死的引脚侧 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 卡 2/卡 4)
|
||
* 修改记录:
|
||
* v1.0 2026-10-25 DTU 项目组 创建:Q16 系数/零点两段落盘 + 折算 + 三态 flags
|
||
* v1.1 2026-10-25 DTU 项目组 卡 10(道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 各写一份,两处一旦
|
||
* 漂移,屏上「未标定」与判据实际停走就不是同一件事
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#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 有符号,卡 3 的
|
||
* 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 理论系数,卡 1 定死,「数据可信」§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 运行期派生),因为它是
|
||
* 卡 1 定稿的存档事实;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;
|
||
|
||
/* 键表: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;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道当前生效零点偏移(码值,浮点)——adc_cal_k_uv 孪生入口
|
||
* 入口参数:sem - 语义通道 adc_semantic_t
|
||
* 返 回 值:b[码值](越带时 s_eff_kb 已回 0);TEMP/越界返 0 float
|
||
* 限定条件:无(任意上下文,纯读)
|
||
* 函数说明:卡 10 的 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(「数据可信」卡 4)
|
||
* 入口参数:sem - 语义通道;k_q16 - 系数(µV/码×65536);b_codes - 零点(码值)
|
||
* 返 回 值:0 - 成功;-1 - 参数非法/越出合法带(RAM 不动,[ERR] 留痕) int
|
||
* 限定条件:整定路径上下文(defaultTask 整定/标定链;iec104Task 经 run_par
|
||
* 直通为卡 10 授权写者,单字原子契约同 §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) {
|
||
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);
|
||
return -1;
|
||
}
|
||
s_kcfg.k[sem] = k_q16;
|
||
s_bcfg.b[sem] = b_codes;
|
||
s_kcfg.valid |= (uint8_t)(1u << (unsigned)sem);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:两点标定公式(卡 12 的算术核,纯函数无状态)
|
||
* 入口参数: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;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:通道码值域贴轨带门限(L5-2 判定的门限来源,按 FS 百分比)
|
||
* 入口参数: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;
|
||
}
|