0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
397 lines
17 KiB
C
397 lines
17 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)
|
||
* + 合法带哨兵回退 + 两点标定公式(台架注入待跑)
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#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;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读通道当前生效系数(µ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;
|
||
}
|