/** * @file sys/run_inhibit.h * @brief 电机"禁止运行"闸门:唯一有权决定桥臂能不能输出的模块。 * @note 分层约定(新增故障必须照此办,不要在检测处写停机动作): * - 故障检测方 只调 request(来源位) —— 申请禁止,不碰 PWM/使能脚 * - 故障解除方 只调 release(来源位) —— 撤销自己的申请,不查别人的位 * - 功率级动作 只发生在本模块 update() —— 由 50us 控制任务驱动 * 因此任何一处都不需要知道其它故障源的存在;"能不能运行"= 位图是否为空。 */ #ifndef _RUN_INHIBIT_H #define _RUN_INHIBIT_H #include "config.h" /** * @brief 禁止来源位图(每一位一个独立申请方,可并存) */ enum InhibitSource : u32 { INHIBIT_DRV_NFAULT = 1u << 0, // 门驱动 nFAULT(PB2) 有效:UVLO/热关断/OCP 锁存 INHIBIT_UNDERVOLTAGE = 1u << 1, // 母线跌幅超阈值或低于绝对下限 INHIBIT_THERMAL = 1u << 2, // 热模型过热(含温度采集正常时的电流热积累) INHIBIT_ISENSE_FAIL = 1u << 3, // 三相电流采样持续失效(原先被并入"过热"上报) // 上位机还没下发过非零模式:保持驱动睡眠。这不是故障,而是"未被使能", // 放在同一张位图里是为了让功率级只有一个执行者。老板 v2 的 EN 有 10k 下拉、 // DRV8313 上电默认睡眠;若上电即唤醒,模式 0(drv_hold 恒 10% 占空比锁止) // 会立刻给 A 相通流发热,而"上电先发模式 0 当静止待机"是现有上位机行为。 INHIBIT_NOT_ENABLED = 1u << 7, }; /** * @brief RunInhibit 类:禁止运行闸门 */ class RunInhibit { // 上电即禁止:等上位机首次下发非零模式解除(见 INHIBIT_NOT_ENABLED) u32 bits = INHIBIT_NOT_ENABLED; bool stopped = false; // 上一拍是否已施加停机(避免每 50us 重复翻转使能脚) u8 wake_ticks = 0; // 唤醒建立倒计时(50us 节拍) public: /** * @brief 申请禁止(幂等;同一位重复申请无副作用) * @param[in] src InhibitSource 位 * @return void */ void request(u32 src); /** * @brief 撤销本来源的禁止申请;位图清空后才允许运行 * @param[in] src InhibitSource 位 * @return void */ void release(u32 src); /** * @brief 是否允许运行(只看位图,不看唤醒状态) * @return bool true=允许 */ bool permitted() const { return bits == 0U; } /** * @brief 允许运行且驱动唤醒建立已完成,控制函数可派发 * @return bool true=可派发 */ bool ready() const { return bits == 0U && wake_ticks == 0U; } /** * @brief 当前禁止来源位图(上报/诊断用) * @return u32 位图 */ u32 sources() const { return bits; } /** * @brief 执行闸门:50us 控制任务每拍调用,唯一碰 PWM 与驱动使能的地方 * @return void */ void update(void); }; extern RunInhibit run_inhibit; #endif