文件
phonerail/firmware/test/test_dac63204.c
T
zhou yang和Claude Opus 5 9eb1b39da6 A4:DAC63204 驱动 + rail_set_mv()
调压是**反向**的(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
2026-09-06 19:11:10 +08:00

294 行
13 KiB
C
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* 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();
}