文件
phonerail/firmware/components/phonerail_hal/sim/sim_hal.h
T
zhou yang和Claude Opus 5 ab37f9dc49 A5:74HC595 24 位链 + 影子寄存器单一写入点
15 路侧键 + 5 路 EDL_EN + HUB_RST 在一条 24 位链上,内存里只有一份影子,
唯一写入点是 sr595_flush()。发送顺序 U33 → U22 → U21,每字节 QH 先发。

位表是从 .claude/pcb1-parts.json 的真实焊盘网络逐脚核回来的,不是照文档抄 ——
设计文档 §17.2 那张是 09-04 的两片版,§26.18 重排后 15 路里有 12 路对不上。
顺手把 §17.2 与 §8.2 里过期的两张表都改了。

sim 侧新增按 datasheet 建模的假移位链(SRCLK 上升沿移一格、RCLK 上升沿锁存),
只看线上真实发生的边 —— 对着驱动自己的影子变量断言等于自己证明自己。
四个注错进去的 bug(字节序反、OE# 先使能、安全影子写 0、位表写成算式)全被拦住。

上电顺序按「先锁存安全值、再使能 OE#」写死:三片 MR# 接死在 +3V3,
上电时移位寄存器内容不确定,先拉低 OE# 就是把随机值放到 15 个栅极上。

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
2026-09-06 19:33:51 +08:00

112 行
4.9 KiB
C
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* 假板子的控制面 —— 只有宿主机测试与 sim/ 自己会包含它,应用代码一律不许包含。
(包含了就编不过:ESP 目标根本不编译 sim/。)
⚠️ 模拟器是用来证明**代码逻辑**对的,不是用来证明**设计**对的。
电压、余量、时序裕量的结论全部来自设计文档的计算与首板实测。见计划 §4.2。 */
#ifndef SIM_HAL_H
#define SIM_HAL_H
#include <stdbool.h>
#include <stdint.h>
#include "hal_gpio.h"
#include "hal_i2c.h"
#include "hal_pwm.h"
#include "hal_time.h"
#ifdef __cplusplus
extern "C" {
#endif
/* 四路 sim 全部恢复出厂:器件表清空、引脚未配置、PWM 未配置、时钟归零、
init 标志清掉。每个测试的 setUp() 里调它。 */
void sim_hal_reset(void);
/* ---------------- I²C ---------------- */
#define SIM_I2C_MAX_DEV 16
#define SIM_I2C_REGS 256
#define SIM_I2C_LOG_BYTES 8
typedef struct {
uint8_t addr;
uint8_t reg_width; /* 1 或 2 字节。2 字节按**大端**——INA226 / DAC63204 / LM75 都是 */
bool present; /* false = 器件失联(A15 降级矩阵用),事务一律 NAK */
int nak_left; /* >0:接下来 n 次事务 NAK 后自动恢复;<0:一直 NAK;0:正常 */
uint16_t regs[SIM_I2C_REGS];
uint8_t ptr; /* 器件内部的寄存器指针,跨事务保持(真器件就是这个行为) */
uint32_t n_write; /* 事务计数,可用来断言「驱动真的访问过它 / 没多访问」 */
uint32_t n_read;
uint8_t last_write[SIM_I2C_LOG_BYTES]; /* 最后一次写进去的原始字节 */
uint8_t last_write_len;
} sim_i2c_dev_t;
/* 往假总线上挂一个器件。地址重复或超出 16 个会 abort(测试写错了,不该悄悄过)。 */
sim_i2c_dev_t *sim_i2c_add(uint8_t addr, uint8_t reg_width);
sim_i2c_dev_t *sim_i2c_dev(uint8_t addr); /* 没挂就返回 NULL */
/* 预置 / 回读寄存器。地址没挂过会 abort。 */
void sim_i2c_set_reg(uint8_t addr, uint8_t reg, uint16_t val);
uint16_t sim_i2c_get_reg(uint8_t addr, uint8_t reg);
/* 故障注入。count>0:接下来 count 次事务 NAK;count<0:一直 NAK;0:清除。 */
void sim_i2c_inject_nak(uint8_t addr, int count);
void sim_i2c_set_present(uint8_t addr, bool present);
/* ---------------- GPIO ---------------- */
#define SIM_GPIO_PINS 48 /* ESP32-S3 的 GPIO 编号上限 */
/* 外部世界把某个**输入**脚拉成 level。对输出脚调用会 abort(那是总线打架)。 */
void sim_gpio_drive(int pin, int level);
/* 该脚当前电平:输出脚 = 固件驱动的值,输入脚 = 外部驱动的值。没配过返回 -1。 */
int sim_gpio_level(int pin);
/* 被 hal_gpio_write() 写成功过几次。断言「改 CH1 没动 CH2~5」用得上(A5)。 */
uint32_t sim_gpio_writes(int pin);
/* ---------------- 74HC595 ×3 的 24 位链 ---------------- */
/* 按 datasheet 建模的假移位链,**只看线上真实发生的边**(SRCLK / RCLK 上升沿)。
对着驱动自己的影子变量断言等于自己证明自己 —— 字节顺序发反、每帧多打一个时钟、
RCLK 打早了,影子变量一律看不出来。位号与 sr595.h 一致:0 = U21.QA,23 = U33.QH。
attach 之后移位寄存器与输出锁存器被塞成 0xA5A5A5:真板子上电时它们的内容
**不确定**(`MR#` 接死在 +3V3,没有复位线),驱动必须先锁存安全值再使能 OE#。 */
void sim_sr595_attach(int ser_pin, int srclk_pin, int rclk_pin, int oe_pin);
uint32_t sim_sr595_outputs(void); /* 输出锁存器(不管 OE# 是高是低) */
uint32_t sim_sr595_shift(void); /* 移位寄存器当前内容(还没锁存的那一份) */
uint32_t sim_sr595_clocks(void); /* SRCLK 上升沿总数:一帧应当正好 +24 */
uint32_t sim_sr595_latches(void); /* RCLK 上升沿总数:一帧应当正好 +1 */
int sim_sr595_oe(void); /* OE# 当前电平。初值 1 —— 板上 R609 拉着 */
/* OE# 第一次被拉低那一刻的现场。断言「输出使能之前已经锁存过安全值」用:
驱动要是把顺序换了,enable_latches 会是 0、enable_outputs 会是那坨 0xA5A5A5。 */
bool sim_sr595_enabled(void);
uint32_t sim_sr595_enable_latches(void);
uint32_t sim_sr595_enable_outputs(void);
/* 某一位在栅极上的实际电平;OE# 为高时返回 -1(高阻,不是 0)。 */
int sim_sr595_pin(int bit);
/* ---------------- PWM ---------------- */
int sim_pwm_pin(int ch); /* 未配置返回 -1 */
uint32_t sim_pwm_freq(int ch); /* 未配置返回 0 */
int sim_pwm_permille(int ch); /* 未配置返回 -1 */
/* ---------------- 时钟 ---------------- */
void sim_time_set_us(uint64_t us);
void sim_time_advance_us(uint64_t us);
void sim_time_advance_ms(uint32_t ms);
uint32_t sim_time_delay_calls(void); /* hal_time_delay_ms() 被调了几次 */
#ifdef __cplusplus
}
#endif
#endif /* SIM_HAL_H */