#ifndef SYS_INITIAL_H #define SYS_INITIAL_H #include #include /** * @brief 应用初始化器:按固定顺序启动模块,成功位置 1,末尾汇总。 * @note 汇总结果(模块位图 / critical 失败位图 / 复位源 / init 状态)经静态 getter 导出, * 供 SPI 0xA3 INIT_STATUS 命令与身份块 [15] 上报 ESP32。 */ class AppInitializer { public: /** 启动模块位(成功置 1);与 SPI 0xA3 上报位图共用,改动须同步 ESP32 侧注释 */ enum ModuleBit : uint32_t { kNvic = 1u << 0, kSysTick = 1u << 1, kEsp32Hold = 1u << 2, kUsart0 = 1u << 3, kAdc = 1u << 4, kCanFd = 1u << 5, kOtaBootReq = 1u << 6, kAppSpi = 1u << 7, kTaskManager = 1u << 8, kScheduler = 1u << 9, kEthercat = 1u << 10, kEthercatSync = 1u << 11, kWatchdog = 1u << 12, }; /** init 总体状态(身份块 [15] 与 0xA3 payload[9] 共用编码) */ enum InitState : uint8_t { kInitOk = 0U, /**< 全部模块成功 */ kInitDegraded = 1U, /**< critical 全成功,非 critical 有失败/跳过 */ kInitCriticalFail = 2U, /**< 有 critical 模块失败 */ }; /** 复位源位图(0xA3 payload[8] 编码),多位置位属正常(如 POR 常伴随其它标志) */ enum ResetCauseBit : uint8_t { kRstPor = 1u << 0, /**< 上电/掉电复位 */ kRstEpr = 1u << 1, /**< 外部引脚复位(NRST) */ kRstFwdgt = 1u << 2, /**< 独立看门狗复位 */ kRstWwdgt = 1u << 3, /**< 窗口看门狗复位 */ kRstSw = 1u << 4, /**< 软件复位(含 OTA 跳转) */ kRstLp = 1u << 5, /**< 低功耗复位 */ }; /** 模块数量(fault code 数组长度,与 ModuleBit 位序一致:bit N → 索引 N) */ static constexpr uint8_t kModuleCount = 13U; /** * @brief 模块故障码(0xA3 payload[12..24],每模块 1B,0=成功)。 * * 各模块码值定义位置: * - nvic : kFaultNvic*(本类下) * - systick : kFaultSystick*(本类下;实际失败时 FATAL 停机不上报,仅备查) * - esp32_hold : kFaultEsp32Hold*(本类下) * - usart0 : Usart0::InitFault(device/usart0.h) * - adc : Adc::InitFault(device/adc.h) * - canfd : CanFd::SelfTestFault(device/canfd.h), * **编码 = 原因码 | (首个失败通道号 << 4)**,通道 0/1/2 * - ota_boot_req: 纯软件,恒 0 * - app_spi : 无自证手段,恒 0 * - task_manager: 纯软件,恒 0 * - scheduler : SchedulerTimer::InitFault(device/timer.h) * - ethercat : kFaultEthercat*(本类下) * - ethercat_sync: 跟随 ethercat,恒 0 * - watchdog : Watchdog::InitFault(device/watchdog.h) */ enum NvicFault : uint8_t { kFaultNvicOk = 0U, kFaultNvicPrigroupMismatch = 1U, /**< AIRCR.PRIGROUP 回读与配置不一致 */ }; enum SystickFault : uint8_t { kFaultSystickOk = 0U, kFaultSystickConfig = 1U, /**< SysTick_Config 失败(备查,实际 FATAL 停机) */ kFaultSystickNoTick = 2U, /**< 节拍未前进(备查,实际 FATAL 停机) */ }; enum Esp32HoldFault : uint8_t { kFaultEsp32HoldOk = 0U, kFaultEsp32HoldClamped = 1U, /**< PC9 输出高后回读仍为 0(被外部钳低) */ }; enum EthercatFault : uint8_t { kFaultEthercatOk = 0U, kFaultEthercatBusNotReady = 1U, /**< EXMC/HBI 总线探活失败(BYTE_TEST 未通过) */ kFaultEthercatSscFail = 2U, /**< SSC MainInit 返回非 0 */ kFaultEthercatCoreNotReady = 3U, /**< EEPROM 未加载完成,EtherCAT 核心未使能(1.5.39) */ }; /** * @brief 按固定顺序完成模块初始化(过程只置位,结束统一报告成败/失败模块)。 */ static void init(); /** @brief 已成功初始化的模块位图。 */ static uint32_t moduleMask() { return s_mask; } /** @brief 初始化失败的 critical 模块位图(0=无 critical 失败)。 */ static uint32_t criticalFailMask(); /** @brief init 总体状态(InitState 编码)。 */ static uint8_t initState() { return s_initState; } /** @brief 本次上电复位源位图(ResetCauseBit 编码,init 开头采集并已清硬件标志)。 */ static uint8_t resetCause() { return s_resetCause; } /** @brief 指定模块(ModuleBit 位序索引 0..12)的故障码,0=成功。 */ static uint8_t faultCode(uint8_t module_index); /** @brief 将全部模块故障码拷贝到 out(至少 kModuleCount 字节),返回长度。 */ static uint8_t faultCodes(uint8_t *out); private: static uint32_t s_mask; static uint8_t s_initState; static uint8_t s_resetCause; static uint8_t s_faultCodes[kModuleCount]; static void mark(ModuleBit b); static void setFault(ModuleBit b, uint8_t code); static void captureResetCause(); static void report(); }; #endif /* SYS_INITIAL_H */