
1. FaultRecovery 库概述FaultRecovery 是一个面向嵌入式实时系统的轻量级故障恢复Fault Recovery库其核心目标是在硬件异常、软件逻辑错误、外设通信中断、内存越界或任务死锁等非致命性故障发生后实现系统级的可控降级与自主恢复而非简单复位重启。该库不依赖特定操作系统可无缝集成于裸机环境、FreeRTOS、Zephyr 或 CMSIS-RTOS 抽象层之上亦不绑定特定 MCU 架构已在 ARM Cortex-M0/M3/M4/M7、RISC-V RV32IMAC 平台完成验证。与传统看门狗Watchdog仅提供“超时复位”这一粗粒度手段不同FaultRecovery 提倡分层响应、状态保持、渐进恢复的设计哲学分层响应区分硬件异常HardFault、MemManage、任务级异常超时、空指针解引用、外设级异常SPI 超时、I2C NACK、UART 接收溢出三类故障源触发不同粒度的处理策略状态保持在进入恢复流程前自动保存关键上下文如当前任务 ID、故障地址、寄存器快照、环形缓冲区读写索引避免恢复后数据丢失渐进恢复支持“重试 → 重初始化 → 模块隔离 → 全局软复位”四级恢复路径由配置决定是否跳过某级确保高可用场景下服务连续性。该库采用 C99 标准编写无动态内存分配malloc/free全部使用静态内存池与编译期确定大小的结构体符合 IEC 61508 SIL2 / ISO 26262 ASIL-B 功能安全开发要求。其代码体积经 GCC -Os 优化后典型值为ARM Cortex-M4 上约 3.2 KiB含所有功能RISC-V 上约 2.8 KiB。2. 系统架构与设计原理2.1 整体架构FaultRecovery 库采用三层模块化架构层级模块名称职责可裁剪性底层驱动适配层HAL Adapterfr_hal.c/h绑定 MCU 特定异常向量HardFault_Handler、MemManage_Handler 等、提供原子操作封装、实现平台无关的临界区保护__disable_irq()/__enable_irq()或arch_critical_enter()✅ 可完全替换为自定义 HAL核心引擎层Core Enginefr_core.c/h故障检测分发、上下文快照捕获、恢复策略调度、状态机管理、日志环形缓冲区fr_log_t❌ 不可裁剪构成库主干策略执行层Recovery Policyfr_policy.c/h,fr_task.c/h,fr_periph.c/h实现具体恢复动作任务重启、外设重初始化、模块禁用、软复位触发含 FreeRTOS 集成钩子vApplicationStackOverflowHook、xPortSysTickHandler增强✅ 按需启用如仅用fr_task.c则禁用外设策略所有模块通过fr_config.h进行编译期配置无运行时动态加载机制确保确定性与时序可预测性。2.2 故障检测与分类机制FaultRecovery 将故障分为三大类每类对应独立的检测入口与上下文捕获逻辑1CPU 异常Hardware Fault由 Cortex-M 的 Fault Status RegistersFSR或 RISC-V 的mcause寄存器触发包括HardFault未定义指令、总线错误、非法内存访问MemManageMPU 违规、不可缓存区域访问BusFault外设地址无效、AHB/AHB-Lite 传输失败UsageFault未对齐访问、除零、无效状态切换。上下文捕获逻辑以 Cortex-M 为例// fr_hal_cortexm.c 中 HardFault_Handler 实现节选 void HardFault_Handler(void) { uint32_t *msp (uint32_t *)__get_MSP(); // 主堆栈指针 fr_context_t ctx {0}; // 从 MSP 栈中提取 R0-R3, R12, LR, PC, xPSR共 8 个字 ctx.regs.r0 msp[0]; ctx.regs.r1 msp[1]; ctx.regs.r2 msp[2]; ctx.regs.r3 msp[3]; ctx.regs.r12 msp[4]; ctx.regs.lr msp[5]; ctx.regs.pc msp[6]; ctx.regs.xpsr msp[7]; // 读取故障状态寄存器 ctx.fault.hfsr SCB-HFSR; ctx.fault.mmsr SCB-CFSR 0xFFFF; ctx.fault.bfsr (SCB-CFSR 16) 0xFF; ctx.fault.ufsr (SCB-CFSR 16) 0xFFFF0000; fr_core_handle_fault(ctx, FR_FAULT_HW); }2任务级异常Task Fault由用户显式调用fr_task_report_fault()或框架自动检测触发典型场景FreeRTOS 任务堆栈溢出通过uxTaskGetStackHighWaterMark()定期轮询任务执行超时基于xTaskGetTickCount()计算用户断言失败FR_ASSERT(condition)宏展开为此函数。关键参数配置fr_config.h#define FR_TASK_MONITOR_PERIOD_MS 100U // 任务健康检查周期毫秒 #define FR_TASK_TIMEOUT_THRESHOLD_MS 2000U // 单任务最大允许执行时间 #define FR_STACK_MIN_WATERMARK 64U // 堆栈最小剩余空间字节3外设级异常Peripheral Fault由外设驱动回调注入例如UART 接收中断中检测到OVERRUN标志SPI 发送后TXE未置位且超时I2C 通信返回HAL_ERROR且重试 3 次失败。统一注入接口// fr_periph.c 提供标准化注入点 typedef enum { FR_PERIPH_UART, FR_PERIPH_SPI, FR_PERIPH_I2C, FR_PERIPH_ADC } fr_periph_type_t; void fr_periph_report_fault(fr_periph_type_t type, uint32_t instance_id, const char* desc, uint32_t error_code);instance_id用于区分同一类型多个外设如SPI1vsSPI2error_code透传底层驱动错误码如HAL_SPI_ERROR_OVR。3. 核心 API 接口详解3.1 初始化与配置 API函数原型作用调用时机注意事项fr_init(const fr_config_t* config)初始化库全局状态注册异常向量创建日志缓冲区main()中HAL_Init()后、MX_FREERTOS_Init()前config必须为静态变量指针生命周期需覆盖整个运行期fr_set_recovery_callback(fr_recovery_cb_t cb)设置全局恢复策略回调覆盖默认行为可在任意时间调用但建议初始化后立即设置回调中禁止调用fr_*系列函数防止递归调用fr_log_enable(uint8_t enable)启用/禁用日志记录影响性能运行时动态开关调试阶段开启量产关闭日志存储于 RAM 环形缓冲区满后自动覆盖旧条目fr_config_t结构体关键字段说明typedef struct { uint32_t log_buffer_size; // 日志缓冲区大小字节0 表示禁用日志 uint32_t max_retries; // 全局最大重试次数影响 fr_policy_retry uint32_t soft_reset_delay_ms; // 软复位前延时毫秒用于 LED 指示 fr_recovery_level_t default_level; // 默认恢复级别FR_RECOV_LEVEL_RETRY 等 } fr_config_t;3.2 故障上报 API函数原型作用典型调用位置参数说明fr_core_handle_fault(const fr_context_t* ctx, fr_fault_type_t type)主故障处理入口由 HAL 层调用HardFault_Handler等异常向量函数内ctx包含完整上下文type指明故障类别fr_task_report_fault(const char* task_name, fr_task_fault_t fault)上报任务级故障FreeRTOS 任务函数内、堆栈检查钩子中task_name为pcTaskGetName(NULL)返回值fault如FR_TASK_FAULT_TIMEOUTfr_periph_report_fault(fr_periph_type_t type, uint32_t id, ...)上报外设故障外设中断服务程序ISR或驱动错误处理分支可变参数用于格式化描述字符串类似printffr_task_fault_t枚举值typedef enum { FR_TASK_FAULT_TIMEOUT, // 执行超时 FR_TASK_FAULT_STACK_OVF, // 堆栈溢出 FR_TASK_FAULT_ASSERT, // 断言失败 FR_TASK_FAULT_NULL_PTR, // 空指针解引用 FR_TASK_FAULT_DIV_ZERO // 除零错误 } fr_task_fault_t;3.3 恢复策略控制 API函数原型作用使用场景返回值fr_policy_execute(fr_recovery_level_t level)手动触发指定级别恢复用户自定义故障处理逻辑中FR_OK成功FR_BUSY正在恢复中FR_NOT_SUPPORTED级别未启用fr_policy_get_current_state(void)获取当前恢复状态机状态用于 UI 显示或远程诊断FR_STATE_IDLE/FR_STATE_EXECUTING/FR_STATE_COMPLETEDfr_policy_abort_recovery(void)中止正在进行的恢复流程紧急人工干预如通过按键无恢复级别fr_recovery_level_t定义级别动作适用场景是否阻塞调用FR_RECOV_LEVEL_RETRY重试当前操作最多config-max_retries次瞬时干扰电源毛刺、EMI否FR_RECOV_LEVEL_REINIT重新初始化故障模块如HAL_UART_Init()外设寄存器错乱、时钟偏移是等待初始化完成FR_RECOV_LEVEL_ISOLATE禁用故障模块切换至备用通道或降级模式关键传感器失效、冗余设计否异步标记FR_RECOV_LEVEL_SOFT_RESET触发NVIC_SystemReset()或__NVIC_SystemReset()全局状态不一致、无法局部修复是永不返回4. FreeRTOS 集成实践FaultRecovery 与 FreeRTOS 的深度集成体现在三个关键钩子上无需修改 FreeRTOS 内核源码。4.1 堆栈溢出钩子增强标准vApplicationStackOverflowHook()仅提供任务名FaultRecovery 扩展为捕获完整上下文// 在 freertos_config.h 中定义 #define configCHECK_FOR_STACK_OVERFLOW 2 // 用户实现替代原钩子 void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) { fr_context_t ctx {0}; // 从任务 TCB 中提取堆栈基址与当前 SP StackType_t *pxTopOfStack (StackType_t*)pvTaskGetStackStart(xTask); uint32_t stack_used (uint32_t)pxTopOfStack - (uint32_t)xTask; ctx.fault.stack_overflow.task_handle xTask; ctx.fault.stack_overflow.stack_used stack_used; ctx.fault.stack_overflow.min_watermark FR_STACK_MIN_WATERMARK; fr_core_handle_fault(ctx, FR_FAULT_TASK); }4.2 SysTick 增强监控利用 SysTick 中断周期性检查任务健康状态// 在 SysTick_Handler 中插入需确保在 FreeRTOS 的 xPortSysTickHandler 之后 void SysTick_Handler(void) { // FreeRTOS 原始处理 xPortSysTickHandler(); // FaultRecovery 健康检查 static uint32_t last_check_tick 0; if (xTaskGetTickCount() - last_check_tick pdMS_TO_TICKS(FR_TASK_MONITOR_PERIOD_MS)) { fr_task_monitor_all(); last_check_tick xTaskGetTickCount(); } }fr_task_monitor_all()内部遍历所有eRunning状态任务调用uxTaskGetStackHighWaterMark()并比较阈值。4.3 任务重启示例HAL FreeRTOS以下代码演示如何安全重启一个 UART 数据采集任务// 全局任务句柄与参数 static TaskHandle_t uart_task_handle NULL; static UART_HandleTypeDef huart1; // 采集任务函数 void uart_collect_task(void *pvParameters) { for(;;) { uint8_t rx_buf[64]; HAL_StatusTypeDef status HAL_UART_Receive(huart1, rx_buf, sizeof(rx_buf), 100); if (status ! HAL_OK) { // 上报 UART 故障并请求重初始化 fr_periph_report_fault(FR_PERIPH_UART, 1, UART1 recv fail: %d, status); fr_policy_execute(FR_RECOV_LEVEL_REINIT); // 恢复后需重新初始化 UART HAL_UART_DeInit(huart1); MX_USART1_UART_Init(); // 你的初始化函数 } vTaskDelay(pdMS_TO_TICKS(10)); } } // 任务创建带故障恢复感知 void create_uart_task_with_recovery(void) { xTaskCreate(uart_collect_task, UART_COLLECT, 256, NULL, 3, uart_task_handle); // 注册任务到 FaultRecovery 监控列表 fr_task_register(uart_task_handle, UART_COLLECT); }5. 实际工程配置与调试技巧5.1fr_config.h典型配置STM32H743 FreeRTOS// 启用全部功能但禁用软复位由外部看门狗保障 #define FR_ENABLE_LOGGING 1 #define FR_LOG_BUFFER_SIZE 2048 #define FR_ENABLE_TASK_MONITOR 1 #define FR_ENABLE_PERIPH_MONITOR 1 #define FR_ENABLE_HARDFAULT_HOOK 1 #define FR_ENABLE_MEMMANAGE_HOOK 1 #define FR_ENABLE_BUSFAULT_HOOK 1 // 恢复策略配置 #define FR_DEFAULT_RECOVERY_LEVEL FR_RECOV_LEVEL_REINIT #define FR_MAX_RETRIES 3 #define FR_SOFT_RESET_DELAY_MS 500 // FreeRTOS 集成开关 #define FR_FREERTOS_INTEGRATION 1 #define FR_TASK_MONITOR_PERIOD_MS 200 #define FR_STACK_MIN_WATERMARK 1285.2 故障日志解析方法日志以二进制格式写入环形缓冲区可通过fr_log_dump()导出为文本// 导出最近 5 条日志 char log_output[1024]; fr_log_dump(log_output, sizeof(log_output), 5); // 输出示例 // [2024-03-15 14:22:01] TASK_FAULT: SENSOR_TASK timeout (2150ms 2000ms) // [2024-03-15 14:22:03] PERIPH_FAULT: UART1 recv fail: 0x10 (HAL_ERROR) // [2024-03-15 14:22:03] RECOVERY_EXEC: LevelREINIT, ModuleUART1, ResultOK5.3 常见问题排查清单现象可能原因解决方案fr_core_handle_fault未被调用HAL 层未正确重定向异常向量检查startup_stm32h743xx.s中HardFault_Handler是否指向FaultRecovery实现恢复后外设仍不工作FR_RECOV_LEVEL_REINIT未重置底层时钟/引脚在fr_policy_reinit_periph()中添加__HAL_RCC_USART1_CLK_DISABLE()__HAL_RCC_USART1_CLK_ENABLE()日志缓冲区快速填满高频故障未被抑制启用FR_FAULT_SUPPRESSION_WINDOW_MS需补丁对同一故障源 1 秒内只记录首次FreeRTOS 任务监控导致 CPU 占用过高FR_TASK_MONITOR_PERIOD_MS设置过小调整为 500ms 或根据任务数线性增加如 10 个任务设为 1000ms6. 安全关键场景应用范例在电机驱动控制器中FaultRecovery 可构建三级防护第一级毫秒级ADC 采样值突变±20%触发FR_RECOV_LEVEL_RETRY重采样 3 次确认第二级百毫秒级PWM 输出异常死区时间违规触发FR_RECOV_LEVEL_REINIT重载 TIMx 寄存器并清除 OC 标志第三级秒级连续 5 次FR_RECOV_LEVEL_REINIT失败触发FR_RECOV_LEVEL_ISOLATE关闭 PWM 输出点亮红色故障 LED并通过 CAN 总线广播FAULT_CODE_MOTOR_CTRL_LOST。此方案避免了单次误触发导致停机又能在真实故障时快速隔离风险符合工业伺服系统 MTBF 100,000 小时的要求。7. 与同类方案对比特性FaultRecovery标准 CMSIS Fault HandlerFreeRTOS SafeRTOS自研看门狗方案故障分类粒度硬件/任务/外设三级仅硬件异常仅任务级堆栈无分类仅超时恢复动作可编程性✅ 支持 4 级策略及自定义回调❌ 固定复位❌ 仅终止任务❌ 固定复位上下文保存完整性✅ R0-R15 xPSR FSR⚠️ 仅部分寄存器❌ 无❌ 无内存占用Cortex-M43.2 KiB0.5 KiB12 KiB1 KiB功能安全认证支持✅ SIL2/ASIL-B 就绪❌✅需额外认证❌外设故障注入接口✅ 标准化fr_periph_report_fault()❌❌❌该库的价值不在于取代看门狗而在于为看门狗提供“提前干预”的能力——当故障尚处于可恢复阶段时即介入将系统不可用时间从秒级降至毫秒级。8. 源码结构与移植指南项目源码目录结构清晰便于裁剪FaultRecovery/ ├── inc/ │ ├── fr_core.h // 核心引擎头文件 │ ├── fr_hal.h // HAL 适配层抽象 │ ├── fr_config.h // 用户配置入口必须修改 │ └── fr_types.h // 公共类型定义 ├── src/ │ ├── fr_core.c // 故障分发、状态机、日志 │ ├── fr_policy.c // 默认恢复策略实现 │ ├── fr_task.c // 任务监控与重启逻辑 │ ├── fr_periph.c // 外设故障注入与处理 │ └── fr_hal_cortexm.c // Cortex-M 系列 HAL 实现需按芯片选择 └── port/ └── riscv/ // RISC-V 架构适配含 vector table setup移植步骤复制src/fr_hal_cortexm.c为fr_hal_your_mcu.c修改HardFault_Handler等向量函数确保使用目标架构的寄存器读取指令如 RISC-V 用csrr读mcause实现fr_hal_critical_enter/exit()使用目标架构的关中断指令在fr_config.h中定义FR_TARGET_ARCH为FR_ARCH_RISCV或FR_ARCH_ARM编译时添加-DFR_FREERTOS_INTEGRATION或-DFR_BARE_METAL宏。所有 HAL 层函数均声明为weak用户可直接重写同名函数覆盖默认实现无需修改库源码。9. 性能实测数据STM32H743 480MHz操作平均耗时最大耗时说明fr_core_handle_fault()HardFault8.2 μs12.5 μs含上下文保存与日志写入fr_task_report_fault()0.8 μs1.3 μs仅入队操作fr_policy_execute(FR_RECOV_LEVEL_RETRY)0.3 μs0.5 μs纯计数器操作fr_policy_execute(FR_RECOV_LEVEL_REINIT)156 μs210 μs含HAL_UART_DeInit()HAL_UART_Init()在 100kHz 中断频率下库自身开销低于 0.03% CPU 时间满足硬实时约束。10. 工程实践忠告在多个车载 ECU 项目中验证以下三点是成功落地的关键永远不要在恢复回调中调用printf或HAL_Delay前者可能因重入导致串口卡死后者会破坏实时性。应改用fr_log_printf()无浮点、无动态内存或直接操作 GPIO 指示灯外设重初始化必须包含时钟与引脚复位常见错误是仅调用HAL_*_DeInit()却忘记__HAL_RCC_xxx_CLK_DISABLE()导致寄存器复位不彻底FR_RECOV_LEVEL_ISOLATE必须有明确的降级路径例如关闭电机 PWM 后必须启动抱闸或切换至安全扭矩关断STO状态否则“隔离”等于“失控”。真正的故障恢复能力不在于库本身多强大而在于工程师能否将恢复策略与硬件安全机制如 STO、Safe Torque Off、系统架构冗余设计、状态机降级深度耦合。FaultRecovery 提供的是那个可靠的、可验证的、可审计的“执行引擎”其余交由系统工程师的领域知识来定义。