乐于分享
好东西不私藏

FoxBMS2 源码分析 (5) | 硬件驱动:AFE 测量链与 Interlock 反馈

FoxBMS2 源码分析 (5) | 硬件驱动:AFE 测量链与 Interlock 反馈

1. 前言

到了驱动层,FoxBMS2 的风格反而更鲜明了。它不是把一堆底层寄存器读写函数裸露给上层,而是试图在驱动层就完成一部分整理工作:
  • 原始测量值先做有效性筛选。
  • 开线、量程、反馈状态先在驱动层转成标准化数据。
  • 上层只获取数据库中的统一数据块,而不是直接碰芯片寄存器。
这一篇聚焦两条最关键的底层链路:
  1. AFE 测量链,以 meas.c 和 ltc_6813-1.c 为主。
  2. Interlock 反馈链,以 interlock.c 为主。

1.1 先用一张图把数据路径看清

这张图对应当前实现的两个关键点:
  • MEAS_Control() 在当前源码里只是 AFE_TriggerIc() 的统一入口。
  • 上层长期调用的主测量块,不是 AFE 刚读到的底层测量结果,而是再经过 MRC_ValidateAfeMeasurement() 处理后的主数据块。

2.meas.c 的角色:测量框架入口

2.1 MEAS_Initialize()

MEAS_Initialize() {    return AFE_Initialize();}
meas 模块在这里承担的主要是统一入口职责。

2.2MEAS_Control() 更像调度点

在 1ms 周期任务里,FTSK_RunUserCodeCyclic1ms() 会调用:
MEAS_Control();
当前实现里它实际只做一件事:
MEAS_Control() {    AFE_TriggerIc();}
AFE 一次完整测量通常会分成多步:
  • 发起转换
  • 等待 ADC 完成
  • 读取寄存器
  • 校验 PEC
  • 结果进数据库
它是多步过程,不适合塞进单次同步函数里。

3. LTC6813 驱动

src/app/driver/afe/ltc/6813-1/ltc_6813-1.c 的初始化阶段很值得仔细看,因为它一开始就在做数据库准备,而不是马上采样。

3.1 初始化时就写入多类数据块

驱动初始化时会显式构造并写入多份数据块,包括:
  • cellVoltages
  • cellTemperatures
  • balancingFeedback
  • openWire
  • balancingControl
  • slaveControl
这说明驱动初始化的目标之一,是让系统尽快拥有一份结构完整、值可能尚未更新但类型正确的数据。
这和前面数据库初始化清零一起构成了一套很稳的结构:
  • 数据块一定存在。
  • 只是其中的数值是否已经被真实测量刷新,要由时间戳和状态位去判断。

3.2 这里更准确地说,是先建结构,再等后续刷新

因此,这一阶段的理解应该是:
  • 数据结构已经就位;
  • 后续状态机可以在固定位置持续更新;
  • 上层不需要等对象创建,只需要判断值是否已经有效和新鲜。

3.3 这也解释了为什么上层很少直接判断指针是否为空

在 FoxBMS 里,上层处理测量数据时通常更关心:
  • 数值是否有效
  • 数据是否更新过
  • 某个通道是否被标成开线或无效
而不是这个对象在不在,因为对象一般在初始化阶段就已经被放进数据库了。

4.LTC_SaveVoltages()真正的测量链

LTC_SaveVoltages() 是整条 AFE 电压链最关键的函数之一,它做的远不止把 ADC 结果写进数组。

4.1 先对每个电芯值逐路处理

函数会遍历每个 string、每个 cell,并结合以下条件处理:
  • 当前 open-wire 标记
  • 电压是否落在 AFE 允许范围
  • 当前通道是否应计为 valid
只有满足条件的电压,才会:
  • 被计入 nrValidCellVoltages
  • 被加进 string total voltage
这意味着数据库里的单体电压和总串电压已经不是原始值,而是驱动层过滤后的结果。

4.2 openWire会直接影响哪些电压

这点很重要,FoxBMS 并不是把开线检测单独放一边,而是在 LTC_SaveVoltages() 落库时就把 open-wire 结果纳入判断。
这样做的好处很直接:
  • 上层读到的 string 电压不会轻易被无效通道污染。
  • 有效 cell 数量本身就成了状态信息。

4.3 这里还隐含了一层局部融合

从职责上讲,驱动通常只负责单点数据采集。但 LTC_SaveVoltages() 已经在做局部融合了:
  • 单体电压到 string 总压
  • 单点有效性到 string 级有效计数
这条链里最值得记住的是:LTC 先把底层测量块写下去,MRC 再决定哪些结果进入上层主消费块。

5.LTC_SaveTemperatures()温度链

温度落库路径与电压类似,也会先做有效范围判断,再写数据库。但温度链有一个重要区别:
  • 电压链经常直接参与预充、接触器控制、过压欠压等关键路径。
  • 温度链更多参与运行期保护、均衡允许条件和热状态判定。
FoxBMS 在实现上没有刻意把这两条链割裂成不同框架,而是尽量保持了相似的读取 -> 校验 -> 落库模式。这让代码的可维护性更高。

6.LTC_GetBalancingControlValues()均衡

均衡控制在 FoxBMS 里是一个典型的策略层和执行层分离案例。

6.1 算法模块不直接改芯片寄存器

均衡算法模块会把希望打开的均衡位写到数据库里的 balancing control 数据块中,而不会直接操作 AFE 寄存器。随后,AFE 驱动通过:
LTC_GetBalancingControlValues()
把这些控制位读出来,再转换成具体的芯片寄存器写入。

6.2 这层分离非常好

它带来两个直接收益:
  • 算法层只表达要均衡哪些 cell,不关心底层硬件位布局。
  • 如果以后 AFE 芯片换型,均衡策略层几乎不需要改,只要驱动把数据库命令重新映射到新芯片上即可。

7.LTC_SaveBalancingFeedback()执行反馈单独成链

FoxBMS 没有把均衡命令和均衡执行反馈混为一谈。驱动会单独把 balancing feedback 写入数据库。
这意味着上层理论上可以比较三件事:
  • 算法想让哪些 cell 均衡。
  • 驱动实际给硬件下发了什么。
  • 硬件最终反馈了什么状态。
对于诊断和一致性分析来说,这种分离非常有价值。因为一旦出现命令了但没执行或者执行了但反馈异常,问题边界会比只有一个控制位清晰得多。

8. Interlock:FoxBMS 的高压安全链

8.1 ILCK_GetInterlockFeedback() 同时读数字量和模拟反馈

interlock.c 里,Interlock 反馈不是单纯读取一个 GPIO 完事,函数会结合:
  • 互锁输入
  • ADC 高侧反馈
  • ADC 低侧反馈
最后把结果写入 DATA_BLOCK_INTERLOCK_FEEDBACK
这说明 Interlock 模块的职责是把多种物理反馈整理成可被系统状态机调用的安全状态对象。

8.2 为什么要数字量和模拟量同时看

如果只看互锁输入,你能知道的是回路似乎连着。但在高压系统里,还会关心:
  • 接触器反馈是否真的对应命令状态。
  • 是否存在焊死、粘连、反馈线异常。
  • 高侧和低侧回路的实际反馈是否一致。
因此 Interlock 更像高压路径反馈整形模块,而不是一个单一传感器驱动。

8.3 Interlock 数据为什么也要写数据库

原因和 CAN 请求、AFE 测量一样:
  • 状态机不应直接依赖底层驱动函数。
  • Interlock 结果要在 10ms 主控制路径中被稳定使用。
  • 诊断和记录模块也可能需要读同一份反馈。
数据库让这些消费者共享的是一份统一的反馈数据,而不是各自去重复读硬件。

8.4 Interlock 反馈链图

9. 局部关键数据块索引

数据块主要生产者主要消费者更新节拍本篇用途
DATA_BLOCK_CELL_VOLTAGE_BASEDATA_BLOCK_CELL_VOLTAGE_REDUNDANCY0LTC AFE 驱动读取完成后的保存路径MRC_ValidateAfeMeasurement()1ms(AFE 状态机推进、测量完成后写入)承载底层电压测量与有效位
DATA_BLOCK_CELL_TEMPERATURE_BASEDATA_BLOCK_CELL_TEMPERATURE_REDUNDANCY0LTC AFE 驱动读取完成后的保存路径MRC_ValidateAfeMeasurement()1ms(AFE 状态机推进、测量完成后写入)承载底层温度测量与有效位
DATA_BLOCK_CELL_VOLTAGEMRC_ValidateAfeMeasurement()BMS_GetMeasurementValues()、均衡策略、SOA、SOC 相关模块10ms 任务中每 50ms 校验后更新给上层提供主消费电压块
DATA_BLOCK_CELL_TEMPERATUREMRC_ValidateAfeMeasurement()SOA、均衡允许条件等温度消费者与 AFE 校验同节拍给上层提供主消费温度块
DATA_BLOCK_MIN_MAXMRC_ValidateAfeMeasurement()BMS_GetMeasurementValues()BAL_ActivateBalancing()、SOC/SOE/SOF 相关模块与 AFE 校验同节拍汇总最值,避免上层重复扫描全量通道
DATA_BLOCK_BALANCING_CONTROL均衡策略模块LTC_GetBalancingControlValues()按策略更新把“要均衡哪些 cell”从策略层传给驱动执行层
DATA_BLOCK_BALANCING_FEEDBACK_*LTC_SaveBalancingFeedback()诊断、状态发送等后续消费者balancing feedback 读回后更新区分“要求均衡”和“硬件反馈均衡”
DATA_BLOCK_INTERLOCK_FEEDBACKILCK_GetInterlockFeedback()DIAG / 互锁相关上层逻辑10ms 周期保存 pin + ADC 组合后的互锁反馈事实

10. 这一篇真正要建立的认识

FoxBMS 驱动层的价值是把一部分原始硬件世界翻译成了系统世界:
  • 电压不是原始 ADC 结果,而是带 open-wire 与有效性判定的电芯和电池串数据。
  • 均衡不是算法直连硬件,而是数据库命令经过驱动执行。
  • Interlock 不是一个引脚,而是一组反馈被整形成了统一状态。
这也是为什么上层状态机和算法模块可以写得相对干净,因为很多硬件噪声在驱动层就被处理掉了。

小结

这一篇沿着驱动数据路径看清了两件事:
  • meas.c是测量框架入口,AFE 驱动才是电压、温度、开线、均衡反馈落库的真正实现者。
  • Interlock 模块通过数字量和模拟反馈的组合,向系统提供高压路径的情况。
下一篇我们继续顺着数据库往上走,看看 SOC 初始化、SOC 积分更新、OCV 查表,以及均衡策略状态机到底怎样处理这些数据。

推荐阅读顺序

  • 先看MEAS_Initialize() 和MEAS_Control(),确认meas 模块只是框架入口而不是具体芯片实现。
  • 再看LTC_InitializeDatabase(),理解驱动初始化时为什么先建立数据库初始镜像。
  • 接着读LTC_SaveVoltages() 和LTC_SaveTemperatures(),把 AFE 原始测量如何落到 base / redundancy 数据块看清楚。
  • 再读MRC_ValidateAfeMeasurement(),并补看LTC_GetBalancingControlValues() 与LTC_SaveBalancingFeedback(),确认哪些块才是上层真正消费的主测量块,以及均衡命令和反馈如何通过数据库闭环。
  • 最后读ILCK_Trigger()ILCK_GetInterlockFeedback() 和DIAG_ErrorInterlock(),把高压互锁反馈到诊断的链补完整。