/* INA226 驱动。只经 hal_i2c_*,不碰任何平台头文件。 */ #include "ina226.h" #include #include #include "hal_i2c.h" /* 设计文档 §8.2:CH1..CH5。⚠️ 不是 0x40 起顺排,CH3/CH4/CH5 三路都不是。 */ static const uint8_t s_rail_addr[INA226_RAIL_COUNT] = { 0x40, 0x41, 0x44, 0x45, 0x42 }; const uint8_t ina226_all_addr[INA226_COUNT] = { 0x40, 0x41, 0x44, 0x45, 0x42, 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) { /* 同一条 I²C 上的其它器件(§8.2):撞上任何一个都是硬件表被改坏了。 */ static const uint8_t others[] = { 0x4A, 0x4B, 0x20, 0x48 }; for (int i = 0; i < INA226_COUNT; i++) { if (ina226_all_addr[i] > 0x7f) { return false; } for (int j = i + 1; j < INA226_COUNT; j++) { if (ina226_all_addr[i] == ina226_all_addr[j]) { return false; } } for (size_t k = 0; k < sizeof(others); k++) { if (ina226_all_addr[i] == others[k]) { return false; } } } /* 前五个必须和 rail 表逐个一致 —— 两张表分家的那天就是错一路的那天。 */ for (int i = 0; i < INA226_RAIL_COUNT; i++) { if (ina226_all_addr[i] != s_rail_addr[i]) { 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, ®, 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; } #define READ_ONE(fn, conv, reg) \ 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; \ } \ *out = conv(raw); \ return HAL_OK; \ } READ_ONE(ina226_read_bus_mv, bus_mv_of, INA226_REG_BUS_V) READ_ONE(ina226_read_shunt_uv, shunt_uv_of, INA226_REG_SHUNT_V) READ_ONE(ina226_read_current_ua, current_ua_of, INA226_REG_CURRENT) READ_ONE(ina226_read_power_mw, power_mw_of, INA226_REG_POWER) 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; }