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 @@
|
||||
# 驱动只准依赖 phonerail_hal,不准直接包含 IDF 的头。
|
||||
idf_component_register(
|
||||
SRCS "ina226.c"
|
||||
"dac63204.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES phonerail_hal)
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/* DAC63204 驱动。只经 hal_i2c_*,不碰任何平台头文件。 */
|
||||
#include "dac63204.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hal_i2c.h"
|
||||
|
||||
/* 设计文档 §8.2(两片地址)+ §26.18(按 PCB 几何重排过的通道分配)。
|
||||
⚠️ 不是「前三路在 U6、通道号 = rail−1」—— 五路里有四路对不上那个算式。 */
|
||||
static const uint8_t s_rail_addr[DAC63204_RAIL_COUNT] = {
|
||||
DAC63204_ADDR_U6, /* CH1 */
|
||||
DAC63204_ADDR_U6, /* CH2 */
|
||||
DAC63204_ADDR_U7, /* CH3 ← 在 U7 上,不在 U6 */
|
||||
DAC63204_ADDR_U6, /* CH4 */
|
||||
DAC63204_ADDR_U7, /* CH5 */
|
||||
};
|
||||
static const uint8_t s_rail_ch[DAC63204_RAIL_COUNT] = { 2, 0, 0, 1, 1 };
|
||||
|
||||
const uint8_t dac63204_all_addr[DAC63204_COUNT] = { DAC63204_ADDR_U6, DAC63204_ADDR_U7 };
|
||||
|
||||
uint8_t dac63204_rail_addr(int rail)
|
||||
{
|
||||
assert(rail >= 1 && rail <= DAC63204_RAIL_COUNT); /* 越界是代码写错,不许悄悄返回一片 */
|
||||
return s_rail_addr[rail - 1];
|
||||
}
|
||||
|
||||
uint8_t dac63204_rail_ch(int rail)
|
||||
{
|
||||
assert(rail >= 1 && rail <= DAC63204_RAIL_COUNT);
|
||||
return s_rail_ch[rail - 1];
|
||||
}
|
||||
|
||||
uint8_t dac63204_used_mask(uint8_t addr)
|
||||
{
|
||||
uint8_t mask = 0;
|
||||
for (int rail = 1; rail <= DAC63204_RAIL_COUNT; rail++) {
|
||||
if (s_rail_addr[rail - 1] == addr) {
|
||||
mask |= (uint8_t)(1u << s_rail_ch[rail - 1]);
|
||||
}
|
||||
}
|
||||
return mask; /* 不是这两片中的一片 -> 0,init 时会被 HAL_ERR_ARG 拦下 */
|
||||
}
|
||||
|
||||
bool dac63204_map_ok(void)
|
||||
{
|
||||
/* 同一条 I²C 上的其它器件(§8.2):撞上任何一个都是硬件表被改坏了。 */
|
||||
static const uint8_t others[] = { 0x40, 0x41, 0x44, 0x45, 0x42, 0x43, 0x20, 0x48 };
|
||||
|
||||
if (dac63204_all_addr[0] == dac63204_all_addr[1]) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < DAC63204_COUNT; i++) {
|
||||
if (dac63204_all_addr[i] > 0x7f) {
|
||||
return false;
|
||||
}
|
||||
for (size_t k = 0; k < sizeof(others); k++) {
|
||||
if (dac63204_all_addr[i] == others[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int a = 1; a <= DAC63204_RAIL_COUNT; a++) {
|
||||
uint8_t addr = s_rail_addr[a - 1];
|
||||
uint8_t ch = s_rail_ch[a - 1];
|
||||
if (ch > 3) {
|
||||
return false;
|
||||
}
|
||||
if (addr != dac63204_all_addr[0] && addr != dac63204_all_addr[1]) {
|
||||
return false;
|
||||
}
|
||||
/* 一个 (片, 通道) 只能被一路占用 —— 两路撞在一起的现象是「调这路那路也动」。 */
|
||||
for (int b = a + 1; b <= DAC63204_RAIL_COUNT; b++) {
|
||||
if (addr == s_rail_addr[b - 1] && ch == s_rail_ch[b - 1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((dac63204_used_mask(addr) & (1u << ch)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---- 寄存器读写 ---------------------------------------------------------
|
||||
和 INA226 那份一样:**每次读之前重写寄存器指针**,不依赖指针自增。
|
||||
sim 的假器件会自增,真器件的行为没在实物上验过(B1 上板时确认)。 */
|
||||
|
||||
static hal_err_t read_reg(uint8_t addr, uint8_t reg, uint16_t *raw)
|
||||
{
|
||||
uint8_t rd[2];
|
||||
hal_err_t err = hal_i2c_write_read(addr, ®, 1, rd, sizeof(rd));
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
*raw = (uint16_t)(((uint16_t)rd[0] << 8) | rd[1]); /* 大端 */
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
static hal_err_t write_reg(uint8_t addr, uint8_t reg, uint16_t val)
|
||||
{
|
||||
const uint8_t wr[3] = { reg, (uint8_t)(val >> 8), (uint8_t)(val & 0xff) };
|
||||
return hal_i2c_write(addr, wr, sizeof(wr));
|
||||
}
|
||||
|
||||
static hal_err_t write_verify(uint8_t addr, uint8_t reg, uint16_t val)
|
||||
{
|
||||
hal_err_t err = write_reg(addr, reg, val);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
uint16_t back = 0;
|
||||
err = read_reg(addr, reg, &back);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
return back == val ? HAL_OK : HAL_ERR_BUS;
|
||||
}
|
||||
|
||||
/* ---- COMMON-CONFIG 的两个形态 ------------------------------------------- */
|
||||
|
||||
/* ② 把 used 里的通道上电,其余保持 Hi-Z。电流输出一律掉电(本板只用电压输出)。 */
|
||||
uint16_t dac63204_cc_run(uint8_t addr)
|
||||
{
|
||||
uint8_t used = dac63204_used_mask(addr);
|
||||
uint16_t v = DAC63204_CC_EN_INT_REF | DAC63204_CC_IOUT_PDN_ALL;
|
||||
for (int ch = 0; ch < 4; ch++) {
|
||||
if ((used & (1u << ch)) == 0) {
|
||||
v |= DAC63204_CC_VOUT_PDN(ch); /* 没用到:FBx 悬空,绝不能上电 */
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* ---- 对外 ---------------------------------------------------------------- */
|
||||
|
||||
hal_err_t dac63204_probe(uint8_t addr)
|
||||
{
|
||||
uint16_t st = 0;
|
||||
hal_err_t err = read_reg(addr, DAC63204_REG_GENERAL_STATUS, &st);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
/* 应答了但 ID 不对:多半贴成了 DAC53204(02h)/DAC43204(03h),
|
||||
那两颗只有 10 位 / 8 位,码值左对齐的位数不一样,装上去电压全错。 */
|
||||
return DAC63204_STATUS_ID(st) == DAC63204_DEVICE_ID ? HAL_OK : HAL_ERR_BUS;
|
||||
}
|
||||
|
||||
hal_err_t dac63204_init(uint8_t addr, uint16_t safe_code)
|
||||
{
|
||||
uint8_t used = dac63204_used_mask(addr);
|
||||
if (used == 0 || safe_code > DAC63204_CODE_MAX) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
|
||||
/* ① 先开内部基准(输出还全部掉电着) */
|
||||
hal_err_t err = write_verify(addr, DAC63204_REG_COMMON_CONFIG, DAC63204_CC_REF_ONLY);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
/* ② 增益 + ③ 安全码。**都要在上电之前**:复位码是 0,而 Vd=0 对应输出 4.010 V。 */
|
||||
for (int ch = 0; ch < 4; ch++) {
|
||||
if ((used & (1u << ch)) == 0) {
|
||||
continue;
|
||||
}
|
||||
err = write_verify(addr, DAC63204_REG_VOUT_CMP_CONFIG(ch), DAC63204_CFG_GAIN_2X_INT);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
err = write_verify(addr, DAC63204_REG_DATA(ch), DAC63204_CODE_TO_REG(safe_code));
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* ④ 用到的通道上电 */
|
||||
return write_verify(addr, DAC63204_REG_COMMON_CONFIG, dac63204_cc_run(addr));
|
||||
}
|
||||
|
||||
hal_err_t dac63204_check(uint8_t addr)
|
||||
{
|
||||
uint8_t used = dac63204_used_mask(addr);
|
||||
if (used == 0) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
uint16_t cc = 0;
|
||||
hal_err_t err = read_reg(addr, DAC63204_REG_COMMON_CONFIG, &cc);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
if (cc != dac63204_cc_run(addr)) {
|
||||
return HAL_ERR_BUS;
|
||||
}
|
||||
for (int ch = 0; ch < 4; ch++) {
|
||||
if ((used & (1u << ch)) == 0) {
|
||||
continue;
|
||||
}
|
||||
uint16_t cfg = 0;
|
||||
err = read_reg(addr, DAC63204_REG_VOUT_CMP_CONFIG(ch), &cfg);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
if (cfg != DAC63204_CFG_GAIN_2X_INT) {
|
||||
return HAL_ERR_BUS;
|
||||
}
|
||||
}
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
hal_err_t dac63204_write_code(uint8_t addr, uint8_t ch, uint16_t code)
|
||||
{
|
||||
if (ch > 3 || code > DAC63204_CODE_MAX) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
return write_reg(addr, DAC63204_REG_DATA(ch), DAC63204_CODE_TO_REG(code));
|
||||
}
|
||||
|
||||
hal_err_t dac63204_read_code(uint8_t addr, uint8_t ch, uint16_t *code)
|
||||
{
|
||||
if (ch > 3 || code == NULL) {
|
||||
return HAL_ERR_ARG;
|
||||
}
|
||||
uint16_t raw = 0;
|
||||
hal_err_t err = read_reg(addr, DAC63204_REG_DATA(ch), &raw);
|
||||
if (err != HAL_OK) {
|
||||
return err;
|
||||
}
|
||||
*code = DAC63204_REG_TO_CODE(raw);
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
int32_t dac63204_code_to_uv(uint16_t code)
|
||||
{
|
||||
/* Vd = code / 2^12 × 2.424 V(datasheet 式 (1),增益 2× 内部基准)。 */
|
||||
int64_t num = (int64_t)code * DAC63204_FULLSCALE_UV + DAC63204_CODE_SPAN / 2;
|
||||
return (int32_t)(num / DAC63204_CODE_SPAN);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/* DAC63204 —— 板上 2 片(U6 = 0x4A、U7 = 0x4B),5 路调压用 5 个通道。
|
||||
|
||||
这颗 DAC **不做全范围调压**,只在固定中心点上做 ±0.24 V 的微调(设计文档 §4.1),
|
||||
而且是**反向**的:写进去的码越大,手机看到的电压越低。换算一律走 `rail.h` 的
|
||||
`rail_set_mv()`,本文件只负责「把一个 12 位码写进某片某通道」。
|
||||
|
||||
本驱动只通过 hal_i2c_* 访问总线,宿主机上用 sim 的假器件即可全部测完。
|
||||
|
||||
寄存器与位定义出处:TI SLASEX3A(DAC63204/53204/43204)表 7-21、7-26、7-32、7-33。 */
|
||||
#ifndef DAC63204_H
|
||||
#define DAC63204_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- 寄存器(全部 16 位、大端) ---------------------------------------- */
|
||||
|
||||
/* 每通道 6 个寄存器一组:03h/09h/0Fh/15h。 */
|
||||
#define DAC63204_REG_VOUT_CMP_CONFIG(ch) ((uint8_t)(0x03 + 0x06 * (ch)))
|
||||
/* 数据寄存器却是**连续**的:19h/1Ah/1Bh/1Ch。两种步长别记混。 */
|
||||
#define DAC63204_REG_DATA(ch) ((uint8_t)(0x19 + (ch)))
|
||||
|
||||
#define DAC63204_REG_COMMON_CONFIG 0x1F
|
||||
#define DAC63204_REG_COMMON_TRIGGER 0x20
|
||||
#define DAC63204_REG_GENERAL_STATUS 0x22
|
||||
|
||||
/* ---- 位域 --------------------------------------------------------------- */
|
||||
|
||||
/* DAC-X-VOUT-CMP-CONFIG bit12-10 = VOUT-GAIN-X。
|
||||
011b = 增益 2×、用内部基准 —— 这一路的满量程 1.212 V × 2 = **2.424 V**
|
||||
(不是 2.5 V,设计文档 §4.5 早期写错过一次)。 */
|
||||
#define DAC63204_VOUT_GAIN_SHIFT 10
|
||||
#define DAC63204_VOUT_GAIN_2X_INT 0x3
|
||||
#define DAC63204_CFG_GAIN_2X_INT ((uint16_t)(DAC63204_VOUT_GAIN_2X_INT << DAC63204_VOUT_GAIN_SHIFT))
|
||||
|
||||
/* COMMON-CONFIG(1Fh,复位值 0FFFh = 八路输出全部掉电)。
|
||||
bit12 EN-INT-REF —— 用内部基准增益之前**必须先置 1**(datasheet 原话)
|
||||
bit11-10 / 8-7 / 5-4 / 2-1 VOUT-PDN-3/2/1/0:00 = 上电,11 = Hi-Z 掉电
|
||||
bit9 / 6 / 3 / 0 IOUT-PDN-3/2/1/0:1 = 掉电(本板不用电流输出) */
|
||||
#define DAC63204_CC_EN_INT_REF ((uint16_t)(1u << 12))
|
||||
#define DAC63204_CC_IOUT_PDN_ALL ((uint16_t)0x0249) /* bit 0/3/6/9 */
|
||||
/* 某通道的 VOUT-PDN 两位全 1 = Hi-Z 掉电。ch: 0~3。 */
|
||||
#define DAC63204_CC_VOUT_PDN(ch) ((uint16_t)(0x3u << (1 + 3 * (ch))))
|
||||
|
||||
/* `dac63204_init()` 第 ① 步写进去的值:**只开内部基准,八路输出仍全部 Hi-Z**。
|
||||
必须先有这一步,安全码才来得及在上电之前写进数据寄存器。 */
|
||||
#define DAC63204_CC_REF_ONLY ((uint16_t)0x1FFF)
|
||||
|
||||
/* GENERAL-STATUS(22h)bit7-2 = DEVICE-ID。DAC63204 是 01h
|
||||
(DAC53204 = 02h、DAC43204 = 03h —— 贴错料就是这里露馅)。 */
|
||||
#define DAC63204_DEVICE_ID 0x01
|
||||
#define DAC63204_STATUS_ID(reg) ((uint8_t)(((reg) >> 2) & 0x3f))
|
||||
#define DAC63204_STATUS_NVM_BUSY ((uint16_t)(1u << 8))
|
||||
|
||||
/* ---- 分辨率与量程 ------------------------------------------------------- */
|
||||
|
||||
#define DAC63204_BITS 12
|
||||
#define DAC63204_CODE_MAX 4095
|
||||
#define DAC63204_CODE_SPAN 4096 /* VOUT = code / 2^12 × VREF × GAIN,见 datasheet 式 (1) */
|
||||
|
||||
/* 内部基准 1.212 V × 增益 2 = 满量程 2.424 V。µV 为单位,整数运算用。 */
|
||||
#define DAC63204_FULLSCALE_UV 2424000
|
||||
|
||||
/* ⚠️ **12 位码在 DAC-X-DATA 里是左对齐的**(bit15-4,低 4 位 don't care)。
|
||||
忘了 `<< 4` 不会报错,只会让输出变成期望值的 1/16 —— 五路全部顶到 4.010 V,
|
||||
而这在本板上是「正常满电」,看不出异常(设计文档 §4.4)。 */
|
||||
#define DAC63204_DATA_SHIFT 4
|
||||
#define DAC63204_CODE_TO_REG(code) ((uint16_t)((uint16_t)(code) << DAC63204_DATA_SHIFT))
|
||||
#define DAC63204_REG_TO_CODE(reg) ((uint16_t)((reg) >> DAC63204_DATA_SHIFT))
|
||||
|
||||
/* ---- 两片的地址与「哪一路挂在哪片哪通道」(设计文档 §8.2 + §26.18) ----- */
|
||||
|
||||
#define DAC63204_COUNT 2
|
||||
#define DAC63204_RAIL_COUNT 5
|
||||
#define DAC63204_ADDR_U6 0x4A /* A0 接 SDA */
|
||||
#define DAC63204_ADDR_U7 0x4B /* A0 接 SCL */
|
||||
|
||||
extern const uint8_t dac63204_all_addr[DAC63204_COUNT];
|
||||
|
||||
/* rail 是 1~5,越界直接 abort。
|
||||
⚠️ **「前三路在 U6、通道号 = rail−1」是错的**:2026-09-06 按 PCB 几何重排过
|
||||
(§26.18),五路里有四路对不上那个算式。真表是
|
||||
|
||||
CH1 → U6.OUT2 CH2 → U6.OUT0 CH3 → **U7**.OUT0 CH4 → U6.OUT1 CH5 → U7.OUT1
|
||||
|
||||
四个通道在电气上完全对等,重排只改固件通道号,一根线都没动 ——
|
||||
所以**接错了板子照样正常工作,只是调 CH1 的命令去调了 CH2**。查表,不要算。 */
|
||||
uint8_t dac63204_rail_addr(int rail);
|
||||
uint8_t dac63204_rail_ch(int rail);
|
||||
|
||||
/* 某片上被真正用到的通道位图(bit0..bit3)。没用到的通道 FBx 悬空,
|
||||
**不许上电**:电压输出模式下 FBx 是运放的反馈脚,悬空上电就是开环
|
||||
(datasheet 表 5-1 要求 FBx 接到 OUTx)。 */
|
||||
uint8_t dac63204_used_mask(uint8_t addr);
|
||||
|
||||
/* 该片跑起来之后 COMMON-CONFIG 应该是什么:内部基准开、用到的通道上电、
|
||||
其余 Hi-Z、四路电流输出全掉电。`dac63204_check()` 拿它对账。 */
|
||||
uint16_t dac63204_cc_run(uint8_t addr);
|
||||
|
||||
/* 两片地址不重复、不与 INA226 六片 / PCA9555 / LM75 撞车,
|
||||
且五路映射覆盖到的 (片, 通道) 互不重复、都在 used_mask 里。
|
||||
启动自检调一次;返回 false = 表被改坏了。 */
|
||||
bool dac63204_map_ok(void);
|
||||
|
||||
/* ---- 器件操作 ----------------------------------------------------------- */
|
||||
|
||||
/* 读 GENERAL-STATUS 认件。
|
||||
不应答 → HAL_ERR_NAK;**应答了但 DEVICE-ID 不是 01h → HAL_ERR_BUS**
|
||||
(这个地址上有东西,但不是 DAC63204:多半贴成了 53204/43204,或地址脚焊错)。 */
|
||||
hal_err_t dac63204_probe(uint8_t addr);
|
||||
|
||||
/* 初始化一片。顺序是有讲究的,四步不能换:
|
||||
① COMMON-CONFIG 先只开 EN-INT-REF,**输出仍全部掉电**
|
||||
(datasheet:用内部基准增益之前这一位必须已经是 1)
|
||||
② 逐个用到的通道写 VOUT-CMP-CONFIG = 增益 2× 内部基准
|
||||
③ 逐个用到的通道写 `safe_code`(上电安全值),**在上电之前写**
|
||||
—— 否则输出会先在复位码 0 上停一下,那是 Vd = 0 V = 输出 4.010 V 的上限
|
||||
④ COMMON-CONFIG 再写一次,把**用到的**通道上电(没用到的保持 Hi-Z)
|
||||
每一步都回读校验,不一致 → HAL_ERR_BUS。 */
|
||||
hal_err_t dac63204_init(uint8_t addr, uint16_t safe_code);
|
||||
|
||||
/* 只校验 COMMON-CONFIG 与各通道增益还在(巡检用)。
|
||||
这两样掉了不会报错,只会让输出悄悄跑到别的量程上。 */
|
||||
hal_err_t dac63204_check(uint8_t addr);
|
||||
|
||||
/* 写 / 读某通道的 12 位码。code > 4095 → HAL_ERR_ARG;ch 越界 → HAL_ERR_ARG。 */
|
||||
hal_err_t dac63204_write_code(uint8_t addr, uint8_t ch, uint16_t code);
|
||||
hal_err_t dac63204_read_code(uint8_t addr, uint8_t ch, uint16_t *code);
|
||||
|
||||
/* 码 → DAC 输出电压 Vd(µV)。四舍五入。 */
|
||||
int32_t dac63204_code_to_uv(uint16_t code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DAC63204_H */
|
||||
在新工单中引用
屏蔽一个用户