需求 §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]>
106 行
4.3 KiB
C
106 行
4.3 KiB
C
/* 假的 74HC595 ×3 移位链 —— 挂在 GPIO sim 上,只看**线上真实发生的边**。
|
||
|
||
为什么要这么一个东西:A5 的两条验收断言(改一路不动别路、字节顺序 U33→U22→U21)
|
||
如果只对着驱动自己的影子变量断言,那是**自己证明自己** —— 字节顺序发反了、
|
||
每帧多打一个时钟、RCLK 在数据发完之前就打了,影子变量一律看不出来。
|
||
所以这里按 datasheet 建模:SRCLK 上升沿移一格,RCLK 上升沿把移位寄存器抄进输出锁存器。
|
||
|
||
24 级的编号与 sr595.h 一致:0 = U21.QA(DS 进来的第一级),23 = U33.QH(链尾)。 */
|
||
#include <assert.h>
|
||
#include <string.h>
|
||
|
||
#include "sim_hal.h"
|
||
|
||
typedef struct {
|
||
bool attached;
|
||
int ser_pin, srclk_pin, rclk_pin, oe_pin;
|
||
int ser, srclk, rclk, oe; /* 当前电平;oe 初值 1 = 板上 R609 拉着 */
|
||
uint32_t shift; /* 移位寄存器(未锁存) */
|
||
uint32_t out; /* 输出锁存器 */
|
||
uint32_t clocks; /* SRCLK 上升沿总数 */
|
||
uint32_t latches; /* RCLK 上升沿总数 */
|
||
bool enabled_once;
|
||
uint32_t enable_latches; /* OE# 第一次被拉低时,已经锁存过几次 */
|
||
uint32_t enable_out; /* OE# 第一次被拉低那一刻,输出锁存器里是什么 */
|
||
} chain_t;
|
||
|
||
static chain_t s_ch;
|
||
|
||
void sim_sr595_reset_internal(void); /* 只给 sim_hal_reset() 用 */
|
||
void sim_sr595_gpio_hook(int pin, int level);
|
||
|
||
void sim_sr595_reset_internal(void) { memset(&s_ch, 0, sizeof(s_ch)); }
|
||
|
||
void sim_sr595_attach(int ser_pin, int srclk_pin, int rclk_pin, int oe_pin)
|
||
{
|
||
memset(&s_ch, 0, sizeof(s_ch));
|
||
s_ch.attached = true;
|
||
s_ch.ser_pin = ser_pin;
|
||
s_ch.srclk_pin = srclk_pin;
|
||
s_ch.rclk_pin = rclk_pin;
|
||
s_ch.oe_pin = oe_pin;
|
||
/* `OE#` 上电就被 R609 拉高 —— 高阻,不是 0。 */
|
||
s_ch.oe = 1;
|
||
/* 上电后移位寄存器与输出锁存器的内容**不确定**(`MR#` 接死在 +3V3,没有复位线)。
|
||
这里塞一个显眼的非零值:驱动要是先使能 OE# 再锁存安全值,
|
||
`sim_sr595_enable_outputs()` 就会把这坨脏东西原样报出来。 */
|
||
s_ch.shift = 0x00A5A5A5u & 0x00FFFFFFu;
|
||
s_ch.out = s_ch.shift;
|
||
}
|
||
|
||
void sim_sr595_gpio_hook(int pin, int level)
|
||
{
|
||
if (!s_ch.attached) {
|
||
return;
|
||
}
|
||
level = level ? 1 : 0;
|
||
|
||
/* 动这条链的四根线之一 = 在用那个共享资源,此刻必须持着 595 那把锁
|
||
(只在 sim_lock_require(HAL_LOCK_SR595, true) 之后才计数,见 hal_lock_sim.c)。 */
|
||
if (pin == s_ch.ser_pin || pin == s_ch.srclk_pin || pin == s_ch.rclk_pin ||
|
||
pin == s_ch.oe_pin) {
|
||
sim_lock_check(HAL_LOCK_SR595);
|
||
}
|
||
|
||
if (pin == s_ch.ser_pin) {
|
||
s_ch.ser = level;
|
||
} else if (pin == s_ch.srclk_pin) {
|
||
if (level == 1 && s_ch.srclk == 0) { /* 上升沿:整条链往编号大的方向挪一格 */
|
||
s_ch.shift = ((s_ch.shift << 1) | (uint32_t)s_ch.ser) & 0x00FFFFFFu;
|
||
s_ch.clocks++;
|
||
}
|
||
s_ch.srclk = level;
|
||
} else if (pin == s_ch.rclk_pin) {
|
||
if (level == 1 && s_ch.rclk == 0) {
|
||
s_ch.out = s_ch.shift;
|
||
s_ch.latches++;
|
||
}
|
||
s_ch.rclk = level;
|
||
} else if (pin == s_ch.oe_pin) {
|
||
if (level == 0 && s_ch.oe != 0 && !s_ch.enabled_once) {
|
||
s_ch.enabled_once = true;
|
||
s_ch.enable_latches = s_ch.latches;
|
||
s_ch.enable_out = s_ch.out;
|
||
}
|
||
s_ch.oe = level;
|
||
}
|
||
}
|
||
|
||
uint32_t sim_sr595_outputs(void) { return s_ch.out; }
|
||
uint32_t sim_sr595_shift(void) { return s_ch.shift; }
|
||
uint32_t sim_sr595_clocks(void) { return s_ch.clocks; }
|
||
uint32_t sim_sr595_latches(void) { return s_ch.latches; }
|
||
int sim_sr595_oe(void) { return s_ch.oe; }
|
||
bool sim_sr595_enabled(void) { return s_ch.enabled_once; }
|
||
uint32_t sim_sr595_enable_latches(void) { return s_ch.enable_latches; }
|
||
uint32_t sim_sr595_enable_outputs(void) { return s_ch.enable_out; }
|
||
|
||
int sim_sr595_pin(int bit)
|
||
{
|
||
assert(bit >= 0 && bit < 24);
|
||
if (s_ch.oe) {
|
||
return -1; /* 高阻。栅极侧靠自己的 100 kΩ 下拉,HUB_RST 侧靠 R716 上拉 */
|
||
}
|
||
return (int)((s_ch.out >> bit) & 1u);
|
||
}
|