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
这个提交包含在:
@@ -4,5 +4,6 @@
|
||||
idf_component_register(
|
||||
SRCS "app_main.c"
|
||||
"version.c"
|
||||
"rail.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES phonerail_hal drv)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define PHONERAIL_H
|
||||
|
||||
/* 固件版本。规则:<主>.<次>.<修订>-<最后完成的任务包> */
|
||||
#define PHONERAIL_FW_VERSION "0.1.0-a2"
|
||||
#define PHONERAIL_FW_VERSION "0.1.0-a4"
|
||||
|
||||
/* 通道数。五路直供,见设计文档 §2。 */
|
||||
#define PHONERAIL_RAIL_COUNT 5
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/* 五路输出电压的换算与设定。与硬件无关,宿主机与 ESP32-S3 上编的是同一份。 */
|
||||
#include "rail.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "dac63204.h"
|
||||
#include "phonerail.h"
|
||||
|
||||
/* 五路数与 DAC 侧的映射表必须是同一个数 —— 分家的那天就是错一路的那天。 */
|
||||
_Static_assert(PHONERAIL_RAIL_COUNT == DAC63204_RAIL_COUNT, "五路数对不上");
|
||||
|
||||
bool rail_mv_in_range(int mv)
|
||||
{
|
||||
return mv >= RAIL_MV_MIN && mv <= RAIL_MV_MAX;
|
||||
}
|
||||
|
||||
uint16_t rail_mv_to_code(int mv)
|
||||
{
|
||||
assert(rail_mv_in_range(mv)); /* 越界要在 rail_set_mv() 里被拒掉,不该走到这儿 */
|
||||
|
||||
/* code = (Vout0 − Vout) / 486416 µV × 4096,四舍五入。
|
||||
**减法在这里**:Vout 越低,差越大,码越大 —— 反向关系只此一处。 */
|
||||
int64_t diff_uv = (int64_t)RAIL_VOUT0_UV - (int64_t)mv * 1000;
|
||||
int64_t code = (diff_uv * DAC63204_CODE_SPAN + RAIL_DAC_SPAN_UV / 2) / RAIL_DAC_SPAN_UV;
|
||||
|
||||
if (code < 0) {
|
||||
code = 0;
|
||||
}
|
||||
if (code > DAC63204_CODE_MAX) {
|
||||
code = DAC63204_CODE_MAX;
|
||||
}
|
||||
return (uint16_t)code;
|
||||
}
|
||||
|
||||
int rail_code_to_mv(uint16_t code)
|
||||
{
|
||||
int64_t drop_uv = ((int64_t)code * RAIL_DAC_SPAN_UV + DAC63204_CODE_SPAN / 2)
|
||||
/ DAC63204_CODE_SPAN;
|
||||
int64_t uv = (int64_t)RAIL_VOUT0_UV - drop_uv;
|
||||
return (int)((uv + 500) / 1000);
|
||||
}
|
||||
|
||||
int32_t rail_code_to_vd_uv(uint16_t code)
|
||||
{
|
||||
return dac63204_code_to_uv(code);
|
||||
}
|
||||
|
||||
hal_err_t rail_volt_init(void)
|
||||
{
|
||||
const uint16_t safe = rail_mv_to_code(RAIL_MV_SAFE);
|
||||
|
||||
for (int i = 0; i < DAC63204_COUNT; i++) {
|
||||
uint8_t addr = dac63204_all_addr[i];
|
||||
hal_err_t err = dac63204_probe(addr);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
/* init 自己就把用到的通道写成 safe 了(上电之前写,见 dac63204_init 的 ③)。 */
|
||||
err = dac63204_init(addr, safe);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
hal_err_t rail_set_mv(int rail, int mv)
|
||||
{
|
||||
if (rail < 1 || rail > PHONERAIL_RAIL_COUNT) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
if (!rail_mv_in_range(mv)) {
|
||||
return HAL_ERR_ARG; /* SR-13。TODO(A11):这里要记一条事件 */
|
||||
}
|
||||
return dac63204_write_code(dac63204_rail_addr(rail), dac63204_rail_ch(rail),
|
||||
rail_mv_to_code(mv));
|
||||
}
|
||||
|
||||
hal_err_t rail_get_mv(int rail, int *mv)
|
||||
{
|
||||
if (rail < 1 || rail > PHONERAIL_RAIL_COUNT || mv == NULL) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
uint16_t code = 0;
|
||||
hal_err_t err = dac63204_read_code(dac63204_rail_addr(rail), dac63204_rail_ch(rail), &code);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
*mv = rail_code_to_mv(code);
|
||||
return HAL_OK;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* 五路输出电压: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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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。
|
||||
任何一片失败就返回它的错误码(降级矩阵在 A15 处理)。 */
|
||||
hal_err_t rail_volt_init(void);
|
||||
|
||||
/* 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)。
|
||||
TODO(A11):被拒的请求要记一条事件;事件环形缓冲还没有,先只返回错误码。 */
|
||||
hal_err_t rail_set_mv(int rail, int mv);
|
||||
|
||||
/* 回读该路 DAC 里的码并换算成 mV。读的是**设定值**,不是实测输出 ——
|
||||
实测要走该路的 INA226(A10 的巡检)。 */
|
||||
hal_err_t rail_get_mv(int rail, int *mv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RAIL_H */
|
||||
在新工单中引用
屏蔽一个用户