调压是**反向**的(DAC 码越大输出越低),所以整块换算只在 rail_mv_to_code() 里出现一次那个减法,除 rail.c 外任何地方都不许碰 dac63204_write_code()。 范围 3524~4010 mV,越界即拒(SR-13,第四道限制),4010 = Vd 0。 通道映射按 §26.18 重排过,五路里四路对不上「前三路在 U6、通道号 = rail−1」: CH1→U6.OUT2 / CH2→U6.OUT0 / CH3→U7.OUT0 / CH4→U6.OUT1 / CH5→U7.OUT1。 四通道电气对等 ⇒ 接错了板子照样正常工作,只是调错了路。设计文档 §17.2 那张 过期的通道表一并改掉了。 驱动只经 hal_i2c_*:认件(DEVICE-ID=01h,53204/43204 当 BUS 错拒掉)、 开内部基准 → 写增益 2× → 写安全码 → 才给用到的通道上电(COMMON-CONFIG 因此写两次: 复位码 0 就是输出上限 4.010 V),未用通道 FBx 悬空一律保持 Hi-Z。 三个「一定会被写出来的错」逐个改进去验过测试拦得住:反向关系写反 → test_rail 红; 忘了码左对齐、通道按 rail−1 算 → test_dac63204 红。 留给下次的两条:越界请求还没记事件(TODO(A11));两点标定还没做, 现在走理论公式,将来顶替的是 RAIL_VOUT0_UV / RAIL_DAC_SPAN_UV 两个常量。 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
294 行
13 KiB
C
294 行
13 KiB
C
/* 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();
|
||
}
|