/* A4 之一:DAC63204 驱动。 两个验收主项在 test_rail.c(反向关系、3900 mV ↔ Vd 0.548 V); 这里盯的是器件层的三条:**通道映射不是算出来的**、**码是左对齐的**、 **没用到的通道不许上电**。 */ #include "dac63204.h" #include "sim_hal.h" #include "unity.h" void setUp(void) { sim_hal_reset(); } void tearDown(void) {} /* 挂一片假 DAC63204 并预置 DEVICE-ID。 */ static uint8_t mount(uint8_t addr) { sim_i2c_add(addr, 2); sim_i2c_set_reg(addr, DAC63204_REG_GENERAL_STATUS, (uint16_t)(DAC63204_DEVICE_ID << 2)); /* bit7-2 = ID,低两位是版本 */ sim_i2c_set_reg(addr, DAC63204_REG_COMMON_CONFIG, 0x0FFF); /* 复位值:八路全掉电 */ TEST_ASSERT_EQUAL_INT(HAL_OK, hal_i2c_init(NULL)); return addr; } static void mount_both(void) { mount(DAC63204_ADDR_U6); mount(DAC63204_ADDR_U7); } /* ---- 通道映射 ------------------------------------------------------------ */ /* ⭐ 设计文档 §26.18 重排之后的真表,逐路钉死。 */ static void test_rail_map_is_the_one_from_26_18(void) { TEST_ASSERT_EQUAL_HEX8(DAC63204_ADDR_U6, dac63204_rail_addr(1)); TEST_ASSERT_EQUAL_UINT8(2, dac63204_rail_ch(1)); TEST_ASSERT_EQUAL_HEX8(DAC63204_ADDR_U6, dac63204_rail_addr(2)); TEST_ASSERT_EQUAL_UINT8(0, dac63204_rail_ch(2)); TEST_ASSERT_EQUAL_HEX8(DAC63204_ADDR_U7, dac63204_rail_addr(3)); /* ← 不在 U6 上 */ TEST_ASSERT_EQUAL_UINT8(0, dac63204_rail_ch(3)); TEST_ASSERT_EQUAL_HEX8(DAC63204_ADDR_U6, dac63204_rail_addr(4)); TEST_ASSERT_EQUAL_UINT8(1, dac63204_rail_ch(4)); TEST_ASSERT_EQUAL_HEX8(DAC63204_ADDR_U7, dac63204_rail_addr(5)); TEST_ASSERT_EQUAL_UINT8(1, dac63204_rail_ch(5)); } /* ⭐ 「前三路在 U6、通道号 = rail−1」这行代码一定会被写出来(需求 §10.4 同款)。 这条测试拦它 —— 五路里只有 CH5 碰巧对,其余四路全错。 ⚠️ 四个通道电气上完全对等,接错了板子照样正常工作,只是调 CH1 调到了 CH2。 */ static void test_rail_map_is_not_the_naive_formula(void) { int mismatches = 0; for (int rail = 1; rail <= DAC63204_RAIL_COUNT; rail++) { uint8_t naive_addr = rail <= 3 ? DAC63204_ADDR_U6 : DAC63204_ADDR_U7; uint8_t naive_ch = (uint8_t)((rail - 1) % 3); if (dac63204_rail_addr(rail) != naive_addr || dac63204_rail_ch(rail) != naive_ch) { mismatches++; } } TEST_ASSERT_EQUAL_INT_MESSAGE(4, mismatches, "CH1~CH4 四路必须与顺排不同"); } static void test_map_has_no_collisions(void) { TEST_ASSERT_TRUE(dac63204_map_ok()); /* 含与 INA226 六片 / PCA9555 / LM75 的撞车检查 */ } /* U6 用 OUT0/1/2,U7 只用 OUT0/1。 */ static void test_used_mask_matches_the_board(void) { TEST_ASSERT_EQUAL_HEX8(0x07, dac63204_used_mask(DAC63204_ADDR_U6)); TEST_ASSERT_EQUAL_HEX8(0x03, dac63204_used_mask(DAC63204_ADDR_U7)); TEST_ASSERT_EQUAL_HEX8(0x00, dac63204_used_mask(0x4C)); /* 不是这两片 */ } /* ---- 寄存器地址 ---------------------------------------------------------- */ /* 两种步长:配置寄存器每通道跨 6(03/09/0F/15),数据寄存器却是连续的(19~1C)。 */ static void test_register_addresses(void) { TEST_ASSERT_EQUAL_HEX8(0x03, DAC63204_REG_VOUT_CMP_CONFIG(0)); TEST_ASSERT_EQUAL_HEX8(0x09, DAC63204_REG_VOUT_CMP_CONFIG(1)); TEST_ASSERT_EQUAL_HEX8(0x0F, DAC63204_REG_VOUT_CMP_CONFIG(2)); TEST_ASSERT_EQUAL_HEX8(0x15, DAC63204_REG_VOUT_CMP_CONFIG(3)); TEST_ASSERT_EQUAL_HEX8(0x19, DAC63204_REG_DATA(0)); TEST_ASSERT_EQUAL_HEX8(0x1A, DAC63204_REG_DATA(1)); TEST_ASSERT_EQUAL_HEX8(0x1B, DAC63204_REG_DATA(2)); TEST_ASSERT_EQUAL_HEX8(0x1C, DAC63204_REG_DATA(3)); } /* ---- 认件 ---------------------------------------------------------------- */ static void test_probe_accepts_real_dac63204(void) { TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_probe(mount(DAC63204_ADDR_U6))); } /* 贴成 DAC53204(10 位,ID 02h):应答了,但码值左对齐的位数不一样, 装上去五路电压全错 —— 必须当成 BUS 错,不能当「没这个器件」混过去。 */ static void test_probe_rejects_the_10bit_sibling(void) { uint8_t a = mount(DAC63204_ADDR_U7); sim_i2c_set_reg(a, DAC63204_REG_GENERAL_STATUS, (uint16_t)(0x02 << 2)); TEST_ASSERT_EQUAL_INT(HAL_ERR_BUS, dac63204_probe(a)); } static void test_probe_absent_device_naks(void) { mount(DAC63204_ADDR_U6); TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, dac63204_probe(DAC63204_ADDR_U7)); } /* ---- 初始化 -------------------------------------------------------------- */ /* ⭐ 内部基准要先开、增益要 2×、**没用到的通道保持 Hi-Z**。 */ static void test_init_enables_ref_and_powers_only_used_channels(void) { uint8_t a = mount(DAC63204_ADDR_U7); /* 只用 OUT0/OUT1 */ TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_init(a, 926)); uint16_t cc = sim_i2c_get_reg(a, DAC63204_REG_COMMON_CONFIG); TEST_ASSERT_TRUE_MESSAGE(cc & DAC63204_CC_EN_INT_REF, "内部基准没开,增益设定无效"); TEST_ASSERT_EQUAL_HEX16_MESSAGE(0, cc & DAC63204_CC_VOUT_PDN(0), "OUT0 该上电"); TEST_ASSERT_EQUAL_HEX16_MESSAGE(0, cc & DAC63204_CC_VOUT_PDN(1), "OUT1 该上电"); /* OUT2/OUT3 的 FBx 悬空,上电就是开环运放。 */ TEST_ASSERT_EQUAL_HEX16_MESSAGE(DAC63204_CC_VOUT_PDN(2), cc & DAC63204_CC_VOUT_PDN(2), "OUT2 没接 FB,必须保持 Hi-Z"); TEST_ASSERT_EQUAL_HEX16_MESSAGE(DAC63204_CC_VOUT_PDN(3), cc & DAC63204_CC_VOUT_PDN(3), "OUT3 没接 FB,必须保持 Hi-Z"); /* 电流输出四路一律掉电 */ TEST_ASSERT_EQUAL_HEX16(DAC63204_CC_IOUT_PDN_ALL, cc & DAC63204_CC_IOUT_PDN_ALL); TEST_ASSERT_EQUAL_HEX16(DAC63204_CFG_GAIN_2X_INT, sim_i2c_get_reg(a, DAC63204_REG_VOUT_CMP_CONFIG(0))); TEST_ASSERT_EQUAL_HEX16(DAC63204_CFG_GAIN_2X_INT, sim_i2c_get_reg(a, DAC63204_REG_VOUT_CMP_CONFIG(1))); /* 没用到的通道连增益都不该被碰 */ TEST_ASSERT_EQUAL_HEX16(0, sim_i2c_get_reg(a, DAC63204_REG_VOUT_CMP_CONFIG(2))); } /* 安全码必须在**上电之前**就写进去:复位码是 0,而 Vd=0 = 输出 4.010 V 的上限。 这条盯两件事:①第一次写进 COMMON-CONFIG 的值把八路输出全按住(`CC_REF_ONLY`); ②COMMON-CONFIG 确实写了**两次**(写一次 = 上电和写码挤在一起了)。 ⚠️ sim 只留得下寄存器终值,看不到中间态,所以顺序是靠这两条间接钉的。 */ static void test_init_writes_safe_code_before_powering_up(void) { /* ① 的那个值本身:基准已开,但四路输出一个都没上电。 */ TEST_ASSERT_TRUE(DAC63204_CC_REF_ONLY & DAC63204_CC_EN_INT_REF); for (int ch = 0; ch < 4; ch++) { TEST_ASSERT_EQUAL_HEX16_MESSAGE(DAC63204_CC_VOUT_PDN(ch), DAC63204_CC_REF_ONLY & DAC63204_CC_VOUT_PDN(ch), "第一步就不许有通道上电"); } uint8_t a = mount(DAC63204_ADDR_U6); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_init(a, 926)); for (uint8_t ch = 0; ch < 3; ch++) { TEST_ASSERT_EQUAL_HEX16(DAC63204_CODE_TO_REG(926), sim_i2c_get_reg(a, DAC63204_REG_DATA(ch))); } /* 八次「写 + 回读」:①ref-only、②③三个通道各 gain+data、④run。 每次回读是一次 read,所以 n_read 就是 write_verify 的次数。 */ TEST_ASSERT_EQUAL_UINT32_MESSAGE(1 + 3 * 2 + 1, sim_i2c_dev(a)->n_read, "COMMON-CONFIG 必须写两次:先按住,写完码再上电"); } /* 跑起来之后 COMMON-CONFIG 该是什么,两片各钉一遍。 */ static void test_cc_run_values(void) { TEST_ASSERT_EQUAL_HEX16(0x1E49, dac63204_cc_run(DAC63204_ADDR_U6)); /* 只 OUT3 掉电 */ TEST_ASSERT_EQUAL_HEX16(0x1FC9, dac63204_cc_run(DAC63204_ADDR_U7)); /* OUT2/OUT3 掉电 */ } static void test_init_rejects_unknown_chip_and_bad_code(void) { mount(DAC63204_ADDR_U6); TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, dac63204_init(0x4C, 926)); /* 不是本板的片 */ TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, dac63204_init(DAC63204_ADDR_U6, 4096)); /* 码越界 */ } static void test_check_catches_lost_gain(void) { uint8_t a = mount(DAC63204_ADDR_U6); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_init(a, 926)); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_check(a)); sim_i2c_set_reg(a, DAC63204_REG_VOUT_CMP_CONFIG(1), 0); /* 增益掉回 1×、外部基准 */ TEST_ASSERT_EQUAL_INT(HAL_ERR_BUS, dac63204_check(a)); } static void test_check_catches_lost_common_config(void) { uint8_t a = mount(DAC63204_ADDR_U7); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_init(a, 926)); sim_i2c_set_reg(a, DAC63204_REG_COMMON_CONFIG, 0x0FFF); /* 掉回复位值 = 输出全掉电 */ TEST_ASSERT_EQUAL_INT(HAL_ERR_BUS, dac63204_check(a)); } /* ---- 码的写读 ------------------------------------------------------------ */ /* ⭐ 12 位码在 DAC-X-DATA 里是**左对齐**的(bit15-4)。 忘了 `<< 4` 输出只有期望的 1/16,而那在本板上表现为「满电 4.010 V」,看不出异常。 */ static void test_code_is_left_aligned_in_the_data_register(void) { uint8_t a = mount(DAC63204_ADDR_U6); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_write_code(a, 2, 0x123)); TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x1230, sim_i2c_get_reg(a, DAC63204_REG_DATA(2)), "码必须左移 4 位"); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_write_code(a, 2, DAC63204_CODE_MAX)); TEST_ASSERT_EQUAL_HEX16(0xFFF0, sim_i2c_get_reg(a, DAC63204_REG_DATA(2))); } static void test_read_code_round_trips(void) { uint8_t a = mount(DAC63204_ADDR_U7); uint16_t back = 0; for (uint16_t code = 0; code <= DAC63204_CODE_MAX; code += 273) { TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_write_code(a, 1, code)); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_read_code(a, 1, &back)); TEST_ASSERT_EQUAL_UINT16(code, back); } } static void test_write_code_rejects_bad_args(void) { uint8_t a = mount(DAC63204_ADDR_U6); TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, dac63204_write_code(a, 4, 100)); TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, dac63204_write_code(a, 0, 4096)); TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, dac63204_read_code(a, 0, NULL)); } /* 每次读之前重写指针 —— sim 的假器件会自增,真器件不一定(和 INA226 同一条坑)。 */ static void test_pointer_is_rewritten_before_every_read(void) { uint8_t a = mount(DAC63204_ADDR_U6); sim_i2c_set_reg(a, DAC63204_REG_DATA(0), 0x1110); sim_i2c_set_reg(a, DAC63204_REG_DATA(1), 0x2220); uint16_t c0 = 0, c1 = 0; TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_read_code(a, 0, &c0)); TEST_ASSERT_EQUAL_INT(HAL_OK, dac63204_read_code(a, 1, &c1)); TEST_ASSERT_EQUAL_HEX16(0x111, c0); TEST_ASSERT_EQUAL_HEX16(0x222, c1); /* 靠自增的话这里会读成 0x111 的下一格 */ } /* ---- 量程 ---------------------------------------------------------------- */ /* 满量程是 1.212 V × 2 = 2.424 V,不是 2.5 V(设计文档 §4.5 早期写错过)。 */ static void test_fullscale_is_2424_mv_not_2500(void) { TEST_ASSERT_EQUAL_INT32(0, dac63204_code_to_uv(0)); TEST_ASSERT_INT32_WITHIN(1, 2423408, dac63204_code_to_uv(DAC63204_CODE_MAX)); TEST_ASSERT_INT32_WITHIN(1, 1212000, dac63204_code_to_uv(2048)); } static void test_bus_not_initialised_propagates(void) { /* 不调 hal_i2c_init():驱动不许把「总线没起来」吞掉。 */ TEST_ASSERT_EQUAL_INT(HAL_ERR_STATE, dac63204_probe(DAC63204_ADDR_U6)); TEST_ASSERT_EQUAL_INT(HAL_ERR_STATE, dac63204_write_code(DAC63204_ADDR_U6, 0, 100)); } static void test_nak_propagates(void) { mount_both(); sim_i2c_inject_nak(DAC63204_ADDR_U6, -1); TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, dac63204_init(DAC63204_ADDR_U6, 926)); TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, dac63204_check(DAC63204_ADDR_U6)); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_rail_map_is_the_one_from_26_18); RUN_TEST(test_rail_map_is_not_the_naive_formula); RUN_TEST(test_map_has_no_collisions); RUN_TEST(test_used_mask_matches_the_board); RUN_TEST(test_register_addresses); RUN_TEST(test_probe_accepts_real_dac63204); RUN_TEST(test_probe_rejects_the_10bit_sibling); RUN_TEST(test_probe_absent_device_naks); RUN_TEST(test_init_enables_ref_and_powers_only_used_channels); RUN_TEST(test_init_writes_safe_code_before_powering_up); RUN_TEST(test_cc_run_values); RUN_TEST(test_init_rejects_unknown_chip_and_bad_code); RUN_TEST(test_check_catches_lost_gain); RUN_TEST(test_check_catches_lost_common_config); RUN_TEST(test_code_is_left_aligned_in_the_data_register); RUN_TEST(test_read_code_round_trips); RUN_TEST(test_write_code_rejects_bad_args); RUN_TEST(test_pointer_is_rewritten_before_every_read); RUN_TEST(test_fullscale_is_2424_mv_not_2500); RUN_TEST(test_bus_not_initialised_propagates); RUN_TEST(test_nak_propagates); return UNITY_END(); }