94b0d79026
本笔含两组收尾工作(同一工作区内交错在 6 个共享文档/门禁配置里, 拆开会产生"文档声称的页数与树里黄金数不一致"的中间提交,故合为一笔): 一、UI 补齐(清单清空) - 新建 6 个内容页模块:ui_page_accident / oper / alarm / start / yk 复用 ui_w_records 共享组件(分页、空态"无记录"、按实例读盘缓存), 记录层与五个写入点已由道F task-10 / 道G task-12 落地,本批只补页面; ui_page_runreport 为 13 行 2 页只读统计,四个数逐个挂范围标签 (自电池周期 / 本次开机),boot_ready=0 显 "--" 不显 0。 - 数据面:ui_data_info_t 尾部追加 9 字段,ui_data_feed 用 boot_stats_get() + iec104_serv_get_stats() 纯搬运;模拟器填确定值假数据 (差异登记 docs/测试说明/模拟器差距清单.md §2.3 两行)。 - 接线:菜单树 6 叶绑 #page=,绑定叶 22→28、UI_PAGE_MAP_COUNT=28; 字库 185→218 字 / 280→313 字形;双端注册 uvprojx +30、CMakeLists +6。 - 回归:内容页黄金 53→60(含 runreport_p2 真拍第 2 页),文本断言 425 正向 / 36 反向;存量 53 张逐字节零漂移;记录 5 页各用独立空目录 (模拟器把操作留痕写进 cwd 的 sim_oper.log,共用 tmp 会让空态拍不到)。 二、数据可信销项 + 「数据可信」任务卡号整族清退 - 删除已闭环的"已知电压注入验证"行与闭环结论块(证据在 docs/硬件平台/ADC通道映射说明.md §8.1 与 docs/测试说明/ADC前端注入验证流程.md §5~§7),§5 标识列改语义短语。 - 卡 0/0b/1~12 共 14 个号全仓 288 站点 / 56 文件清退成语义短语, 代码注释、测试、门禁配置与文档一并改;换算表入 docs/编号规范.md §9.3。 这一族是中文前缀,不进 check_ids 语料,刻意不入 id_aliases.json。 验证(实测) - @build 0 错 0 警:Code=236456 RO=28124 RW=1876 ZI=137084; RAM +464 B(RW +304 / ZI +160),RW_IRAM1 62.2% / RW_CCM 87.9%, 体积同码凭证与 RAM 台账已回填。 - check_menu_rows 8 状态 rc=0(池 5368/5448 常量未动); check_content_pages 60 页 rc=0;check_font_charset / registration / coding-standard / bom-drift / mojibake / doc-links --all / layer-deps / nested-comment 全 rc=0;check_ids --strict 无错误;主机单测 ui_data 295 / boot_stats 21 / sys_log_mcu 141 / soe 32 / soe_store 27。 未做(不是遗漏) - 六页真机判据(真记录翻页、掉电保持、清除后归零、boot_count 自增、 拔 RTC 电池落 RESTARTED、IWDG 复位落 rst_cause、主站连一次 conn_up+1) 已登记 docs/任务规划/登记簿.md §7;模拟器结构上照不到。 - docs/产品需求/产品需求设计文档.md 属另一条在飞改动,本笔不含; 其中我已同步 6 处现状数字(28 叶 / 60 页 / 218 字),随该笔入库。
218 lines
12 KiB
C
218 lines
12 KiB
C
/*
|
||
* 模块名称:ADC 采样有效性校验(L1 采集与预处理)
|
||
*(保护功能清单见 docs/算法/继电保护功能复现规划.md)
|
||
* 模块功能:对两片转换器的"节拍是否还在喂数据"做**判定层**(观测计数由
|
||
* `adc_input` / `tpafe5160` 驱动提供,本模块只做判定与告警位):
|
||
* ① 静默(stall):帧序号连续 >= ADC_HEALTH_STALL_MS 无前进;
|
||
* ② 丢帧(gap):帧序号一次前进 >1(消费者超拍,中间帧被覆盖);
|
||
* ③ 单调性破坏:帧序号倒退(计数器被改写 / 驱动 bug,锁存);
|
||
* ④ 错误计数增量:FRSTDATA 跳变 / HAL ADC·DMA 错误 / STM32 停摆计数。
|
||
* ⚠ 本层**纯逻辑**:不碰寄存器、不取数、不打日志——输入是调用方
|
||
* 采集的计数快照,输出是故障位;日志与自检上报由消费者
|
||
* (`App/task/adc_task.c`)按位执行 → 主机侧可单测(L0.5)。
|
||
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||
* 作者:王建锋
|
||
* 创建日期:2026-10-11
|
||
* 修改记录:
|
||
* v1.0 2026-10-11 王建锋 创建:采样有效性校验(静默/丢帧/单调性/错误增量)
|
||
* v1.1 2026-10-21 王建锋 新增 adc_health_age_stale()(无模块状态的帧年龄
|
||
* 纯判据,参照系由调用方自存)与 ADC_HF_STALE 位:
|
||
* 判定层的 stall/backward 位只在 adcTask 每拍跑
|
||
* check() 时才可能置起,节拍源停摆会把本任务卡在
|
||
* osThreadFlagsWait 上 → 那些位永远是 0,而相量冻在
|
||
* 最后一帧仍被 protectTask 用于判据。该纯判据让保护侧
|
||
* 能用**自己的时基 + 中断侧计数器**独立判"数据还新不新" * v1.2 2026-10-25 DTU 项目组 新增 采样通道自检 残余「码值越界(贴轨)检出」:
|
||
* adc_health_rail_watch()/rail_sticky()/rail_init()
|
||
* ——连续 N 拍贴满量程带且该通道见过释放沿才锁存
|
||
* (空载恒 0 / 开机恒 0 是合法工况,不误报);
|
||
* 判据动因 docs/问题记录/采集与数据处理短板评测.md
|
||
* §6.2,门限来源 adc_cal_rail_ths()(按 FS 百分比)
|
||
|
||
*/
|
||
|
||
#ifndef ADC_HEALTH_H
|
||
#define ADC_HEALTH_H
|
||
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* 静默判定门限(ms):帧序号连续无前进达到该时长即判静默。
|
||
* 名义节拍 1 kHz → 正常每拍 +1;100 拍无前进不可能是抖动,只可能是
|
||
* 节拍源/转换器/EXTI 真停(消费者超拍只会造成 gap=d>1,不会 d=0)。 */
|
||
#define ADC_HEALTH_STALL_MS 100U
|
||
|
||
/* 电平故障位(adc_health_flags(),当前活跃;stall 可自愈清除,backward 锁存)
|
||
* ——供后续「自检失败闭锁」直接消费 */
|
||
#define ADC_HF_TPAFE_STALL (1uL << 0) /* TPAFE 帧序号静默 */
|
||
#define ADC_HF_STM32_STALL (1uL << 1) /* STM32 DMA 完成计数静默 */
|
||
#define ADC_HF_SEQ_BACKWARD (1uL << 2) /* 帧序号倒退(锁存,不自愈) */
|
||
|
||
/* 锁存故障位(adc_health_sticky(),出现过即置位、直到 init 才清;
|
||
* 与电平位共用高位编号,gap 类"常态性超拍"不入锁存) */
|
||
#define ADC_HF_FRSTDATA (1uL << 3) /* FRSTDATA 跳变计数有增量(锁存) */
|
||
#define ADC_HF_STM32_ERR (1uL << 4) /* STM32 停摆计数有增量(锁存) */
|
||
#define ADC_HF_HW_ERR (1uL << 5) /* HAL ADC/DMA 错误回调有增量(锁存) */
|
||
|
||
/* 边沿事件位(adc_health_check() 出参 events,仅本次调用有效)。
|
||
* ⚠ FRSTDATA/STM32_ERR/HW_ERR 三个事件位与同名锁存位**编号一致**——
|
||
* 消费者可直接用 events 掩码对 sticky/logged 位图做"每类留痕一次"的编排 */
|
||
#define ADC_HE_TPAFE_GAP (1uL << 6) /* 本次 TPAFE 序号前进 >1 */
|
||
#define ADC_HE_STM32_GAP (1uL << 7) /* 本次 STM32 gap 计数有增量 */
|
||
#define ADC_HE_STM32_ERR (1uL << 4) /* 本次 STM32 停摆计数有增量 */
|
||
#define ADC_HE_FRSTDATA (1uL << 3) /* 本次 FRSTDATA 计数有增量 */
|
||
#define ADC_HE_HW_ERR (1uL << 5) /* 本次 HAL 错误计数有增量 */
|
||
|
||
/* 兜底新鲜度位(bit8,与判定层 bit0..bit7 位段错开)
|
||
* ⚠ 本位**不由本模块置位**:由 protectTask 调 adc_health_age_stale() 独立观测后
|
||
* OR 进闭锁位图。因此它不进 sticky、不进 adc_task 编排层的 logged 位图、
|
||
* 也不由本模块打日志——留痕走 protect 侧既有的闭锁边沿日志与 SOE。
|
||
* 位段纪律见 docs/算法/采样有效性校验设计说明.md §14 */
|
||
#define ADC_HF_STALE (1uL << 8) /* 帧年龄超限(电平,序号恢复前进即清) */
|
||
|
||
/* 计数快照(由调用方采集,本模块不触碰任何驱动/外设) */
|
||
typedef struct {
|
||
uint32_t tpafe_seq; /* tpafe5160_frame_seq() */
|
||
uint32_t tpafe_frst_err; /* tpafe5160_frstdata_err()(驱动为 uint16,此处放宽) */
|
||
uint32_t stm32_seq; /* adc_input_stm32_seq() */
|
||
uint32_t stm32_errs; /* adc_input_stm32_errs() */
|
||
uint32_t stm32_gaps; /* adc_input_stm32_gaps() */
|
||
uint32_t stm32_hw_errs; /* adc_input_stm32_hw_errs() */
|
||
uint32_t now_ms; /* osKernelGetTickCount()(tick 回绕由差值法吸收) */
|
||
} adc_health_in_t;
|
||
|
||
/* 判定结果快照 */
|
||
typedef struct {
|
||
uint32_t flags; /* 电平故障位 ADC_HF_TPAFE/STM32_STALL/SEQ_BACKWARD */
|
||
uint32_t events; /* 本次边沿事件位 ADC_HE_xxx */
|
||
uint32_t sticky; /* 锁存故障位(含电平位的历史并集) */
|
||
uint32_t tpafe_gaps; /* TPAFE 累计丢帧点数(序号前进 >1 的超出部分之和) */
|
||
uint32_t tpafe_stall_ms; /* TPAFE 当前静默时长(0=正常) */
|
||
} adc_health_status_t;
|
||
|
||
/* 帧年龄参照系(adc_health_age_stale() 用)。状态**归调用方所有**,所以多个观察者
|
||
* 各判各的、互不污染——这是它与本模块其余接口的关键区别(check() 用的是模块级
|
||
* 单写者状态,只能有一个每拍调用者) */
|
||
typedef struct {
|
||
uint32_t seq; /* 上次看到的序号 */
|
||
uint32_t adv_ms; /* 最后一次看到序号前进的时刻(与调用方时基同源) */
|
||
uint8_t primed; /* 0 = 尚未建立基线(首拍只记录、不判定) */
|
||
} adc_health_age_t;
|
||
|
||
/*
|
||
* 函数功能:采样有效性校验初始化(幂等,内核启动前可调)
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
* 限定条件:无
|
||
* 函数说明:清全部状态;首次 adc_health_check() 只建基线、不判定。
|
||
*/
|
||
void adc_health_init(void);
|
||
|
||
/*
|
||
* 函数功能:按计数快照做一次有效性判定(每拍调用,开销为常数次整数运算)
|
||
* 入口参数:in - 计数快照(NULL 则本次跳过,out 不动)
|
||
* out - 判定结果(NULL 则仅更新内部状态)
|
||
* 返 回 值:无
|
||
* 限定条件:仅任务上下文(与 adc_input_poll() 同节奏调用)
|
||
* 函数说明:首次调用建立基线(不产生事件/故障)。序号差值按
|
||
* `(int32_t)(now - prev)` 口径计算,tick/计数回绕均安全。
|
||
*/
|
||
void adc_health_check(const adc_health_in_t *in, adc_health_status_t *out);
|
||
|
||
/*
|
||
* 函数功能:帧年龄判定(无模块状态,供与消费者存活解耦的兜底门使用)
|
||
* 入口参数:a_p_age - 调用方自存的参照系
|
||
* a_cur_seq - 本次读到的序号(须是**中断侧**递增的计数器,如
|
||
* tpafe5160_frame_seq() / adc_input_stm32_seq())
|
||
* a_now_ms - 本次时基(与 a_p_age->adv_ms 取同一个任务的 tick)
|
||
* a_age_ms - 判老门限(ms)
|
||
* 返 回 值:1 - 已陈旧(调用方应据此闭锁);0 - 新鲜,或仍在建立基线
|
||
* 限定条件:任意任务上下文;零内核依赖、零共享状态 → 主机侧免桩可直接单测
|
||
* 函数说明:差值口径与 adc_health_check() 一致((int32_t) 差值,计数与 tick
|
||
* 回绕均安全)。首次调用只建基线并返回 0;序号有任何前进即返回 0
|
||
* (一次前进多帧属消费者超拍,不是故障);倒退不在此判老,单调性
|
||
* 破坏归 ADC_HF_SEQ_BACKWARD 管。参数错返回 0——宁可不锁,不可误锁。
|
||
*/
|
||
uint8_t adc_health_age_stale(adc_health_age_t *a_p_age, uint32_t a_cur_seq,
|
||
uint32_t a_now_ms, uint32_t a_age_ms);
|
||
|
||
/*
|
||
* 函数功能:读当前电平故障位
|
||
* 入口参数:无
|
||
* 返 回 值:ADC_HF_TPAFE_STALL / ADC_HF_STM32_STALL / ADC_HF_SEQ_BACKWARD 组合
|
||
* 限定条件:无
|
||
* 函数说明:供自检失败闭锁判定与 UI;与 check() 出参 flags 同源。
|
||
*/
|
||
uint32_t adc_health_flags(void);
|
||
|
||
/*
|
||
* 函数功能:读锁存故障位(出现过即置位,直到 init 才清)
|
||
* 入口参数:无
|
||
* 返 回 值:电平位 + ADC_HF_FRSTDATA/STM32_ERR/HW_ERR 的并集
|
||
* 限定条件:无
|
||
* 函数说明:stall 自愈后 sticky 仍保留该次故障痕迹("曾经停摆过"必须留痕)。
|
||
*/
|
||
uint32_t adc_health_sticky(void);
|
||
|
||
/*
|
||
* 函数功能:读 TPAFE 累计丢帧点数
|
||
* 入口参数:无
|
||
* 返 回 值:序号前进 >1 的超出部分累计(与 STM32 侧 adc_input_stm32_gaps() 对称)
|
||
* 限定条件:无
|
||
* 函数说明:消费者超拍(单拍耗时 > 1 ms)所致;非零本身不是闭锁条件,
|
||
* 但持续增长说明相量窗出现时间空洞,须排查耗时。
|
||
*/
|
||
uint32_t adc_health_tpafe_gaps(void);
|
||
|
||
/* ===================== 采样通道自检 残余:码值越界(贴轨)检出,2026-10-25 ===================== */
|
||
|
||
/* 贴轨锁存的连续拍数:20 拍 = 1 kHz 节拍下一个 50 Hz 周波。削顶/触轨波形的
|
||
* 贴带段持续 ≥ 半周波;噪声毛刺不可能连续 20 拍钉在同侧轨带内(短板评测 §6.2
|
||
* 判据①「连续 N 点贴满量程」的定标)。 */
|
||
#define ADC_RAIL_N_RUN 20u
|
||
|
||
/* 贴轨监视的通道数上限(9 = 8 电气量 + 温度;温度是否监视由调用方经
|
||
* adc_cal_rail_ths() 返回 -1 决定,本层不内嵌语义知识) */
|
||
#define ADC_RAIL_CH_NUM 9u
|
||
|
||
/*
|
||
* 函数功能:贴轨观察器复位(adc_health_init() 已内含,单独暴露供编排层复用)
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
* 限定条件:与 check() 同——仅任务上下文(单写者)
|
||
*/
|
||
void adc_health_rail_init(void);
|
||
|
||
/*
|
||
* 函数功能:单通道单拍贴轨观察(采样通道自检 码值越界检出,纯算术)
|
||
* 入口参数:ch - 通道号(0..ADC_RAIL_CH_NUM-1,越界忽略) uint8_t
|
||
* code - 本拍原始码值(**码值域**,与标定无关) int32_t
|
||
* lo_th - 下轨带上界(code ≤ 此值算贴底) int32_t
|
||
* hi_th - 上轨带下界(code ≥ 此值算贴顶) int32_t
|
||
* 返 回 值:1 - 本拍**新锁存**该通道(边沿,供编排层落日志);0 - 无新事件 uint32_t
|
||
* 限定条件:每拍每通道至多一次(与采样帧同节奏,单写者)
|
||
* 函数说明:锁存条件 = 连续 ADC_RAIL_N_RUN 拍贴**同一侧**轨带,且该通道此前
|
||
* 出现过中带样本(「释放沿」)。释放沿要求的理由:空载信号恒 0、
|
||
* 开机未采到帧恒 0 都是**合法工况**(短板评测 §6.2:空载与零值都是
|
||
* 合法工况)——只有「动过又钉死在轨上」才是削顶/越界签名。锁存后
|
||
* 不再重复报边沿,直到 rail_init/health_init 重置。
|
||
*/
|
||
uint32_t adc_health_rail_watch(uint8_t ch, int32_t code, int32_t lo_th, int32_t hi_th);
|
||
|
||
/*
|
||
* 函数功能:读贴轨锁存位图
|
||
* 入口参数:无
|
||
* 返 回 值:bit ch = 该通道曾检出贴轨(1 kHz × 9 路 → 低 9 位有效) uint32_t
|
||
* 限定条件:无
|
||
* 函数说明:供心跳日志 / 后续品质位映射(遥测快照发布与 104 消费的 qds 消费者)只读。
|
||
*/
|
||
uint32_t adc_health_rail_sticky(void);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* ADC_HEALTH_H */
|