13a4e61c27
新增 App/algo/adc_health.c/h(纯逻辑判定层,零平台依赖免桩单测): - 静默:两侧帧序号连续 >=100ms 无前进(电平位可自愈,flags() 为 L5-4 闭锁预留) - 丢帧:TPAFE 序号一次前进 >1 计 tgap(与 STM32 侧 adc1_gap 对称,边沿事件) - 单调性:序号倒退锁存位;tick/计数差值法 (int32_t)(a-b) 回绕安全 - 错误增量:FRSTDATA / HAL 错误 / STM32 停摆计数增量,事件位与锁存位编号对齐, 消费者按位图做"每类每次开机留痕一次"编排 adc_task.c v2.9 告警编排(判定与编排分离):严重事件/电平上升沿 DBG_ERROR + SYSLOG_SELFTEST(每类每次开机一次防刷屏),下降沿 DBG_INFO 恢复;gap 不告警 (消费者超拍是已知现象,告警会淹没真故障);心跳行增 tgap 观测字段。 主机单测 test/host/adc_health/ 31 用例全绿(100ms 门限两侧 / 边沿语义 / 倒退锁存 / tick 回绕 / NULL 守卫 / init 幂等),登记 adc-health-unit-test 钩子。 台架 @test PASS:零 [ERR](健康不误报)、tgap=1->2 与 adc1_gap 同步可见、 frames 1000.0 Hz;证据 boot_log_data/20261011_prod_adc_health_boot_log.txt。 抬栈:adc_health_report->syslog_post 控制台回显尾使 adcTask 静态最坏链 832->1080 B(512 words 余 968 B < 1024 B ISR 底线)-> 512->640 words, 复测余 1480 B / 57%,堆 min_ever 7152->6640 B(-512 B 符合预算)。 AGENTS/README 任务表、RAM 方案 §2.3、规划文档 §3.1 L1-6 已同步置完成。
201 lines
6.8 KiB
C
201 lines
6.8 KiB
C
/*
|
|
* 模块名称:ADC 采样有效性校验(L1 采集与预处理,规划项 L1-6)
|
|
* 模块功能:见 adc_health.h 文件头。本文件为**纯逻辑实现**:输入计数快照、
|
|
* 输出故障位/事件位,零平台依赖(仅 stdint.h)→ 主机侧免桩单测。
|
|
* 适用平台:STM32F407ZGTx + TPAFE5160
|
|
* 作者:王建锋
|
|
* 创建日期:2026-10-11
|
|
* 修改记录:
|
|
* v1.0 2026-10-11 王建锋 创建:L1-6 采样有效性校验
|
|
*/
|
|
|
|
#include "adc_health.h"
|
|
|
|
/* ===================== 内部状态 ===================== */
|
|
|
|
/* 基线是否已建立:首次 check() 只记基线、不判定(开机首拍无参照系) */
|
|
static uint8_t s_primed;
|
|
|
|
/* 上次看到的计数(差值法判"有无前进",回绕安全) */
|
|
static uint32_t s_tpafe_seq;
|
|
static uint32_t s_tpafe_frst;
|
|
static uint32_t s_stm32_seq;
|
|
static uint32_t s_stm32_errs;
|
|
static uint32_t s_stm32_gaps;
|
|
static uint32_t s_stm32_hw;
|
|
|
|
/* 两侧"最后一次看到序号前进"的时刻(判静默) */
|
|
static uint32_t s_tpafe_adv_ms;
|
|
static uint32_t s_stm32_adv_ms;
|
|
|
|
/* 故障位 */
|
|
static uint32_t s_flags; /* 电平位(stall 可自愈;backward 锁存在此) */
|
|
static uint32_t s_sticky; /* 锁存位(含电平位历史并集) */
|
|
static uint32_t s_tpafe_gaps;
|
|
|
|
/* ===================== 对外接口 ===================== */
|
|
|
|
/*
|
|
* 函数功能:采样有效性校验初始化(幂等)
|
|
* 入口参数:无
|
|
* 返 回 值:无
|
|
* 限定条件:无(内核启动前可调)
|
|
* 函数说明:清全部内部状态;判定从下一次 check() 的首拍基线重新开始。
|
|
*/
|
|
void adc_health_init(void) {
|
|
s_primed = 0u;
|
|
s_tpafe_seq = 0u;
|
|
s_tpafe_frst = 0u;
|
|
s_stm32_seq = 0u;
|
|
s_stm32_errs = 0u;
|
|
s_stm32_gaps = 0u;
|
|
s_stm32_hw = 0u;
|
|
s_tpafe_adv_ms = 0u;
|
|
s_stm32_adv_ms = 0u;
|
|
s_flags = 0u;
|
|
s_sticky = 0u;
|
|
s_tpafe_gaps = 0u;
|
|
}
|
|
|
|
/*
|
|
* 函数功能:按计数快照做一次有效性判定
|
|
* 入口参数:in - 计数快照;NULL 则本次跳过(out 不动)
|
|
* out - 判定结果;NULL 则仅更新内部状态
|
|
* 返 回 值:无
|
|
* 限定条件:仅任务上下文(与 adc_input_poll() 同节奏,每拍一次)
|
|
* 函数说明:判定顺序 = 建基线 → 序号差值(倒退/丢帧/前进)→ 静默时长 →
|
|
* 错误计数增量。events 仅本次有效;flags/sticky 为持续状态。
|
|
*/
|
|
void adc_health_check(const adc_health_in_t *in, adc_health_status_t *out) {
|
|
uint32_t events = 0u;
|
|
|
|
if (in == 0) {
|
|
return;
|
|
}
|
|
|
|
/* 1) 首拍基线:只记录,不判定(开机时序号已非 0,无"上一拍"参照) */
|
|
if (s_primed == 0u) {
|
|
s_primed = 1u;
|
|
s_tpafe_seq = in->tpafe_seq;
|
|
s_tpafe_frst = in->tpafe_frst_err;
|
|
s_stm32_seq = in->stm32_seq;
|
|
s_stm32_errs = in->stm32_errs;
|
|
s_stm32_gaps = in->stm32_gaps;
|
|
s_stm32_hw = in->stm32_hw_errs;
|
|
s_tpafe_adv_ms = in->now_ms;
|
|
s_stm32_adv_ms = in->now_ms;
|
|
} else {
|
|
uint32_t d; /* 序号差值(两侧共用,cppcheck:作用域收在本块) */
|
|
|
|
/* 2) TPAFE 序号差值:倒退 = 单调性破坏(锁存);>1 = 丢帧(事件 + 计数) */
|
|
d = in->tpafe_seq - s_tpafe_seq;
|
|
if ((int32_t)d < 0) {
|
|
s_flags |= ADC_HF_SEQ_BACKWARD;
|
|
s_sticky |= ADC_HF_SEQ_BACKWARD;
|
|
s_tpafe_seq = in->tpafe_seq; /* 重新对齐,避免持续误判 */
|
|
s_tpafe_adv_ms = in->now_ms;
|
|
} else {
|
|
if (d > 1u) {
|
|
s_tpafe_gaps += (d - 1u);
|
|
events |= ADC_HE_TPAFE_GAP;
|
|
}
|
|
if (d >= 1u) {
|
|
s_tpafe_seq = in->tpafe_seq;
|
|
s_tpafe_adv_ms = in->now_ms;
|
|
}
|
|
}
|
|
|
|
/* 3) STM32 序号差值:同一口径(gap 计数在 adc_input 侧另有细账,
|
|
* 这里只取"前进/倒退/静默"三态,避免两处判据打架) */
|
|
d = in->stm32_seq - s_stm32_seq;
|
|
if ((int32_t)d < 0) {
|
|
s_flags |= ADC_HF_SEQ_BACKWARD;
|
|
s_sticky |= ADC_HF_SEQ_BACKWARD;
|
|
s_stm32_seq = in->stm32_seq;
|
|
s_stm32_adv_ms = in->now_ms;
|
|
} else if (d >= 1u) {
|
|
s_stm32_seq = in->stm32_seq;
|
|
s_stm32_adv_ms = in->now_ms;
|
|
}
|
|
|
|
/* 4) 静默判定(电平位,可自愈):距最后一次前进 >= 门限 */
|
|
if ((uint32_t)(int32_t)(in->now_ms - s_tpafe_adv_ms) >= ADC_HEALTH_STALL_MS) {
|
|
s_flags |= ADC_HF_TPAFE_STALL;
|
|
s_sticky |= ADC_HF_TPAFE_STALL;
|
|
} else {
|
|
s_flags &= ~ADC_HF_TPAFE_STALL;
|
|
}
|
|
if ((uint32_t)(int32_t)(in->now_ms - s_stm32_adv_ms) >= ADC_HEALTH_STALL_MS) {
|
|
s_flags |= ADC_HF_STM32_STALL;
|
|
s_sticky |= ADC_HF_STM32_STALL;
|
|
} else {
|
|
s_flags &= ~ADC_HF_STM32_STALL;
|
|
}
|
|
|
|
/* 5) 错误计数增量(事件 + 锁存):三类计数正常应恒为 0 增量 */
|
|
if (in->tpafe_frst_err != s_tpafe_frst) {
|
|
events |= ADC_HE_FRSTDATA;
|
|
s_sticky |= ADC_HF_FRSTDATA;
|
|
s_tpafe_frst = in->tpafe_frst_err;
|
|
}
|
|
if (in->stm32_errs != s_stm32_errs) {
|
|
events |= ADC_HE_STM32_ERR;
|
|
s_sticky |= ADC_HF_STM32_ERR;
|
|
s_stm32_errs = in->stm32_errs;
|
|
}
|
|
if (in->stm32_gaps != s_stm32_gaps) {
|
|
events |= ADC_HE_STM32_GAP;
|
|
s_stm32_gaps = in->stm32_gaps;
|
|
}
|
|
if (in->stm32_hw_errs != s_stm32_hw) {
|
|
events |= ADC_HE_HW_ERR;
|
|
s_sticky |= ADC_HF_HW_ERR;
|
|
s_stm32_hw = in->stm32_hw_errs;
|
|
}
|
|
}
|
|
|
|
if (out != 0) {
|
|
out->flags = s_flags;
|
|
out->events = events;
|
|
out->sticky = s_sticky;
|
|
out->tpafe_gaps = s_tpafe_gaps;
|
|
out->tpafe_stall_ms =
|
|
((s_flags & ADC_HF_TPAFE_STALL) != 0u)
|
|
? (uint32_t)(int32_t)(in->now_ms - s_tpafe_adv_ms)
|
|
: 0u;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 函数功能:读当前电平故障位
|
|
* 入口参数:无
|
|
* 返 回 值:见 adc_health.h ADC_HF_TPAFE_STALL/STM32_STALL/SEQ_BACKWARD
|
|
* 限定条件:无
|
|
* 函数说明:与 check() 出参 flags 同源。
|
|
*/
|
|
uint32_t adc_health_flags(void) {
|
|
return s_flags;
|
|
}
|
|
|
|
/*
|
|
* 函数功能:读锁存故障位
|
|
* 入口参数:无
|
|
* 返 回 值:电平位 + FRSTDATA/STM32_ERR/HW_ERR 的并集
|
|
* 限定条件:无
|
|
* 函数说明:出现过即留痕,直到 adc_health_init() 才清。
|
|
*/
|
|
uint32_t adc_health_sticky(void) {
|
|
return s_sticky;
|
|
}
|
|
|
|
/*
|
|
* 函数功能:读 TPAFE 累计丢帧点数
|
|
* 入口参数:无
|
|
* 返 回 值:序号前进 >1 的超出部分累计
|
|
* 限定条件:无
|
|
* 函数说明:与 STM32 侧 adc_input_stm32_gaps() 对称的 TPAFE 侧细账。
|
|
*/
|
|
uint32_t adc_health_tpafe_gaps(void) {
|
|
return s_tpafe_gaps;
|
|
}
|