固件:R2 迁移 R-0 ~ R-6(底板 + 5 片同构刀片)

2026-09-16 完成、当时未提交,这里补提交(原样,不含 09-27 上板的修改)。

- 器件层 / 拓扑层整体从 R1 换到 R2:TCA9548A 分通道、五路同址 INA226 0x40 + 输入侧 0x43、
  刀片 PCA9555(VBUS_EN 挪到 P0.4)、CH224A 只读驱动、风扇闭环 t_thermal
- 新增 i2cbus(mux 原子事务)、slot(刀片 I/O 门面)、host/qtest.sh
- 74HC595 链退役(代码与测试保留)
- 判据阈值与时间常量未动;19 个 ctest 全绿,idf.py build 通过

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-09-27 10:26:24 +08:00
共同撰写人 Claude Opus 5.5
父节点 5a80d8a872
当前提交 b8ad69681f
共修改 51 个文件,包含 10882 行新增和 4068 行删除
+48 -19
查看文件
@@ -6,10 +6,14 @@
#include "hal_i2c.h"
/* 设计文档 §8.2:CH1..CH5。⚠️ 不是 0x40 起顺排,CH3/CH4/CH5 三路都不是。 */
static const uint8_t s_rail_addr[INA226_RAIL_COUNT] = { 0x40, 0x41, 0x44, 0x45, 0x42 };
/* R2:五片刀片完全同构 ⇒ 五路同址。仍然写成表而不是直接 `return 0x40`,
理由和 mux 通道那张表一样:**同址是当下的事实,不是不变量**。
哪天某一片改了 strap,改这里一处,`rail_selftest()` 会立刻发现两边分家。 */
static const uint8_t s_rail_addr[INA226_RAIL_COUNT] = {
INA226_ADDR_RAIL, INA226_ADDR_RAIL, INA226_ADDR_RAIL, INA226_ADDR_RAIL, INA226_ADDR_RAIL,
};
const uint8_t ina226_all_addr[INA226_COUNT] = { 0x40, 0x41, 0x44, 0x45, 0x42, INA226_ADDR_INPUT };
const uint8_t ina226_addr_list[INA226_ADDR_COUNT] = { INA226_ADDR_RAIL, INA226_ADDR_INPUT };
uint8_t ina226_rail_addr(int rail)
{
@@ -19,27 +23,28 @@ uint8_t ina226_rail_addr(int rail)
bool ina226_addr_table_ok(void)
{
/* 同一条 I²C 上的其它器件(§8.2):撞上任何一个都是硬件表被改坏了。 */
static const uint8_t others[] = { 0x4A, 0x4B, 0x20, 0x48 };
/* 会与之撞车的其它器件:主干上的 DAC63204 / TCA9548A,刀片支路上的 PCA9555。
⚠️ 0x48(R1 载板上的 LM75)**不在这张表里** —— R2 没有载板,那片不存在。 */
static const uint8_t others[] = { 0x4A, 0x4B, 0x70, 0x20 };
for (int i = 0; i < INA226_COUNT; i++) {
if (ina226_all_addr[i] > 0x7f) {
for (int i = 0; i < INA226_ADDR_COUNT; i++) {
if (ina226_addr_list[i] > 0x7f) {
return false;
}
for (int j = i + 1; j < INA226_COUNT; j++) {
if (ina226_all_addr[i] == ina226_all_addr[j]) {
return false;
for (int j = i + 1; j < INA226_ADDR_COUNT; j++) {
if (ina226_addr_list[i] == ina226_addr_list[j]) {
return false; /* 刀片那片与输入侧那片必须不同址:它们同时可见 */
}
}
for (size_t k = 0; k < sizeof(others); k++) {
if (ina226_all_addr[i] == others[k]) {
if (ina226_addr_list[i] == others[k]) {
return false;
}
}
}
/* 前五个必须和 rail 表逐个一致 —— 两张表分家的那天就是错一路的那天。 */
/* 五路那张表里的每一个都必须在扫描表里 —— 分家的那天就是错一路的那天。 */
for (int i = 0; i < INA226_RAIL_COUNT; i++) {
if (ina226_all_addr[i] != s_rail_addr[i]) {
if (s_rail_addr[i] != ina226_addr_list[0]) {
return false;
}
}
@@ -133,7 +138,30 @@ hal_err_t ina226_check(uint8_t addr)
return cal == INA226_CALIB ? HAL_OK : HAL_ERR_BUS;
}
#define READ_ONE(fn, conv, reg) \
/* ---- Kelvin 对调接的片子 --------------------------------------------------
IN+ / IN− 在板上对调接(IN+ 接分流电阻下游)的片子,分流电压 / 电流读出来是负的。
驱动在这里取反,调用方看到的永远是「正 = 正常供电方向」—— SW-8 反灌判据靠这个符号。
母线电压、功率寄存器是无符号量,不受影响。
R1 全部正接(默认);R2 底板的输入片 U13(0x43)为了让两条 Kelvin 在 PCB 上不交叉
而对调(2026-09-11),R2 固件启动时要 ina226_set_reversed(INA226_ADDR_INPUT, true)。 */
static uint16_t s_reversed; /* bit (addr − 0x40);INA226 的地址只有 0x40~0x4F */
void ina226_set_reversed(uint8_t addr, bool reversed)
{
if (addr < 0x40 || addr > 0x4F) {
return;
}
uint16_t bit = (uint16_t)(1u << (addr - 0x40));
s_reversed = reversed ? (uint16_t)(s_reversed | bit) : (uint16_t)(s_reversed & ~bit);
}
bool ina226_is_reversed(uint8_t addr)
{
return addr >= 0x40 && addr <= 0x4F && (s_reversed & (1u << (addr - 0x40))) != 0;
}
/* pol_ = 1:有符号量(分流电压 / 电流),按登记取反 */
#define READ_ONE(fn, conv, reg, pol_) \
hal_err_t fn(uint8_t addr, int32_t *out) \
{ \
if (out == NULL) { \
@@ -144,14 +172,15 @@ hal_err_t ina226_check(uint8_t addr)
if (err != HAL_OK) { \
return err; \
} \
*out = conv(raw); \
int32_t v = conv(raw); \
*out = ((pol_) && ina226_is_reversed(addr)) ? -v : v; \
return HAL_OK; \
}
READ_ONE(ina226_read_bus_mv, bus_mv_of, INA226_REG_BUS_V)
READ_ONE(ina226_read_shunt_uv, shunt_uv_of, INA226_REG_SHUNT_V)
READ_ONE(ina226_read_current_ua, current_ua_of, INA226_REG_CURRENT)
READ_ONE(ina226_read_power_mw, power_mw_of, INA226_REG_POWER)
READ_ONE(ina226_read_bus_mv, bus_mv_of, INA226_REG_BUS_V, 0)
READ_ONE(ina226_read_shunt_uv, shunt_uv_of, INA226_REG_SHUNT_V, 1)
READ_ONE(ina226_read_current_ua, current_ua_of, INA226_REG_CURRENT, 1)
READ_ONE(ina226_read_power_mw, power_mw_of, INA226_REG_POWER, 0)
hal_err_t ina226_read_vi(uint8_t addr, ina226_vi_t *out)
{