文件
phonerail/firmware/components/drv/ina226.c
T
zhou yang和Claude Opus 5.5 b8ad69681f 固件:R2 迁移 R-0 ~ R-6(底板 + 5 片同构刀片)
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]>
2026-09-27 10:26:24 +08:00

214 行
7.9 KiB
C
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* INA226 驱动。只经 hal_i2c_*,不碰任何平台头文件。 */
#include "ina226.h"
#include <assert.h>
#include <stddef.h>
#include "hal_i2c.h"
/* R2:五片刀片完全同构 ⇒ 五路同址。仍然写成表而不是直接 `return 0x40`,
理由和 mux 通道那张表一样:**同址是当下的事实,不是不变量**。
哪天某一片改了 strap,改这里一处,`rail_selftest()` 会立刻发现两边分家。 */
static const uint8_t s_rail_addr[INA226_RAIL_COUNT] = {
INA226_ADDR_RAIL, INA226_ADDR_RAIL, INA226_ADDR_RAIL, INA226_ADDR_RAIL, INA226_ADDR_RAIL,
};
const uint8_t ina226_addr_list[INA226_ADDR_COUNT] = { INA226_ADDR_RAIL, INA226_ADDR_INPUT };
uint8_t ina226_rail_addr(int rail)
{
assert(rail >= 1 && rail <= INA226_RAIL_COUNT); /* 越界是代码写错,不许悄悄返回一个地址 */
return s_rail_addr[rail - 1];
}
bool ina226_addr_table_ok(void)
{
/* 会与之撞车的其它器件:主干上的 DAC63204 / TCA9548A,刀片支路上的 PCA9555。
⚠️ 0x48(R1 载板上的 LM75)**不在这张表里** —— R2 没有载板,那片不存在。 */
static const uint8_t others[] = { 0x4A, 0x4B, 0x70, 0x20 };
for (int i = 0; i < INA226_ADDR_COUNT; i++) {
if (ina226_addr_list[i] > 0x7f) {
return false;
}
for (int j = i + 1; j < INA226_ADDR_COUNT; j++) {
if (ina226_addr_list[i] == ina226_addr_list[j]) {
return false; /* 刀片那片与输入侧那片必须不同址:它们同时可见 */
}
}
for (size_t k = 0; k < sizeof(others); k++) {
if (ina226_addr_list[i] == others[k]) {
return false;
}
}
}
/* 五路那张表里的每一个都必须在扫描表里 —— 分家的那天就是错一路的那天。 */
for (int i = 0; i < INA226_RAIL_COUNT; i++) {
if (s_rail_addr[i] != ina226_addr_list[0]) {
return false;
}
}
return true;
}
/* ---- 寄存器读写 ---------------------------------------------------------
⚠️ 每次读之前都重写一遍寄存器指针,**不依赖指针自增**:INA226 的指针按 datasheet
是停在最后写入值上的(未在实物上验过,B1 上板时确认)。而 sim 的假器件是**会**自增的,
所以「靠自增一次读完 0x01~0x04」在宿主机上能绿,到板上会把四个量读成同一个。 */
static hal_err_t read_reg(uint8_t addr, uint8_t reg, uint16_t *raw)
{
uint8_t rd[2];
hal_err_t err = hal_i2c_write_read(addr, &reg, 1, rd, sizeof(rd));
if (err != HAL_OK) {
return err;
}
*raw = (uint16_t)(((uint16_t)rd[0] << 8) | rd[1]); /* 大端 */
return HAL_OK;
}
static hal_err_t write_reg(uint8_t addr, uint8_t reg, uint16_t val)
{
const uint8_t wr[3] = { reg, (uint8_t)(val >> 8), (uint8_t)(val & 0xff) };
return hal_i2c_write(addr, wr, sizeof(wr));
}
static hal_err_t write_verify(uint8_t addr, uint8_t reg, uint16_t val)
{
hal_err_t err = write_reg(addr, reg, val);
if (err != HAL_OK) {
return err;
}
uint16_t back = 0;
err = read_reg(addr, reg, &back);
if (err != HAL_OK) {
return err;
}
return back == val ? HAL_OK : HAL_ERR_BUS;
}
/* ---- 换算 ---------------------------------------------------------------- */
static int32_t div_round(int32_t num, int32_t den) /* 四舍五入,负数远离零 */
{
return num >= 0 ? (num + den / 2) / den : (num - den / 2) / den;
}
static int32_t s16(uint16_t raw) { return (int32_t)(int16_t)raw; }
static int32_t bus_mv_of(uint16_t raw) { return div_round((int32_t)raw * 5, 4); } /* 1.25 mV */
static int32_t shunt_uv_of(uint16_t raw) { return div_round(s16(raw) * 5, 2); } /* 2.5 µV */
static int32_t current_ua_of(uint16_t raw) { return s16(raw) * INA226_CURRENT_LSB_UA; }
static int32_t power_mw_of(uint16_t raw) { return div_round((int32_t)raw * 25, 2); } /* 12.5 mW */
/* ---- 对外 ---------------------------------------------------------------- */
hal_err_t ina226_probe(uint8_t addr)
{
uint16_t mfg = 0;
hal_err_t err = read_reg(addr, INA226_REG_MFG_ID, &mfg);
if (err != HAL_OK) {
return err;
}
uint16_t die = 0;
err = read_reg(addr, INA226_REG_DIE_ID, &die);
if (err != HAL_OK) {
return err;
}
/* 应答了但不是 INA226:地址跳线焊错 / 贴错件。不能当「没这个器件」混过去。 */
return (mfg == INA226_MFG_ID && die == INA226_DIE_ID) ? HAL_OK : HAL_ERR_BUS;
}
hal_err_t ina226_init(uint8_t addr)
{
hal_err_t err = write_verify(addr, INA226_REG_CONFIG, INA226_CONFIG);
if (err != HAL_OK) {
return err;
}
return write_verify(addr, INA226_REG_CALIB, INA226_CALIB);
}
hal_err_t ina226_check(uint8_t addr)
{
uint16_t cal = 0;
hal_err_t err = read_reg(addr, INA226_REG_CALIB, &cal);
if (err != HAL_OK) {
return err;
}
return cal == INA226_CALIB ? HAL_OK : HAL_ERR_BUS;
}
/* ---- Kelvin 对调接的片子 --------------------------------------------------
IN+ / IN− 在板上对调接(IN+ 接分流电阻下游)的片子,分流电压 / 电流读出来是负的。
驱动在这里取反,调用方看到的永远是「正 = 正常供电方向」—— SW-8 反灌判据靠这个符号。
母线电压、功率寄存器是无符号量,不受影响。
R1 全部正接(默认);R2 底板的输入片 U13(0x43)为了让两条 Kelvin 在 PCB 上不交叉
而对调(2026-09-11),R2 固件启动时要 ina226_set_reversed(INA226_ADDR_INPUT, true)。 */
static uint16_t s_reversed; /* bit (addr − 0x40);INA226 的地址只有 0x40~0x4F */
void ina226_set_reversed(uint8_t addr, bool reversed)
{
if (addr < 0x40 || addr > 0x4F) {
return;
}
uint16_t bit = (uint16_t)(1u << (addr - 0x40));
s_reversed = reversed ? (uint16_t)(s_reversed | bit) : (uint16_t)(s_reversed & ~bit);
}
bool ina226_is_reversed(uint8_t addr)
{
return addr >= 0x40 && addr <= 0x4F && (s_reversed & (1u << (addr - 0x40))) != 0;
}
/* pol_ = 1:有符号量(分流电压 / 电流),按登记取反 */
#define READ_ONE(fn, conv, reg, pol_) \
hal_err_t fn(uint8_t addr, int32_t *out) \
{ \
if (out == NULL) { \
return HAL_ERR_ARG; \
} \
uint16_t raw = 0; \
hal_err_t err = read_reg(addr, (reg), &raw); \
if (err != HAL_OK) { \
return err; \
} \
int32_t v = conv(raw); \
*out = ((pol_) && ina226_is_reversed(addr)) ? -v : v; \
return HAL_OK; \
}
READ_ONE(ina226_read_bus_mv, bus_mv_of, INA226_REG_BUS_V, 0)
READ_ONE(ina226_read_shunt_uv, shunt_uv_of, INA226_REG_SHUNT_V, 1)
READ_ONE(ina226_read_current_ua, current_ua_of, INA226_REG_CURRENT, 1)
READ_ONE(ina226_read_power_mw, power_mw_of, INA226_REG_POWER, 0)
hal_err_t ina226_read_vi(uint8_t addr, ina226_vi_t *out)
{
if (out == NULL) {
return HAL_ERR_ARG;
}
hal_err_t err = ina226_read_bus_mv(addr, &out->bus_mv);
if (err != HAL_OK) {
return err;
}
return ina226_read_current_ua(addr, &out->current_ua);
}
hal_err_t ina226_read_all(uint8_t addr, ina226_sample_t *out)
{
if (out == NULL) {
return HAL_ERR_ARG;
}
hal_err_t err = ina226_read_bus_mv(addr, &out->bus_mv);
if (err == HAL_OK) {
err = ina226_read_shunt_uv(addr, &out->shunt_uv);
}
if (err == HAL_OK) {
err = ina226_read_current_ua(addr, &out->current_ua);
}
if (err == HAL_OK) {
err = ina226_read_power_mw(addr, &out->power_mw);
}
return err;
}