2026-09-16 完成、当时未提交,这里补提交(原样,不含 09-27 上板的修改)。 - 器件层 / 拓扑层整体从 R1 换到 R2:TCA9548A 分通道、五路同址 INA226 0x40 + 输入侧 0x43、 刀片 PCA9555(VBUS_EN 挪到 P0.4)、CH224A 只读驱动、风扇闭环 t_thermal - 新增 i2cbus(mux 原子事务)、slot(刀片 I/O 门面)、host/qtest.sh - 74HC595 链退役(代码与测试保留) - 判据阈值与时间常量未动;19 个 ctest 全绿,idf.py build 通过 Co-Authored-By: Claude Opus 5.5 <[email protected]>
137 行
6.3 KiB
C
137 行
6.3 KiB
C
/* INA226 —— 板上 6 片(CH1~CH5 输出各一片 + 输入侧一片),分流电阻全部 5 mΩ / 1%。
|
||
地址表见设计文档 §8.2,**不是顺排的**,详见下面 ina226_rail_addr() 的警告。
|
||
|
||
本驱动只通过 hal_i2c_* 访问总线,宿主机上用 sim 的假器件即可全部测完。 */
|
||
#ifndef INA226_H
|
||
#define INA226_H
|
||
|
||
#include <stdbool.h>
|
||
#include <stdint.h>
|
||
|
||
#include "hal_err.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* ---- 寄存器(全部 16 位、大端) ---------------------------------------- */
|
||
|
||
#define INA226_REG_CONFIG 0x00
|
||
#define INA226_REG_SHUNT_V 0x01
|
||
#define INA226_REG_BUS_V 0x02
|
||
#define INA226_REG_POWER 0x03
|
||
#define INA226_REG_CURRENT 0x04
|
||
#define INA226_REG_CALIB 0x05
|
||
#define INA226_REG_MASK_EN 0x06
|
||
#define INA226_REG_ALERT_L 0x07
|
||
#define INA226_REG_MFG_ID 0xFE
|
||
#define INA226_REG_DIE_ID 0xFF
|
||
|
||
#define INA226_MFG_ID 0x5449 /* 'TI' */
|
||
#define INA226_DIE_ID 0x2260
|
||
|
||
/* ---- 量程与标定(五路与输入侧同一颗分流电阻,所以是常量不是参数) ------ */
|
||
|
||
#define INA226_SHUNT_UOHM 5000 /* 5 mΩ,设计文档 §8.2 */
|
||
#define INA226_CURRENT_LSB_UA 500 /* 0.5 mA —— 分流 LSB 2.5 µV ÷ 5 mΩ */
|
||
|
||
/* CAL = 0.00512 / (Current_LSB × R_shunt) = 0.00512 / (500 µA × 5 mΩ) = 2048 */
|
||
#define INA226_CALIB 2048
|
||
|
||
/* Config:保留位 100 + AVG=4 + VBUSCT=1.1 ms + VSHCT=1.1 ms + 连续测量分流与母线。
|
||
转换周期 4 × (1.1 + 1.1) = 8.8 ms < t_safety 的 20 ms,所以每次巡检都拿到新的一帧。 */
|
||
#define INA226_CONFIG 0x4327
|
||
|
||
/* 上面那个 Config 折算出来的**一帧转换时间**:AVG=4 × (1.1 ms + 1.1 ms) = 8.8 ms。
|
||
`t_safety` 的周期必须长于它,否则同一帧会被读两次 —— 现象是「电流特别稳」,
|
||
不是任何一种错误。tasks.c 的表自检拿它和 20 ms 对一次。 */
|
||
#define INA226_CONV_US 8800
|
||
|
||
/* ---- 地址表(**R2**:五片同址 + 输入侧一片,计划 §5.2) -----------------
|
||
|
||
⚠️⚠️ **R1 的「六片六个地址」在 R2 不成立了。** R2 的五片 INA226 各在一片刀片
|
||
上(`U3`),五片刀片完全同构 ⇒ **地址全是 0x40**,靠底板 TCA9548A 的通道区分
|
||
(方案 D8)。板上出处:`.claude/r2/_blade_pcb_nets.json` 的 `U3` pin1/pin2
|
||
(A1/A0)都接 GND。
|
||
|
||
于是「查表不要算」这条规矩换了对象:**要查的不再是地址,而是 mux 通道**
|
||
(`rail_cfg.h` 的 `mux_ch`)。选错通道读到的是另一台手机的电压电流,
|
||
而量纲、范围、变化趋势全都正常 —— 比 R1 的「地址算错」更难发现。 */
|
||
|
||
#define INA226_RAIL_COUNT 5
|
||
#define INA226_COUNT 6 /* 物理片数:五片刀片 + 一片输入侧 */
|
||
|
||
/* 五片刀片上那一片的地址(**五片相同**)。 */
|
||
#define INA226_ADDR_RAIL 0x40
|
||
|
||
/* U13,底板输入侧 V+I,整板功率预算与 PD 限功率用(计划 §5.9)。
|
||
挂**主干**,不在任何 mux 通道后面。R2 底板上 Kelvin 对调接,见 ina226_set_reversed()。 */
|
||
#define INA226_ADDR_INPUT 0x43
|
||
|
||
/* 扫描用:总线上出现过的 INA226 地址只有这两个(不是六个)。 */
|
||
#define INA226_ADDR_COUNT 2
|
||
|
||
/* 登记「这片的 IN+ / IN− 在板上对调接了」(IN+ 接分流电阻下游):之后驱动对它的
|
||
分流电压 / 电流取反,调用方看到的永远是「正 = 正常供电方向」。母线电压 / 功率不受影响。
|
||
默认全部正接(R1)。R2 底板的 U13(0x43)为了让两条 Kelvin 在 PCB 上不交叉而对调
|
||
(2026-09-11),R2 固件启动时必须调 ina226_set_reversed(INA226_ADDR_INPUT, true),
|
||
否则 SW-8 反灌判据会把正常供电当成反灌。地址不在 0x40~0x4F 的调用被忽略。 */
|
||
void ina226_set_reversed(uint8_t addr, bool reversed);
|
||
bool ina226_is_reversed(uint8_t addr);
|
||
|
||
/* CH1~CH5 那一片的地址。rail 是 1~5,越界直接 abort。
|
||
⚠️ **R2 里它对五路返回同一个值(0x40)**,这不是 bug。
|
||
真正区分是哪一路的是 `rail_cfg(rail)->mux_ch` —— 光有地址是选不中的,
|
||
所以任何「拿到地址就直接读」的代码在 R2 上都读的是**上一次选中的那一路**。 */
|
||
uint8_t ina226_rail_addr(int rail);
|
||
|
||
extern const uint8_t ina226_addr_list[INA226_ADDR_COUNT]; /* {0x40, 0x43},扫描用 */
|
||
|
||
/* 两个地址不重复、都合法、且不与主干上的 DAC63204(0x4A/0x4B) / TCA9548A(0x70)
|
||
撞车,也不与刀片支路上的 PCA9555(0x20) 撞车。
|
||
启动自检调一次;返回 false 说明表被改坏了。 */
|
||
bool ina226_addr_table_ok(void);
|
||
|
||
/* ---- 采样 --------------------------------------------------------------- */
|
||
|
||
typedef struct {
|
||
int32_t bus_mv; /* 母线电压,1.25 mV/LSB */
|
||
int32_t current_ua; /* **有符号** —— 反灌时为负(SW-8 靠这个判据);对调接的片子已由驱动取反 */
|
||
} ina226_vi_t;
|
||
|
||
typedef struct {
|
||
int32_t bus_mv;
|
||
int32_t shunt_uv; /* 2.5 µV/LSB,四舍五入到 µV(±1.25 µV ≈ ±0.25 mA) */
|
||
int32_t current_ua;
|
||
int32_t power_mw; /* 12.5 mW/LSB,向零截断 */
|
||
} ina226_sample_t;
|
||
|
||
/* 读 Manufacturer ID / Die ID 认件。
|
||
器件不应答 → HAL_ERR_NAK;**应答了但 ID 不对 → HAL_ERR_BUS**
|
||
(这个地址上有东西,但不是 INA226:多半地址跳线焊错或贴错件)。 */
|
||
hal_err_t ina226_probe(uint8_t addr);
|
||
|
||
/* 写 Config 与 Calibration 并**逐个回读校验**。回读不一致 → HAL_ERR_BUS。 */
|
||
hal_err_t ina226_init(uint8_t addr);
|
||
|
||
/* 只校验 Calibration 还在。掉了的现象是 **Current/Power 恒读 0**(不是报错),
|
||
看上去就像「这台手机不耗电」,所以巡检里要定期查一次。 */
|
||
hal_err_t ina226_check(uint8_t addr);
|
||
|
||
/* 巡检主路径:母线电压 + 电流,两次事务。 */
|
||
hal_err_t ina226_read_vi(uint8_t addr, ina226_vi_t *out);
|
||
|
||
/* 诊断/事件记录用:四个量一次读齐,四次事务。 */
|
||
hal_err_t ina226_read_all(uint8_t addr, ina226_sample_t *out);
|
||
|
||
hal_err_t ina226_read_bus_mv(uint8_t addr, int32_t *mv);
|
||
hal_err_t ina226_read_shunt_uv(uint8_t addr, int32_t *uv);
|
||
hal_err_t ina226_read_current_ua(uint8_t addr, int32_t *ua);
|
||
hal_err_t ina226_read_power_mw(uint8_t addr, int32_t *mw);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* INA226_H */
|