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
这个提交包含在:
@@ -0,0 +1,242 @@
|
||||
/* 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();
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户