文件
phonerail/firmware/test/test_ina226.c
T
zhou yang和Claude Opus 5 ceadf08345 A3:INA226 驱动 + 六片地址表
地址表按设计文档 §8.2 钉死(0x40/41/44/45/42 + 输入侧 0x43),只走
ina226_rail_addr(),并有一条测试专门拦「ina_addr = 0x40 + ch」这一手
(CH3/CH4/CH5 三路全错,需求 §10.4)。

驱动只经 hal_i2c_*:认件(MFG/DIE ID)、写 Config 0x4327 + CAL 2048 并
回读校验、读 V/I/分流/功率。电流有符号,反灌为负(SW-8 判据)。
巡检走 read_vi(),每片两次事务,对应 §10.3 的 8% I²C 占用。

两条留给下次的坑:寄存器指针不依赖自增(sim 会自增、真件不会,靠自增
在宿主机上测不出来);CAL 丢了不报错只让 Current/Power 恒读 0,所以
有 ina226_check()。

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
2026-09-06 18:43:34 +08:00

243 行
9.2 KiB
C
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* A3:INA226 驱动 + 六片地址表。
两个验收主项:①六个地址就是 0x40/41/44/45/42/43;②通道号 ≠ 地址低位。 */
#include "ina226.h"
#include "sim_hal.h"
#include "unity.h"
void setUp(void) { sim_hal_reset(); }
void tearDown(void) {}
/* 挂一片假 INA226 并预置 ID,返回地址。 */
static uint8_t mount(uint8_t addr)
{
sim_i2c_add(addr, 2);
sim_i2c_set_reg(addr, INA226_REG_MFG_ID, INA226_MFG_ID);
sim_i2c_set_reg(addr, INA226_REG_DIE_ID, INA226_DIE_ID);
TEST_ASSERT_EQUAL_INT(HAL_OK, hal_i2c_init(NULL));
return addr;
}
/* ---- 地址表 -------------------------------------------------------------- */
/* ⭐ 验收主项一:设计文档 §8.2 的六个地址,逐个钉死。 */
static void test_addr_table_is_the_one_from_8_2(void)
{
TEST_ASSERT_EQUAL_HEX8(0x40, ina226_rail_addr(1));
TEST_ASSERT_EQUAL_HEX8(0x41, ina226_rail_addr(2));
TEST_ASSERT_EQUAL_HEX8(0x44, ina226_rail_addr(3));
TEST_ASSERT_EQUAL_HEX8(0x45, ina226_rail_addr(4));
TEST_ASSERT_EQUAL_HEX8(0x42, ina226_rail_addr(5));
TEST_ASSERT_EQUAL_HEX8(0x43, INA226_ADDR_INPUT);
static const uint8_t want[INA226_COUNT] = { 0x40, 0x41, 0x44, 0x45, 0x42, 0x43 };
TEST_ASSERT_EQUAL_HEX8_ARRAY(want, ina226_all_addr, INA226_COUNT);
}
/* ⭐ 验收主项二:`0x40 + ch` 这行代码一定会被写出来(需求 §10.4),
这条测试就是拦它的 —— 五路里有三路对不上,把表改成算式必红。 */
static void test_channel_index_is_not_the_address_low_bits(void)
{
int mismatches = 0;
for (int rail = 1; rail <= INA226_RAIL_COUNT; rail++) {
uint8_t naive = (uint8_t)(0x40 + (rail - 1));
if (ina226_rail_addr(rail) != naive) {
mismatches++;
}
}
TEST_ASSERT_EQUAL_INT_MESSAGE(3, mismatches, "CH3/CH4/CH5 三路必须与顺排不同");
/* 反过来也钉一遍:拿地址低位算回通道号同样会算错。 */
TEST_ASSERT_NOT_EQUAL(0x42, ina226_rail_addr(3)); /* CH3 是 0x44,不是 0x42 */
TEST_ASSERT_NOT_EQUAL(0x44, ina226_rail_addr(5)); /* CH5 是 0x42,不是 0x44 */
}
static void test_addr_table_has_no_collisions(void)
{
TEST_ASSERT_TRUE(ina226_addr_table_ok()); /* 含与 DAC/PCA9555/LM75 的撞车检查 */
}
/* ---- 认件 ---------------------------------------------------------------- */
static void test_probe_accepts_real_ina226(void)
{
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_probe(mount(0x40)));
}
/* 应答了但 ID 不对 = 贴错件或地址跳线焊错,必须和「没这个器件」区分开。 */
static void test_probe_rejects_wrong_die_id(void)
{
uint8_t a = mount(0x41);
sim_i2c_set_reg(a, INA226_REG_DIE_ID, 0x1234);
TEST_ASSERT_EQUAL_INT(HAL_ERR_BUS, ina226_probe(a));
}
static void test_probe_absent_device_naks(void)
{
mount(0x40);
TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, ina226_probe(0x44)); /* 没挂 */
sim_i2c_set_present(0x40, false);
TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, ina226_probe(0x40)); /* 失联 */
}
/* 需求 §12.1 步 2:六片全应答。 */
static void test_all_six_answer_scan(void)
{
for (int i = 0; i < INA226_COUNT; i++) {
mount(ina226_all_addr[i]);
}
for (int i = 0; i < INA226_COUNT; i++) {
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_probe(ina226_all_addr[i]));
}
}
static void test_bus_not_initialised_propagates(void)
{
sim_i2c_add(0x40, 2);
int32_t mv = 0;
TEST_ASSERT_EQUAL_INT(HAL_ERR_STATE, ina226_read_bus_mv(0x40, &mv));
}
/* ---- 初始化 -------------------------------------------------------------- */
static void test_init_writes_config_and_calibration(void)
{
uint8_t a = mount(0x45);
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_init(a));
TEST_ASSERT_EQUAL_HEX16(INA226_CONFIG, sim_i2c_get_reg(a, INA226_REG_CONFIG));
TEST_ASSERT_EQUAL_HEX16(2048, sim_i2c_get_reg(a, INA226_REG_CALIB));
/* 两个寄存器都是「写完立刻回读校验」:4 次写(两次三字节的写 + 两次一字节的
读指针)、2 次读。最后落下的那次写就是 CAL 的读指针。 */
const sim_i2c_dev_t *d = sim_i2c_dev(a);
TEST_ASSERT_EQUAL_UINT32(4, d->n_write);
TEST_ASSERT_EQUAL_UINT32(2, d->n_read);
TEST_ASSERT_EQUAL_UINT8(1, d->last_write_len);
TEST_ASSERT_EQUAL_HEX8(INA226_REG_CALIB, d->last_write[0]);
}
/* CAL 掉了的现象是 Current/Power 恒读 0,不是报错 —— 所以要能查出来。 */
static void test_check_catches_lost_calibration(void)
{
uint8_t a = mount(0x40);
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_init(a));
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_check(a));
sim_i2c_set_reg(a, INA226_REG_CALIB, 0); /* 器件掉过一次电 */
TEST_ASSERT_EQUAL_INT(HAL_ERR_BUS, ina226_check(a));
}
/* ---- 换算 ---------------------------------------------------------------- */
/* 满载一路:4.010 V / 4 A / 16 W。5 mΩ + 0.5 mA LSB 下分流码与电流码同值。 */
static void test_readings_at_full_load(void)
{
uint8_t a = mount(0x44);
sim_i2c_set_reg(a, INA226_REG_BUS_V, 3208); /* ×1.25 mV */
sim_i2c_set_reg(a, INA226_REG_SHUNT_V, 8000); /* ×2.5 µV = 20 mV */
sim_i2c_set_reg(a, INA226_REG_CURRENT, 8000); /* ×0.5 mA */
sim_i2c_set_reg(a, INA226_REG_POWER, 1280); /* ×12.5 mW */
ina226_sample_t s = { 0, 0, 0, 0 };
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_all(a, &s));
TEST_ASSERT_EQUAL_INT32(4010, s.bus_mv);
TEST_ASSERT_EQUAL_INT32(20000, s.shunt_uv);
TEST_ASSERT_EQUAL_INT32(4000000, s.current_ua);
TEST_ASSERT_EQUAL_INT32(16000, s.power_mw);
}
/* ⭐ 反灌(SW-8):电流寄存器是**有符号**的。当成无符号会得到 +32 A 这种数,
而判据是「电流为负」,于是保护永远不触发。 */
static void test_reverse_current_is_negative(void)
{
uint8_t a = mount(0x42);
sim_i2c_set_reg(a, INA226_REG_CURRENT, (uint16_t)(int16_t)-120); /* −60 mA */
sim_i2c_set_reg(a, INA226_REG_SHUNT_V, (uint16_t)(int16_t)-120); /* −300 µV */
int32_t ua = 0, uv = 0;
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_current_ua(a, &ua));
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_shunt_uv(a, &uv));
TEST_ASSERT_EQUAL_INT32(-60000, ua);
TEST_ASSERT_EQUAL_INT32(-300, uv);
}
/* 3.90 V 出厂默认值那一点,顺带看四舍五入。3120 × 1.25 = 3900 mV 整。 */
static void test_bus_voltage_rounding(void)
{
uint8_t a = mount(0x40);
sim_i2c_set_reg(a, INA226_REG_BUS_V, 3120);
int32_t mv = 0;
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_bus_mv(a, &mv));
TEST_ASSERT_EQUAL_INT32(3900, mv);
sim_i2c_set_reg(a, INA226_REG_BUS_V, 3121); /* 3901.25 → 3901 */
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_bus_mv(a, &mv));
TEST_ASSERT_EQUAL_INT32(3901, mv);
}
/* ⚠️ 驱动不许依赖寄存器指针自增:sim 的假器件会自增,INA226 按 datasheet 不会。
靠自增省事的写法在这里会绿,到板上四个量全读成同一个。 */
static void test_pointer_is_rewritten_before_every_read(void)
{
uint8_t a = mount(0x41);
ina226_sample_t s = { 0, 0, 0, 0 };
sim_i2c_dev(a)->n_write = 0;
sim_i2c_dev(a)->n_read = 0;
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_all(a, &s));
TEST_ASSERT_EQUAL_UINT32(4, sim_i2c_dev(a)->n_write);
TEST_ASSERT_EQUAL_UINT32(4, sim_i2c_dev(a)->n_read);
}
/* 巡检主路径:只读两个量(§10.3 那 8 % I²C 占用是按这个算的)。 */
static void test_read_vi_uses_two_transactions(void)
{
uint8_t a = mount(0x40);
sim_i2c_set_reg(a, INA226_REG_BUS_V, 3120);
sim_i2c_set_reg(a, INA226_REG_CURRENT, 4000);
sim_i2c_dev(a)->n_read = 0;
ina226_vi_t vi = { 0, 0 };
TEST_ASSERT_EQUAL_INT(HAL_OK, ina226_read_vi(a, &vi));
TEST_ASSERT_EQUAL_INT32(3900, vi.bus_mv);
TEST_ASSERT_EQUAL_INT32(2000000, vi.current_ua);
TEST_ASSERT_EQUAL_UINT32(2, sim_i2c_dev(a)->n_read);
}
/* 器件中途失联:错误必须一路传上去,不能返回一个看着正常的 0。 */
static void test_read_propagates_nak(void)
{
uint8_t a = mount(0x45);
sim_i2c_inject_nak(a, -1);
ina226_vi_t vi = { 123, 456 };
TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, ina226_read_vi(a, &vi));
}
static void test_null_output_rejected(void)
{
mount(0x40);
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, ina226_read_vi(0x40, NULL));
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, ina226_read_all(0x40, NULL));
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, ina226_read_bus_mv(0x40, NULL));
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_addr_table_is_the_one_from_8_2);
RUN_TEST(test_channel_index_is_not_the_address_low_bits);
RUN_TEST(test_addr_table_has_no_collisions);
RUN_TEST(test_probe_accepts_real_ina226);
RUN_TEST(test_probe_rejects_wrong_die_id);
RUN_TEST(test_probe_absent_device_naks);
RUN_TEST(test_all_six_answer_scan);
RUN_TEST(test_bus_not_initialised_propagates);
RUN_TEST(test_init_writes_config_and_calibration);
RUN_TEST(test_check_catches_lost_calibration);
RUN_TEST(test_readings_at_full_load);
RUN_TEST(test_reverse_current_is_negative);
RUN_TEST(test_bus_voltage_rounding);
RUN_TEST(test_pointer_is_rewritten_before_every_read);
RUN_TEST(test_read_vi_uses_two_transactions);
RUN_TEST(test_read_propagates_nak);
RUN_TEST(test_null_output_rejected);
return UNITY_END();
}