A8:任务骨架与调度(5 个任务、两把锁)
需求 §10.3 落地。四件事: 1. 五任务表(tasks.c):周期与优先级写在一张 const 表里,开机自检查的是 相对顺序(safety 唯一最高、log 唯一最低、thermal 与 actuator 同级), 外加「巡检周期必须长于 INA226 一帧转换 8.8 ms」。不过就 abort。 2. 节拍器(sched.c):记下一次该醒的绝对时刻,回绕安全(int32_t 差判先后)。 过载时跳过错过的整周期、保住相位,不补跑。 3. 两把锁(hal_lock):I²C 与 74HC595,禁止嵌套持有 —— 这块板上两者没有交集, 「拿着 A 去拿 B」永远是写错了。ESP 侧用 mutex 取优先级继承。 4. t_safety 的一个周期:拿锁 → 6 片 INA226 的 V+I + PCA9555 两端口 → 放锁。 不死等锁(5 ms 超时就跳过这圈),一片读失败不中断整圈、保留上一圈的值。 I²C 占用由 HAL 按线上真实字节数记账(两个目标同一份代码),静态预算与实测 必须相等:一次寄存器读 48 位,一圈 13 次事务 = 1560 µs / 20 ms = 7.8 %, 与 §10.3 手算的「约 1.5 ms / 约 8 %」相符(本节的 46 位没数 STOP 和重复 START)。 sim 侧配套两样:假 I²C 事务真的把虚拟时钟花掉(否则时序测试是空话), 以及可强制的加锁纪律检查(配一条反向测试,防止检查本身失灵)。 11 条 ctest(161 例)全绿,idf.py build 通过;16 个注错实验全部变红。 Co-Authored-By: Claude Opus 5 <[email protected]>
这个提交包含在:
+108
-103
@@ -1,103 +1,108 @@
|
||||
/* INA226 —— 板上 6 片(CH1~CH5 输出各一片 + 输入侧一片),分流电阻全部 5 mΩ / 1%。
|
||||
地址表见设计文档 §8.2,**不是顺排的**,详见下面 ina226_rail_addr() 的警告。
|
||||
|
||||
本驱动只通过 hal_i2c_* 访问总线,宿主机上用 sim 的假器件即可全部测完。 */
|
||||
#ifndef INA226_H
|
||||
#define INA226_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- 寄存器(全部 16 位、大端) ---------------------------------------- */
|
||||
|
||||
#define INA226_REG_CONFIG 0x00
|
||||
#define INA226_REG_SHUNT_V 0x01
|
||||
#define INA226_REG_BUS_V 0x02
|
||||
#define INA226_REG_POWER 0x03
|
||||
#define INA226_REG_CURRENT 0x04
|
||||
#define INA226_REG_CALIB 0x05
|
||||
#define INA226_REG_MASK_EN 0x06
|
||||
#define INA226_REG_ALERT_L 0x07
|
||||
#define INA226_REG_MFG_ID 0xFE
|
||||
#define INA226_REG_DIE_ID 0xFF
|
||||
|
||||
#define INA226_MFG_ID 0x5449 /* 'TI' */
|
||||
#define INA226_DIE_ID 0x2260
|
||||
|
||||
/* ---- 量程与标定(五路与输入侧同一颗分流电阻,所以是常量不是参数) ------ */
|
||||
|
||||
#define INA226_SHUNT_UOHM 5000 /* 5 mΩ,设计文档 §8.2 */
|
||||
#define INA226_CURRENT_LSB_UA 500 /* 0.5 mA —— 分流 LSB 2.5 µV ÷ 5 mΩ */
|
||||
|
||||
/* CAL = 0.00512 / (Current_LSB × R_shunt) = 0.00512 / (500 µA × 5 mΩ) = 2048 */
|
||||
#define INA226_CALIB 2048
|
||||
|
||||
/* Config:保留位 100 + AVG=4 + VBUSCT=1.1 ms + VSHCT=1.1 ms + 连续测量分流与母线。
|
||||
转换周期 4 × (1.1 + 1.1) = 8.8 ms < t_safety 的 20 ms,所以每次巡检都拿到新的一帧。 */
|
||||
#define INA226_CONFIG 0x4327
|
||||
|
||||
/* ---- 地址表(设计文档 §8.2) ------------------------------------------- */
|
||||
|
||||
#define INA226_RAIL_COUNT 5
|
||||
#define INA226_COUNT 6
|
||||
#define INA226_ADDR_INPUT 0x43 /* U13,输入侧 V+I,整板功率预算用 */
|
||||
|
||||
/* CH1~CH5 → U8~U12 的地址。rail 是 1~5,越界直接 abort。
|
||||
⚠️ **`0x40 + ch` 是错的**,CH3/CH4/CH5 三路全错(0x44 / 0x45 / 0x42)。
|
||||
地址按布线难度重排过(§8.2 那张 A1/A0 表),永远查表,不要算。 */
|
||||
uint8_t ina226_rail_addr(int rail);
|
||||
|
||||
extern const uint8_t ina226_all_addr[INA226_COUNT]; /* CH1..CH5 + 输入侧,扫描用 */
|
||||
|
||||
/* 六个地址互不重复、且不与 DAC63204(0x4A/0x4B) / PCA9555(0x20) / LM75(0x48) 撞车。
|
||||
启动自检调一次;返回 false 说明表被改坏了。 */
|
||||
bool ina226_addr_table_ok(void);
|
||||
|
||||
/* ---- 采样 --------------------------------------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
int32_t bus_mv; /* 母线电压,1.25 mV/LSB */
|
||||
int32_t current_ua; /* **有符号** —— 反灌时为负(SW-8 靠这个判据) */
|
||||
} ina226_vi_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t bus_mv;
|
||||
int32_t shunt_uv; /* 2.5 µV/LSB,四舍五入到 µV(±1.25 µV ≈ ±0.25 mA) */
|
||||
int32_t current_ua;
|
||||
int32_t power_mw; /* 12.5 mW/LSB,向零截断 */
|
||||
} ina226_sample_t;
|
||||
|
||||
/* 读 Manufacturer ID / Die ID 认件。
|
||||
器件不应答 → HAL_ERR_NAK;**应答了但 ID 不对 → HAL_ERR_BUS**
|
||||
(这个地址上有东西,但不是 INA226:多半地址跳线焊错或贴错件)。 */
|
||||
hal_err_t ina226_probe(uint8_t addr);
|
||||
|
||||
/* 写 Config 与 Calibration 并**逐个回读校验**。回读不一致 → HAL_ERR_BUS。 */
|
||||
hal_err_t ina226_init(uint8_t addr);
|
||||
|
||||
/* 只校验 Calibration 还在。掉了的现象是 **Current/Power 恒读 0**(不是报错),
|
||||
看上去就像「这台手机不耗电」,所以巡检里要定期查一次。 */
|
||||
hal_err_t ina226_check(uint8_t addr);
|
||||
|
||||
/* 巡检主路径:母线电压 + 电流,两次事务。 */
|
||||
hal_err_t ina226_read_vi(uint8_t addr, ina226_vi_t *out);
|
||||
|
||||
/* 诊断/事件记录用:四个量一次读齐,四次事务。 */
|
||||
hal_err_t ina226_read_all(uint8_t addr, ina226_sample_t *out);
|
||||
|
||||
hal_err_t ina226_read_bus_mv(uint8_t addr, int32_t *mv);
|
||||
hal_err_t ina226_read_shunt_uv(uint8_t addr, int32_t *uv);
|
||||
hal_err_t ina226_read_current_ua(uint8_t addr, int32_t *ua);
|
||||
hal_err_t ina226_read_power_mw(uint8_t addr, int32_t *mw);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INA226_H */
|
||||
/* INA226 —— 板上 6 片(CH1~CH5 输出各一片 + 输入侧一片),分流电阻全部 5 mΩ / 1%。
|
||||
地址表见设计文档 §8.2,**不是顺排的**,详见下面 ina226_rail_addr() 的警告。
|
||||
|
||||
本驱动只通过 hal_i2c_* 访问总线,宿主机上用 sim 的假器件即可全部测完。 */
|
||||
#ifndef INA226_H
|
||||
#define INA226_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- 寄存器(全部 16 位、大端) ---------------------------------------- */
|
||||
|
||||
#define INA226_REG_CONFIG 0x00
|
||||
#define INA226_REG_SHUNT_V 0x01
|
||||
#define INA226_REG_BUS_V 0x02
|
||||
#define INA226_REG_POWER 0x03
|
||||
#define INA226_REG_CURRENT 0x04
|
||||
#define INA226_REG_CALIB 0x05
|
||||
#define INA226_REG_MASK_EN 0x06
|
||||
#define INA226_REG_ALERT_L 0x07
|
||||
#define INA226_REG_MFG_ID 0xFE
|
||||
#define INA226_REG_DIE_ID 0xFF
|
||||
|
||||
#define INA226_MFG_ID 0x5449 /* 'TI' */
|
||||
#define INA226_DIE_ID 0x2260
|
||||
|
||||
/* ---- 量程与标定(五路与输入侧同一颗分流电阻,所以是常量不是参数) ------ */
|
||||
|
||||
#define INA226_SHUNT_UOHM 5000 /* 5 mΩ,设计文档 §8.2 */
|
||||
#define INA226_CURRENT_LSB_UA 500 /* 0.5 mA —— 分流 LSB 2.5 µV ÷ 5 mΩ */
|
||||
|
||||
/* CAL = 0.00512 / (Current_LSB × R_shunt) = 0.00512 / (500 µA × 5 mΩ) = 2048 */
|
||||
#define INA226_CALIB 2048
|
||||
|
||||
/* Config:保留位 100 + AVG=4 + VBUSCT=1.1 ms + VSHCT=1.1 ms + 连续测量分流与母线。
|
||||
转换周期 4 × (1.1 + 1.1) = 8.8 ms < t_safety 的 20 ms,所以每次巡检都拿到新的一帧。 */
|
||||
#define INA226_CONFIG 0x4327
|
||||
|
||||
/* 上面那个 Config 折算出来的**一帧转换时间**:AVG=4 × (1.1 ms + 1.1 ms) = 8.8 ms。
|
||||
`t_safety` 的周期必须长于它,否则同一帧会被读两次 —— 现象是「电流特别稳」,
|
||||
不是任何一种错误。tasks.c 的表自检拿它和 20 ms 对一次。 */
|
||||
#define INA226_CONV_US 8800
|
||||
|
||||
/* ---- 地址表(设计文档 §8.2) ------------------------------------------- */
|
||||
|
||||
#define INA226_RAIL_COUNT 5
|
||||
#define INA226_COUNT 6
|
||||
#define INA226_ADDR_INPUT 0x43 /* U13,输入侧 V+I,整板功率预算用 */
|
||||
|
||||
/* CH1~CH5 → U8~U12 的地址。rail 是 1~5,越界直接 abort。
|
||||
⚠️ **`0x40 + ch` 是错的**,CH3/CH4/CH5 三路全错(0x44 / 0x45 / 0x42)。
|
||||
地址按布线难度重排过(§8.2 那张 A1/A0 表),永远查表,不要算。 */
|
||||
uint8_t ina226_rail_addr(int rail);
|
||||
|
||||
extern const uint8_t ina226_all_addr[INA226_COUNT]; /* CH1..CH5 + 输入侧,扫描用 */
|
||||
|
||||
/* 六个地址互不重复、且不与 DAC63204(0x4A/0x4B) / PCA9555(0x20) / LM75(0x48) 撞车。
|
||||
启动自检调一次;返回 false 说明表被改坏了。 */
|
||||
bool ina226_addr_table_ok(void);
|
||||
|
||||
/* ---- 采样 --------------------------------------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
int32_t bus_mv; /* 母线电压,1.25 mV/LSB */
|
||||
int32_t current_ua; /* **有符号** —— 反灌时为负(SW-8 靠这个判据) */
|
||||
} ina226_vi_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t bus_mv;
|
||||
int32_t shunt_uv; /* 2.5 µV/LSB,四舍五入到 µV(±1.25 µV ≈ ±0.25 mA) */
|
||||
int32_t current_ua;
|
||||
int32_t power_mw; /* 12.5 mW/LSB,向零截断 */
|
||||
} ina226_sample_t;
|
||||
|
||||
/* 读 Manufacturer ID / Die ID 认件。
|
||||
器件不应答 → HAL_ERR_NAK;**应答了但 ID 不对 → HAL_ERR_BUS**
|
||||
(这个地址上有东西,但不是 INA226:多半地址跳线焊错或贴错件)。 */
|
||||
hal_err_t ina226_probe(uint8_t addr);
|
||||
|
||||
/* 写 Config 与 Calibration 并**逐个回读校验**。回读不一致 → HAL_ERR_BUS。 */
|
||||
hal_err_t ina226_init(uint8_t addr);
|
||||
|
||||
/* 只校验 Calibration 还在。掉了的现象是 **Current/Power 恒读 0**(不是报错),
|
||||
看上去就像「这台手机不耗电」,所以巡检里要定期查一次。 */
|
||||
hal_err_t ina226_check(uint8_t addr);
|
||||
|
||||
/* 巡检主路径:母线电压 + 电流,两次事务。 */
|
||||
hal_err_t ina226_read_vi(uint8_t addr, ina226_vi_t *out);
|
||||
|
||||
/* 诊断/事件记录用:四个量一次读齐,四次事务。 */
|
||||
hal_err_t ina226_read_all(uint8_t addr, ina226_sample_t *out);
|
||||
|
||||
hal_err_t ina226_read_bus_mv(uint8_t addr, int32_t *mv);
|
||||
hal_err_t ina226_read_shunt_uv(uint8_t addr, int32_t *uv);
|
||||
hal_err_t ina226_read_current_ua(uint8_t addr, int32_t *ua);
|
||||
hal_err_t ina226_read_power_mw(uint8_t addr, int32_t *mw);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INA226_H */
|
||||
|
||||
在新工单中引用
屏蔽一个用户