🚨 Что такое Hard Fault простыми словами
Hard Fault — это критическая ошибка процессора.
Проще говоря, это ситуация, когда микроконтроллер встречает что-то настолько «невозможное» для себя, что не может продолжить выполнение программы.
Типичный пример — попытка обратиться к памяти, которой не существует, или выполнение запрещённой инструкции.
Когда это происходит, процессор сразу передаёт управление специальному обработчику — Hard Fault Handler.
🔍 Когда чаще всего возникает Hard Fault
💣 1. Разыменование NULL-указателя
int *ptr = NULL; *ptr = 123; // Опа! Памяти по адресу 0 нет -> Hard Fault.
💣 2. Выход за границы массива
int arr[4] = {1, 2, 3, 4}; arr[10] = 55; // Запись туда, где памяти нет или там чужие данные.
💣 3. Неправильно настроенные регистры периферии
Например, не инициализировал SPI, но уже обращаешься к его регистрам.
Процессор не может выполнить доступ — бац, Hard Fault.
💣 4. Деление на ноль
int a = 10; int b = 0; int c = a / b; // На ARM Cortex-M это UsageFault, который может перейти в Hard Fault.
💣 5. Переполнение стека
void recursive() { recursive(); }
Если стек маленький, через пару тысяч вызовов он перезапишет чужую память — и ты получишь Hard Fault.
⚡ Почему это больно
Если не настроить обработчик:
-
MCU зависнет,
-
перезагрузится,
-
или уйдёт в бесконечный «reset loop».
Ты не увидишь никакой полезной информации, кроме «ну, упало…».
🎯 Как правильно готовить Hard Fault в Zephyr
✅ 1. Настроить собственный обработчик
Пример для ARM Cortex-M (CMSIS):
#include <zephyr/kernel.h> #include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h> #include <zephyr/sys/printk.h> void HardFault_Handler(void) { uint32_t *stack_pointer; __asm volatile("mrs %0, msp" : "=r"(stack_pointer)); uint32_t r0 = stack_pointer[0]; uint32_t r1 = stack_pointer[1]; uint32_t r2 = stack_pointer[2]; uint32_t r3 = stack_pointer[3]; uint32_t r12 = stack_pointer[4]; uint32_t lr = stack_pointer[5]; uint32_t pc = stack_pointer[6]; uint32_t psr = stack_pointer[7]; printk("\n=== HARD FAULT ===\n"); printk("R0 = 0x%08X\n", r0); printk("R1 = 0x%08X\n", r1); printk("R2 = 0x%08X\n", r2); printk("R3 = 0x%08X\n", r3); printk("R12 = 0x%08X\n", r12); printk("LR = 0x%08X\n", lr); printk("PC = 0x%08X\n", pc); printk("PSR = 0x%08X\n", psr); printk("CFSR = 0x%08X\n", SCB->CFSR); printk("HFSR = 0x%08X\n", SCB->HFSR); k_fatal_halt(0); }
❓ Что это даёт
-
PC
(Program Counter) — инструкция, где всё сломалось. -
LR
(Link Register) — откуда пришёл вызов. -
PSR
— статус процессора (флаги). -
CFSR
,HFSR
— что пошло не так.
✅ 2. Проверь заранее
void main(void) { printk("Triggering hard fault for test...\n"); int *null_ptr = NULL; *null_ptr = 42; // Ловим сбой! }
✅ 3. Понимай регистры
-
CFSR
— Configurable Fault Status Register. -
HFSR
— Hard Fault Status Register.
🧵 Логирование
-
Логируй в NVRAM или по UART.
-
Используй watchdog и assert.
-
Добавляй проверку стека:
CONFIG_THREAD_STACK_INFO=y CONFIG_THREAD_STACK_OVERFLOW_CHECK=y
✅ Чеклист
✔ Свой обработчик
✔ Лог PC, LR, PSR, CFSR, HFSR
✔ Assert и sanity checks
✔ Лог в NVRAM/UART
✔ Тест сбоя заранее
🏁 Итог
Hard Fault — не мистика. Больше инфы = быстрее фиксим баг.
ссылка на оригинал статьи https://habr.com/ru/articles/925298/
Добавить комментарий