/* 五路输出电压:mV ↔ DAC 码的**唯一**入口(需求 §10.5)。 ⚠️ 这一节最容易写反的一条:**DAC 写得越大,手机看到的电压越低。** 写反了不会报错 —— 「调高电压」的命令会把手机调到 3.52 V,手机照样开机, 只是更容易在负载尖峰下掉电,而且要跑一阵子才看得出来。 所以:**除了本文件,任何地方都不许直接调 dac63204_write_code()。** 分压网络(设计文档 §4.2 / §4.3,R1 30.1k / R2 5.49k / Rinj 150k、Vref 0.6 V): Vout = 0.6 × (1 + R1/R2 + R1/Rinj) − (R1/Rinj) × Vd = 4.010 V − 0.2007 × Vd Vd ∈ [0, 2.424] V DAC 满摆在输出端只值 486.4 mV —— 这是**有意的**:软件在物理上无法产生 3.524~4.010 V 以外的电压(设计文档 §4.1「本质安全」)。 */ #ifndef RAIL_H #define RAIL_H #include #include #include "hal_err.h" #ifdef __cplusplus extern "C" { #endif /* ---- 换算常量(全部 µV,整数运算,不引入浮点) ------------------------- */ /* Vd = 0 时的输出:0.6 V × (1 + 30100/5490 + 30100/150000) = 4.0100175 V。 取整到 µV 丢掉 0.5 µV,而一个码在输出端是 118.75 µV,可以忽略。 */ #define RAIL_VOUT0_UV 4010018 /* DAC 满量程 2.424 V 经 R1/Rinj = 30100/150000 折算到输出端:正好 486416 µV。 注意**是精确值不是近似**:2424000 × 30100 / 150000 = 486416。 */ #define RAIL_DAC_SPAN_UV 486416 /* 可设定范围。上限由 SR-13 拦(这是**第四道**限制,前三道在硬件里); 下限是 DAC 满码也到不了更低。 */ #define RAIL_MV_MAX 4010 #define RAIL_MV_MIN 3524 /* 上电安全值 = DAC 的 NVM 出厂默认(Vd 0.548 V),不依赖 ESP32 初始化成功。 */ #define RAIL_MV_SAFE 3900 /* ---- 纯换算,不碰总线 ---------------------------------------------------- */ bool rail_mv_in_range(int mv); /* mv 必须先过 rail_mv_in_range(),越界直接 abort(那是调用方漏判)。 反向关系就在这里:mv 越小,返回的码越大。 */ uint16_t rail_mv_to_code(int mv); int rail_code_to_mv(uint16_t code); int32_t rail_code_to_vd_uv(uint16_t code); /* 码 → DAC 自己的输出 Vd(µV) */ /* ---- 器件操作 ------------------------------------------------------------ */ /* 认件 + 初始化两片 DAC,并把五路都置到 RAIL_MV_SAFE。 两片都会试;返回第一个错误码(降级矩阵在 bringup.c)。 */ hal_err_t rail_volt_init(void); /* 只做其中一片(`dac63204_all_addr[]` 里的地址)。A15 的启动序列按片调它, 好分别记「哪一片没应答」(§10.10 DAC 那一行)。两片的失败互不影响。 */ hal_err_t rail_volt_init_one(uint8_t addr); /* rail 是 **1~5**(不是 0~4),越界 → HAL_ERR_ARG。 mv > 4010 → HAL_ERR_ARG(SR-13:越界即拒,让操作者早点知道自己写错了)。 mv < 3524 → HAL_ERR_ARG(DAC 到不了)。 ⚠️ **不要用它做保护** —— 异常处理是 EFUSE_EN 的活(需求 §10.5 规矩 2)。 ⚠️ **本函数不拿 I²C 锁**(它比 `i2cbus` 早)。运行期调用必须包在 `i2cbus_xact(I2CBUS_CH_TRUNK, ...)` 里,否则就是在巡检的事务中间插一笔(A12 的 cmd_exec.c)。 被拒的请求由调用方记事件(A12:`LOG_EV_CMD_REJECT`),本函数只返回错误码。 */ hal_err_t rail_set_mv(int rail, int mv); /* 回读该路 DAC 里的码并换算成 mV。读的是**设定值**,不是实测输出 —— 实测要走该路的 INA226(A10 的巡检)。 */ hal_err_t rail_get_mv(int rail, int *mv); /* 最后一次**成功**写进那一路 DAC 的设定值(mV)。不碰总线。 A10 的 SR-5 判据要的就是这个「设定值」(`|实测 − 设定| > 100 mV`)。 不用 `rail_get_mv()` 是因为它是一次 I²C 事务:巡检每 20 ms 跑一圈, 五路各多一次读就是每圈多 5 次事务,把 §10.3 那 7.8 % 的预算顶到 11 %, 而这个数是编译期就知道的。 上电默认是 `RAIL_MV_SAFE`(3900)—— 那不是猜的,是 DAC63204 **NVM 里的 出厂默认值**(rail.h 开头那段),所以在 `rail_volt_init()` 跑之前它也是真的。 写失败时不更新:那一次 DAC 里到底是什么值不知道,留着上一个成功的更可信。 */ int rail_setpoint_mv(int rail); /* 把五路的设定值影子放回 `RAIL_MV_SAFE`,**不碰 DAC**。 给两处用:宿主机测试的 `setUp()`(`sim_hal_reset()` 够不到应用层的静态变量), 以及 A15 在「DAC 无应答」降级路径上声明「现在生效的是 NVM 默认值」。 */ void rail_setpoint_reset(void); #ifdef __cplusplus } #endif #endif /* RAIL_H */