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
这个提交包含在:
@@ -0,0 +1,229 @@
|
||||
/* A4 之二:`rail_set_mv()` —— 三个验收主项(固件开发计划 §5 A4 那一行):
|
||||
① 4020 mV 被拒(SR-13)
|
||||
② **写更大的码值,输出更低**(反向关系)
|
||||
③ 3900 mV ↔ Vd 0.548 V
|
||||
都在下面打了 ⭐。 */
|
||||
#include "dac63204.h"
|
||||
#include "phonerail.h"
|
||||
#include "rail.h"
|
||||
#include "sim_hal.h"
|
||||
#include "unity.h"
|
||||
|
||||
void setUp(void) { sim_hal_reset(); }
|
||||
void tearDown(void) {}
|
||||
|
||||
/* 两片假 DAC 都挂上,并预置 DEVICE-ID 与 COMMON-CONFIG 的复位值。 */
|
||||
static void mount_both(void)
|
||||
{
|
||||
for (int i = 0; i < DAC63204_COUNT; i++) {
|
||||
uint8_t a = dac63204_all_addr[i];
|
||||
sim_i2c_add(a, 2);
|
||||
sim_i2c_set_reg(a, DAC63204_REG_GENERAL_STATUS, (uint16_t)(DAC63204_DEVICE_ID << 2));
|
||||
sim_i2c_set_reg(a, DAC63204_REG_COMMON_CONFIG, 0x0FFF);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, hal_i2c_init(NULL));
|
||||
}
|
||||
|
||||
/* 直接从假器件的寄存器里把某一路的码抠出来(绕开被测代码自己的读路径)。 */
|
||||
static uint16_t raw_code(int rail)
|
||||
{
|
||||
uint16_t reg = sim_i2c_get_reg(dac63204_rail_addr(rail),
|
||||
DAC63204_REG_DATA(dac63204_rail_ch(rail)));
|
||||
return DAC63204_REG_TO_CODE(reg);
|
||||
}
|
||||
|
||||
/* ---- ① 越界即拒(SR-13) --------------------------------------------------- */
|
||||
|
||||
/* ⭐ 验收主项一:4020 mV 必须被拒,而且**一个字节都不许发出去**。
|
||||
这是第四道限制,前三道在硬件里(设计文档 §4.1)—— 它的作用是让操作者早点
|
||||
知道自己写错了,不是靠它保安全。 */
|
||||
static void test_4020_mv_is_rejected(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
|
||||
for (int rail = 1; rail <= PHONERAIL_RAIL_COUNT; rail++) {
|
||||
uint16_t before = raw_code(rail);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(HAL_ERR_ARG, rail_set_mv(rail, 4020), "4020 mV 必须被拒");
|
||||
TEST_ASSERT_EQUAL_UINT16_MESSAGE(before, raw_code(rail), "被拒的请求不许改到 DAC");
|
||||
}
|
||||
TEST_ASSERT_FALSE(rail_mv_in_range(4011));
|
||||
TEST_ASSERT_TRUE(rail_mv_in_range(RAIL_MV_MAX));
|
||||
}
|
||||
|
||||
/* 下限同样拒:DAC 满码也到不了 3523 mV,悄悄夹到 3524 就成了「设了但没设到」。 */
|
||||
static void test_below_range_is_rejected(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, rail_set_mv(1, RAIL_MV_MIN - 1));
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, rail_set_mv(1, 0));
|
||||
TEST_ASSERT_FALSE(rail_mv_in_range(3523));
|
||||
TEST_ASSERT_TRUE(rail_mv_in_range(RAIL_MV_MIN));
|
||||
}
|
||||
|
||||
static void test_rail_index_is_one_based(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, rail_set_mv(0, RAIL_MV_SAFE));
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, rail_set_mv(6, RAIL_MV_SAFE));
|
||||
int mv = 0;
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, rail_get_mv(0, &mv));
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, rail_get_mv(1, NULL));
|
||||
}
|
||||
|
||||
/* ---- ② 反向关系 ---------------------------------------------------------- */
|
||||
|
||||
/* ⭐ 验收主项二:**码越大,输出越低。**
|
||||
写反了不报错 —— 「调高电压」的命令把手机调到 3.52 V,手机照样开机,
|
||||
要跑一阵子才在负载尖峰下掉电。所以这里既查换算,也查真写进 DAC 的那个数。 */
|
||||
static void test_higher_code_means_lower_voltage(void)
|
||||
{
|
||||
/* 换算层:单调**递减**,端点钉死。 */
|
||||
TEST_ASSERT_EQUAL_UINT16(0, rail_mv_to_code(RAIL_MV_MAX));
|
||||
TEST_ASSERT_TRUE(rail_mv_to_code(RAIL_MV_MIN) > rail_mv_to_code(RAIL_MV_MAX));
|
||||
TEST_ASSERT_TRUE(rail_mv_to_code(3600) > rail_mv_to_code(4000));
|
||||
|
||||
uint16_t prev = rail_mv_to_code(RAIL_MV_MIN);
|
||||
for (int mv = RAIL_MV_MIN + 1; mv <= RAIL_MV_MAX; mv++) {
|
||||
uint16_t code = rail_mv_to_code(mv);
|
||||
TEST_ASSERT_TRUE_MESSAGE(code <= prev, "mv 变大,码必须变小或持平");
|
||||
prev = code;
|
||||
}
|
||||
|
||||
/* 总线层:同一路先设高再设低,DAC 里的数必须变大。 */
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_set_mv(1, 4000));
|
||||
uint16_t at_4000 = raw_code(1);
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_set_mv(1, 3600));
|
||||
uint16_t at_3600 = raw_code(1);
|
||||
TEST_ASSERT_TRUE_MESSAGE(at_3600 > at_4000, "3.60 V 的码必须比 4.00 V 的大");
|
||||
}
|
||||
|
||||
/* ---- ③ 3900 mV ↔ Vd 0.548 V --------------------------------------------- */
|
||||
|
||||
/* ⭐ 验收主项三:NVM 出厂默认那一点(设计文档 §4.3 / §10.5 的表)。 */
|
||||
static void test_3900_mv_is_vd_548_mv(void)
|
||||
{
|
||||
uint16_t code = rail_mv_to_code(3900);
|
||||
TEST_ASSERT_EQUAL_UINT16(926, code);
|
||||
/* Vd = 926 / 4096 × 2.424 V = 548.0 mV */
|
||||
TEST_ASSERT_INT32_WITHIN_MESSAGE(500, 548000, rail_code_to_vd_uv(code),
|
||||
"3900 mV 对应的 Vd 必须是 0.548 V");
|
||||
TEST_ASSERT_EQUAL_INT(3900, rail_code_to_mv(code));
|
||||
}
|
||||
|
||||
/* 设计文档 §4.3 那三个角点,一个不落。 */
|
||||
static void test_transfer_curve_endpoints(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(RAIL_MV_MAX, rail_code_to_mv(0)); /* Vd 0 V -> 4.010 V */
|
||||
TEST_ASSERT_EQUAL_INT(3900, rail_code_to_mv(926)); /* Vd 0.548 -> 3.900 V */
|
||||
TEST_ASSERT_EQUAL_INT(3524, rail_code_to_mv(DAC63204_CODE_MAX)); /* 满码 -> 3.524 V */
|
||||
|
||||
/* 上下限都必须落在 12 位码里 —— 越界说明 RAIL_MV_MIN/MAX 和电阻值对不上了。 */
|
||||
TEST_ASSERT_TRUE(rail_mv_to_code(RAIL_MV_MIN) <= DAC63204_CODE_MAX);
|
||||
TEST_ASSERT_TRUE(rail_mv_to_code(RAIL_MV_MAX) <= DAC63204_CODE_MAX);
|
||||
}
|
||||
|
||||
/* 全量程往返:设进去再读回来,误差不超过 1 mV(一个码在输出端 0.119 mV)。 */
|
||||
static void test_set_get_round_trip(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
for (int rail = 1; rail <= PHONERAIL_RAIL_COUNT; rail++) {
|
||||
for (int mv = RAIL_MV_MIN; mv <= RAIL_MV_MAX; mv += 7) {
|
||||
int back = 0;
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_set_mv(rail, mv));
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_get_mv(rail, &back));
|
||||
TEST_ASSERT_INT_WITHIN(1, mv, back);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 五路互不串扰 --------------------------------------------------------- */
|
||||
|
||||
/* 改一路不许动到别的四路。CH1 与 CH2 在同一片(U6)上,是最容易串的一对。 */
|
||||
static void test_setting_one_rail_leaves_the_others_alone(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
|
||||
uint16_t before[PHONERAIL_RAIL_COUNT + 1];
|
||||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||||
before[r] = raw_code(r);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_set_mv(1, 3600));
|
||||
for (int r = 2; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||||
TEST_ASSERT_EQUAL_UINT16_MESSAGE(before[r], raw_code(r), "只该动 CH1");
|
||||
}
|
||||
TEST_ASSERT_TRUE(raw_code(1) != before[1]);
|
||||
}
|
||||
|
||||
/* 五路各写一个不同的值,逐路读回来必须各是各的 —— 映射表串了这条就红。 */
|
||||
static void test_each_rail_keeps_its_own_value(void)
|
||||
{
|
||||
static const int want[PHONERAIL_RAIL_COUNT] = { 3600, 3700, 3800, 3900, 4000 };
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_set_mv(r, want[r - 1]));
|
||||
}
|
||||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||||
int back = 0;
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_get_mv(r, &back));
|
||||
TEST_ASSERT_INT_WITHIN(1, want[r - 1], back);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 初始化 -------------------------------------------------------------- */
|
||||
|
||||
/* 上电即安全值:init 完五路都应该在 3.90 V 上,不依赖后续有人来设。 */
|
||||
static void test_init_leaves_every_rail_at_the_safe_value(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) {
|
||||
int mv = 0;
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_get_mv(r, &mv));
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(RAIL_MV_SAFE, mv, "上电安全值必须是 3900 mV");
|
||||
}
|
||||
}
|
||||
|
||||
/* 少一片 DAC:init 必须如实失败(降级留给 A15,这里不许悄悄当成功)。 */
|
||||
static void test_init_fails_when_a_chip_is_missing(void)
|
||||
{
|
||||
sim_i2c_add(DAC63204_ADDR_U6, 2);
|
||||
sim_i2c_set_reg(DAC63204_ADDR_U6, DAC63204_REG_GENERAL_STATUS,
|
||||
(uint16_t)(DAC63204_DEVICE_ID << 2));
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, hal_i2c_init(NULL));
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, rail_volt_init()); /* U7 没挂 */
|
||||
}
|
||||
|
||||
static void test_set_propagates_bus_errors(void)
|
||||
{
|
||||
mount_both();
|
||||
TEST_ASSERT_EQUAL_INT(HAL_OK, rail_volt_init());
|
||||
sim_i2c_set_present(dac63204_rail_addr(3), false);
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, rail_set_mv(3, 3800));
|
||||
int mv = 0;
|
||||
TEST_ASSERT_EQUAL_INT(HAL_ERR_NAK, rail_get_mv(3, &mv));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_4020_mv_is_rejected);
|
||||
RUN_TEST(test_below_range_is_rejected);
|
||||
RUN_TEST(test_rail_index_is_one_based);
|
||||
RUN_TEST(test_higher_code_means_lower_voltage);
|
||||
RUN_TEST(test_3900_mv_is_vd_548_mv);
|
||||
RUN_TEST(test_transfer_curve_endpoints);
|
||||
RUN_TEST(test_set_get_round_trip);
|
||||
RUN_TEST(test_setting_one_rail_leaves_the_others_alone);
|
||||
RUN_TEST(test_each_rail_keeps_its_own_value);
|
||||
RUN_TEST(test_init_leaves_every_rail_at_the_safe_value);
|
||||
RUN_TEST(test_init_fails_when_a_chip_is_missing);
|
||||
RUN_TEST(test_set_propagates_bus_errors);
|
||||
return UNITY_END();
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户