60PL HVCH 学习文档

🔥

产品总览

产品名称:HVCH — High Voltage Coolant Heater(高压冷却液加热器)

客户:Toyota 丰田

供应商:Valeo THS TCC(法雷奥热系统事业部)

料号:bcd8880_00-ESF020

这个产品是干什么的?

电动车没有发动机余热来供暖,所以需要一个独立的加热器。HVCH就是这个角色——它用电池包的高压电(~400V)加热冷却液,热的冷却液通过管路流到暖风芯体,给座舱供暖。

简单理解:就是一个接在高压电上的"热得快",但有很复杂的控制和安全保护。

核心工作流程

车辆空调控制器 → 通过LIN总线发指令 → MCU收到并解析 → 计算加热功率 → PWM驱动IGBT开关 → IGBT导通高压电流经过加热元件(HE) → 冷却液被加热 → 同时持续监控温度/电压/电流 → 异常则锁机保护

关键参数

参数
输入电压(高压侧)~400V DC
输入电压(低压侧)12V DC (VSUP)
加热功率7KW (60PL) / 10KW (600D/800D)
通信协议LIN 2.1
MCUNXP S32K144 (ARM Cortex-M4)
SBCTJA1128E
📐

系统架构

硬件架构总览

整个板子分为低压侧(LV)高压侧(HV)两个域,通过隔离电源(Flyback)和隔离信号连接。

低压侧 (12V域)

  • LV_CONNECTOR:外部连接器,接12V电源(LV+)、LIN总线、WSTB待机信号
  • DM_FILTER:输入滤波,防EMI 📄 Page 3
  • SBC (TJA1128E):系统基础芯片 — 5V稳压 + LIN收发 + 看门狗 📄 Page 8
  • MCU (S32K144):主控芯片 📄 Page 5
  • 温度传感器:T_OUTLET / T_LS_CMOS / T_LS_NTC 📄 Page 4
  • 外部ADC:I2C接口,采高压侧测量值 📄 Page 2

高压侧 (400V域)

  • HV_CONNECTOR:高压连接器,接电池包400V
  • IGBT模块:HS-IGBT(高侧) + LS-IGBT(低侧),功率开关 📄 Page 7
  • Gate Driver (UCC5350):IGBT栅极驱动 📄 Page 7
  • HE1/HE2:加热元件(Heating Element),Busbar连接
  • Flyback电源:低压侧→高压侧隔离供电(17V_HS/17V_LS) 📄 Page 6
  • 过流保护:电流采样 + 比较器(LM2903) 📄 Page 15-16
  • 高压测量:HV_MEAS分压采样 📄 Page 2

信号流框图

┌─────────────┐     LIN      ┌──────────┐
│ 空调控制器    │─────────────▶│  SBC     │
│ (车上)       │              │ TJA1128E │
└─────────────┘              └────┬─────┘
                                  │ SPI/UART
                             ┌────▼─────┐
                   ADC ◄─────│   MCU    │─────▶ CMD_HS1 (PWM)
              T_OUTLET ◄─────│ S32K144  │─────▶ CMD_LS1/LS2
              T_LS_NTC ◄─────│          │─────▶ FLY_EN
             T_LS_CMOS ◄─────│          │─────▶ VSUP_EN
                I2C ◄───────│          │─────▶ WND (看门狗)
                             └──────────┘
                                  │
                          ┌───────┴───────┐
                          │  Flyback 隔离  │
                          └───────┬───────┘
                                  │ 17V_HS / 17V_LS
                          ┌───────▼───────┐
                          │  Gate Driver   │
                          │   UCC5350     │
                          └───────┬───────┘
                                  │ GATE_HS / GATE_LS
                    HV+ ──▶┌──────▼──────┐──▶ HE1/HE2 ──▶ HVGND
                  (400V)   │ HS-IGBT     │    (加热元件)
                           │ LS-IGBT     │
                           └─────────────┘
      
🔀

项目变体

变体型号电压功率HW CMOS
00Full-featured400V
0160PL400V7KW
0260PL400V7KW
03600D400V10KW

你当前代码的产品编号是 7DA10017,属于 60PL 系列。变体之间主要区别是功率等级和是否有CMOS安全温度传感器。

🧱

软件分层结构

代码严格分层,从上到下:

命名规则:前缀决定层级。AP* = Applicative层,SCE*/SVC* = Services层,HD* = Handler硬件层,OS* = 操作系统层。

第1层:入口 (APMAIN)

文件:APMAIN_Applicative.c

main() 函数只做三件事:ICONF_vidInit() → ICONF_vidInitConfiguration() → APMODMGR_vidApplicaive()(进入无限循环)

第2层:应用层 (Applicative)

文件:Applicative/APMODMGR/

核心是模式管理状态机,for(;;) 死循环 + switch-case 切换模式。每个模式函数里按时间片执行不同的任务。

第3层:服务层 (Services)

模块功能
SCECOMLIN通信管理:解析指令、组装回复
SCEMONI主监控:电压/电流/温度/DCDC监控
SCEMONIS安全传感器监控:NTC/CMOS温度监控和一致性校验
SCEDRIV驱动控制:加热功率算法、温度调节
SCEFCTR故障计数器:故障记录和EEPROM管理
SCEWDG看门狗监管:喂狗 + 各时间片任务监控
SVCCKHW硬件自检:IGBT HW Check模式

第4层:硬件抽象层 (Handler)

模块硬件功能
HDADC片内ADC采样低压侧模拟信号
HDEXTADC外部ADC (I2C)采样高压侧测量值(隔离的)
HDDIOGPIO数字IO控制(CMD_HS1等)
HDLINLIN PHYLIN总线底层驱动和超时管理
HDNVMEEPROM非易失存储读写(故障计数等)
HDPWMPWM模块PWM输出控制
HDSBCTJA1128ESBC配置和管理
HDCPUS32K144CPU相关:时钟、定时器、中断

其他模块

  • OSRTE — 调度器(定时中断分频)
  • MEMCK — 内存/堆栈检查
  • ICONF — 初始化配置
  • COMMON — 通用定义、版本信息
🔄

运行模式状态机

系统上电后进入 Start 模式,然后根据条件在各模式间切换。模式切换由 enuManageModeTras() 函数决定。

模式一览

模式枚举值说明
🟢 StartSTD_START_MODE上电初始化,跑250ms后切到Neutral或HW Check
🔵 NeutralSTD_NEUTRAL_MODE空闲待命,监控传感器但不加热
🔵 IGBT HW CheckSTD_IGBT_HW_CHECK_MODEIGBT硬件自检模式
🔴 HeatingSTD_HEATING_MODE正在加热!驱动IGBT输出功率
⚙️ CalibrationSTD_CALIBRATION_MODE标定模式(工厂用)
⚠️ Temp LockSTD_TEMP_LOCK_MODE临时锁定:检测到可恢复故障,停止加热但保持监控
⚠️ LCMSTD_LCM_MODELife Cycle Management:带限制的运行模式
🔒 Perm LockSTD_PERM_LOCK_MODE永久锁定:严重故障,需要维修
💤 SleepSTD_SLEEP_MODE休眠:SBC进低功耗,等LIN唤醒
📥 DownloadSTD_DOWNLOAD_MODE刷写模式:更新固件

典型流转

上电 → Start → [HW Check OK] → Neutral → [收到加热指令] → Heating
                                    ↑                         │
                                    └─── [指令=停止] ─────────┘

Heating → [过温/过流] → Temp Lock → [故障恢复] → Neutral
Heating → [严重故障] → Perm Lock(需维修)
Neutral → [待机指令] → Sleep → [LIN唤醒] → Start
      

每个模式里发生什么

每个模式函数(如 enuAppHeatingMode())内部结构一样:

1ms到了?
  ├── 做1ms的事(ADC采样、喂狗、传感器监控)
  ├── 4ms到了?
  │     └── 做4ms的事(LIN通信、故障管理、模式切换判断)
  ├── 10ms到了?
  │     └── 做10ms的事(温度监控、LIN状态刷新、驱动控制)
  ├── 50ms到了?
  │     └── 做50ms的事(加热器管理、功率管理)
  └── 100ms到了?
        └── 做100ms的事(IO刷新、堆栈检查、过流监控)
      

易错点 / 关键笔记

HE_OC ≠ 过流!HE_OC = Heating Element Open Circuit(开路),检测负载阻抗异常偏大/电流异常偏小。开路=电阻大电流小,不存在过热风险。与HE_SC(Short Circuit短路)相反。
OC含义取决于上下文:在传感器模块(NTC_OC/CMOS_OC/TOUTLET_OC)中 OC = Open Circuit。在IAV模块(HE_OVC)中 OVC = Over Voltage/Current。注意区分。
并非所有故障在Neutral模式就能检测到。传感器故障(SC/OC)和一致性故障在Neutral即可检测(ADC采样不需要加热电流)。但IAV过流/过压、功率校验、HE开路必须在Heating模式下才能检测(需要电流/功率数据)。
Neutral到Heating不是直接切换。中间必须经过IGBT HW Check(硬件自检),检测IGBT开关是否正常。自检通过后才允许进入Heating开始通电。
启动逻辑变更(60PL新版本):旧版本只要NVM里FAILURE_LOCK标志被置位,启动就进永久锁。新版本必须FAILURE_LOCK + IGBT短路标志同时满足才进永久锁,其他故障全部进Neutral允许恢复。这是"去掉永久锁"的核心改动。
⏱️

调度器 OSRTE

文件:OS/Src/OSRTE_Scheduler.c

OSRTE 不是操作系统,是一个极简的定时中断分频器。

工作原理

硬件定时器每 1ms 产生一次中断,调用 OSRTE_vidITSchedulerManage()。这个中断函数做的事很简单:

  1. 设置 1ms flag = TRUE(每次都设)
  2. 计数器++,到4就设 4ms flag
  3. 计数器++,到10就设 10ms flag
  4. 计数器++,到50就设 50ms flag
  5. 计数器++,到100就设 100ms flag

主循环里各模式函数通过 OSRTE_bReadClearFlag*msTask() 轮询这些flag,到了就执行对应的任务。读完flag自动清零。

注意:4ms/10ms/50ms/100ms 的flag是互斥的(if-else if结构),也就是说每个1ms周期里最多只会额外触发一个较慢的任务。这是为了避免在一个1ms里塞太多事导致超时。

时间片任务分配规律

时间片典型任务特点
1msADC采样、喂狗、传感器状态检测、DCDC监控最高优先级,每个周期都跑
4msLIN通信、故障管理、模式切换、NVM写入通信和决策
10ms温度监控、LIN状态回复、驱动控制律控制环路
50ms加热管理、功率管理慢速控制
100msIO刷新、堆栈检查、过流监控后台维护

SCEDRIV — 驱动控制服务

路径:Services/SCEDRIV/

负责加热功率控制的核心模块。接收LIN指令的目标功率/温度,计算PWM占空比,驱动IGBT。

关键函数

SCEDRIV_vidTaskManageCtrlLaw()
控制律管理 — 10ms调用,计算输出功率
SCEDRIV_vidTaskManageHeater()
加热管理 — 50ms调用,管理PTC加热元件的开关
SCEDRIV_vidTaskManagePower()
功率管理 — 50ms调用,监控和限制输出功率
SCEDRIV_vidManageTempDerate()
温度降额 — 100ms调用,冷却液温度过高时降功率
SCEDRIV_vidTaskManageResCalc()
负载电阻计算 — 100ms调用,通过V/I算加热丝电阻
只在Heating模式下运行。Neutral模式不调用SCEDRIV的任何函数。
📡

SCECOM — LIN通信服务

路径:Services/SCECOM/

管理LIN总线通信:解析空调控制器发来的指令帧,组装HVCH的状态回复帧。

关键函数

SCECOM_vidManageLinConf()
LIN配置管理 — 4ms调用,处理LIN帧收发
SCECOM_vidManageRefLinStatus()
刷新LIN状态 — 10ms调用,把当前模式/温度/故障状态写进LIN回复帧
SCECOM_vidLinCommMoni()
LIN通信监控 — 检测通信超时/错误
SCECOM_vidHandleDataDumpRequest()
Data Dump请求处理 — 诊断用
🔍

SCEMONI / SCEMONIS — 监控服务

路径:Services/SCEMONI/

系统最复杂的模块之一。分两部分:SCEMONI管主监控,SCEMONIS管安全传感器。

SCEMONI — 主监控

SCEMONI_vidTaskDcdcMonitor()
DCDC电压监控 — 检测高压侧隔离电源是否正常
SCEMONI_vidManageLvMoni()
低压监控 — 检测12V是否过高/过低
SCEMONI_vidManageHvMoni()
高压监控 — 检测400V是否在安全范围
SCEMONI_vidManagOutltSensorMoni()
出口温度传感器监控 — T_OUTLET开路/短路检测
SCEMONI_vidManageTOutletMoni()
出口温度值监控 — 检测冷却液温度是否过高
SCEMONI_vidIavMoni()
平均电流监控 — 检测加热电流
SCEMONI_vidIHeOcMoni()
加热元件过流监控 — 100ms调用
SCEMONI_vidTaskScHeMont()
加热元件短路监控
SCEMONI_vidManageStandby()
待机管理 — 判断是否进Sleep

SCEMONIS — 安全传感器监控

SCEMONIS_vidManageNtcSensorMoni()
NTC传感器状态监控 — 开路/短路检测
SCEMONIS_vidManagCmosSensorMoni()
CMOS传感器状态监控
SCEMONIS_vidManageNtcTempMoni()
NTC过温监控
SCEMONIS_vidManageCmosTempMoni()
CMOS过温监控
SCEMONIS_vidManageConsisMoni()
NTC与CMOS一致性监控 — 两个传感器互相校验
SCEMONIS_vidTempGradMoni()
温度梯度监控 — 温升速率是否异常
SCEMONIS_vidLowFlowMoni()
低流量监控 — 冷却液流量是否过低
Inhibitor机制:代码里大量出现 bGetXxxInhibStatus()。Inhibitor = 抑制器。如果某个传感器/通道被检测到故障,就把它的inhibitor激活,后续不再用这个通道的数据做判断,防止用坏数据做决策。
📊

SCEFCTR — 故障计数器服务

路径:Services/SCEFCTR/

管理所有故障的计数和记录。检测到故障时计数器增加,故障消失时递减(去抖动),达到阈值时触发模式切换。

关键函数

SCEFCTR_vidManageFail()
1ms调用 — 管理所有故障的状态机(出现/确认/恢复)
SCEFCTR_vidManageNvmFailCnt()
4ms调用 — 把故障计数写入EEPROM持久化
故障 → 模式切换:故障确认后会改变模式请求,enuManageModeTras() 读取这些请求决定切到哪个模式。比如过温确认 → 请求Temp Lock,严重故障 → 请求Perm Lock。
🐕

SCEWDG — 看门狗服务

路径:Services/SCEWDG/

双重看门狗机制:

  1. 外部看门狗:SBC(TJA1128E)内置的看门狗,SCEWDG_vidManageWatchDog() 每1ms通过WND引脚喂一次。不喂就复位。
  2. 软件看门狗SCEWDG_vidMonitor*msTask() 监控各时间片任务有没有按时执行。如果某个任务卡住超时,也会触发保护。
🔧

Handler 硬件抽象层

路径:_S32K144/Handler/

每个Handler封装一类硬件外设,上层服务不直接操作寄存器,而是调Handler的接口函数。

HDADC — 片内ADC

管理S32K144片内ADC通道的采样。主要采低压侧信号:温度传感器(T_OUTLET, T_LS_NTC)、电压(LV_MEASURE)等。

关键函数:HDADC_vidRefreshAdcChannels() — 1ms调用,刷新所有ADC通道

HDEXTADC — 外部ADC (I2C)

通过I2C接口读取高压侧的测量值。高压侧和低压侧物理隔离,片内ADC够不到,所以用外部ADC桥接。

关键函数:HDEXTADC_vidRfreshAdcExtChannel() — 1ms调用(DCDC OK时)

HDDIO — 数字IO

控制GPIO引脚的输出状态。

关键函数:HDDIO_vidSetCmdHs() — 控制高侧IGBT使能;HDDIO_vidSetFlyEn() — 控制Flyback电源使能;HDDIO_vidRefreshPorts() — 100ms刷新所有IO状态

HDLIN — LIN驱动

LIN总线底层驱动,管超时计时。

关键函数:HDLIN_vidLinTimerTask() — 1ms调用,LIN超时管理

HDNVM — EEPROM

非易失存储,保存故障计数器、标定数据等断电不能丢的东西。

关键函数:HDNVM_vidTaskManageNvmWriting() — 4ms调用,管理写入队列

HDSBC — 系统基础芯片

配置和管理TJA1128E,包括看门狗窗口设置、SPI通信等。

HDCPU — CPU管理

时钟监控、定时器管理。

关键函数:HDCPU_vidManageClockMoni() — 4ms调用,时钟监控;HDCPU_vidClearSchTimerFlag() — 在调度器中断里清除定时器标志

🏷️

状态机枚举(STD_tenuMode)

定义在 standard.hSTD_tenuMode 枚举,是系统所有运行模式的数值定义:

宏名含义
0STD_START_MODE启动
1STD_NEUTRAL_MODE空闲 / 中性
2STD_IGBT_HW_CHECK_MODEIGBT 硬件检查
3STD_HEATING_MODE加热
4STD_TEMP_LOCK_MODE临时锁定
5STD_PERM_LOCK_MODE永久锁定
6STD_CALIBRATION_MODE标定
7STD_SLEEP_MODE休眠
8STD_DOWNLOAD_MODE下载 / 刷写
9STD_LCM_MODE生命周期管理
注意区分:这是代码级别的枚举值(0-9),和 LIN 总线上报的 ECU 状态(HDLIN_enuEcuState,值 0-7)是两套不同的编码。模式管理用 STD_tenuMode,对外通信用 HDLIN_enuEcuState
📈

故障确认累积机制

所有故障(传感器 OC/SC、功率校验、温度不一致、过流等)采用累积计数器机制,不是检测到一次就确认。

7.1 基本原理

核心逻辑:

  • 检测到故障条件 → 计数器 += ConfStep
  • 故障条件消失 → 计数器 -= RehabStep
  • 计数器 >= Counter_MAX确认故障(Confirmed),计数器锁定在 Counter_MAX
  • 计数器 == 0清除故障(No failure)
  • 0 < 计数器 < Counter_MAX偶发状态(Punctual)
步长计算公式:
ConfStep = Ceil(CounterMax x 检测周期 / 故障确认时间)
RehabStep = Ceil(CounterMax x 检测周期 / 故障恢复时间)

7.2 各故障确认/恢复时间

故障类型确认时间恢复时间ConfStep = RehabStep?
T_OUTLET SC250ms250ms是,步长相等
T_OUTLET OC250ms250ms是,步长相等
功率校验 Pwr_Check5000ms5000ms是(同一个 FmPowerTimeCfm)
温度不一致 (Inconsistency)500ms500ms是,步长相等
过流 IAV(HE_Over_Curr)500ms500ms是(代码层面共用 FmIavInc 一个变量)
NTC 传感器 SC/OC250ms250ms是,步长相等
CMOS 传感器 SC/OC250ms250ms是,步长相等
注意:v11 代码中所有故障的 ConfStep 和 RehabStep 默认值都相等(确认多快清除就多快),但它们是独立参数,可以通过 NVM 标定修改为不同值。

7.3 特殊情况:HE_OC

HE_OC(加热元件开路)不使用累积计数器机制。它的确认逻辑是:

  1. 第一次检测到硬件过流 → 设 flag bHeOcFailDetect = TRUE
  2. 第二次检测到 → 直接确认为临时故障(TEMP_FAIL)
  3. 故障计数器达到上限 → 永久锁定(PERMENENT_FAIL)
相当于连续两次检测到就确认,不走累积步长。

7.4 确认后行为

  • 故障确认后计数器锁定在 Counter_MAX,不会自动递减
  • 需要 HW Reset 才能清除
  • HW Reset 后计数器清零,系统从 START 重新开始

7.5 传感器检测在哪些模式下运行

传感器开短路检测函数(NTC、CMOS、T_OUTLET)在以下模式都运行:

  • Neutral(空闲)
  • Heating(加热)
  • Temp Lock(临时锁定)
  • LCM(生命周期管理)

不运行的模式:Start、Sleep、Download

7.6 时序图场景分析

场景1:故障在确认时间内完全消失

Counter   ▲
          │    ╱╲
          │   ╱  ╲
          │  ╱    ╲
          │ ╱      ╲
          │╱        ╲
──────────┴──────────╲────────▶ t
          0s    4s    ╲→0

故障状态:No failurePunctualNo failure

HVCH Status:保持 Heating 不变

故障条件出现后计数器累加,在 4s 确认时间内故障消失,计数器递减到 0,回到 No failure。

场景2:4s 时故障还没完全消失但最终消失

Counter   ▲
          │    ╱╲  ╱╲
          │   ╱  ╲╱  ╲
          │  ╱        ╲
          │ ╱          ╲
          │╱            ╲
──────────┴───┼──────────╲──▶ t
          0s  4s          ╲→0

故障状态:No failurePunctualNo failure(在计数器减到 0 时才回 No failure,不是在 4s 处)

HVCH Status:保持 Heating 不变

场景3:故障最终累积到 Counter_MAX 确认

Counter   ▲  ·····Counter_MAX·····
          │    ╱╲  ╱╲╱╲  ╱────┐
          │   ╱  ╲╱      ╱    │(锁定)
          │  ╱           ╱     │
          │ ╱                  │
          │╱                   │
──────────┴───┼────────┼───────┼──▶ t
          0s  4s      4+n    HW Reset→0

故障状态:No failurePunctualConfirmed → (HW Reset) → No failure

HVCH Status:Heating → Temp Lock(或对应故障模式)→ (HW Reset) → Start

计数器在 4s 内因抖动没到 Counter_MAX,但残留值未清零,后续故障持续导致在 4+n 时刻达到 Counter_MAX 确认。n = 4 x (1 - 残留比例)。确认后计数器锁定,需 HW Reset 清除。

🌡️

旧逻辑 · Outlet SC/OC(11版本)

范围说明:本节记录的是 7DA10011(11版本)的旧有逻辑,属于带永久锁的老设计。 新逻辑另开章节,不要与本节混用。
代码位置:SCEMONI_Services.c → SCEMONI_vidManagOutltSensorMoni()(第 3162 行起)

1. 检测周期与调用范围

  • 检测周期:u8TOUTLET_MAX_PERIODCHK = 4(4ms 一次)
  • 采样源:HDADC_u16GetAdcMeasure(HDADC_MES_TEMP_T_OUTLET)
  • SC 与 OC 是两个独立 switch,各自一套状态变量与计数器,逻辑完全对称

调用点共 5 处(APMODMGR_Applicative.c),分属 4 个模式:

模式函数行号
NeutralenuAppNeutralMode752
HeatingenuAppHeatingMode1081
Temp LockenuAppTempLockMode1451、1664
LCMenuAppLCMMode1871
关键:Temp Lock 下仍在检测。正因为临时锁状态里这个函数还在跑, 60 秒持续计时才能在故障确认之后继续累加。否则 Condition A 的"持续 60 秒"永远无法满足, 也就升不到永久锁。Start / HwCk / Calib / PermLock / Sleep / Download 不调用。

2. 判据与迟滞(ADC 原始值,非温度值)

方向触发条件恢复条件迟滞量
SC 短路ADC ≤ OutletTempMin = 29ADC ≥ MinWithHys = 40TOUTLET_SC_HYST = 11
OC 开路ADC ≥ OutletTempMax = 1004ADC ≤ MaxWithHys = 991TOUTLET_OC_HYST = 13

四个值均可由 NVM 标定覆盖(HDNVM_u8OUTLET_SC_THR_MSB/LSBOUTLET_SC_HYST_MSB/LSB 等)。

迟滞是不对称使用的:Punctual 态里判"故障还在"用原阈值,判"故障消失"用迟滞线。 中间带(SC: 29 < ADC < 40;OC: 991 < ADC < 1004)两个条件都不成立,代码走 else 的 "Do nothing, keep last value",保持上一次的 Event 标志。 因此计数器在迟滞带内沿原方向继续走,不会来回翻转。

3. 三态状态机

SCEMONI_OC_SC_NO_FAILURESCEMONI_OC_SC_PUNCTUAL(中立/待确认) → SCEMONI_OC_SC_FAILURE

default 分支兜底置为 FAILURE——状态变量被踩坏时往安全方向倒,不往"无故障"倒。

4. Punctual 累积机制(确认 / 消失)

  • 条件成立 → 计数器 += ConfStep
  • 条件消失 → 计数器 -= RehabStep
  • 计数器 >= 65535SCEMONI_u16FmCounterMax)→ 确认,进 FAILURE,并钉死在 65535
  • 计数器 == 0 → 回 NO_FAILURE
  • 两者之间 → 留在 PUNCTUAL
计数器上限u16FM_COUNTER_MAX65535
检测周期u8TOUTLET_MAX_PERIODCHK4 ms
SC 确认时间SCEMONI_u8TOUTLET_SC_FAIL_CNFT252 ms(实际;宏配置 250)
SC 恢复时间SCEMONI_u8TOUTLT_SC_FAIL_REHT252 ms(实际;宏配置 250)
OC 确认时间SCEMONI_u8TOUTLET_OC_FAIL_CNFT252 ms(实际;宏配置 250)
OC 恢复时间SCEMONI_u8TOUTLT_OC_FAIL_REHT252 ms(实际;宏配置 250)
ConfStepCeil(65535 × 4 / 250)1049
RehabStepCeil(65535 × 4 / 250)1049

连续故障 63 次调用到顶(63 × 1049 = 66087 ≥ 65535),63 × 4ms = 252 ms。 SC 与 OC 的确认/恢复时间相同,故四个步长全部相等。

对外口径以 252 ms 为准。*_FAIL_CNFT / *_FAIL_REHT 配的是 250, 但 250 / 4 = 62.5 不是整数,ConfStepCeil 向上取整为 1049 后, 实际需要 63 次调用才越过 65535,对应 252 ms。 250 是配置值,252 是器件真实行为,报客户用 252。
防下溢保护:递减前先判 counter > RehabStep 才减,否则直接置 0, 避免 uint32 绕回成天文数字。

5. 确认之后:临时锁 → 永久锁

进 FAILURE 的瞬间做四件事:

  • 状态置 SCEMONI_WATERTMP_SENSOR_FAILURE
  • ClassAFail 置 SCEMONI_SENSOR_TEMPORARY_FAILUR(临时锁)
  • 写 NVM:SCEFCTR_vidSetFailState(SCEFCTR_OUTLET_SC_CFAIL_ID)
  • 寿命计数 +1:SCEFCTR_vidIncFailLifeTimeCntr(HDNVM_u8OUTLET_TEMP_SC_CNT)

PUNCTUAL 里那套递减逻辑不在 FAILURE 分支内,所以确认之后条件消失也不会自愈。

60 秒持续计时:每次调用 LOC_u8ToutletScOneSecCntr + 1, 数到 u8COUNTS_TO_ONE_SECOND = 250 才算一秒(250 × 4ms = 1000ms), 然后 SCEFCTR_vidIncFailCntr(TOUTLET_SC_FAIL_CNTR_ID)。 中间套这一层分频是为了保护 NVM 寿命,代码注释原文 "respect the NVM lifetime and prevent it from corruption"。

升永久锁的判定:先等 FAIL_CNTR ≥ TOUTLET_SC_FAIL_THR = 59(满 60 秒), 再查 RSET_CNTR ≥ TOUTLET_SC_RESET_THR = 3。两者都满足 → SCEMONI_SENSOR_PERMENANT_FAILUR + 置 SCEFCTR_FAILURE_LOCK_ID。 不到 3 次则停在临时锁,等下一次重启。

6. HW Reset 计数从哪来

SCEMONI_Services.c 1317–1337 行,初始化时读 NVM: 若 SCEFCTR_bGetFailState(SCEFCTR_OUTLET_SC_CFAIL_ID) 为真, 则置 SCEMONI_bToutletScResetFlag = TRUE

函数开头检测到该 flag 且 RSET_CNTR < u8CLASS_A_MAX_RESET_COUNT (15) 时, RSET_CNTR + 1 并清 flag。因此这 3 次累积的是 "带着已确认故障重启过几次",不是任意重启都算。

易混淆点:u8CLASS_A_MAX_RESET_COUNT = 15 名字里带 CLASS_A, 但它不是 Condition A 的 3 次判据,只是递增前的溢出封顶保护。 真正判永久锁的是 SCEFCTR_bIsFailCntReachLim()RESET_THR = 3 去比。两个数各管各的。

7. 完整恢复路径

彻底清零只发生在 NO_FAILURE 态且 ADC 正常时:

  • FAIL_CNTR(60 秒计数)
  • RSET_CNTR(重启次数)
  • ClassAFail 回 SENSOR_NORMAL
  • 清 NVM 故障状态 SCEFCTR_vidResetFailState()

代码注释:"to be able to do the Class A failure recovery once again"——本次 Class A 周期作废重来。

从 FAILURE 出来只有一条路:HW Reset。重启后状态机从 NO_FAILURE 起步; 若此时 ADC 已正常则走上述清零,若仍异常则直接再进 PUNCTUAL,且 RSET_CNTR 已经 +1。

⚖️

旧逻辑 · 温度不一致 Sfty_LS_Inconsist(11版本)

范围说明:本节记录 7DA10011(11版本)的旧有逻辑,Condition A 类故障。
LIN 信号:FM_Sfty_LS_Inconsist_Cfail
代码位置:SCEMONIS_Services.c → SCEMONIS_vidManageConsisMoni()(第 3445 行起)
注意文件名是 SCEMONIS(带 S),与 outlet 所在的 SCEMONI 不是同一个文件。

1. 检测周期与调用范围

  • 检测周期:u8INCONSIS_OT_CHECK_PERIOD_MS = 10(10ms 一次,outlet 是 4ms)
  • 采样源:HDADC_u8GetCmosPhVal()HDADC_u16GetNtcPhVal()——用物理值,不是 ADC 原始值

调用点共 5 处(APMODMGR_Applicative.c),分属 4 个模式,与 outlet 完全相同:

模式函数行号
NeutralenuAppNeutralMode847
HeatingenuAppHeatingMode1187
Temp LockenuAppTempLockMode1548、1742
LCMenuAppLCMMode1954

2. 安全冗余机制(本节独有)

这是与 outlet 最本质的区别。本函数所有关键变量都额外维护一份 __attribute__((section(".SAFEDATABYCOPY"))) 副本,四个位置逐项比对, 只要主副本对不上就立即 HDCPU_vidPerformReset() 硬复位。 原因:T_Safety 一致性检查本身就是安全机制,其自身数据必须防 RAM 单点翻转(bit flip)。 outlet 那个函数完全没有这套。
检查位置比对项项数
函数入口状态、resetFlag2
NO_FAILURE 态差值阈值、IncStep2
PUNCTUAL 态阈值、迟滞、IncStep、DecStep、计数器、Flag6
FAILURE 态resetFlag、一秒计数器2

3. 判据与迟滞(物理值)

先各自换算再取绝对差:

  • CMOS 物理值 = HDADC_u8GetCmosPhVal()
  • NTC 物理值 = HDADC_u16GetNtcPhVal() / SCEMONIS_u8NTC_OT_THR_FACTOR(10)
  • delta = |CMOS − NTC|
动作条件
触发delta ≥ 140SCEMONIS_u8TLS_CONSIST_THR
恢复delta ≤ 138(140 − 2)SCEMONIS_u8TLS_CONSIST_HYS = 2

迟滞带 138 < delta < 140 内两个条件都不成立,保持上次标志,与 outlet 同一套路。 NTC 需先除以 10,阈值单位待 ADC-温度转换表确认,此处先记原始数值。

4. 三态状态机

SCEMONIS_OVER_TEMP_NO_FAILURESCEMONIS_OVER_TEMP_PUNCTUALSCEMONIS_OVER_TEMP_FAILURE

default 分支兜底置 FAILURE(主变量与安全副本同时置)。

5. Punctual 累积机制(确认 / 消失)

计数器上限u16INCONSIS_OT_FAIL_MAX_STEP65535
检测周期u8INCONSIS_OT_CHECK_PERIOD_MS10 ms
确认时间SCEMONIS_u16INCONSIS_FAIL_CNF_P500 ms
恢复时间SCEMONIS_u16INCONSIS_FAIL_REH_P500 ms
IncStepCeil(65535 × 10 / 500)1311
DecStepCeil(65535 × 10 / 500)1311
本节正好整除,对外报 500 ms 即准确。 到顶次数 = 65535 / 1311 = 49.99 → 50 次,50 × 10ms = 500 ms。 与 outlet 不同——outlet 是 62.5 次凑成 63 次、产生 252ms 的零头,本节没有这个偏差。

累加与递减时主计数器和 LOC_u32SafeTInconOvrTempCntr 安全副本同步操作; 递减同样有防下溢保护(先判 counter > DecStep,否则置 0)。

6. 确认之后:临时锁 → 永久锁

  • ClassAFail 置 SCEMONIS_SENSOR_TEMPORARY_FAIL(主 + 安全副本)
  • 计数器钉死 65535,写 NVM SCEFCTR_TEMP_INCONSIS_CFAIL_ID
  • 寿命计数 +1:HDNVM_u8SFTY_INCONSIS_CNT

60 秒计时:u8INCONSIS_COUNTS_TO_ONE_SECOND = 100,100 × 10ms = 1 秒, 同样是为保护 NVM 寿命做的分频(outlet 是 250 × 4ms)。满一秒才 SCEFCTR_vidIncFailCntr(SCEFCTR_SFTY_CONSIS_CNTR_ID)

升永久锁:先等 SFTY_CONSIS_CNTR_THR = 59(满 60 秒), 再查 SFTY_CONSIS_RESET_THR = 3。两者都满足 → SCEMONIS_SENSOR_PERMENANT_FAIL + 置 SCEFCTR_FAILURE_LOCK_ID

7. HW Reset 计数与恢复路径

入口处若 SCEMONIS_bInconsisResetFlag 为真且 RSET_CNTR < u8CLASS_A_MAX_RESET_COUNT (15),则 SCEFCTR_SFTY_CONSIS_HW_RES_ID + 1 并清 flag(主 + 安全副本同步)。

彻底清零只发生在 NO_FAILURE 态且 delta 正常时:清 60 秒计数、清 HW Reset 计数、 ClassAFail 回 SENSOR_NORMAL、清 NVM 故障状态。

与 outlet 一致:从 FAILURE 出来只有 HW Reset 一条路。 本节为标准 Condition A(3 次 + 60 秒),与故障清单表格完全吻合。

🔬

旧逻辑 · NTC / CMOS SC·OC(11版本)

范围说明:7DA10011(11版本)旧有逻辑,Condition A 类故障,共 4 个通道。
LIN 信号:FM_Sfty_LS_Temp_OC/SC_Cfail(NTC)、FM_Sfty2_LS_Temp_OC/SC_Cfail(CMOS)
代码位置:SCEMONIS_Services.cSCEMONIS_vidManageNtcSensorMoni()(3890 行)、 SCEMONIS_vidManagCmosSensorMoni()(4744 行)
两个函数均带 .SAFEDATABYCOPY 安全副本机制(同温度不一致节),使用 ADC 原始值

1. 检测周期与调用范围

检测周期为 1msu8NTC_SFTY_OC_SC_FAIL_PERIOD = 1u8CMOS_SFTY_OC_SC_FAIL_PERIOD = 1)。 注意三个模块各不相同:outlet 是 4ms,温度不一致是 10ms,本节是 1ms。

两个函数调用点各 5 处,分属同样 4 个模式:

模式NTC 行号CMOS 行号
Neutral731734
Heating10591062
Temp Lock1430、16461433、1649
LCM18541857

2. 判据与迟滞(ADC 原始值)

通道触发恢复迟滞量阈值宏
NTC SC29≥ 4011SCEMONIS_u16MIN_TLSNTC_VAL
NTC OC1004≤ 97826SCEMONIS_u16MAX_TLSNTC_VAL
CMOS SC4≥ 2117SCEMONIS_u16MIN_TLSCMOS_VAL
CMOS OC391≤ 37219SCEMONIS_u16MAX_TLSCMOS_VAL

NTC 的 29 / 1004 与 outlet 完全相同;CMOS 是 4 / 391,量程小得多,应为不同类型的传感器。 四个迟滞值 11 / 26 / 17 / 19 各不相同,不像 outlet 那样成对配置。

3. 三态状态机与累积机制

状态机结构与前两节一致(NO_FAILURE → PUNCTUAL → FAILURE),迟滞带内保持上次标志。 主计数器与安全副本同步加减,递减带防下溢保护。

四个通道的累积参数完全一致:

计数器上限u16SAFETY_CONF_COUNT_LIMIT65535
检测周期u8*_SFTY_OC_SC_FAIL_PERIOD1 ms
确认时间SCEMONIS_u8{NTC,CMOS}_{SC,OC}_SFTY_CNFT250 ms
恢复时间SCEMONIS_u8{NTC,CMOS}_{SC,OC}_SFTY_REHT250 ms
ConfStepCeil(65535 × 1 / 250)263
RehabStepCeil(65535 × 1 / 250)263
本节对外报 250 ms 即准确,与 outlet 相反。 到顶次数 = 65535 / 263 = 249.18 → 250 次,250 × 1ms = 250 ms,无零头。 outlet 是 62.5 进位到 63 次多出 2ms,才需要报 252ms;本节进位后正好落在 250ms 上。

4. Condition A 参数

与故障清单表格一致:NTC_SC/OC_FAIL_THRCMOS_SC/OC_FAIL_THR 均为 59(满 60 秒), 四个 *_RESET_THR 均为 3。两者都满足才升永久锁。

5. 已知差异:60 秒分频常量为 999(不修)

三个模块数 60 秒的分频常量写法不统一:
模块常量周期实际一格
outletu8COUNTS_TO_ONE_SECOND = 2504 ms1000 ms
温度不一致u8INCONSIS_COUNTS_TO_ONE_SECOND = 10010 ms1000 ms
NTC / CMOSu16COUNTS_TO_ONE_SECOND = 9991 ms999 ms
判断语句为 if (u16COUNTS_TO_ONE_SECOND == LOC_u16NtcSftyScOneSecCntr), 计数器从 0 起每次 +1,等于 999 时触发并清零,中间经过 999 次调用,故一格为 999ms 而非 1000ms。
影响:60 格 = 59.94 秒,比标称 60 秒短 60ms,偏差 0.1%。
结论:不修。功能上无实际差别,而改动安全相关模块的时间常量需重新走验证, 代价远大于收益。此处仅作记录备查。

旧逻辑 · HE_OC(11版本 · Condition C)

范围说明:7DA10011(11版本)旧有逻辑Condition C 类故障。
LIN 信号:FM_HE_OC_Cfail 永久锁阈值:SCEFCTR_u8HE_OC_FAIL_THR = 3
代码位置:SCEMONI_Services.c → SCEMONI_vidIHeOcMoni()(第 3850 行起)
本节与前三节几乎没有共同点——不使用累积计数器,不需要 60 秒持续,状态机是 4 态。

1. 时间基准(先理清,否则后续全错)

真正的判断周期是 10 秒,由两层构成:
① 函数在 100ms 任务内调用(bFlagTask100ms 分支)
② 内部再分频:SCEMONI_u16HeOcCheckPeriod = (SCEMONI_u8HE_OC_CHECK_PERIOD(10) × u8HE_OC_CHECK_FACTOR(10)) − 1 = 99SCEMONI_u16HeOcTaskCntr 每次调用 +1,到 99 才执行一次判断
⇒ 100 次 × 100ms = 10 秒 / 判断周期
模式函数行号
NeutralenuAppNeutralMode897
HeatingenuAppHeatingMode1241
Temp LockenuAppTempLockMode1600

只有 3 个调用点(前三节都是 5 个),LCM 模式下不调用

2. 四态状态机

SCEMONI_HE_OC_NO_FAILSCEMONI_HE_OC_PUNCTUALSCEMONI_HE_OC_TEMP_FAILSCEMONI_HE_OC_PERMENEMT_FAIL

比前三节多一个 PERMENEMT 态——Condition A 的永久锁体现在 ClassAFail 变量上, 本节直接做进主状态机。

3. 检测条件(三层嵌套,全中才算)

条件宏 / 值
1PWM 占空比 > 300u16HE_OC_FAIL_PWM_THR
2(估算负载 × 100) > (标称负载 × 150)
IAV < 10
u8HE_OC_LOAD_ESTIM_FACTOR = 100
SCEMONI_u16HeOcLoadCoef = 150
u8IAV_INCONSIS_THRESHOLD = 10
3HV − HE < 10 → 判 HE_OCu16HE_OC_FAIL_VOLT_THR
3'HV − HE > 10,且 IAV < 10,且 STB=ON,且 HS 命令=ON → 判 HS_IGBT_OC另一条分支

4. 确认机制:两次命中(非累积计数器)

  • 第一次条件成立 → 仅置 SCEMONI_bHeOcFailDetect = TRUE不报故障
  • 第二次成立 → vidHeOcDetection()TEMP_FAIL,写 NVM SCEFCTR_HE_OC_CFAIL_ID,寿命计数 HDNVM_u8HE_OC_CNT +1

两次之间相隔一个判断周期,即 10 秒

升永久锁:进 TEMP_FAIL 时立即查 SCEFCTR_HE_OC_FAIL_CNTR_ID 是否达 HE_OC_FAIL_THR = 3,达到则直接 PERMENEMT_FAIL + 置 SCEFCTR_FAILURE_LOCK_ID没有 60 秒持续这道门槛,这是 Condition C 与 Condition A 的本质区别。

5. HS_IGBT_OC 分支(带自恢复)

置位后若 HV − HE 回落到 10 以下且 HS 仍为 ON,则清除 SCEMONI_bHsIgbtOcState。 寿命计数走 HDNVM_u8IGBT_OC_CNT

6. 60 秒清零机制(方向与 Condition A 相反)

两个 60 秒务必分清:
Condition A 的 60 秒:故障持续存在 60 秒 → 满足升永久锁的条件之一
HE_OC 的 60 秒正常运行 60 秒 → 把累积的故障次数清零,往回退

代码在 3974–3992:

  • PWM ≥ u16HE_OC_FAIL_60S_CNTR_PWM_THR(150)SCEMONI_u8HeOcHwRstCntrTimer ≥ u8HEOC_HW_RST_60S_CNTR(5)bHeOcFailDetect == FALSESCEFCTR_vidResetFailCntr(HE_OC_FAIL_CNTR_ID)
  • PWM < 150 → 计时器归零
  • 其余 → 计时器 ++

60 秒怎么算出来的:计时器在判断周期的 else 分支 +1,每周期 10 秒。 从 0 起,第 1~5 个周期把值从 0 加到 5,第 6 个周期时 5 ≥ 5 成立触发清零—— 共 6 个周期 × 10 秒 = 60 秒,与注释 "Wait until 60 Seconds Passed" 一致。

两个 PWM 门槛分工不同:
u16HE_OC_FAIL_PWM_THR = 300 —— 检测故障用
u16HE_OC_FAIL_60S_CNTR_PWM_THR = 150 —— 维持清零计时用
低门槛清零、高门槛检测,意味着 PWM 处于 150~300 之间时,既不会被判故障,又能让清零计时器继续走。

注意范围:该机制清零的只是 SCEFCTR_HE_OC_FAIL_CNTR_ID 这个累积次数。 一旦已进入 PERMENEMT_FAILFAILURE_LOCK 写入 NVM,此机制救不回来。

🔌

旧逻辑 · Pwr_Check 功率校验(11版本 · Condition C)

范围说明:7DA10011(11版本)旧有逻辑Condition C 类故障。
LIN 信号:FM_Pwr_Check_Cfail 永久锁阈值:SCEFCTR_u8PWR_CHK_FAIL_THR = 3
代码位置:SCEDRIV_Services.c → SCEDRIV_vidTaskManagePower()(第 1335 行起)
注意本节在 SCEDRIV 模块,不在 SCEMONI / SCEMONIS。
代码里该功能统一用 PLS(Plausibility,合理性)前缀,与温度不一致 Sfty_LS_Inconsist 是两个不同的东西。
⚠ 本节修正了旧文档的一处错误。 「故障确认累积机制」表格原记「功率不一致 500ms」,实际为 5000ms(5 秒),差一个数量级,现已改正。

1. 时间基准

  • 调用点仅 1 处APMODMGR_Applicative.c:121750ms 任务仅 Heating 模式(Neutral / TempLock / LCM 都不调用——不加热时没有功率可校验)
  • 检查周期 SCEDRIV_u16FM_MAX_PWR_CHK_PER = 50(与 50ms 任务一致)
  • 确认时间 SCEDRIV_u16FM_PWR_TIME_CFM_DEF = 50005 秒
5000ms 的旁证:同一函数内 60 秒清零计数器 u16PWRPLAUS_HW_RST_60S_CNTR = 1200,1200 × 50ms = 60000ms 正好 60 秒。 同一时间基准下两个数都能对上,确认 50ms 周期无误,故 5000 即 5 秒。

2. 判据:双区间(按目标功率分界)

区间故障条件阈值宏
目标 > 3500估算功率 ≥ 目标 × 120%SCEDRIV_u8PWR_HI_THR_DEF = 120
目标 ≤ 3500估算功率 ≥ 目标 + 700
且 目标 ≥ 200
SCEDRIV_u8PWR_HI_THR2_DEF(70) × u8RESOLUTION_FACTOR(10)
SCEDRIV_MIN_POWER_REQ = 200

两个区间均额外要求 SCEDRIV_bPwmInconsistencyState == FALSE。 分界值宏为 u16PWR_PLS_THR_CHCK = 3500

方向:检测功率过高,不是过低。判据是「估算功率 ≥ 阈值」, 即实际输出比请求的更高才报故障——防超功率(过热风险),不是防功率不足。
注释与代码不一致(以代码为准):源码注释写 "greater than 1750 W", 而宏值是 3500(3500 = 1750 × 2,疑为功率变量 0.5W/LSB)。 大概率是开发改了代码未同步注释。此处按代码记 3500,单位待 ADC-功率转换表确认。

3. 本通道无迟滞(与前四节不同)

高低阈值配的是同一个数——PWR_HI_THR_DEF = PWR_LO_THR_DEF = 120PWR_HI_THR2_DEF = PWR_LO_THR2_DEF = 70。 前四节都是「触发用原值、恢复用迟滞线」,中间留一段带子保持上次状态;本节两线重合,没有带子。

推论:else 分支是死代码。以「目标 > 3500」支为例:

  • 故障条件 = (估算 ≥ 120%) && (PWM 一致)
  • 恢复条件 = (估算 ≤ 120%) || (PWM 不一致)

后者即前者的逻辑非,两分支覆盖全部情况,第三个 else 里的 "Do nothing, Keep last value" 永远走不到。另一区间同理。

因此每个 50ms 周期都会明确判定一次,计数器要么 +656 要么 −656,不存在停在原地的情况。 功率信号本身波动较大,缺少迟滞在临界点会来回抖动,但 100 次累积把抖动摊平了, 这应是它不加迟滞的原因。

4. 累积机制

宏 / 公式
计数器上限SCEDRIV_u16FM_COUNTER_MAX65535
检查周期SCEDRIV_u16FM_MAX_PWR_CHK_PER50 ms
确认时间SCEDRIV_u16FM_PWR_TIME_CFM_DEF5000 ms
恢复时间同上(共用同一参数)5000 ms
IncCeil(65535 × 50 / 5000)656
DecCeil(65535 × 50 / 5000)656

到顶次数 = 65535 / 656 = 99.9 → 100 次,100 × 50ms = 5000 ms,正好整除。 递减同样带防下溢保护。

5. 60 秒清零机制(同 HE_OC,方向与 Condition A 相反)

位于 NO_FAILURE 态的无故障分支内(代码 1432–1455):

  • PWM 占空比 ≥ u16CLASS_C_PWM_THR(150) → 计时器 SCEDRIV_u16PwrPlsHwRstCntrTMR++
  • 计时器 ≥ u16PWRPLAUS_HW_RST_60S_CNTR(1200) → 计时器归零,并 SCEFCTR_vidResetFailCntr(SCEFCTR_PWR_CHK_COUNTER_ID) 清空累积次数
  • PWM < 150 → 计时器直接归零

1200 × 50ms = 60 秒。与 HE_OC 的清零机制同为「正常运行满 60 秒即把累积的故障次数一笔勾销」, 与 Condition A 的「故障持续 60 秒才升永久锁」方向相反。

6. Condition C 两节对照

HE_OCPwr_Check
所在模块SCEMONISCEDRIV
任务周期100ms(内部分频至 10s)50ms
确认方式两次命中(间隔 10s)累积计数(656 × 100 次 = 5s)
迟滞
调用模式Neutral / Heating / TempLock仅 Heating
60 秒清零有(6 × 10s)有(1200 × 50ms)
永久锁阈值3 次3 次

两者都带「正常运行 60 秒清账」机制,应为 Condition C 的共同设计。

🔩

旧逻辑 · HE_SC(11版本 · Condition C · 15次)

范围说明:7DA10011(11版本)旧有逻辑Condition C 类故障。
LIN 信号:FM_HE_SC_Cfail 永久锁阈值:SCEFCTR_u8HE_SC_THR = 15(全部故障中最高)
代码位置:SCEMONI_Services.c → SCEMONI_vidTaskScHeMont(STD_tenuMode)(第 4057 行起)
本节不采 ADC——读数字输入引脚 HDDIO_bGetScHeInput(),引脚为低即判短路。

1. 调用范围

  • 10ms 任务,3 个调用点:Neutral(861)、Heating(1134)、Temp Lock(1564)
  • 三处均包在 if(STD_bFALSE == SCEMONI_bGetDcdcInhibStatus()) 内—— DCDC 抑制激活时不检测
  • 函数带模式入参 enuCurrentMode,这在其他故障模块里没有

2. 四态状态机

SCEMONI_SC_NO_FAILURESCEMONI_SC_FAILURE_NO_LOCKSCEMONI_SC_TEMP_LOCK / SCEMONI_SC_PERM_LOCK

易漏看的限制:FAILURE_NO_LOCK 态的整段处理逻辑外面套着 if(STD_TEMP_LOCK_MODE == enuCurrentMode)。 在 Neutral 和 Heating 下检测到引脚为低,只会切到该状态然后卡住不动, 必须等系统进入 Temp Lock 模式才开始处理。

3. 硬件复位重试机制

与其他故障"累积计数"的思路完全不同,本节是主动尝试复位硬件

时机动作
计数器 = 0(首次)HDDIO_vidSetScRst() 拉高复位引脚
计数器 = 1HDDIO_vidClearScRst() 放开复位引脚u8HE_SC_CLEAR_TIME = 1
计数器 ≥ 99归零,置 bRecDelayElapsed,回 NO_FAILURE 重测u8HE_SC_FAIL_DETECT_DELAY = 99

99 × 10ms = 990ms,约一秒一轮重试。

4. 两层计数(解释了为何阈值是 15)

含义
内层u8HE_SC_FAIL_CHK_NUM3连续 3 轮复位重试都未清除 → 确认故障、写 NVM
外层SCEFCTR_u8HE_SC_THR15确认时查 HW Reset 累积次数,达 15 → PERM_LOCK
为什么故障清单里只有 HE_SC 是 15、其余 Condition C 都是 3: 因为它内层已经消耗了 3 轮重试,外层再给 15 次机会,实际容忍度远高于其他几个。 硬件短路可能是瞬时的,先尝试复位清锁存 —— 这个设计是合理的。

TEMP_LOCKPERM_LOCK 两态内均为空实现,什么都不做,等下次上电。

🌊

旧逻辑 · HE_Over_Curr / IAV 过流(11版本 · Condition C)

范围说明:7DA10011(11版本)旧有逻辑Condition C 类故障。
LIN 信号:FM_HE_Over_Curr_Cfail 永久锁阈值:SCEFCTR_u8HE_OVC_THR = 3
代码位置:SCEMONI_Services.c → SCEMONI_vidIavMoni()(第 2305 行起)

1. 时间基准

  • 1ms 任务,仅 1 个调用点(APMODMGR 1049),仅 Heating 模式
  • 检测周期 u8HE_IAV_MAX_PERIODICTY = 1(1ms)——全部故障中检测最密的一个

2. 判据与迟滞(物理值)

动作条件
触发IavPhyVal ≥ 550SCEMONI_u16IAV_OVER_ON_THR_DEF
恢复IavPhyVal ≤ 530(550 − 20)SCEMONI_u16IAV_OVER_ON_HYS_DEF = 20

迟滞带 530 < val < 550 保持上次标志。

3. 累积机制

宏 / 公式
计数器上限SCEMONI_u16FmCounterMax65535
检测周期u8HE_IAV_MAX_PERIODICTY1 ms
确认时间SCEMONI_u8IAV_CONF_TIME500 ms(宏配置)
Inc = DecCeil(65535 × 1 / 500)132
对外口径:500 ms(按宏配置值报)。
代码实际行为为 497 ms:到顶次数 = 65535 / 132 = 496.48 → 497 次,497 × 1ms = 497 ms,比标称短 3ms。 差值仅 0.6%,此处对外仍按 500ms 报,实际值记录备查。
与 outlet 方向相反的原因:outlet 的理论次数 250/4 = 62.5 不是整数,向上凑到 63 次, 63 × 4 = 252ms 比标称;IAV 的理论次数 500/1 = 500 本是整数, 但步长 Ceil 后 132 > 131.07,每步多走一点,497 次就够了,比标称。 一个向上凑次数、一个向上凑步长,效果就反过来了。
两处口径不同,勿混:Outlet SC/OC 对外报 252 ms(实际值,比标称长 2ms); 本节对外报 500 ms(标称值,实际 497ms)。

4. 两个易踩的细节

① 递减复用同一变量。递减用的也是 SCEMONI_u16FmIavInc, 没有独立的 Dec 变量。其他模块都是 Inc / Dec 两个变量分开算(默认值虽相等,但可分别标定), 本节从代码层面锁死了两者必须相等。
② NVM 标定带 400 偏移。 LOC_u8FmIavcfm = au8Page[HDNVM_u8HE_OVER_CUR_TIME_CFM] + u8IAV_OC_CONFIRM_OFFSET(400)
标定表里填的数要 +400 才是实际确认时间,读标定表时切勿看岔。
SCEMONI_enuGetIavState() 是有副作用的 getter。 该函数(2489 行)名为 Get,但内部会改写状态
if (HE_OVC 计数达限 && 当前为 STD_PERM_LOCK_MODE) → enuIavState = SCEMONI_PERMENEMT_FAIL
即使主状态机没走到永久锁,只要计数器已达 3 且系统处于 PERM_LOCK 模式, 这个 getter 会顺手把状态改成永久锁。排查状态变化时只盯 vidIavMoni 主函数会漏掉这条路径。
④ Punctual 状态不对外暴露。getter 内有过滤: if (enuIavState != SCEMONI_PUNCTUAL) enuRetIavtState = enuIavState;
返回的是上一次非 Punctual 的值,因此外部只能拿到 NO_FAIL / TEMP_FAIL / PERMENEMT_FAIL 三种, 永远看不到 Punctual。对 LIN 上报应为有意设计——偶发状态不报给整车。

确认之后的分支(2438–2451)与其他 Condition C 同构:寿命计数 HDNVM_u8HE_OVER_CURR_CNT +1 → 查 SCEFCTR_HE_OVC_COUNTER_ID 是否达限(3) → 达限进 PERMENEMT_FAIL 并置 FAILURE_LOCK_ID,否则 TEMP_FAIL。 两个终态的 case 内均为自赋值的空实现,进去出不来。

5. 60 秒清零机制

与 Pwr_Check 结构相同,位于 NO_FAILURE 态的无故障分支内:

  • PWM 占空比 ≥ u16CLASS_C_PWM_THR(150)SCEMONI_u16HeOVCHwRstCntrTimer++
  • 计时器 > u16HEOVC_HW_RST_60S_CNTR(60000) → 归零,并清空 SCEFCTR_HE_OVC_COUNTER_ID
  • PWM < 150 → 计时器直接归零

60000 × 1ms = 60 秒

6. Condition C 四节总对照

HE_OCPwr_CheckHE_SCHE_Over_Curr
模块SCEMONISCEDRIVSCEMONISCEMONI
任务周期100ms(分频至 10s)50ms10ms1ms
信号源PWM+负载+电压功率估算数字引脚电流物理值
确认方式两次命中累积 656×1003 轮复位重试累积 132×497
实际确认时长10 s5000 ms约 990ms/轮497 ms
迟滞—(数字量)有(20)
调用模式Neu/Heat/TL仅 HeatingNeu/Heat/TL仅 Heating
60 秒清零
永久锁阈值33153

四者中三个带「正常运行 60 秒清账」机制,唯 HE_SC 没有——它改用内层 3 轮复位重试来容错。

🔄

新逻辑 · 新旧 Condition A/C 清单对照

本节起为新逻辑,与前面「故障逻辑 · 旧(11版本)」各节并列,请勿混用。
旧逻辑 = 7DA10011 已实现的代码行为;新逻辑 = 客户新要求下的目标设计。

1. 分类定义的变化

类别旧(11版本)
自恢复Minor failure,12 个,不进永久锁OK 诊断后立即恢复
Condition A3 次 HW Reset 有一次持续 60 秒 → 永久锁2 次 NG 诊断(每次确认 4 秒)→ 输出 010「温度传感器特性异常」,不进永久锁
Condition B—(无此类)仅 IGN ON 时诊断;SW reset 后 OK 即恢复,等效自恢复
(本项目暂不涉及)
Condition C次数达阈值(3 或 15)直接永久锁1 次连续 4 秒判异常, IGBT 异常 → 输出 011「电路异常」
永久锁独立类别(HS/LS_Igbt_SC,1 次 HW reset 后锁)并入 Condition C,上报为「电路异常(含永久锁)」,不可恢复
两个最重要的变化:
Condition A 不再进永久锁——counter 封顶在 2,最坏情况是一直挂在 010 等 HW reset, 直到某次诊断 OK 才恢复。永久锁现在只可能来自 Condition C。
永久锁不再是独立类别,并入 C 后对整车上报口径统一为 011, 总线上看永久锁与一般电路异常长得一样,区别只在内部。

2. DTC 编码

含义说明
001自恢复故障同时被借用为 Condition A 第 1 次 NG 之后的中间态
010需 HW reset 的故障Rationality abnormal / 温度传感器特性异常
011会进永久锁的故障Circuit abnormal(含永久锁)
⚠ 001 是个坑。Condition A 第 1 次 NG 后本应输出 010,但 DTC 编码不够用, 只能借 001 当中间态过渡。看日志时见到 001 不能直接当成自恢复小毛病—— 它可能是 A 类故障走到一半的状态。

3. 故障清单对照

旧 Condition A(7 个)→ 新 Condition A(3 个)

新清单对应旧清单备注
HVH_FailPowerCheckFM_Pwr_Check_Cfail从旧 C 移入新 A(判定放宽),已确认——客户明确「Condition A 的功率不一致确认时间改 5s」
HVH_FailSensorTOutFM_Outlet_Coolant_Temp_OC_Cfail
FM_Outlet_Coolant_Temp_SC_Cfail
本就是同一信号可报 SC / OC 两种故障,不存在"合并"问题
FM_Sfty_LS_Inconsist_Cfail同名唯一留在 A 类未变的

旧 Condition C(4 个)+ 旧永久锁(2 个)→ 新 Condition C(4 个)

新清单对应旧清单备注
HVH_LoadOCFailFM_HE_OC_Cfail负载开路,已确认——客户 Condition C 图示标题直接写明 1.HVH_LoadOCFail
Abnormal safety sensorFM_Sfty_LS_Temp_OC/SC_Cfail
FM_Sfty2_LS_Temp_OC/SC_Cfail
从旧 A 移入新 C(判定收严)= NTC / CMOS 四个通道,已确认(2026-07-29)
HVH_FailHighCurrent (SW)FM_HE_Over_Curr_Cfail软件测电流(IAV),已确认——即讨论中的 overcurrent,确认时间 500ms
HVH_FailHighCurrent (HW)FM_HE_SC_Cfail硬件引脚检测,已确认(客户文档直接标注 HE_SC_Cfail)
(IGBT 异常)FM_HS_Igbt_SC_Cfail
FM_LS_Igbt_SC_Cfail
旧独立永久锁类别,现并入 C 的永久锁分支
两个换了阵营、方向相反的调整:
Pwr_Check:旧 C(3 次直接永久锁)→ 新 A(2 次 NG,不锁)——放宽
安全温度传感器四个:旧 A(3 次 + 60 秒才锁)→ 新 C(1 次连续 4 秒就锁)——收严

4. 待定事项清单(截至 2026-07-29)

#待定内容影响范围状态 / 倾向
1overcurrent 自恢复间隔取 1.25 s 还是 3 sOvercurrent等 7/29 下午会议。1.25s 总时长正好 4000ms;3s 为 7500ms
2uint8 装不下 4000 怎么解决——加分辨率因子,还是改 NVM 两字节布局outlet + NTC/CMOS 两个建议加分辨率因子(改动最小);改 NVM 布局会牵扯标定工具与下线程序
3HE_SC 自恢复 14 次还是 15 次HE_SC14 次→间隔 280ms;15 次→260ms,均落在 4 秒内
4HE_SC 的 FAILURE_NO_LOCK 态是否保留 TempLock 模式限制HE_SC旧代码套着 if(TEMP_LOCK_MODE == enuCurrentMode),保留则需在该模式下连续待满 4 秒
5overcurrent 的 60 秒清零机制是否删除Overcurrent建议删除——新逻辑 3 次全在数秒内完成,该机制几乎不会触发
6Abnormal safety sensor 是否即 NTC/CMOS 四通道NTC/CMOS✅ 已确认(2026-07-29):就是那四个通道

前 5 条均为选项问题,可在动手前择期确定;第 6 条属前提问题,已确认, C-2 NTC/CMOS 的全部分析据此成立。

⏱️

新逻辑 · Condition A 判定流程

客户要求 4 秒。新 A 的三个故障采样周期不变, 只把确认时间统一改为 4 s。以 Outlet SC/OC 为例(采样 4ms): n = 4000 / 4 = 1000 次

1. 判定流程

阶段条件结果
第 1 次 NG诊断 4 秒不通过counter = 1,发 001(借用的中间态),等待 HW reset
第 2 次 NGreset 后重测,仍不通过counter = 2,Fail confirmed,发 010,等待 HW reset
第 3 次及以后仍不通过counter 保持 2,维持 010,继续等待 HW reset
任意一次 OK诊断通过恢复

每两次 NG 诊断之间必须经过一次 HW reset;counter 封顶 2,不进永久锁

1.1 三条已确认的实现约定

① 是「连续两次 NG」,不是「累计两次 NG」。 只要任意一次诊断 OK,counter 立即归零,此前那次 NG 作废,下次再坏要从头数起。
两者差别很大:累计两次 = 历史上坏过两回就报;连续两次 = 必须坏得连着。本项目取后者。
问题结论
第 2 次诊断 OK 时 counter 如何处理清零,什么时候 OK 就什么时候清零
001 与自恢复故障的 001 冲突客户已接受该复用
HW reset 由谁触发整车上下电,用户手动;软件不主动请求复位
counter 何时 +1跑完一次 4 秒诊断且结果 NG 时才 +1不是沿用旧机制的「上电读到 NVM 故障标记就 +1」
outlet 的 SC / OC 计数合并计数——同属 HVH_FailSensorTOut 一个信号,两者都算在同一个 counter 里
关于 counter +1 时机的说明:旧代码数的是「带着已确认故障重启过几次」—— 上电时读到 SCEFCTR_bGetFailState() 为真就 RSET_CNTR++。 该机制下,用户若上电后立即下电(未跑满 4 秒、未完成诊断),counter 仍会 +1; 连续开关三次即可把 counter 刷到 2 并报出 010,而实际一次完整诊断都没做过。
新逻辑取「完成诊断且 NG 才 +1」,需在诊断完成处显式递增,不能沿用上电 flag 顺带的写法。
关于 SC / OC 合并计数:代码现状是两套独立状态机 + 两个独立 NVM 计数器 (TOUTLET_SC_RSET_CNTR_ID / TOUTLET_OC_RSET_CNTR_ID)。 新逻辑要求合并——第一次测出 SC NG、上下电后测出 OC NG,算作连续两次 NG,直接报 010
✅ Fox01 实际落地方式(2026-08-10 更新):没有合并 NVM 计数器,改为「两个独立计数器取和」。
新增 SCEMONI_u8GetToutletNgCntVal() 返回两个 NVM 计数之和,所有判定(递增前的封顶判断、 严重度选择)一律走这个和值;两个 NVM 计数器仍各存各的。
这么做比合并更好:NVM 布局一个字节都不用动,而且诊断 data dump 帧里 SC / OC 那两个 4bit 字段保持独立,售后仍能分辨是短路还是开路——正好满足「诊断帧里 SC / OC 仍要分开看」这条要求。
清零时两个计数器一起清,但带守卫:只有对面通道也干净(状态机在 NO_FAILURE 且 NVM 里没有对面已确认的故障)才清,否则每个周期都会被健康的那条通道抹掉。 详见「实现记录 · Condition A 代码改动」第 4 节。

1.15 counter +1 的时机:新旧对比(2026-07-29 定)

旧逻辑:HW reset 之后(上电时)就 +1,与本次诊断结果无关。
SCEMONI_vidInit() 上电执行一次,若 SCEFCTR_bGetFailState(OUTLET_SC_CFAIL_ID) 为真(NVM 存着上次断电前已确认的故障),置 bToutletScResetFlag = TRUE
② 监控函数开头见到该 flag 即 SCEFCTR_vidIncFailCntr(TOUTLET_SC_RSET_CNTR_ID) 并清旗
⇒ 触发条件是「NVM 里有已确认故障」,不是「这次诊断 NG」; 上电后监控函数一开始跑就加了,此时本轮 4 秒诊断尚未开始。
旧逻辑的漏洞:用户上电后立即下电、未跑满诊断时,ResetFlag 仍因 NVM 标记置位, counter 照常 +1。连续开关三次车门即可把 counter 刷到 3,而一次完整诊断都没做过。

新逻辑:跑完一次 4 秒诊断、且结果为 NG,才 +1。

1.16 实现方案:把 +1 挪进确认分支(NVM 结构不动)

原方案(2026-07-29):现在是在函数开头 if (ResetFlag) { RSET_CNTR++; ResetFlag = FALSE; }; 改为把这段挪进「计数器到顶、确认故障」的分支内,条件本身不变(即仍保留 ResetFlag 判断)。 理由是两个条件天然同时满足:ResetFlag 为真 ⇒ 上次是带着已确认故障重启的; 能走到该分支 ⇒ 本轮诊断已完成且为 NG。

⚠ 该方案已被推翻。Fox01 实际落地的是「不看 ResetFlag,确认即 +1」(2026-08-06 舟舟拍板)。
原方案的问题:第一次 NG 时 NVM 里还没有故障标记,ResetFlag 为假,counter 加不上去, 要到第二次 NG 才变成 1 —— 跟客户要求的「1 次 NG → count 1、2 次 NG → count 2」整整差一次。
实际做法:SCEMONI_bToutletScResetFlag / OcResetFlag 两个变量、 它们的初始化、上电置旗的两段 if、按旗递增的两段,全部删除; 改为在 SC / OC 各自的确认分支里、写完 NVM 故障状态之后直接递增,递增前判两个计数器之和是否已达 2。
「开关车门刷计数」那个漏洞照样堵住了,而且堵得更彻底——现在触发递增的唯一条件就是「跑完一次诊断且结果 NG」, 跟上电次数彻底脱钩。
NVM 结构完全不变——存的仍是故障状态 CFAIL_ID 与 reset 计数 RSET_CNTR_ID,字节数、位置、含义均不动,变的只是写入时机

1.17 为什么不采用「恢复后才 +1」

该方案有一处硬伤:数不到最后一次。 HE_SC 是 14 次自恢复、第 15 次报 011 锁死,锁死后不再自恢复;Overcurrent 第 3 次同理。 若规定「恢复之后才 +1」,最后一次永远等不到恢复动作,counter 卡在 14(或 2), 永远报不出 011。除非为最后一次开特例,逻辑即成补丁。

「确认即 +1」无此问题,且 counter 与 DTC 始终一一对应—— A 类 1→001、2→010;HE_SC 1~14→001、15→011,看日志无需偏移换算。 DTC 上报时机也随之确定:与 counter 同一时刻。

1.18 counter 存储位置:按恢复方式分开

类型故障存储理由
HW reset 型outlet、温度不一致、Pwr_CheckNVM中途断电 RAM 全丢;间隔不可控(可能数天);语义本就是「带故障重启了几次」
自恢复型HE_SC、OvercurrentRAM全程在一次上电周期内跑完(4s / 7.5s);语义是「连续 N 次」,中途断电本就该从头数
对「少改动」有利:自恢复型 counter 不进 NVM ⇒ 无需改 NVM 布局,也不消耗写入寿命。 HE_SC 在 4 秒内需写 15 次,若真放 NVM 反而是隐患。

1.19 改动位置与难度

故障改动位置
outlet SC/OC纯挪代码,每处四五行,逻辑不变SCEMONI_vidManagOutltSensorMoni() 开头 3184–3216 → 各自 PUNCTUAL 确认分支(SC 约 3325)
温度不一致SCEMONIS_vidManageConsisMoni() 3514–3536 → 3684–3704
Pwr_CheckSCEDRIV_vidTaskManagePower() 1349–1357 → 确认分支
HE_SC计数改本地 static,间隔 99→28 或 26SCEMONI_vidTaskScHeMont()
Overcurrent新增 TEMP_FAIL 自恢复出口 + 3 秒等待计时器SCEMONI_vidIavMoni()
闲置的 NVM 计数器建议保留不删。HE_SC / Overcurrent 改用 RAM 后, HE_SC_COUNTER_IDHE_OVC_COUNTER_ID 不再使用。删除需动 SCEFCTR 枚举与阈值数组、 可能牵连标定表布局;保留仅占几字节,注释标注「新逻辑不再使用」即可——这是最划算的省法
⚠ 最容易翻车的点:同样的改动要重复六遍——outlet 的 SC / OC 两处, NTC/CMOS 四个通道。漏掉任一个(例如改了 SC 忘了 OC),两边计数行为不一致, 台架上极难发现,因为通常只造一种故障。改的时候拿清单逐项划掉。

1.2 由此推出的两点实现要求

  • counter 必须存 NVM。两次 NG 之间隔着用户手动上下电,RAM 全丢,只能靠 NVM 带过去。 旧代码的 SCEFCTR_TOUTLET_SC/OC_RSET_CNTR_ID 本就是 NVM 计数,机制现成,无需新建。
  • 清零逻辑可复用旧实现。旧逻辑在 NO_FAILURE 态、条件不满足时清零,即「诊断 OK」, 与新要求一致。真正要改的是判定条件:旧的需 3 次 reset + 一次持续 60 秒, 新的只要连续 2 次 NG、不看持续时间,60 秒那整套删除
⚠ 测试须知:故障可无限期停留在 001。 由于 reset 依赖用户手动上下电,若用户一直不下电,counter 就停在 1、DTC 停在 001, 永远不会升到 010。这是设计意图(001 已提示检修,010 是复现确认), 但测试若按「4 秒后必出 010」来验证将无法复现——必须在两次诊断之间插入一次真实的上下电。
例外:Pwr_Check 确认时间为 5 s,不是 4 s。 客户反馈功率校验可用 5 s / 采样 100 次。 而 SCEDRIV_vidTaskManagePower() 现状恰好就是:检测周期 50ms、 FM_PWR_TIME_CFM_DEF = 5000ConfStep = 656、到顶 100 次 × 50ms = 5000ms。
⇒ 该故障维持现状即可,参数一个都不用改。

2. 实现方式:保留累积法

新逻辑仍使用累积计数器,不改用连续计数。
团队评估:改连续法需要给每个故障加滤波取平均,改动量大;累积法只需调参数,改动最小。

3. 对外口径:n = 1000 次 / 4 s

对外一律按 n = 1000 次、确认时间 4 s 表述。
采样周期 4ms × 1000 次 = 4000ms。这是与客户沟通、对外文档、TMC 交付的统一口径。
内部备注(对外不提):沿用累积法、确认时间参数改为 4000 时, ConfStep = Ceil(65535 × 4 / 4000) = 66,到顶次数 = 65535 / 66 = 992.95 → 993 次, 即 3972ms,比 4000ms 少 28ms(Ceil 使步长略大于理论值,故走不满 1000 次)。
该差值不对外说明——客户实测无法分辨(两次 NG 之间还隔着 HW reset 的时间), 提出反而会引来大量追问。此处仅作工程记录,便于日后调试时对得上数。

3.5 对客户的正式描述(2026-07-29 定稿,可直接引用)

沿用客户 draft 的 1)2)3)4) 结构与原有措辞(含 Temporally 的原拼写), 只用状态名、不用 DTC 编码。
1) After an abnormality is confirmed (detected continuously for 4 s), the count
   number is incremented to 1, HVCH moves to [Temporally Lock] and holds until
   HW reset.

2) After HW reset (IGN OFF ⇒ ON), the diagnosis is performed again. If the result
   is normal, HVCH recovers and the count number is reset to 0.

3) If the abnormality is confirmed again (detected continuously for 4 s) after
   HW reset, the count number is incremented to 2, HVCH moves to
   [Rationality abnormal] and holds until HW reset.

4) From the 3rd confirmation onward, the count number remains at 2 and
   [Rationality abnormal] is maintained. HVCH does NOT move to Permanent lock.
   Whenever the diagnosis result becomes normal, the count number is reset to 0.

Note 1: Confirmation time is 4 s, except HVH_FailPowerCheck which is 5 s.
Note 2: The count number is incremented only when a diagnosis is completed and
        the result is NG. A HW reset alone does not increment the count.
相对客户 draft 的修改原因
计数主语由 HW reset count 改为挂在「异常被确认」上draft 沿用旧逻辑说法;新逻辑要求跑完诊断且 NG 才 +1
第 4 条门槛由「2 consecutive HW resets」改为「从第 3 次确认起」实际第 2 次诊断 NG 即报 Rationality abnormal,中间只隔 1 次 HW reset,draft 差一次
补写 does NOT move to Permanent lockdraft 删掉了旧版的 Permanent lock 却未说明「到此为止不再升级」,对比两版会以为是漏写
补写 4 秒确认时间及 Pwr_Check 的 5 秒例外draft 完全没有时间条件,读不出「连续异常多久算一次 NG」
detectingconfirmed检测到 ≠ 确认:ADC 越阈值即为检测到,须连续 4 秒才算确认,counter 在确认后才加
新增 Note 2新旧逻辑最大差异,不写则看过旧文档者会沿用老理解,实现与验收将对不上

4. 累积法的确认时间与故障占空比(2026-08-05 已结论)

累积法的确认时间取决于故障占空比,不存在时间上界。客户曾就「为何采用累积法」提出疑问, 我方已说明最快 4 秒、最慢不作保证客户已接受。方案维持累积法不变。

「占空比」的准确定义——两个常见误解:
  • 分母是总采样次数,不是未触发次数。它是占比不是比值: 80% 占空比 = 每 5 次采样有 4 次越阈值,写成比值才是 4:1。
  • 不存在「一个完整的检测周期」。累积法没有窗口——计数器自由累加, 越阈值 +ConfStep、未越 −RehabStep,不会在任何周期边界清零重来。 所以占空比是整段故障从头到尾的平均比例,可跨几十秒甚至更久。 这正是它与滑动时间窗最本质的区别:滑动窗有记忆上限,累积法没有。

4.1 确认时间计算公式

以 outlet SC/OC 新参数为例:采样 4ms,确认时间参数 4000 ⇒ ConfStep = RehabStep = Ceil(65535 × 4 / 4000) = 66

p = 越阈值采样占全部采样的比例,走 N 次采样后:

净增 = N·p·66 − N·(1−p)·66 = 66·N·(2p−1)

到顶需净增 ≥ 65535,即净增步数 993:

    N = 993 / (2p − 1)          确认时间 = 4ms × N
占空比 p所需采样次数 N确认时间说明
100%9933972 ms(约 4 秒)对外口径的 4 秒即此值
80%16556620 ms(约 6.6 秒)
60%496519860 ms(约 20 秒)
50%永远确认不了加一次减一次,净增恒为 0
< 50%永远确认不了整体往下走,计数器趴在 0
⚠ 对外措辞风险:累积法给出的 4 秒是最快值(下界),不是上界。 PPT 与对外文档中若写成 max. confirmation time = 4 s,等于书面承诺了一个累积法给不了的保证。 客户已口头接受「最慢不保证」,但书面措辞仍建议用 min / typical 而非 max。

4.2 平衡点为何是 50%,以及不采用的调节手柄

分界线正好落在 50%,是因为 ConfStep 与 RehabStep 配了同一个值(均为 66)。 这不是架构决定的——旧代码里确认时间与恢复时间本就是两个独立的宏 (SCEMONI_u8TOUTLET_SC_FAIL_CNFT / SCEMONI_u8TOUTLT_SC_FAIL_REHT), 只是历史上都配了 250,才看着像一个数。

因此存在一个现成的手柄:把 REHT 配得比 CNFT 大,减得就比加得慢,平衡点自然下移。 例如 CNFT=4000(步长 66)、REHT=8000(步长 33),平衡点从 50% 降到 33%, 占空比三分之一以上的故障都能确认。

结论:不采用。原则是在改动最小的基础上改代码,且客户已接受现有口径。 该手柄还会打破「+1 与清零各需一次完整 4 秒诊断」的对称性(清零将变为 8 秒)。 此处仅作记录——若日后客户提出间歇性故障漏检,改这两个宏即可,无需改架构。
背景:本软件为复用产品,已是第三代,最初版本约五六年前由开罗团队研发。 累积法满足旧 Condition A/C 的要求(旧要求只有次数、无时间限制), 新要求引入「多少秒内报出」后,该方法的适用性才成为问题。

新逻辑 · Condition C 判定流程

判定框架:1 次 NG 诊断、连续 4 秒判异常 → counter = 1DTC = 011「电路异常」→ 永久锁,HVCH 停止工作
本框架主要适用 HE_OCNTC / CMOS SC·OCovercurrent 与 HE_SC 情况特殊,待另行讨论。

1. 与 Condition A 的根本区别

Condition ACondition C
需要几次 NG2 次(连续)1 次
两次之间必须经过用户手动上下电—(没有第二次)
中间态第 1 次 NG 后发 001无中间态
确认后DTC 010,等 HW reset,可恢复DTC 011,永久锁,不可恢复
counter 上限21

2. HE_OC(HVH_LoadOCFail)

客户图示:采样周期 100msn = 40,40 × 100ms = 4 s。 与舟舟给客户报的 4s 一致。

⚠ 澄清:旧代码里的 99 不是「判断 99 次」。
if (HeOcTaskCntr >= 99) { HeOcTaskCntr = 0; ...执行判断... } else { HeOcTaskCntr++; }
计数器从 0 数到 99 共 100 次调用,只在第 100 次执行 1 次判断,然后清零。 即「每 100 次调用判断 1 次」,判断本身只做一次。100 × 100ms = 10 秒一判
该分频是为 HE_OC 设计的(变量名 HeOcCheckPeriod / HeOcTaskCntr 均带 HeOc 前缀), HS_IGBT_OC 只是写在同一判断块内被动跟随。

2.1 改动清单

分频HeOcCheckPeriod = 99,10 秒一判整个删除,每次 100ms 调用都判
确认方式两次命中(bHeOcFailDetect),实际 20 秒累积计数器
确认时间—(无此参数)新增 4000 ms
步长Ceil(65535 × 100 / 4000) = 1639
到顶次数65535/1639 = 39.98 → 40 次 = 4000ms(正好整除)
永久锁阈值HE_OC_FAIL_THR = 31(或去掉计数,确认即锁)
60 秒清零删除(1 次即锁,清零无意义)
DTC011

三层检测条件本身不动:PWM > 300、(估算负载 × 100) > (标称负载 × 150) 或 IAV < 10、HV − HE < 10。

2.2 改 4 秒是好事——判据反而更严格

反直觉但重要:旧的「两次命中」只看相隔 10 秒的两个瞬间各命中一次, 中间 9.9 秒完全不看;改累积法后,4 秒内要累积 40 次净增才到顶,中间条件消失计数器就回退。
⇒ 从「采两个点」变成「看一整段」,误报会变少而不是变多,同时确认时间从最长 20 秒缩到 4 秒。

2.3 物理背景(判据设计依据)

HE_OC 是开路(短路是 HE_SC)。加热元件为两根 HE 并联

  • 开路一根 → 只剩一半功率,等效电阻翻倍,估算负载达标称的 2 倍 → 越过阈值 SCEMONI_u16HeOcLoadCoef = 150(1.5 倍),由第一个分支报出
  • 开路两根 → 完全不加热,电流消失 → 由第二个分支 IAV < 10 报出

判据里那个「或」的两个分支正好对应开一根和开两根两种情况,是有意设计的。

2.4 HS_IGBT_OC 的副作用

IGBT 逻辑代码不动(仅置 flag + 寿命计数,有 if(bHsIgbtOcState == FALSE) 保护只触发一次)。 但它与 HE_OC 共用同一函数,删掉分频后它也从 10 秒粒度变为 100ms 粒度,检出会变快。
IGBT 短路属严重故障,早发现是正面的——除非历史上有 IGBT 误报记录,需确认。

3. NTC / CMOS SC·OC(Abnormal safety sensor)

同属新 Condition C,采用相同判定框架。其采样周期为 1ms(与 HE_OC 的 100ms 不同), 按 4 秒确认推算:

检测周期1 ms
确认时间4000 ms
ConfStepCeil(65535 × 1 / 4000) = 17
到顶次数65535 / 17 = 3855 次(17 × 3855 = 65535 正好)
实际确认时间3855 × 1ms = 3855 ms
对外口径 4 s(与 outlet 的处理一致)。内部实际 3855ms,比标称少 145ms, 原因同样是 Ceil 使步长略大于理论值。此差值不对外说明。

4. HE_SC(HVH_FailHighCurrent HW)—— 自恢复循环型

本故障不套用上面的「累积 4 秒」框架,走自己的一套。 LIN 信号 FM_HE_SC_Cfail,读数字引脚而非 ADC。

4.1 大逻辑

步骤动作
1检测到 SC → counter++,报 DTC = 001
2约 285ms 后自恢复(复位硬件锁存,故障消失)
3进行下一次检测;若仍 SC,重复 1–2
4如此自恢复 14 次
5第 15 次仍 NG → DTC = 011,进永久锁

全过程约 4 秒内由软件自行完成,用户无需任何操作。 285ms ≈ 4000 / 14;若取 15 次则为 4000 / 15 ≈ 266.7ms。 HE_SC 检测本身耗时极短,可忽略不计,故用 4s ÷ 次数估算间隔即可。

4.2 此处的 001 是「真」自恢复,与 Condition A 不同

Condition A 的 001 是借用——第 1 次 NG 后本该发 010,因 DTC 编码不够用才借 001 顶替。
HE_SC 的 001 是名副其实——每次复位锁存后故障确实消失了,报自恢复故障完全对应实际行为。
两者含义不同,排查时不可混为一谈。

4.3 ⚠ 新旧「15 次」含义完全不同

旧的 15 次 = ECU 整机重启。 SCEFCTR_HE_SC_COUNTER_IDSCEMONI_bHeScFailResetFlag 驱动, 而该 flag 在 SCEMONI_vidInit()(1354–1358 行)中置位——条件是 NVM 存有 HE_SC 故障且计数未达上限。 vidInit 每次上电才执行一次,因此这 15 次是「带着故障重启 15 次」, 靠用户反复上下电攒出来,过程可长达数日。

新的 15 次 = 复位硬件锁存。使用代码中现成的 HDDIO_vidSetScRst() / HDDIO_vidClearScRst(), 软件自主拉引脚,4 秒内即可完成 15 次

两者都叫「reset」、都是 15,但一个是整机重启、一个是拉引脚,差着几个数量级。 改代码与写文档时必须区分,否则极易照旧变量名的语义往下写而走岔。

4.4 结论

可以实现。「自恢复」所需的动作(清硬件锁存)代码中本就存在, 要改的是间隔、次数,以及把跨上下电的计数换成本地循环计数。具体实现待后续细化。

5. Overcurrent / IAV(HVH_FailHighCurrent SW)—— 三次确认+自恢复

结构与 HE_SC 同属「确认 + 自恢复」循环,区别在于次数(3 次 vs 15 次)与间隔量级。 LIN 信号 FM_HE_Over_Curr_Cfail,检测周期 1ms,确认时间 500 msSCEMONI_u8IAV_CONF_TIME = 500;累积法实际 497ms,对外报 500ms)。

5.1 时序

步骤动作耗时
1第 1 次检测,故障确认500 ms
2counter = 1,报 DTC = 001
3等待后故障自恢复,开始第 2 次检测3 s 或 1.25 s(待定)
4第 2 次检测确认 → counter = 2,DTC = 001500 ms
5再次等待、自恢复,开始第 3 次检测3 s 或 1.25 s(待定)
6第 3 次检测确认 → counter = 3DTC = 011,永久锁500 ms

5.2 两个候选间隔的总时长(待 2026-07-29 下午会议确定)

方案算式总时长是否落在 4 s 内
1.25 s500 + 1250 + 500 + 1250 + 5004000 ms✅ 正好 4 秒
3 s500 + 3000 + 500 + 3000 + 5007500 ms❌ 超出
1.25 s 很可能是照「4 秒内出结果」倒推出来的——三段 500ms 检测加两段间隔, 恰好凑成 4000ms,落在 4 秒线上,不像随手选的数。

取舍:若客户对 overcurrent 同样要求 4 秒上界,则只能选 1.25 s,3 s 出局; 若无此要求,两者皆可,而 3 s 的间隔更长,更不易被连续的瞬时干扰凑够三次,误报风险更低

最终取值待会议结果确定,届时更新本节。

5.3 实现分析

结论:可以实现,难度中等。

现状即符合、无需改动的两项:
确认时间 —— SCEMONI_u8IAV_CONF_TIME = 500,本就是 500ms
永久锁阈值 —— SCEFCTR_u8HE_OVC_THR = 3,本就是 3 次
⚠ 需新增:TEMP_FAIL 目前是死胡同,没有"出来"的路。
现有代码该 case 内仅一句 SCEMONI_enuIavState = SCEMONI_TEMP_FAIL;(自赋值), 进入后无法退出,只能等重启。
新逻辑要求确认后等待 3s / 1.25s 再自恢复并进行下一次检测,因此必须新写: 等待计时器 → 计时到 → 清故障状态、回 NO_FAILcounter 保留不清零,累计至 3 才进永久锁。
⚠ 新旧「3 次」语义不同(与 HE_SC 同一个坑)。 SCEMONI_bIavFailResetFlagSCEMONI_vidInit()(789 行)置位, 条件为 NVM 存有 OVER_CURR 故障且计数未达上限—— 故旧的 3 次 = 带着故障重启 3 次(跨上下电攒); 新的 3 次 = 数秒内本地循环 3 次。数字相同、语义迥异,实现时不可沿用旧路径。
待定:60 秒清零机制是否保留。 旧逻辑中它有意义(3 次要攒很久,中途正常运行就该清账); 新逻辑 3 次全在 4 秒内完成,不存在「长期累积」,该机制留着几乎不会触发, 建议删除更干净

6. NTC / CMOS 的改动分析(补充)

⚠ 这是 Condition C 四个故障里改动面最大的一个,原因有四条叠加。
#问题说明
1确认时间是 uint8,装不下 4000SCEMONIS_u8NTC_SC_SFTY_CNFT 等 8 个宏全为 (uint8)250; 局部变量 uint8 LOC_u8NtcScSftyFailConfTime;NVM 亦为单字节直读。与 outlet 完全相同的卡点
2类别从 A 变 C判定逻辑整个换掉——删 60 秒持续、删 3 次 reset、改为确认即永久锁
3带安全冗余所有关键变量都有 .SAFEDATABYCOPY 副本,每处改动必须同步改副本,否则触发 HDCPU_vidPerformReset() 硬复位
4四个通道NTC SC / NTC OC / CMOS SC / CMOS OC,同样的改动要做四遍

参数推算见本节第 3 部分(ConfStep = 17,到顶 3855 次 = 3855ms,对外报 4 s)。

7. Condition C 四个故障的改动难度对照

故障难度主要工作
HE_OC最好改删分频、换累积法、改几个数值——都是现成机制挪位置
Overcurrent中等参数全部现成,但需新写 TEMP_FAIL 的自恢复出口与等待计时器
HE_SC稍麻烦涉及硬件引脚拉放时序,且需把 15 次从跨上下电改为本地循环
NTC / CMOS最麻烦uint8 卡点 + 类别 A→C 逻辑重写 + 安全副本同步 + 四通道重复,四项叠加

四者均可实现,无原理性障碍,但 NTC / CMOS 的工作量与验证成本明显高于其余三个。

🔗

outlet 传感器故障的连带影响(跨旧/新逻辑)

核心事实:outlet 传感器一旦故障,所有依赖它的功能全部失效,只剩 NTC/CMOS 一条链路兜底。 原因是这些功能取的都是同一个「最后有效值」SCEMONI_u16GetTOutletLastValid(), 而该值在 SC/OC 状态离开 NO_FAILURE 的瞬间即冻结。

1. LIN 上报:永远是数值,不会变 invalid / SNA

上报函数 u8UpdateOutletWaterTemperature() 直接返回冻结值除以分辨率因子, 没有任何「故障时改报无效」的分支。故障状态走独立信号 u8OutletTempSensorFail_Stat3enuGetToutletSensorState())。

  • 短路时 ADC 掉到 29 以下、开路时冲到 1004 以上,这些极端读数一次都不会出现在 LIN 上——报的是故障前最后一个正常值
  • 该故障位不区分开路与短路SC==FAILURE || OC==FAILURE 都映射为同一个 SENSOR_FAILURE
  • Punctual 期间该函数走 else 分支「keep last state」,确认之前仍报 NORMAL; 按新逻辑 4 秒确认计,物理故障与 LIN 状态变化之间有 4 秒延迟

2. 温度降额失效 ⇒ 满功率持续加热

SCEDRIV_vidManageTempDerate() 取的同样是 SCEMONI_u16GetTOutletLastValid(),再拿它查降额表 (82℃→2600W、85℃→1100W、89℃→0W、90℃→0W)。
outlet 故障后该值冻结(例如冻在 50℃),降额条件永远不成立, HVCH 满功率持续加热

真正的兜底是 NTC / CMOS 这条独立的安全温度链路,到 90℃ 触发过温保护停止加热。 因此可以说温度闭环控制断了,但不是完全无保护。

3. 低流量检测同样失效

SCEMONIS_vidLowFlowMoni() 的判据是 (GetTOutletLastValid() - u16LowFlowToutPreviousVal) >= SCEMONIS_u16LowFlowThr, 用的还是那个冻结值。outlet 坏了之后温度不再更新,差值恒为 0,永远判不出低流量。

参数说明
SCEMONIS_u8LOWFLOW_THR20 ⇒ 2.0℃除以温度分辨率因子 TOUT_OT_THR_FACTOR = 10
u8LOW_FLOW_FILTER_SIZE10滑动窗大小
SCEMONIS_u8LOWFLOW_REH_THR0恢复阈值
SCEMONIS_u16LOWFLOW_REH2_THR600第二恢复阈值(另加温度偏移)

版本核对:7DA1000E / 7DA10011 / 7DA10017 / 7DA10018 四个版本 LOWFLOW_THR 全部为 20(即 2.0℃),未曾变动。 该值可由 NVM 标定(HDNVM_u8LOW_FLOW_THR),实际运行值以标定表为准。

另注:判断前提是目标功率处于 SCEDRIV_MIN_POWER_REQ ~ MAX_POWER_REQ 之间,不加热时不判。

4. 给前端的答复口径

前端曾提议将此情形命名为「Heater temperature out of control」。
可接受,但需限定含义:若指「失去温度闭环控制」,准确; 若被理解为「温度可无限上升」,则不准确——安全温度传感器仍提供保护。
建议答复中同时写明「降额同样失效、满功率运行」与「NTC/CMOS 在 90℃ 兜底」两句, 这段话日后也是「已说明存在二次保护」的书面证据
🛠️

实现记录 · Condition A 代码改动(Fox01 · outlet OC/SC)

状态:2026-08-10 台架验证通过(SC / OC 双向)。 改动落在 3 个文件、22 处:SCEMONI_Services.cSCEMONI_Services.hSCEFCTR_Services.c。 未做编译验证(无 S32K144 工具链),编译与台架在舟舟侧完成。

1. 需求 ↔ 实现对照

#要求实现
1故障确认时间 250ms → 4000ms加 16 倍分辨率因子,250×16 = 4000ms
2counter 在故障确认时直接加一,不等 reset加一挪进 SC/OC 各自确认分支,位于写 NVM 故障状态之后
31 次 NG → counter = 1一次完整诊断出 NG 加一次
42 次 NG → counter = 2FAILURE 无自愈出口,一个上电周期最多确认一次
5counter 封顶不再增长u8CLASS_A_NG_COUNT_MAX = 2,加一前判 < 2
6SC 与 OC 合起来算(对整车是一个信号 HVH_FailSensorTOutSCEMONI_u8GetToutletNgCntVal() 返回两个 NVM 计数之和
7诊断帧里 SC / OC 仍分开看两个 NVM 计数各存各的,data dump 两个 4bit 字段未动
8counter = 1 → ECU state 001MINOR → SCEFCTR minor → TEMPORALLY_LOCK = 1
9counter = 2 → ECU state 010TEMPORARY → LOCKED_UNTIL_NEXT_HW_RESET = 2
10报 1 但不真自恢复,必须等 HW reset结构性成立,无需额外代码(见下)
11outlet 不再进永久锁60 秒累积 + 3 次 reset + vidSetFailState(FAILURE_LOCK_ID) 整段删除
12诊断 OK 后计数清零两个 NO_FAILURE 分支互清,带守卫

第 10 条为什么天然成立:TEMP_LOCK 回 NEUTRAL 的唯一条件是 SCEFCTR_NO_FAILUREAPMODMGR_Applicative.c:2588),而 outlet 状态机进 FAILURE 后没有任何出口, ClassAFail 恒为 MINOR 或 TEMPORARY,SCEFCTR 永远回不到 NO_FAILURE。 MINOR 与 TEMPORARY 在 APMODMGR:2465 都进 TEMP_LOCK,锁的行为完全一样,区别只在 LIN 上报的数字

2. ECU state 编码对照(新文档二进制 ↔ 旧文档十进制)

二进制十进制枚举名客户文档叫法
0000OFFOFF(初期値)
0011TEMPORALLY_LOCKTemporally Lock(旧称 MINOR_TEMPORAY_LOCK)
0102LOCKED_UNTIL_NEXT_HW_RESETRationality abnormal(旧称 MAJOR_TEMPORARY_LOCK)
0113PERMANENT_LOCKCircuit abnormal(永久锁)
100 / 101 / 1104 / 5 / 6温控 / Derating / Power Control
值与顺序两版完全一致,只是命名不同。outlet OC/SC 只会在 001 与 010 之间,永远不进 011—— 011 是永久锁,与 Condition A「删除永久锁」直接冲突。

3. 改动清单

SCEMONI_Services.c

  • 新增 u16TOUTLET_TIME_RES_FACTOR = 16(185 行)、u8CLASS_A_NG_COUNT_MAX = 2(261 行)
  • 删除 SCEMONI_bToutletScResetFlag / OcResetFlag、它们在 vidInit 的初始化、上电置标志的两段 if、上电按标志加计数的两段、两个一秒计数器
  • 四处 COMMON_u16Ceil 分母乘上因子(确认 / 恢复 × SC / OC)
  • SCEMONI_enuGetClassAFailStatus() 补四级优先级 PERMANENT > TEMPORARY > MINOR > NORMAL,末尾 else 降为防御兜底
  • 新增 SCEMONI_u8GetToutletNgCntVal()
  • 两个确认分支加 NG 计数递增(封顶判两者之和)
  • 两个 FAILURE 分支改为按计数置 MINOR / TEMPORARY,并删掉永久锁整段
  • 两个 NO_FAILURE 分支互清对方计数,带守卫

SCEMONI_Services.h

  • class A 枚举末尾新增 SCEMONI_SENSOR_MINOR_FAILUR(= 3), 原 0/1/2 的值一个不动——这三个值进 NVM,改了跟旧件存量数据对不上
  • 导出 SCEMONI_u8GetToutletNgCntVal() 声明

SCEFCTR_Services.c

  • vidClassifyFails 的 minor 分支条件加 enuClassAfailState == SCEMONI_SENSOR_MINOR_FAILUR(1354 行)。 不加这句 MINOR 匹配不到任何分支,会掉进最后的空 else, SCEFCTR_enuFailureFlag 保持上次的值不更新——真 bug,不是风格问题

4. 踩过的坑:互清计数导致计数永远为 0

现象(2026-08-10 台架):故障触发后上下电,ECU state 恒为 1 不变 2,不论触发几次; Rst_cnt 恒为 0;寿命计数正常。

根因:为满足要求 6(SC/OC 合算),在两个 NO_FAILURE 分支各加了「把对方计数器也清掉」。 但这两个分支不是恢复时跑一次,而是每个周期都在跑。SC 故障时 OC 通道健康, OC 的 NO_FAILURE 分支每周期清一次,把 SC 刚加上的 1 立刻抹掉 → 计数恒 0 → 恒 < 2 → 恒 MINOR → ECU state 恒 1。

修复:清零加双重守卫,只有对面通道也干净时才清。

if ((SCEMONI_OC_SC_NO_FAILURE == SCEMONI_enuToutletCurrOcState)
        && (STD_bTRUE != SCEFCTR_bGetFailState(SCEFCTR_OUTLET_OC_CFAIL_ID)))
{
    /* 清两个 RSET 计数 */
}

两个条件缺一不可:状态条件管本上电周期内对面正在诊断(PUNCTUAL)的情况; NVM 条件管上次上电就已确认、而状态机开机不恢复的情况。 SC 状态机先于 OC 执行(3210 行 vs 3402 行),OC 故障时第一个周期 SC 会先跑, 那时 OC 本周期还没更新,只能靠 NVM 条件兜住。 四种组合(SC 故障 / OC 故障 / 双故障 / 修复后)均已推演;双故障修复后能在同一周期解开, 因为 vidResetFailState 不在守卫内。

5. 标定单位变更(外部影响)

四个参数:outlet OC/SC 的确认与恢复时间,NVM PAGE3 偏移 100 / 101 / 106 / 107,默认值均为 250。 字节宽度、诊断帧、LDF 全不变,只有单位变了:1 count 从 1ms 变成 16ms。 写入值 = 目标毫秒 ÷ 16。

  • CAPL 脚本不用改:写入路径是通用的原始字节透传(面板填 WRITE_ID + D2~D5),无换算、无量程校验
  • 要改的是人那边:标定表 LSB 1ms→16ms、量程上限 4080ms、下线程序与文档里「250 = 250ms」改成 4000ms、测试用例期望值改 4000ms
  • 两个恢复时间没有诊断读写入口(SCECOM 只有确认时间那两个字节的处理器),现场标不了,只能改代码默认值。既有状况,非本次引入
  • 实际确认时间是 3972ms 不是 4000ms:步长 = Ceil(65535×4/4000) = 66,993 次 × 4ms。Ceil 取整使实际值偏小(早 28ms 确认),方向保守。改动前的 250ms 同样有此偏差
  • 该字节当除数用,不能写 0
  • 工程内已有同类先例:SCEDRIV_u16FM_PWR_TIME_CFM_DEF / u8PWR_CHK_RES_FACTOR

6. 上电时序行为(设计的必然结果,非 bug)

严重度不从 NVM 恢复:vidInit 每次上电把 ClassAFail 置 NORMAL。 NVM 存的是故障标志和 NG 计数,但没有代码在上电时用计数恢复严重度。 所以每次上电都要重跑完整诊断(约 3972ms)才会重新置 TEMPORARY 报 2,第 n 次上电也一样。

outlet 诊断在 NEUTRAL 模式就在跑SCEMONI_vidManagOutltSensorMoni 在 APMODMGR 的 NEUTRAL / HEATING / TEMP_LOCK / LCM 四处 4ms 任务里被调用), 4 秒从上电起算,不是从开机请求起算
  • 上电后立刻请求开机 → 真的加热约 4 秒 → 确认故障 → 切 TEMP_LOCK 停机报 2
  • 上电 4 秒后才请求开机 → 故障已在 NEUTRAL 中确认,SCEFCTR_NO_FAILURE 不成立, 请求不起作用,该次上电一次都不工作
  • 因永久锁已删除,这个循环无限重复,永远不会进入「再也开不起来」的状态
待客户确认的安全侧输入:现状等于明知 NVM 已记录该传感器坏过两次, 仍带着失效的 outlet 温度传感器加热 4 秒,期间无有效温度保护。 若要求上电立刻锁,可在初始化时按 NVM 的 NG 计数直接置 TEMPORARY / MINOR(三五行); 代价是传感器修好后上电也会先报 2,直到本次诊断确认 OK 才清零。
2026-08-10 决定:维持现状(「上电先工作同时检测,确认故障后才会停下来」)。

7. 台架验证记录(2026-08-10)

  • SC:LIN_Coolant_temp_out_of_range 先置起(短路使读数看起来温度极高,过温先于 4 秒判出), 几秒后 LIN_fail_outlet_temp_sensor 置起,HVCH_state 变 2,诊断上过温故障同时置起
  • OC:几秒后 LIN_fail_outlet_temp_sensor 置起,HVCH_state 变 2, 无 out_of_range(原因见下一节 9.1)
  • 台架未通高压时,高压欠压故障恒在 → SCEFCTR 恒为 minor → 上电即 TEMP_LOCK 报 1,outlet 确认后升 2。 此时产品全程未工作,「先加热 4 秒」的场景在台架上不会出现,需通高压才能复现

观察点:ECU state 在 0x11 帧 byte4;Rst_cnt 在 0x1B 帧 byte5 (高 4 位 OC、低 4 位 SC,判定用的是两者之和);寿命计数在 0x24 帧。

⚖️

实现记录 · Condition A 代码改动(Fox02 · NTC/CMOS 温度不一致)

状态:2026-08-11 台架验证通过。 故障 FM_Sfty_LS_Inconsist_Cfail,新 Condition A 三个故障里唯一留在 A 类没变的那个。 改动落在 4 个文件:SCEMONIS_ExtConfiguration.hSCEMONIS_Services.hSCEMONIS_Services.cSCEFCTR_Services.c。基线 Fox01 → 交付 Fox02。 未做编译验证(无 S32K144 工具链)。

1. 参数改动

参数旧值新值说明
SCEMONIS_u8TLS_CONSIST_THR168温差阈值,单位摄氏度
SCEMONIS_u8TLS_CONSIST_HYS22(不变)所以 8 度触发、6 度恢复
SCEMONIS_u16INCONSIS_FAIL_CNF_P5004000确认时间
SCEMONIS_u16INCONSIS_FAIL_REH_P5004000恢复时间,与确认保持一致

单位来源:CMOS 直接取物理值,NTC 取 HDADC_u16GetNtcPhVal() / SCEMONIS_u8NTC_OT_THR_FACTOR 换成物理值,两者相减取绝对值,所以 delta 的单位就是摄氏度,8 就是 8 度。 (注意站点「旧逻辑 · 温度不一致」那节记的是 11 版本的 140,20 版本已经是 16,本次再降到 8。)

✅ 这个故障不需要分辨率因子,标定侧零改动。 与 outlet 最大的不同:确认和恢复时间在 NVM 里本来就是两字节存的 (HDNVM_u8INCONSIS_TIME_CFM_MSB / _LSB..._REH_MSB / _LSB), 4000 直接放得下,单位仍然是 1 ms。阈值和迟滞也各有自己的 NVM 字节 (HDNVM_u8INCONSIS_THR / HDNVM_u8INCONSIS_HYST),8 也在 uint8 范围内。 所以不会出现 outlet 那种「250 现在等于 4000ms」的标定单位变更。

时间算得比 outlet 还准:检查周期 10 ms, IncStep = Ceil(65535 × 10 / 4000) = 164,到顶次数 = 65535 / 164 = 400, 400 × 10 ms = 正好 4000 ms,没有 outlet 那 28 ms 的零头。

2. 逻辑改动

  • 删掉上电按 flag 加计数那套SCEMONIS_bInconsisResetFlag 及其安全副本、 它们的初始化、vidInit 里读 NVM 置旗那段、监控函数入口按旗递增那段,全部删除; 那两处 RAM 校验里对这个旗的比对也跟着去掉
  • 新增 SCEMONIS_u8GetInconsisNgCntVal():读的还是 SCEFCTR_SFTY_CONSIS_HW_RES_ID 这个 NVM 计数器,NVM 布局没动, 只是写入时机从上电挪到了确认
  • 确认分支里递增 NG 计数,递增前判 < 2 封顶
  • FAILURE 分支按计数选严重度:1 次置 MINOR(LIN 报 001)、2 次置 TEMPORARY(报 010), 主变量与安全副本同时写
  • 删掉 60 秒累积和永久锁整段(原来是满 60 秒 + HW reset 计数到限才升永久锁)
  • 清零沿用原有实现:NO_FAILURE 态且温差正常时清 SFTY_CONSIS_HW_RES_ID, 正好就是新逻辑要的「诊断 OK 就清零」。这里不需要 outlet 那种守卫—— 温度不一致只有一个通道,不存在被另一条健康通道抹掉计数的问题
  • SCEFCTR 的 minor 分支条件加上 enuSafeClassAfailState == SCEMONIS_SENSOR_MINOR_FAIL

3. 与 outlet 那次的三处结构差异

差异outlet温度不一致
时间参数宽度NVM 单字节,4000 放不下,要加 16 倍分辨率因子NVM 两字节,直接存 4000
安全冗余所有关键变量维护 SAFEDATA 副本,四处逐项比对,对不上就硬复位
ClassA 枚举编码普通顺序 0/1/2/34 位汉明距离编码 0x05/0x12/0x2B/0xFA
新增枚举值必须算汉明距离,不能随手写。 SCEMONIS_SENSOR_MINOR_FAIL0x3C,与原有四个值两两距离均为 4,已逐对验算。 同族码字里 0x48 也满足,可作备选。
为什么要这样编:普通枚举 0/1/2/3 相邻值只差 1 个 bit,RAM 里一位翻转就会从一个合法值变成另一个合法值, 程序无从察觉;两两差 4 位之后,翻 1 到 3 位都会落到非法值上,能被发现。 这个模块本身就是安全机制,其自身数据必须防单点翻转。

4. 又踩到同一个坑:聚合函数把 MINOR 吃掉了

SCEMONIS_enuGetClassAFailStatus() 原本只有三个分支——任一永久就报永久、 全部正常就报正常、否则一律当临时。新置的 MINOR 会掉进最后那个 else 被当成 TEMPORARY 传出去, SCEFCTR 里新加的 minor 判断永远匹配不到,第一次 NG 就直接报 010,改动等于白做。

已补成四级优先级 永久 > 临时 > minor > 正常,末尾 else 降级为防御兜底。 这与 outlet 那次是同一位置的同一个坑(那边是 SCEMONI_enuGetClassAFailStatus())。 以后再给别的故障加 MINOR,第一件事就是去看它的聚合函数。

5. 这个故障特有的检测条件:低压必须 ≥ 8.0 V

判据是 LOC_u8PhyTempDelta >= 阈值 && u8LvMeasure >= u8LvUvThr, 即低压电压低于 8.0 V 时根本不做这个诊断,8.0 V 整这一点仍然检测(是大于等于)。

单位追溯:LV 的 ADC 查表出来是 0.01 V 分辨率,再除以 u16LV_VOLTAGE_CALCULATION = 10 变成 0.1 V 分辨率;阈值 SCEMONI_u8LV_UV_THR_DEF = 808.0 V,可由 NVM 标定 (HDNVM_u8LV_UV_THR)。

测试须知:2026-08-11 台架上把电源调到 8 V 测出「不检测」, 说明 ECU 实际读到的是 79(7.9 V)——电源表显示 8.00 V,线阻、接插件压降、ADC 舍入凑一凑就差这零点一伏。 验这条逻辑不要卡在 8.0 V 上测,拉开距离取两点:9 V 应照常报、7 V 应完全不报, 这样零点几伏的误差不影响结论。

6. 保留未删的东西(按「少改动」原则)

  • u8INCONSIS_COUNTS_TO_ONE_SECOND(= 100):60 秒分频用的,新逻辑不再使用,已加注释
  • SCEFCTR_SFTY_CONSIS_CNTR_ID:原来的 60 秒累积 NVM 计数器,现在只剩 NO_FAILURE 分支里的清零调用,已加注释
  • u8CLASS_A_MAX_RESET_COUNT(= 15):本故障不再使用,但同文件其它监控还在用,不能删

函数内那两个一秒计数器局部变量(含 SAFEDATABYCOPY 副本)是删掉了的—— 它们随 60 秒累积一起失去用途,留着会触发 set-but-not-used 警告。保留的是宏和 NVM 计数器,不是这两个变量。

7. 台架验证观察点

温差拉到 8 度以上 → 4 秒后第一次确认,NG 计数 1、ECU state 报 001; 断电重上、温差仍在 → 第二次确认后计数 2、state 变 010;再上电仍是 2 不再涨; 温差降到 6 度以下且诊断走 OK → 计数清 0。

实现记录 · Condition A 代码改动(Fox03 · 功率不一致)

状态:2026-08-14 台架验证通过(大功率 / 小功率两个区间)。 故障 FM_Pwr_Check_Cfail,从旧 Condition C(3 次直接永久锁)移入新 Condition A(2 次 NG、不锁)。 代码在 SCEDRIV_Services.c → SCEDRIV_vidTaskManagePower(),不在 SCEMONI / SCEMONIS。 改动落在 4 个文件、19 处。diff Fox03_pwrcheck.diff(466 行,第二版含清零延时修复)。

1. 需求 ↔ 实现对照

#要求实现
12 次 NG 封顶u8CLASS_A_NG_COUNT_MAX = 2,加一前判 < 2
2counter 在故障确认时加一,不等 reset加一挪进确认分支,删掉上电按标志递增那段
31 次 NG → 001SCEDRIV_POWER_MINOR_FAILURE → SCEFCTR minor → TEMPORALLY_LOCK
42 次 NG → 010SCEDRIV_POWER_TEMP_FAILURELOCKED_UNTIL_NEXT_HW_RESET
5不再进永久锁删掉确认分支里置 SCEFCTR_FAILURE_LOCK_ID 那段
6诊断 OK 后计数清零连续 5 秒正常且 PWM 达标才清(第二版,见第 4 节)
7NG 计数跨电源循环保留存 NVM,上电不清——与 Fox04 过流正好相反

2. 时间参数一个都没改

检查周期 50 ms、SCEDRIV_u16FM_PWR_TIME_CFM_DEF = 5000、 Inc = Dec = Ceil(65535 × 50 / 5000) = 656、到顶 65535 / 656 = 99.9 → 100 次, 100 × 50 ms = 正好 5000 ms。客户要 5 秒(这是新 A 里唯一不是 4 秒的),现在就是 5 秒。

3. 改动清单

SCEDRIV_Services.c(13 处)

  • 新增 u8CLASS_A_NG_COUNT_MAX = 2u16PWRPLAUS_OK_5S_CNTR = 100(100 × 50 ms = 5 秒)
  • 新增计时器 SCEDRIV_u16PwrChkOkTMR,在 vidInitvidHeatingInit 各初始化一次
  • 删掉上电按标志递增 SCEFCTR_PWR_CHK_COUNTER_ID 那段,递增挪进确认分支
  • 确认分支:递增前判封顶;严重度按计数分 MINOR / TEMP;删掉升永久锁整段
  • 新增 case SCEDRIV_POWER_MINOR_FAILURE必须单独写——少了它会掉进 default, 下一周期状态被重置成 NO_FAILURE,第 1 次 NG 保不到硬件复位
  • NO_FAILURE 分支:清零改成连续 5 秒才清(第二版核心)
  • 旧的 u16PWRPLAUS_HW_RST_60S_CNTR = 1200 保留不删,注释写明被新宏取代

SCEDRIV_Services.h(2 处)

  • 枚举末尾追加 SCEDRIV_POWER_MINOR_FAILURE(= 4)。 这个枚举是普通顺序 0/1/2/3,不是汉明距离编码,加末尾即可,比温度不一致那次简单
  • 导出 SCEDRIV_u8GetPwrChkNgCntVal()

SCEFCTR_Services.c(2 处):高边故障标志、故障分类,两处 minor 分支各加一条判断。

SCECOM_Services.c(2 处):诊断 data dump 帧的 Power Check 位、LIN power check failure signal, 两处原本只判 TEMP / PERM 的列表各加 MINOR。

4. 踩过的坑:清零太激进,大功率下计数永远为 1

现象(2026-08-14 台架):功率 < 3500 W 时计数能正常累到 2,测试通过; 功率 > 3500 W 时 Rst_cnt 永远卡在 1——第 1 次 NG 结束 state=1、Rst_cnt=1、NVM_cnt=1, 第 2 次 NG 结束 state 还是 1、Rst_cnt 还是 1,只有 NVM_cnt 变成 2。

第一版的做法:照搬 Fox01 的写法——检查一读 OK 就清 NG 计数,外面套 PWM 占空比门槛。

根因:这个状态机除了硬件复位没有别的出口,所以第 2 次 NG 必然从一次断电重启开始。 而重启后功率要从 0 爬到目标,这段时间估算功率还没到阈值,检查读数就是 OK, 那一拍就把上次存的 NG 计数抹了。等功率爬上来、故障重新确认,计数又从 0 加到 1。 NVM 寿命计数器是无条件累加的,不受影响,所以它老实数到 2——这个数字反过来证明检测本身是好的,坏的只是计数保持。

为什么小功率没暴露:清零那行外面套着 u16CLASS_C_PWM_THR = 150 的占空比门槛。 小功率时占空比低够不到门槛,清零被挡住,计数侥幸保住了。 不是小功率正确、大功率有 bug,是小功率恰好被门槛遮住了同一个 bug。

为什么 Fox01 同样写法却没事:outlet 传感器短路 / 断路是开关量, 故障在就一直在,重启后立刻又检出,中间没有「读 OK」的窗口。 功率不一致是连续量,加热启动必然有爬升过程,这段时间物理上就是正常的。 Fox01 的写法不能直接套到连续量的故障上。

第二版:改成连续 5 秒正常才清(100 × 50 ms)。功率约 1 秒爬到位,5 秒余量充足—— 真实的爬升期填不满计时器,而真正正常运行的产品几乎立刻就清。 原厂那个 60 秒计时器等于用小一个量级的值恢复了回来。

为什么 vidHeatingInit() 里那次清零是关键: 计时器在两个地方清零,vidInit()(上电一次)和 vidHeatingInit()(每次进加热模式)。 后者才是修复真正生效的地方——断电重启后进加热模式,计时器从 0 开始, 爬升期那几十个 OK 周期无论如何也累不到 100。

5. PWM 占空比门槛不能删

它区分的是「功率检查通过了」和「根本没有功率可检查」。小功率分支里目标功率低于 SCEDRIV_MIN_POWER_REQ(200 W)会直接判定正常——这是代码构造出来的正常,不是真的做了检查。 没有这个门槛,产品挂在加热模式但实际不发热时计数就会被反复清掉,上一个电源周期存下的 NG 白白丢失。

6. 三条已定的决策(2026-08-11)

#问题决定
160 秒清零机制留不留清零机制与 Condition A 保持一致,但保留一个 5 秒的延时(第二版修正)
2只在 Heating 模式检测,不加热就不诊断已知悉,按现状
3SCEDRIV_enuGetPowerStatus() 里那条独立永久锁路径保留,做好注释

关于第 2 条的后果(记录备查):第一次 NG 报 001 之后,用户下次上电如果只通电不加热, 这个故障永远不会被重新诊断,counter 一直停在 1。必须下次真的加热起来、 并再连续异常 5 秒才会升到 010。测试时两次 NG 之间不光要上下电,还得真的加热。 另外两个故障在 Neutral 就开始诊断,没有这个限制。

7. 高低阈值目前标定成一样

SCEDRIV_u8PWR_HI_THR_DEFSCEDRIV_u8PWR_LO_THR_DEF 都是 120, 所以大功率分支里「保持上次值」的 else 实际永远进不去,判定没有迟滞带。 本版没改这个标定(Condition A 的要求里没提),若以后想要迟滞,改这两个标定值即可,代码结构支持。

8. 台架验证记录(2026-08-14)

  • 功率 < 3500 W:Rst_cnt 正常累到 2,清除正常,NVM_cnt 正常 —— 通过
  • 功率 > 3500 W:第一版 Rst_cnt 卡在 1;第二版修复后累到 2、state 正常升到 010 —— 通过
🔥

实现记录 · Condition C 代码改动(Fox04 · HE 过流 IAV)

状态:2026-08-17 台架验证通过。 故障 HVH_FailHighCurrent(SW detection),需求编号 2-2-2。 这一版是 Condition C,不是 Condition A——产品会自己恢复, 与 Fox01/02/03 的 MINOR 含义相反,见第 6 节。 改动落在 5 个文件、18 处。diff Fox04_ovc.diff(435 行,第二版含清零延时修复), 源码包 Fox04_src_0814b.tar.gz

1. 规格时序(TMC 提案,2026-07-30 Accepted)

Cycle time 1 ms,检测到过流后连续确认 500 ms 算一次 NG。过流阈值默认 49.0 A, 迟滞 2.0 A(掉回 47.0 A 才算恢复)。

第几次 NGError_counterDTC之后
第 1 次1001锁定,3 秒后自恢复,继续下一次检测
第 2 次2001锁定,3 秒后自恢复,继续下一次检测
第 3 次3011永久锁,HVCH can't work

名义总时长 500 ms × 3 + 3 s × 2 = 7.5 秒。 实测会偏长——自恢复的实际路径是 TEMP_LOCK → NEUTRAL → IGBT 硬件自检 → HEATING, 整个序列要过两次自检,规格没算这段。已与需求方确认 7.5 秒是名义值,不作硬指标

2. 需求 ↔ 实现对照

要求实现
确认时间 500 ms标定 SCEMONI_u8IAV_CONF_TIME = 500
判定 3 次SCEFCTR_u8HE_OVC_THR = 3
第 1、2 次报 001MINOR → SCEFCTR minor → TEMP_LOCK 模式 → TEMPORALLY_LOCK
第 3 次报 011PERMENEMT + FAILURE_LOCK_ID → PERM_LOCK 模式 → PERMANENT_LOCK
正常工作报 110HEATING 模式 → POWER_CONTROL_MODE
Self-recovery 3 秒u16IAV_SELF_RECOVERY_TIME_MS = 3000 × 1 ms
计数不跨电源循环上电无条件清零——与 Fox03 功率检查正好相反
过流消失则序列重来连续正常 2 秒后清零(第二版,见第 5 节)

3. 改动清单

SCEMONI_Services.c(12 处)

  • 新增 u16IAV_SELF_RECOVERY_TIME_MS = 3000u16IAV_OK_2S_CNTR = 2000
  • 新增计时器 SCEMONI_u16IavSelfRecovCntr(自恢复)与 SCEMONI_u16IavOkTMR(正常运行)
  • 删掉 SCEMONI_bIavFailResetFlag,上电改为无条件清零 Error_counter
  • 确认分支:Error_counter 在这里递增;达上限 → PERMENEMT + 永久锁,未达 → MINOR + 启动自恢复计时
  • 新增 case SCEMONI_MINOR_FAIL:计时到点后清 NVM 故障位、清两个计时器、清确认计数器、回 NO_FAIL
  • NO_FAIL 分支:检出过流时打断正常计时;读数正常则累加,满 2 秒才清 Error_counter
  • 删除旧的 60 秒硬件复位计时器 SCEMONI_u16HeOVCHwRstCntrTimer(声明 / 初始化 / vidHeatingInit 三处)。 注意 OC 那个 SCEMONI_u8HeOcHwRstCntrTimer(u8)没动——那是 HE 短路故障的计时器,不是同一个故障

SCEMONI_Services.h(1 处):枚举末尾追加 SCEMONI_MINOR_FAIL, 已有成员取值一个不动(这些值进 NVM 和诊断帧)。

APMODMGR_Applicative.c(1 处)enuAppTempLockMode() 的 1 ms 任务里补上 SCEMONI_vidIavMoni() 调用 —— 死锁修复,见第 4 节。

SCEFCTR_Services.c(2 处):高边故障标志、故障分类,两处各加 MINOR 判断。

SCECOM_Services.c(2 处):诊断 data dump 帧 D4 的 b0 位、 LIN 的 IAV failure signal,两处原本只判 TEMP / PERM 的列表各加 MINOR。

4. 踩过的坑一:必现死锁

MINOR 会让 SCEFCTR 分类成 SCEFCTR_MINOR_FAILURE,而 APMODMGR 见到它就把产品 从 HEATING 踢到 TEMP_LOCK。可是 3 秒自恢复计时器是在 SCEMONI_vidIavMoni() 里数的,而这个函数原本只在 HEATING 模式调度

结果是第 1 次过流确认就死锁:进故障 → 立刻被踢出加热模式 → 计时器不再递增 → 3 秒永远数不完 → 永远不解锁 → 永远回不到加热模式。产品卡死,只能断电,7.5 秒序列连第 2 次都走不到。

修法:enuAppTempLockMode() 的 1 ms 分支补上同样的调用, 守卫条件与加热模式那处完全一致(DCDC 与 I2C 抑制都未激活才跑), 位置放在 SCEFCTR_vidManageFail() 之前——这样自恢复完成的当拍,故障管理就能看到状态已回到 NO_FAIL

通用教训:凡是「进故障态后靠计时自恢复」的设计, 都要先确认那个计时器所在的函数在故障态下还跑不跑

5. 踩过的坑二:清零太激进(同一个错犯了两次)

现象(2026-08-17 台架):上电后过流 → 停 3 秒 → 起来 → 又过流 → 停 3 秒 → 起来 → 第三次过流却没进永久锁,继续 3 秒一轮无限循环。手动停下时 NVM_cnt 已经 10 次,Rst_cnt 还是 1

根因:自恢复到点后产品解锁、重新加热,电流要约 1 秒才爬回来。 这段爬升期电流低于 49 A,读数就是正常,那一拍就把上次确认存下的 Error_counter 清成 0 了。 等电流爬上来、过流重新确认,计数又从 0 加到 1。所以每转一圈都是 0→1,永远到不了 3、永远进不了永久锁。

NVM 寿命计数器无条件累加,不受影响,所以老实数到 10——这个数字反过来证明检测本身是好的。

这个错犯了两次。Fox03 功率不一致的第一版清零策略是同一个毛病,台架先暴露的是 Fox03; 改过流时照抄了 Fox03 的第一版写法,注释里甚至留着 Same handling as the power check failure,等于把刚被否掉的逻辑又复制了一份。

第二版:改成连续正常 2 秒才清(u16IAV_OK_2S_CNTR = 2000 × 1 ms)。 电流爬升约 1 秒,2 秒给了一倍余量;过流真的消失时 2 秒后照样清零,「序列被打断」的需求保住了。 另外在检出过流的那一拍把正常计时器归零——「连续正常 2 秒」被打断就得重新计。

通用教训:凡是物理量渐变的故障(电流、功率), 都不能把单拍读数正常当成诊断通过——启动和恢复必然有爬升期,那段时间读数天然是正常的。 开关量故障(传感器短路 / 断路)没有这个问题,Fox01 的写法不能直接套过来。

6. 与 Condition A 的两处「相反」

对比项Condition A(Fox01/02/03)Condition C(Fox04)
MINOR 的含义第 1 次 NG,报 001,不会自己恢复,要断电或 HW reset第 1/2 次 NG,报 001,3 秒后自己恢复继续检测
状态机出口没有,进 FAILURE 就锁到硬件复位有,自恢复延时到点就退出
NVM 计数要求必须跨电源循环保留(两次 NG 之间隔着断电重启)必须上电清零(三次检查是软件 7.5 秒内自己跑完的)

改 SCEFCTR 分类时两者共用同一个 minor 分支,但注释要分开写,不要混。NVM 计数那条最容易记混。

7. 掉电重启的行为

过流确认那一刻,代码连着动三样东西,都在相邻几行:

含义上电处理
故障标志位 SCEFCTR_OVER_CURR_CFAIL_ID只有 0/1,「故障是否正挂着」不清,检查正常时自动清 0
Rst_cnt SCEFCTR_HE_OVC_COUNTER_ID图里的 1→2→3本版改为清零
NVM cnt HDNVM_u8HE_OVER_CURR_CNT这个件一辈子发生过几次过流不清,历史履历
初始化顺序是关键:计数器上电会先从 EEPROM PAGE5 读回来,我们的清零必须排在读之后才有效。 已确认 ICONF_Initialization.cSCEFCTR_enuInit()(读 NVM) 在 SCEMONI_vidInit()(清零)之前,顺序正确。以后若有人调整初始化顺序,这条会被破坏。

掉电时机不同,结果不同:

  • 第 1 个 500 ms 之内掉电:三样都没动过,干净。因为置位、加计数这三行都在「500 ms 数满」之后才执行。 这段时间状态机处在疑似态(SCEMONI_PUNCTUAL),而故障分类表里没有这一项,所以整机正常加热,状态仍是 110
  • 确认完、正在数 3 秒时掉电:标志位是 1,重启后仍是 1;Rst_cnt 被清成 0
  • 自恢复完成后掉电:标志位已清 0,干净

残留的标志位只影响诊断帧 D5 上那一位的显示,不影响检测流程,第一次检查正常后自动清掉。 另有一个原厂固有机制:改内存和真正写 NVM 之间有个时间窗(先打「有变动」标记,后统一写页), 卡在这个窗口掉电 NVM 里存的还是旧值。本版未改动该机制。

8. 自恢复依赖持续的加热请求

自恢复要走完,前提是整车这期间一直在请求加热。 中途撤销加热请求,模式会停在 NEUTRAL,序列自然中断,下次重新请求加热时从头开始。 已与需求方确认此行为可接受。做台架用例时如果有「故障期间松开加热请求」的场景,注意预期要按这个来。

9. 台架验证记录(2026-08-17)

  • 第一版:无限循环,Rst_cnt 卡在 1,NVM_cnt 数到 10 —— 不通过
  • 第二版:Rst_cnt 正常走 1→2→3,第三次确认进永久锁报 011 —— 通过

实现记录 · Condition C 代码改动(Fox05 · HE_SC 硬件过流)

状态:2026-08-18 台架验证通过(3 次测试版与 15 次正式版均通过)。 故障 HVH_FailHighCurrentHW detection),需求编号 2-2-1。 这是 Fox04 那个故障的另一半——Fox04 是 SW 检测(2-2-2,读 ADC 电流值), 这一版是硬件比较器引脚检测,两套状态机完全独立,Fox04 的 diff 一行都没碰过 HE_SC。 Condition C 的改动只落在 SCEMONI_Services.c 一个文件, 另外还原了一处基础软件遗留的 HDLIN 改动。 diff Fox05_hesc.diff(314 行),源码包 Fox05_src_0818.tar.gz

1. 规格时序

检出信号是 HDDIO_bGetScHeInput()——硬件比较器(LM2903)输出引脚, 低电平 = 过流,硬件 10 µs 内响应并锁存。 与 Fox04 读 ADC 电流值比阈值完全不是一条路径。

第几次检出Error_counterECU_State之后
第 1 ~ 14 次1 ~ 14001产品停止输出,285 ms 后自恢复,继续下一轮
第 15 次15011永久锁,HVCH can't work

15 次检测 = 14 次自恢复。客户依据:过流会 damage IGBT 所以「一回判定就先停止」; 15 次是「贵司现有产品有实绩的次数」;中间是 self recovery、同一电源周期, 所以虽不满足 Condition C 的「1 回判定」,客户认为可接受。

2. 285 ms 除不尽 → 取 290 ms

SCEMONI_vidTaskScHeMont() 挂在 10 ms 任务上 (三个调用点分别在 enuAppNeutralMode / enuAppHeatingMode / enuAppTempLockMode), 所以所有时间参数都只能是 10 ms 的整数倍:28 拍 = 280 ms,29 拍 = 290 ms,没有正好 285 ms 的

29 拍 = 290 ms:IGBT 保护场景宁可多给恢复时间,不要少给。 要精确 285 ms 只能把状态机挪到 1 ms 任务,会连带动复位脉冲宽度和整个时序,风险与收益不成比例。

3. 需求 ↔ 实现对照

要求实现
自恢复 285 msu8HE_SC_FAIL_DETECT_DELAY = 29 × 10 ms = 290 ms(旧值 99 = 990 ms)
检测 15 次u8HE_SC_FAIL_CHK_NUM = 15(台架先用 3 验证,通过后只改这一个数)
前 14 次报 001零改动——原厂 SCEMONI_SC_FAILURE_NO_LOCK 本就被分类成 minor
第 15 次报 011SCEMONI_SC_PERM_LOCK + FAILURE_LOCK_ID
计数不跨电源循环上电无条件清零 NVM 计数(同 Fox04)
某轮正常则序列重来加热模式守卫 + 连续正常 2 秒(第二版,见第 6 节)

4. 001 的上报一行代码都没改

这是本版比 Fox04 省事得多的地方。SCEFCTR_Services.cvidClassifyFails() 里, HE_SC 的三个状态原厂就已经映射好了

状态机状态SCEFCTR 分类ECU_State
SCEMONI_SC_FAILURE_NO_LOCKMINOR001
SCEMONI_SC_TEMP_LOCKTEMPORARY010
SCEMONI_SC_PERM_LOCKPERMANENT011

状态机一进 FAILURE_NO_LOCK 就报 001,整个 290 ms 自恢复期间保持 001。 而 Fox04 的 IAV 原本只有 TEMP / PERMENEMT 两级,为了报 001 必须新增 SCEMONI_MINOR_FAIL 枚举, 再去 SCECOM 两处、SCEFCTR 两处补判断列表——那是 Fox04 diff 里最零碎的部分,这一版全省了。

产品也确实会停:APMODMGR 的模式转换里 Neutral 和 Heating 两处都写着 MINOR_FAILURE → STD_TEMP_LOCK_MODE,符合「一回判定就先停止」。

⚠️ 和 Fox04 那个死锁正好相反。 Fox04 踩过的坑是:MINOR 把产品踢出加热模式,而自恢复计时器写在 SCEMONI_vidIavMoni() 里、 原本只在加热模式调度,于是第一次确认就永久卡死,得去 enuAppTempLockMode() 补调用。 HE_SC 这边不存在这个问题:自恢复计时器所在分支本来就写着 if(STD_TEMP_LOCK_MODE == enuCurrentMode),而该模式函数里本来就有调用点。原厂这套是自洽的。

5. 代码里有两个 15,含义相反

SCEFCTR_u8HE_SC_THR 也是 15,但和新需求的 15 完全不是一回事,极易看混。
旧的 15(SCEFCTR_u8HE_SC_THR新的 15(u8HE_SC_FAIL_CHK_NUM
含义15 个电源循环15 次本地检测
谁来数用户反复开关机,可能跨几天软件自己 4 秒内跑完
旧逻辑确认 3 次 → 临时锁;断电重启计数 +1;累计 15 次才永久锁
SCEFCTR_u8HE_SC_THR 保持 15 不动(datadump 的 4 位字段还用着它), 但新逻辑不再拿它判锁——改成直接比本地计数。源码注释里也写了这段警告。

6. 清零策略:改过两版(本版最关键的一处)

第一版只要「引脚正常 + 产品在加热模式」就清零计数。 当时的判断是「HE_SC 是硬件比较器引脚、开关量,不像电流功率要爬升,所以不需要延时」。 台架现象:计数一直停在 1,进不到 3。

错在哪:加热模式只说明产品被「允许」加热,不说明电流已经建立。 每次自恢复后产品回到加热模式,电流从零开始爬,爬升期间当然没有过流、引脚读正常—— 而第一版看到「加热模式 + 正常」就把计数清了,于是计数在 0 和 1 之间反复震荡,永远到不了 15。

这与 Fox03 功率检查、Fox04 SW 过流是同一个失效模式,第三次出现。 教训是:需要延时的从来不是信号本身,而是信号背后的产品状态。 判据不该问「信号是开关量还是渐变量」,而该问 「读到『正常』的这一拍,产品是否真的处于能暴露该故障的工作状态?」

第二版改成两个条件同时满足才清零:

  • 条件 ① 产品确实在加热模式(STD_HEATING_MODE
  • 条件 ② 已经连续正常 u16HE_SC_OK_TIME = 200 × 10 ms = 2 秒

配套三个细节:检出时把计时器清零(每次过流后都要重新累计满 2 秒); 离开加热模式时把计时器清零而不是冻结(每次重新加热电流都要重新爬,Fox03 就栽在留了残值); SCEFCTR_vidResetFailState() 不受这两个条件限制,引脚一正常就立即调用—— 它负责让产品退出临时锁重新加热,和「清计数」是两件事,一起延后会让产品卡在锁定里。

2 秒取自 Fox04(那里电流约 1 秒爬到位,留一倍余量)。

交付前用逐拍模拟扫过「产品恢复耗时」和「电流爬升时间」两个未知量: 第一版在电流爬升 ≥ 50 ms 时全部锁不住(检出 44~111 次,计数卡在 1); 第二版在 0 ~ 1000 ms 全区间都稳定锁定。故障真消失时仍能正常清零 (检出 1 次后修好 → 2.3 秒后计数归零),已进永久锁则不清——那是正确行为。

7. 顺带修掉一个 off-by-one

原结构在自恢复完成时ConfCtr++、判断放在下一轮开头, 于是第 N 次检出时计数还是 N-1,要等第 N+1 次检出才判到条件成立——设 3 实际第 4 次才锁。 改成检出即计数即判,第 N 次检出就是锁定的那一次。

同时不再读 NVM 上限决定 PERM 还是 TEMP,直接按本地计数判。 SCEMONI_SC_TEMP_LOCK(010)在新逻辑下不再被进入,但枚举和消费点全部保留不动——别的故障还在用。

8. IGBT 自检不受影响

自检确实和电流有关,但走的是另一条路,三层理由:

  • 自检模式函数 enuAppHwCkMode()没有 HE_SC 状态机的调用点,那段跑的是 SVCCKHW_vidTaskHwCheck()
  • 自检读的是 HDEXTADC_u16GetIlsPhVal() 相电流 ADC 值,不是 HE_SC 引脚; HDDIO_bGetScHeInput() 全工程只有两处读取(本状态机 + HDLIN 上报)
  • 进自检要求 SCEFCTR_NO_FAILURE,而序列进行中产品报着 001,根本进不去

9. 总时长:名义值,不作验收指标

客户图上标「4 s 後の最終判定」,来自 14 × 285 ms = 3.99 s。 本版实现是 14 × 300 ms = 4.2 s——每轮 = 290 ms 自恢复 + 10 ms 重新读引脚那一拍。 多出的 0.21 s 就是这两项差值 × 14。

电流爬升时间每轮15 次总时长
0 ms(真短路,瞬间过流)330 ms4.65 s
100 ms430 ms6.15 s
500 ms830 ms12.15 s

客户的 4 s 假设自恢复后立刻又过流,即爬升时间 ≈ 0。HE_SC 是短路级保护, 真短路时电流确实瞬间上去,实机应接近 4.65 s;台架若用信号注入模拟,则取决于注入设备响应。 验收看次数和行为,不看总时长(同 Fox04 的 7.5 秒)。

10. 还原基础软件遗留改动(与本需求无关)

HDLIN_Handler.c 里 LIN 的 HVCH_ID_Code 信号被改成报 HE_SC 引脚电平, 看着是为了用 LIN 分析仪直接观测那个引脚临时挂上去的。本版已还原:

还原后(正确)还原前
信号赋值kpstrSignalsStatusData->u8ID_Code_Stat2HDDIO_bGetScHeInput()
include多一行 #include "HDDIO_Handler.h"

已核实它不影响任何逻辑HDDIO_bGetScHeInput() 只调 bReadInputPin(), 纯读 GPIO,不清硬件锁存、无副作用。2026-08-17「计数卡在 1」的现象与它无关,那是清零策略的问题。 删 include 安全:该文件里 HDDIO_ 只有这两处引用。

2026-08-18 补充:还原得更彻底了,并挖出一个行尾符问题。 拿到真正的原厂纯净版之后逐字节对比,发现除了上面两处内容改动, HDLIN_Handler.c 还曾在 Unix 环境下被编辑过,行尾符从 CRLF 变成了 LF—— 它是全工程 58 个源文件里唯一一个不一致的。
纯净原厂版之前当基线的那份
信号赋值u8ID_Code_Stat2HDDIO_bGetScHeInput()
includeHDDIO_Handler.h
行尾符CRLF(与全工程一致)LF(唯一的例外)
行尾符这项不修的话,任何以原厂版为基准的 diff 都会把整个文件 1072 行报成全文替换—— 第一次比对出来 2149 行,其中真正的差异只有 6 行。现已转回 CRLF, 该文件与原厂逐字节一致,在 7DA10021 的全量 diff 里完全消失

11. 台架验证记录(2026-08-18)

  • 第一版(只有加热模式守卫):计数一直停在 1,进不到 3 —— 不通过
  • 第二版 3 次测试版(模式守卫 + 连续正常 2 秒):计数正常累加,第 3 次进永久锁 —— 通过
  • 15 次正式版(只改 u8HE_SC_FAIL_CHK_NUM 一个宏):通过

先用 3 次验证序列逻辑是刻意安排的——每次检出都是一次真实过流、都在冲击 IGBT, 所以在逻辑没验证过之前把次数压到最低,通过后再上规格要求的 15 次。

📦

交付版本 · 7DA10021 总包(五个故障)

7DA10021 = Fox01 ~ Fox05 五个故障的合并交付版,全部台架验证通过。 基线是真正的原厂纯净版,全量 diff 2183 行 / 11 个文件,全部是故障改动

1. 五个故障

#故障需求类型台架通过
1outlet 温度传感器 OC/SCCondition A2026-08-10
2NTC/CMOS 温度不一致Condition A2026-08-11
3功率不一致Condition A2026-08-14
4IAV 过流(SW 检测)2-2-2Condition C2026-08-17
5HE_SC 过流(HW 检测)2-2-1Condition C2026-08-18

2. 版本号怎么升

Common/Include/COMMON_Versions.h 里四个字节拼成软件版本号,只改 PATCH 一个

对应
COMMON_u8TYPE_SW_VERSION0x7D7D
COMMON_u8MAJOR_SW_VERSION0xA1A1
COMMON_u8MINOR_SW_VERSION0x0000
COMMON_u8PATCH_SW_VERSION0x2121 ← 只改这个

该版本号经 LIN 的 READ_SW_VERSION(0x9A) 与 datadump 帧上报,烧录后用诊断仪读一下即可确认版本。 后续 0x22 → 7DA10022、0x23 → 7DA10023。

3. ⚠️ diff 基准换成了真正的原厂纯净版

最初当基线的那份 7DA10020_src 不是纯净的原厂版,它带着一处人为改动 (LIN 的 HVCH_ID_Code 被挪去报 HE_SC 引脚电平)和一个行尾符问题 (HDLIN_Handler.c 是 LF,全工程其余 57 个源文件都是 CRLF)。

2026-08-18 拿到真正的原厂版后,两者对比忽略行尾符实际只差 6 行,就是那两处。 换基准并把行尾符转回 CRLF 之后:

旧基准新基准(纯净原厂版)
diff 行数22032183
涉及文件12 个11 个
HDLIN夹在 diff 里完全消失
改动性质混着一处与故障无关的还原全部是故障改动

4. 改动分布

文件+涉及故障
SCEMONI/Src/SCEMONI_Services.c549286#1 #4 #5
SCEDRIV/Src/SCEDRIV_Services.c19353#3
SCEMONI/Src/SCEMONIS_Services.c132128#2
SCEMONI/Include/SCEMONI_Services.h302#4
SCEFCTR/Src/SCEFCTR_Services.c281#3 #4
SCEMONI/Include/SCEMONIS_Services.h260#2
SCECOM/Src/SCECOM_Services.c231#3 #4
SCEDRIV/Include/SCEDRIV_Services.h231#3
APMODMGR/Src/APMODMGR_Applicative.c210#4
SCEMONI/Include/SCEMONIS_ExtConfiguration.h203#2
Common/Include/COMMON_Versions.h11版本号
合计1046476

5. 后续版本规划

7DA10021(本版,五个故障,冻结)
    ├─ 7DA10022   剩余两个故障用 Condition A
    └─ 7DA10023   剩余两个故障用 Condition C

22 与 23 是并行分支、内容互斥,均以本版为基线,等客户拍板采用哪套方案。 两者的 diff 都以 7DA10021 为基准,便于直接对比两种方案的差异。

6. 一条贯穿五个故障的经验

诊断计数的清零条件,别问「信号是开关量还是渐变量」,要问:

读到「正常」的这一拍,产品是否真的处于能暴露该故障的工作状态?

只要产品刚从锁定/停机恢复,答案就是否——负载还没建立,这时读到的「正常」不携带任何故障信息。 拿它清零,计数就永远在 0/1 之间震荡,锁不住也报不上去。

这个坑在 #3、#4、#5 上各踩了一次(每次都是台架才暴露)。 最终写法是双条件:产品确实在工作模式(模式守卫)已连续正常 N 秒(计时器)。 单靠任何一个都不够;计时器还必须在离开工作模式时清零而不是冻结,否则残值会让修复失效。

🌡️

7DA10022 · 安全传感器 NTC/CMOS 开短路(Condition A)

四个故障一起改:NTC_SC、NTC_OC、CMOS_SC、CMOS_OC。 以 7DA10021 为基线,diff 1081 行 / 3 个文件。 2026-08-19 台架测出一个问题,方案已定但尚未实施——见第 5 节。

1. 先厘清一件事:它不是 LIN 上那个 out_of_range

LIN 信号 LIN_Safety_Temp_Out_Of_Range 报的是 NTC/CMOS 过温, 驱动源头是 enuGetSafetyTempOutOfRngState(),只读 NtcOtState / CmosOtState 两个过温状态,开短路那四个状态根本没进这个函数。

开短路走的是 datadump 诊断帧,四个 bit 各占一位(D2 b0 = NTC OC,D3 有 CMOS OC/SC 等)。 所以:过温走 LIN 周期信号,开短路走诊断帧,两套完全分离。本节改的是后者。

2. 与 Fox01(outlet)的异同

Fox01 outlet本次 安全传感器
确认时间宏250(uint8)250(uint8)× 4 个
步进公式Ceil(65535×周期, 确认时间)同构
检测周期4 ms1 ms
双变量校验——每个变量带 Safe 影子,不一致直接硬复位
分支数2(SC/OC)4(NTC/CMOS × SC/OC)

Safe 影子是本次独有的负担:16 处步进计算全部成对存在,每改一处都要同步改影子, 漏一个就是运行时硬复位。

3. 确认时间:250 ms → 4000 ms

沿用 Fox01 的分辨率因子:标定字节仍是 uint8、值仍是 250, 单位从 1 ms 改成 16 ms,只在步进公式分母乘因子。

#define u16SFTY_TIME_RES_FACTOR    ((uint16)16)

步进 = ceil(65535 / (250×16)) = ceil(16.38) = 17
次数 = ceil(65535 / 17) = 3856 次 × 1 ms = 3856 ms
实际 3856 ms,不是 4000 ms(−3.6%)。 65535 除不尽,步进只能取整数,16 与 17 之间没有中间值: 步进 17 → 3856 ms,步进 16 → 4096 ms(超 4 秒)。 规格给的是 max confirmation time 4 s,宁短勿超,故取 3856。 Fox01 同样原因落在 3972 ms。

4. NG 计数:四个故障共享一个

客户要求:四路独立检测、独立存储,但 NG 次数合起来算。 即 NTC_SC 一次 → counter=1;CMOS_OC 再一次 → counter=2;第三次仍是 2。

做法照搬 Fox01,只是从两路扩到四路——物理计数器各存各的(诊断帧四个 bit 照常独立显示), 只在判定时取和

uint8 SCEMONIS_u8GetSftySensorNgCntVal(void)
{
    return NTC_SC_RESET_CNTR + NTC_OC_RESET_CNTR
         + CMOS_SC_RESET_CNTR + CMOS_OC_RESET_CNTR;
}
改的项原厂改成
计数时机上电时 +1(HW 复位计数)确认 NG 时 +1
上限阈值 3 → 永久锁封顶 2,无永久锁
严重度只有 TEMPORARY / PERMENANTNG<2 → MINOR(001),NG≥2 → TEMPORARY(010)
ResetFlag四套完整机制全删(声明/初始化/置位/上电累加)

u8CLASS_A_MAX_RESET_COUNT = 15 改后已无引用, 与 Fox01 一致地保留(原厂通用命名宏,删了反而给客户 diff 添无关噪音),两个文件均已加注释说明。

5. ⚠️ 2026-08-19 台架发现:NG 计数被过早清零(待改)

现象(舟舟实测):
1. 触发 NTC_SC → 计数 1,ECU_State = 1     ✓
2. 下电,修复样件
3. 触发 NTC_OC,上电
4. NTC_OC 计数 = 1,NTC_SC 计数 = 0
   → 总数仍是 1,ECU_State = 1   ✗ 应为 2

根因是时序,不是漏加守卫。四路按固定顺序跑,SC 排在 OC 前面:

上电第 1 拍  SC 先跑 → 读数正常 → 查「别的分支有事吗」
                     → OC 此刻尚未开始检测,看着是干净的
                     → 清零自己那格   ← NG 就在这一拍丢的
上电第 1 拍  OC 后跑 → 读数异常 → 进入确认计时
约 4 秒后    OC 确认故障 → 计数 1(但 SC 那个 1 早没了)
客户视角的正确语义(舟舟原话):SC 和 OC 同属 outlet 这一个故障, SC 报了一次时 OC 还没测完,整体结论还没出来,这个 1 就不该清。 等 OC 也报一次,1+1=2,才算该故障坏了两次。

⚠️ Fox01(outlet)有完全相同的行为——它的清零条件是「SC 恢复 + OC 那路健康」, 照同样步骤测 outlet 会得到一模一样的结果。这是从 Fox01 一路带下来的设计,不是本版新引入的。

同一个坑第三次出现。Fox03(功率)、Fox05(HE_SC)都栽在 「上电头几拍读到正常,不代表真的正常」——那时产品还没进入能暴露故障的状态。 当时的解法都是加时间守卫:连续正常 N 秒才算数。这次只是换了个位置冒出来。 参见本站「渐变量清零陷阱」相关章节。

同一 bug 的第二种表现:上电瞬间被清零(更隐蔽)

现象(舟舟实测,第二例):
1. 触发 NTC_SC → NTC_SC = 1
2. 上下电
3. 同时触发 NTC_SC 与 CMOS_OC
4. 实测:NTC_SC = 1,CMOS_OC = 1(总数 2,ECU_State 2)

上下电之后 NTC_SC 是第二次确认,应该累加到 2。实测却仍是 1, 而 CMOS_OC 反倒记上了 1。逐拍推演两种假设,实测数据只与「有 bug」一组吻合:

步骤无 bug(正确)有 bug(实际)
首次 NTC_SCNTC_SC=1NTC_SC=1
上电头几拍读到「正常」→ NTC_SC 清零
NTC_SC 确认NTC_SC=2,总数 2NTC_SC=1,总数 1
CMOS_OC 确认总数已封顶 → CMOS_OC=0CMOS_OC=1,总数 2
结果NTC_SC=2 CMOS_OC=0NTC_SC=1 CMOS_OC=1 ← 实测

根子与第一例相同——「读到一次正常就清零」,不区分那次正常是否真实。 区别只在触发时机:第一例是修复样件后,本例是上电信号尚未建立时 (样件刚通电、采样未稳,软件读到的「正常」是假的)。

⚠️ 本例比第一例隐蔽:总数与 ECU_State 都是 2,看起来完全正常。 错的是分配——NTC_SC 实际坏了两次却记成一次。 只看 ECU_State 无法发现,必须查诊断帧里每一路的计数。

已定方案对两种表现同时有效:上电头几拍那个「正常」撑不满 5 秒,不会触发清零。

衍生问题:封顶导致后来的分支记不上(待向客户确认)

即使修好清零问题,本例的正确结果是 NTC_SC=2、CMOS_OC=0—— CMOS_OC 确实坏过一次,却记成 0,因为总数封顶 2 之后不再往任何一路累加。

若诊断帧那四个数是给售后看「哪一路坏过几次」的,这个封顶会让后发生的故障记不上。 可选方案:各路计数照实记录,只把 ECU_State 封顶在 2。此项尚未向客户确认。

已定方案(尚未实施,2026-08-19 舟舟决定先继续测试)

清零需同时满足两条:

  1. 四路当前全部正常——含当前状态与 NVM 故障标记两个条件。 状态覆盖本次上电周期内正在进行的诊断,NVM 标记覆盖上电之前已确认的故障 (状态机启动时不恢复历史状态,正是本场景的关键)。
    守卫函数 bAreOtherSftyBranchesClean() 已写好,尚未随版本发布。
  2. 该状态持续超过最长确认时间——建议 5 秒(4 秒确认 + 1 秒余量), 确保每一路都真正测完一轮。

改后该场景变为:

上电 → SC 读正常但先不清(等待期内)
     → 4 秒内 OC 确认故障 → 计数 1
     → SC 见 OC 有事,不清零,自己那个 1 保住
     → 总数 = 1 + 1 = 2  →  ECU_State = 2  ✓

实施时 Fox01(outlet)必须同步改——同为 Condition A, 两个故障不能一个改一个不改,否则客户复测又是一轮。

仍待确定:最终清零规则。按上述改法,四路全好且稳住 5 秒后仍会清零归 0。 若客户要的是「坏过就一直记着」,则需另一种改法(如仅诊断仪清码时归零、或换件后归零)。 此问题尚未向客户确认。

2026-08-19 已实施:封顶位置从「计数」移到「State 判定」

原写法是判断用总数、累加却是自己那格,总数一满 2,后发生的分支自己那格也停在 0:

if (四路总数 < 2)      ← 看总数
    该路计数 + 1;      ← 加自己那格
步骤改前改后
NTC_SC 第 1 次NTC_SC=1 总1 State1NTC_SC=1 总1 State1
上下电,NTC_SC 第 2 次NTC_SC=2 总2 State2NTC_SC=2 总2 State2
CMOS_OC 再坏一次CMOS_OC=0CMOS_OC=1
ECU_State22(不变)

危害在售后:诊断帧读出 CMOS_OC=0 会被判断成「CMOS 没坏过」, 可能只换 NTC 而漏掉 CMOS 侧问题;且总数封顶后诊断帧从此定格,之后哪路坏都记 0。

改法:各路计数照实累加(SCEFCTR_vidIncFailCntr() 自带溢出保护), 封顶只留在严重度判定处。ECU_State 行为完全不变,仍封顶 010。

关于 min 的说明min(总数, 2)把总数截断在 2—— 总数 3、4、10 一律 State 2,只会封在 2,不会退回 1。 代码实现是 if (总数 < 2) 报 001 else 报 010,效果等价。

清零语义:A(现行)与 B(备选)

2026-08-19 舟舟决定:先按 A 做,不改。B 记录在此备查,以防客户后续要求变更。
A · 单路消失即清自己那格(现行)B · 四路全消失才清
行为某路读数恢复正常,立即清零自己的 NG需其余三路当前状态正常 NVM 无故障标记
已知副作用本节前述两例(修复后清零、上电瞬间清零)仍然存在两例均可解
实现原厂逻辑,无需改动守卫函数 bAreOtherSftyBranchesClean()(已写好未发布) + 5 秒时间保护(4 秒确认 + 1 秒余量), 否则上电头几拍的假「正常」仍会触发清零

选 B 时 Fox01(outlet)必须同步改——它的清零条件同为「SC 恢复 + OC 那路健康」, 不同步会导致两个 Condition A 故障行为不一致。

待客户确认:两路同时故障是否直接跳 010

首次上电即 NTC 与 CMOS 同时故障时,两路各 +1,总数直接到 2:

NTC_SC  确认 → 总数 0→1  ECU_State=1
CMOS_SC 确认 → 总数 1→2  ECU_State=2

001 中间态确实存在但仅几毫秒——两路同属一轮 4 秒确认、几乎同时结束, LIN 上大概率采不到,表现为「第一次检测就直接 010」。

规格未覆盖此场景:截图那张图画的是时间轴上 1NG → HW reset → 2NG 依次发生, 未考虑同时故障。两种读法均说得通:

算 2 次(现行)算 1 次
依据确实存在两个故障NG = 一轮诊断不合格,不论几路
结果首次上电直接 010首次 001,第二次才 010
改动无需改需增加「本轮是否已计数」标记

建议维持现行:NTC 与 CMOS 是两个独立传感器,同时故障多半是共因(线束、供电、地线), 比单路故障更严重,安全件宜宁重勿轻;且改为「同轮算一次」需额外状态跟踪, 并会把两个真实故障记成一次,诊断帧上亦难解释。 但须客户点头——若客户按「第一次应报 001」验收,一上来看到 010 会判不通过。

6. 交付与验证

基线7DA10021
diff1081 行 / 3 个文件
净代码变化+118 / −260(净减 142 行,其余为注释与空行)
版本号COMMON_u8PATCH_SW_VERSION = 0x22
行尾符CRLF,全工程 58/58 统一
台架状态测试中——已发现第 5 节的问题

删得比加得多,因为本次主体是删逻辑:四路各删「60 秒累积 + 永久锁判定」, 加上四套 ResetFlag(OneSecCntr 32 行、ResetFlag 20 行、永久锁 8 行、复位计数递增 4 行, 再计入配套的双变量校验项与变量声明)。

⚠️

待处理 · 历史遗留问题

两条都是基础软件的既有问题,与 Condition A 改动无关。 2026-08-10 决定:暂不处理,后面有时间再回来

9.1 Coolant_temp_out_of_range 只判上限,低温侧缺失

现象:outlet OC(断路,读数看起来温度极低)时 LIN_Coolant_temp_out_of_range 不置起;SC(短路,读数看起来温度极高)时会置起。

原因:SCECOM_Services.c:10783enuGetCoolantTempOutOfRngState() 唯一输入是 SCEMONI_enuGetToutOverTempState(),即 outlet 过温状态; OT 状态机(SCEMONI_Services.c:2603)的判据只有单向的 u16ToutMeasure >= SCEMONI_u16ToutOverThr,全程没有下限比较。 信号名叫 out_of_range,实现上只是过温警告。

版本对比:该函数在 7DA10011 与 7DA10020 中逐字节相同, 非本次改动引入,也非 20 版本引入,基础软件一直如此。

待办:对 LIN 规格书确认客户对该信号的定义。 若为双向(高于上限或低于下限都要置),则低温那一半从 11 版起就缺失,需补下限判断或新增低温超范围状态机; 但下限阈值、确认时间、迟滞需客户给定。若客户定义就是过温警告,则行为无误,改文档即可。

9.2 寿命计数(NVM Fail Life Time Counter)偶发被清

现象(2026-08-10 台架):故障消失后,寿命计数 (0x24 帧的 NVM_Outlet_Coolant_Temp_OC_Cnt / SC_Cnt有时被清除、有时没有,不稳定。判定为之前就存在的问题。

已知事实:全代码搜索 HDNVM_u8OUTLET_TEMP_SC_CNT / OC_CNT 只有三类引用——SCEMONI_Services.c:3353 / 3560vidIncFailLifeTimeCntr 递增, SCECOM_Services.c:8279 / 8282 的读取。没有任何一处清零。 所以按代码逻辑它只增不减,看到被清就是异常

排查时别混:Rst_cnt(0x1B 帧)清零是设计行为(要求 12), 且只可能发生在断电重上之后——状态机进 FAILURE 后本上电周期无出口, 清零代码写在 NO_FAILURE 分支里,不断电根本执行不到。这一条与 9.2 不是一回事。

待查方向(尚未验证,仅为线索):

  1. NVM / EEPROM 模拟层的页写入、擦除或掉电保护逻辑,是否在某些时序下整页回默认
  2. 写入时机与断电的竞争:是否存在计数刚递增、页还没落盘就断电的窗口
  3. 读回路径:0x24 帧的字节偏移与 CAPL 解析是否始终一致(脚本 1428、1429 行)
  4. 先记录「清了 / 没清」两种情况下的断电时机与故障持续时长,找出规律再看代码
🔗

代码 ↔ 硬件对照表

代码信号名原理图网络MCU引脚功能原理图页
CMD_HS1CMD_HS1PTD15高侧IGBT使能Page 5, 16
CMD_LS1CMD_LS1低侧IGBT1控制Page 2
CMD_LS2CMD_LS2低侧IGBT2控制Page 2
FLY_ENFLY_ENPTD3Flyback电源使能Page 5, 6
VSUP_ENVSUP_ENPTD3电源使能Page 5
WNDWNDPTB1看门狗喂狗Page 5, 8
T_OUTLETT_OUTLETADC ch出口水温Page 4, 5
T_LS_NTCT_LS_NTCADC chNTC安全温度Page 4, 5
T_LS_CMOST_LS_CMOSADC chCMOS安全温度Page 4, 5
LV_MEASURELV_MEASUREADC ch12V电源电压Page 3, 5
DATA_EXT_ADCDATA_EXT_ADCPTA2外部ADC I2C数据Page 5
CLK_EXT_ADCCLK_EXT_ADCPTA3外部ADC I2C时钟Page 5
TXD_SDITXD_SDISBC SPI发送Page 8
LINLINLIN总线Page 8
SHORT_CIRCUITSHORT_CIRCUITPTB0短路检测Page 5
STBSTBSBC待机控制Page 9
📖

缩写速查表

缩写全称中文
HVCHHigh Voltage Coolant Heater高压冷却液加热器
IGBTInsulated Gate Bipolar Transistor绝缘栅双极晶体管(功率开关)
HEHeating Element加热元件
SBCSystem Basis Chip系统基础芯片
MCUMicro Controller Unit微控制器
LINLocal Interconnect Network本地互联网络(车载通信协议)
ADCAnalog to Digital Converter模数转换器
NVMNon-Volatile Memory非易失存储(EEPROM)
PWMPulse Width Modulation脉宽调制
DCDCDC-DC Converter直流变换器
NTCNegative Temperature Coefficient负温度系数热敏电阻
CMOSCMOS温度传感器(ES0003)
OCOpen Circuit / Over Current开路(HE_OC)/ 过流(视上下文)
SCShort Circuit短路
LCMLife Cycle Management生命周期管理
WDGWatchDog看门狗
OSRTEOS Real Time Environment实时环境调度器
HD*Handler硬件抽象层模块
SCE*Service服务层模块
AP*Applicative应用层模块
vidvoid函数名中表示无返回值
bboolean函数名前缀表示返回布尔值
enuenum函数名前缀表示返回枚举值
u8/u16/u32unsigned 8/16/32 bit无符号整型
📋

原理图页码索引

页码内容关联代码模块
1变更历史 + 项目信息
2系统总览框图(信号流)全部
3DM_FILTER 输入滤波 + LV_MEASUREHDADC
4温度传感器 (OUTLET / CMOS / NTC)SCEMONI, SCEMONIS
5MCU S32K144 引脚分配全部
6Flyback隔离电源 + 5V_HV_LSHDDIO (FLY_EN)
7IGBT Gate Driver (UCC5350)HDDIO (CMD_HS1), SCEDRIV
8SBC TJA1128E + LINHDSBC, HDLIN, SCECOM
9WSTB待机 + STB控制SCEMONI (Standby)
10-14其他外围电路
15-16过流保护 + IGBT使能逻辑 (LM2903)SCEMONI (OC/SC), HDDIO