Taming Hard Faults in Zephyr OS: Практическое руководство для embedded-разработчиков

от автора

🚨 Что такое 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *