- MyLibs - максимально платформонезависимые библиотеки (кроме разве что RTT) - RTT - STM32_General - библиотеки для периферии stm32
591 lines
24 KiB
C
591 lines
24 KiB
C
/**
|
||
**************************************************************************
|
||
* @file trace.h
|
||
* @brief Заголочный файл для работы с трассировкой.
|
||
**************************************************************************
|
||
* @addtogroup TRACE Trace defines
|
||
* @ingroup MYLIBS_DEFINES
|
||
* @brief Дефайны для работы с трассировкой
|
||
*************************************************************************/
|
||
#ifndef __TRACE_H_
|
||
#define __TRACE_H_
|
||
#include "mylibs_defs.h"
|
||
|
||
#include <string.h>
|
||
/**
|
||
* @addtogroup TRACE_SERIAL Serial trace defines
|
||
* @ingroup TRACE
|
||
* @brief Дефайны для работы с serial трассировкой (SWO, RTT)
|
||
* @details В зависимости от настроек определяется дефайн @ref my_printf() и @ref log_printf() для работы с трассировкой:
|
||
- @ref SERIAL_TRACE_ENABLE - Если трассировка отключена, то все дефайны определяются как 'ничего'
|
||
и на производительность кода не влияют
|
||
|
||
- @ref RTT_TRACE_ENABLE - для RTT это будет вызов функции SEGGER_RTT_printf()
|
||
|
||
Предварительно надо подключить библиотеку SEGGER RTT (SEGGER_RTT.h) и вызвать функцию SEGGER_RTT_Init()
|
||
|
||
- @ref SWO_TRACE_ENABLE для SWO это будет просто printf()
|
||
|
||
Предварительно надо подключить библиотеку STDOUT и retarget под ITM:
|
||
|
||
@verbatim
|
||
Manage Run-Time Environment -> Compiler -> I/O -> STDOUT -> ITM
|
||
@endverbatim
|
||
|
||
Для SWO также надо включить трассировку:
|
||
|
||
@verbatim
|
||
Options For Target -> Debug -> Debugger Settings
|
||
@endverbatim
|
||
|
||
В вкладке Debug:
|
||
- Port = SW
|
||
В вкладке Trace:
|
||
- Указать Core Clock
|
||
- Выставить Trace Port = SWO
|
||
- ITM - выбрать нужный порт (для Keil нулевой порт)
|
||
|
||
|
||
* @{
|
||
*
|
||
* @def my_printf(...)
|
||
* @brief Универсальный макрос для вывода трассировки
|
||
* @details Варианты реализации:
|
||
* - RTT_TRACE_ENABLE `SEGGER_RTT_printf(0, ...)`
|
||
* - SWO_TRACE_ENABLE - `printf(...)`
|
||
* - NO_TRACE - пустой макрос
|
||
*
|
||
* @def log_printf(TAG, fmt, ...)
|
||
* @brief Макрос логирования с поддержкой уровней @ref LOG_LEVEL
|
||
* @param TAG Тэг лога
|
||
* @param fmt, ... Форматируемая строка
|
||
* @details Варианты реализации:
|
||
* - @ref LOG_LEVEL == 0 - логирование отключено (макрос пустой)
|
||
* - @ref LOG_LEVEL == 1 - выводится время @ref local_time и TAG
|
||
* @code
|
||
[123] [ADC] Measure Done
|
||
[456] [ADC] Measure Err
|
||
* @endcode
|
||
* - @ref LOG_LEVEL >= 2 - выводится время, TAG, имя файла и номер строки
|
||
* @code
|
||
[123] [ADC] (../Core/Src/adc.c:75) Measure Done
|
||
[456] [ADC] (../Core/Src/adc.c:80) Measure Err
|
||
* @endcode
|
||
*/
|
||
#ifdef SERIAL_TRACE_ENABLE
|
||
|
||
#if defined(RTT_TRACE_ENABLE)
|
||
#undef SWO_TRACE_ENABLE
|
||
#include "SEGGER_RTT.h"
|
||
#define my_printf(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||
#elif defined(SWO_TRACE_ENABLE)
|
||
#undef RTT_TRACE_ENABLE
|
||
#define my_printf(...) printf(__VA_ARGS__)
|
||
#else // NO_TRACE
|
||
#define my_printf(...)
|
||
#warning No trace is selected. Serial debug wont work.
|
||
#endif // RTT_TRACE_ENABLE/SWO_TRACE_ENABLE/NO_TRACE
|
||
#else //SERIAL_TRACE_ENABLE
|
||
|
||
#define my_printf(...)
|
||
#undef RTT_TRACE_ENABLE
|
||
#undef SWO_TRACE_ENABLE
|
||
|
||
#endif //SERIAL_TRACE_ENABLE
|
||
|
||
|
||
#ifndef local_time
|
||
#define local_time() HAL_GetTick() ///< Локальное время
|
||
#endif
|
||
#ifndef LOG_LEVEL
|
||
#define LOG_LEVEL 1 ///< @brief Уровень логирования (по умолчанию == 1)
|
||
#endif
|
||
|
||
|
||
#if LOG_LEVEL == 0 // лог отключен
|
||
#define \
|
||
log_printf(TAG, fmt, ...)
|
||
#elif LOG_LEVEL == 1 // только тэг
|
||
#define log_printf(TAG, fmt, ...) \
|
||
my_printf("\n[%lu] [%s] " fmt, \
|
||
(unsigned long)local_time(), TAG, ##__VA_ARGS__)
|
||
#elif LOG_LEVEL >= 2 // всё
|
||
#define log_printf(TAG, fmt, ...) \
|
||
my_printf("\n[%lu] [%s] (%s:%d) " fmt, \
|
||
(unsigned long)local_time(), TAG, __FILE__, __LINE__, ##__VA_ARGS__)
|
||
#endif
|
||
|
||
/** TRACE_SERIAL
|
||
* @}
|
||
*/
|
||
|
||
/**
|
||
* @addtogroup TRACE_GPIO GPIO trace defines
|
||
* @ingroup TRACE
|
||
* @brief Дефайны для работы с GPIO трассировкой
|
||
* @details Определяется дефайны для работы с GPIO трассировкой:
|
||
- TRACE_GPIO_RESET() - для сброса ножки GPIO (через BSRR)
|
||
- TRACE_GPIO_SET() - для выставления ножки GPIO (через BSRR)
|
||
|
||
- Если трассировка @ref GPIO_TRACE_ENABLE отключена, то все дефайны определяются как 'ничего'
|
||
и на производительность кода не влияют
|
||
* @{
|
||
*
|
||
* @def TRACE_GPIO_RESET(_gpio_, _pin_)
|
||
* @brief Сбросить указанную ножку GPIO
|
||
* @param _gpio_ Указатель на структуру GPIO (напр. GPIOA)
|
||
* @param _pin_ Номер ножки (напр. GPIO_PIN_0)
|
||
* @details Варианты реализации:
|
||
* - GPIO_TRACE_ENABLE не определён - макрос пустой
|
||
* - GPIO_TRACE_ENABLE определён - устанавливает бит сброса через BSRR ((_pin_)<<16)
|
||
*
|
||
* @def TRACE_GPIO_SET(_gpio_, _pin_)
|
||
* @brief Установить указанную ножку GPIO
|
||
* @param _gpio_ Указатель на структуру GPIO (например GPIOA)
|
||
* @param _pin_ Номер ножки (напр. GPIO_PIN_0)
|
||
* @details Варианты реализации:
|
||
* - GPIO_TRACE_ENABLE не определён - макрос пустой
|
||
* - GPIO_TRACE_ENABLE определён - устанавливает бит установки через BSRR (_pin_)
|
||
*/
|
||
#ifndef GPIO_TRACE_ENABLE
|
||
#define TRACE_GPIO_SET(_gpio_,_pin_)
|
||
#define TRACE_GPIO_RESET(_gpio_,_pin_)
|
||
#else
|
||
#define TRACE_GPIO_SET(_gpio_,_pin_) (_gpio_)->BSRR = (((_pin_)))
|
||
#define TRACE_GPIO_RESET(_gpio_,_pin_) (_gpio_)->BSRR = ((_pin_)<<16)
|
||
#endif //GPIO_TRACE_ENABLE
|
||
|
||
|
||
/** TRACE_GPIO
|
||
* @}
|
||
*/
|
||
|
||
|
||
|
||
#if defined(HAL_MODULE_ENABLED) && defined(RTT_TRACE_ENABLE)
|
||
|
||
/**
|
||
* @addtogroup TRACE_RTT_FLASH Flash RTT Buffer
|
||
* @ingroup TRACE
|
||
* @brief Макросы и функции для сохранения/чтения RTT буфера в Flash
|
||
* @details Модуль позволяет сохранять данные RTT буфера во Flash и читать их обратно по тегам.
|
||
* Теги работают следующим образом:
|
||
* - Базовый тег (младший байт = 0): модуль сам выбирает первый свободный слот во Flash;
|
||
* новые записи получают автоинкрементированный младший байт тега (от 0x00 до 0xFF).
|
||
* - Конкретный тег (младший байт != 0): запись или чтение происходит строго с указанным тегом;
|
||
* если слот с таким тегом уже занят, запись не выполняется.
|
||
* - Автоинкремент позволяет хранить несколько последовательных записей в пределах одного базового тега,
|
||
* без необходимости вручную отслеживать адреса Flash или позиции буферов.
|
||
*
|
||
* Параметры:
|
||
* - @ref RTT_FLASH_BUFFER_SIZE - Размер буфера RTT в Flash
|
||
* - @ref RTT_FLASH_SECTOR - Сектор FLASH куда положится RTT буфер
|
||
* - @ref RTT_FLASH_SECTOR_START - Начало сектора RTT_FLASH_SECTOR
|
||
* - @ref RTT_FLASH_SECTOR_END - Конец сектора RTT_FLASH_SECTOR
|
||
*
|
||
* @{
|
||
*/
|
||
|
||
|
||
/**
|
||
* @brief Структура RTT, которая будет положена в Flash
|
||
*/
|
||
typedef struct {
|
||
uint32_t tag; ///< Уникальный идентификатор буфера
|
||
uint32_t size; ///< Размер данных
|
||
char data[RTT_FLASH_BUFFER_SIZE]; ///< Буфер RTT
|
||
} RTT_FlashHeader_t;
|
||
|
||
/**
|
||
* @brief Подготовка Flash к записи
|
||
* @details Сбрасывает ошибки Flash и ожидает готовности перед записью
|
||
*/
|
||
__STATIC_FORCEINLINE void RTT_FlashPrepare(void)
|
||
{
|
||
HAL_FLASH_Unlock();
|
||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_PGSERR | FLASH_FLAG_WRPERR | FLASH_FLAG_OPERR);
|
||
while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) {
|
||
__NOP();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Сохраняет последние символы RTT-буфера в Flash по тегу
|
||
* @param tag Базовый или конкретный идентификатор буфера.
|
||
* @param tail_size Количество последних символов RTT для копирования
|
||
* @param buf_num Указатель на переменную в которую запишется номер буфера для конкретного тега
|
||
* @return >=0 — номер буфера (тег) для записи, <0 — ошибка (нет места, тег уже занят, ошибка записи в флеш)
|
||
*
|
||
* @details Автоматически копирует последние tail_size символов из RTT-буфера
|
||
* и записывает их во Flash.
|
||
* Тег может быть базовым или конкретным:
|
||
* - Если базовый (младший байт == 0) — будет выбран первый свободный слот с автоинкрементом.
|
||
* Автоинкремент формируется в пределах от 0x1 до 0xFF
|
||
* - Если конкретный (младший байт != 0) — запись выполняется только с этим тегом, иначе ошибка.
|
||
*
|
||
*/
|
||
__STATIC_FORCEINLINE int RTT_SaveToFlash(uint32_t tag, uint32_t tail_size)
|
||
{
|
||
if (tag == 0xFFFFFFFF)
|
||
return -1; // Неверный тег
|
||
|
||
SEGGER_RTT_BUFFER_UP *up = &_SEGGER_RTT.aUp[0];
|
||
unsigned buf_size = up->SizeOfBuffer;
|
||
unsigned wr = up->WrOff;
|
||
|
||
// Ограничиваем по размеру буфера RTT и RTT_FLASH_BUFFER_SIZE
|
||
unsigned n = (tail_size > buf_size) ? buf_size : tail_size;
|
||
if (n > RTT_FLASH_BUFFER_SIZE)
|
||
n = RTT_FLASH_BUFFER_SIZE;
|
||
|
||
uint32_t addr = RTT_FLASH_SECTOR_START;
|
||
RTT_FlashHeader_t *flash_hdr = NULL;
|
||
uint32_t base_tag = tag & 0xFFFFFF00;
|
||
uint32_t next_tag = (tag & 0xFF) == 0 ? tag + 1 : tag;
|
||
|
||
// Ищем первый свободный слот, параллельно автоинкрементируем тег
|
||
while ((addr + sizeof(RTT_FlashHeader_t)) <= RTT_FLASH_SECTOR_END)
|
||
{
|
||
flash_hdr = (RTT_FlashHeader_t *)addr;
|
||
|
||
if (flash_hdr->tag == 0xFFFFFFFF)
|
||
break; // Нашли свободное место
|
||
|
||
|
||
if((flash_hdr->tag & 0xFFFFFF00) == base_tag) // выбраный тег
|
||
{
|
||
if ((tag & 0xFF) == 0) // если он базовый - ищем последний
|
||
next_tag = flash_hdr->tag + 1; // автоинкремент
|
||
else
|
||
if(flash_hdr->tag == tag) // если он конкретный и уже существует - то ошибка
|
||
return -1; // конкретный тег уже занят
|
||
}
|
||
|
||
|
||
if(next_tag - tag > 0xFF)
|
||
return -1; // автоинкремент слишком большой
|
||
|
||
addr += sizeof(RTT_FlashHeader_t);
|
||
}
|
||
|
||
if ((addr + sizeof(RTT_FlashHeader_t)) > RTT_FLASH_SECTOR_END)
|
||
return -1; // Нет свободного места
|
||
|
||
// Копируем последние n символов из RTT
|
||
char temp[RTT_FLASH_BUFFER_SIZE];
|
||
unsigned valid_count = 0;
|
||
|
||
for (unsigned i = 0; i < n; i++)
|
||
{
|
||
unsigned idx = (wr + buf_size - n + i) % buf_size;
|
||
char c = up->pBuffer[idx];
|
||
if (c != 0)
|
||
temp[valid_count++] = c;
|
||
}
|
||
|
||
RTT_FlashPrepare();
|
||
|
||
// Формируем структуру в RAM
|
||
RTT_FlashHeader_t flash_data;
|
||
flash_data.tag = next_tag;
|
||
flash_data.size = valid_count;
|
||
memcpy(flash_data.data, temp, valid_count);
|
||
|
||
// Записываем структуру во Flash (по 4 байта)
|
||
const uint32_t *p = (const uint32_t *)&flash_data;
|
||
for (unsigned i = 0; i < sizeof(RTT_FlashHeader_t) / 4; i++)
|
||
{
|
||
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i * 4, p[i]) != HAL_OK)
|
||
return -1;
|
||
}
|
||
|
||
HAL_FLASH_Lock();
|
||
__DSB();
|
||
__ISB();
|
||
|
||
|
||
return (int)(next_tag&0xFF);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Читает последние символы RTT-буфера из Flash по тегу
|
||
* @param tag Базовый или конкретный идентификатор буфера.
|
||
* @param Buffer Буфер назначения для копирования данных
|
||
* @param tail_size Количество последних символов, которые нужно прочитать
|
||
* @param read_size Количество считанных символов
|
||
* @return >=0 — номер буфера (тег) для записи, <0 — ошибка (тег не найден или структура повреждена)
|
||
*
|
||
* @details Копирует последние tail_size символов из найденной записи Flash в Buffer.
|
||
* Тег может быть базовым или конкретным:
|
||
* - Если базовый (младший байт == 0) — будет прочитана последняя запись из группы.
|
||
* - Если конкретный (младший байт != 0) — прочитывается именно эта запись.
|
||
*/
|
||
__STATIC_FORCEINLINE int RTT_ReadFromFlash(uint32_t tag, char *Buffer, uint32_t tail_size, uint32_t *read_size)
|
||
{
|
||
if (!Buffer || tail_size == 0)
|
||
return -1; // Неверные параметры
|
||
|
||
if (tag == 0xFFFFFFFF)
|
||
return -1; // Недопустимый тег
|
||
|
||
uint32_t addr = RTT_FLASH_SECTOR_START;
|
||
RTT_FlashHeader_t *flash_hdr = NULL;
|
||
RTT_FlashHeader_t *target_hdr = NULL;
|
||
uint32_t base_tag = tag & 0xFFFFFF00;
|
||
|
||
// Поиск записи по тегу
|
||
while ((addr + sizeof(RTT_FlashHeader_t)) <= RTT_FLASH_SECTOR_END)
|
||
{
|
||
flash_hdr = (RTT_FlashHeader_t *)addr;
|
||
|
||
if (flash_hdr->tag == 0xFFFFFFFF)
|
||
break; // Достигнут конец записанных структур
|
||
|
||
// выбраный тег
|
||
if((flash_hdr->tag & 0xFFFFFF00) == base_tag)
|
||
{
|
||
if ((tag & 0xFF) == 0) // если он базовый - ищем последний
|
||
target_hdr = flash_hdr; // сохраняем последний в группе
|
||
else
|
||
if(flash_hdr->tag == tag) // если он конкретный и найден - берем его
|
||
{
|
||
target_hdr = flash_hdr;
|
||
break; // конкретный тег найден
|
||
}
|
||
}
|
||
|
||
addr += sizeof(RTT_FlashHeader_t);
|
||
}
|
||
|
||
if (!target_hdr) return -1; // Тег не найден
|
||
|
||
// Проверка корректности размера
|
||
if (target_hdr->size > RTT_FLASH_BUFFER_SIZE)
|
||
return -1; // Повреждённая запись
|
||
|
||
// Определяем количество читаемых символов
|
||
uint32_t n = (tail_size > target_hdr->size) ? target_hdr->size : tail_size;
|
||
// Начальная позиция для чтения последних tail_size символов
|
||
uint32_t start = target_hdr->size - n;
|
||
// Копируем данные из Flash в RAM
|
||
memcpy(Buffer, &target_hdr->data[start], n);
|
||
|
||
if(read_size != NULL)
|
||
{
|
||
*read_size = n;
|
||
}
|
||
|
||
__DSB();
|
||
__ISB();
|
||
|
||
return (int)(target_hdr->tag & 0xFF);
|
||
}
|
||
|
||
/**
|
||
* @brief Стирание сектора Flash с RTT-буфером
|
||
*/
|
||
__STATIC_FORCEINLINE int RTT_EraseFlash(void)
|
||
{
|
||
FLASH_EraseInitTypeDef eraseInit;
|
||
uint32_t pageError = 0;
|
||
|
||
RTT_FlashPrepare();
|
||
|
||
eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
|
||
eraseInit.Sector = RTT_FLASH_SECTOR;
|
||
eraseInit.NbSectors = 1;
|
||
|
||
if (HAL_FLASHEx_Erase(&eraseInit, &pageError) != HAL_OK)
|
||
{
|
||
return -1;
|
||
}
|
||
return 0;
|
||
HAL_FLASH_Lock();
|
||
}
|
||
|
||
/** TRACE_RTT_FLASH
|
||
* @}
|
||
*/
|
||
|
||
#else // HAL_MODULE_ENABLED && RTT_TRACE_ENABLE
|
||
#define RTT_FlashPrepare(...)
|
||
#define RTT_EraseFlash(...) 0
|
||
#define RTT_SaveToFlash(...) 0
|
||
#define RTT_ReadFromFlash(...) 0
|
||
#endif // HAL_MODULE_ENABLED && RTT_TRACE_ENABLE
|
||
|
||
|
||
|
||
|
||
/**
|
||
* @addtogroup TRACE_HARDFAULT Hardfault trace defines
|
||
* @ingroup TRACE
|
||
* @brief Модуль трассировки HardFault с возможностью сохранения RTT буфера во Flash
|
||
* @details
|
||
* Этот модуль позволяет сохранять контекст процессора и последние символы RTT буфера при возникновении HardFault.
|
||
*
|
||
* Механизм работы:
|
||
* - При срабатывании HardFault вызывается HF_HandleFault(), который:
|
||
* 1. Получает указатель на стек, где произошёл HardFault (MSP или PSP).
|
||
* 2. Выводит значения регистров R0-R3, R12, LR, PC, PSR и системных регистров SCB.
|
||
* 3. Формирует строку с регистрами и копирует последние символы RTT буфера.
|
||
* 4. Сохраняет данные во Flash с базовым тегом HF_RTT_TAG_BASE.
|
||
* - Для восстановления последнего HardFault используется HF_CheckRecovered(), который:
|
||
* 1. Читает запись во Flash по базовому тегу.
|
||
* 2. Выводит сохранённый RTT буфер и контекст регистров.
|
||
* 3. Опционально стирает Flash после восстановления.
|
||
*
|
||
* Параметры:
|
||
* - @ref HARDFAULT_SERIAL_TRACE - Включить обработку и serial трассировку Hardfault
|
||
* Если отключена то вставляются заглушки, никак не влияющие на параметры и остальную программу
|
||
* - @ref HF_RTT_TAG_BASE - Базовый тег RTT Flash для HardFault
|
||
* - @ref HF_RTT_TAIL_SIZE - Размер буфера RTT, который сохранится при Hardfault
|
||
* - @ref HF_STACK_DUMP_WORDS - Сколько слов стека будет проанализировано во время Hardfault
|
||
* - @ref HF_FLASH_ADDR - Адрес FLASH куда положится RTT буфер
|
||
* - @ref HF_RAM_END - Конец RAM памяти (чтобы во время анализа стека не выйти за пределы)
|
||
*
|
||
@code
|
||
void Hardfault()
|
||
{
|
||
HF_HandleFault();
|
||
NVIC_SystemReset();
|
||
}
|
||
|
||
int main()
|
||
{
|
||
if(HF_CheckRecovered(0))
|
||
{
|
||
//set hardfault error
|
||
RTT_EraseFlash(); // erase rtt flash after message readed
|
||
}
|
||
}
|
||
@endcode
|
||
* @{
|
||
*/
|
||
#if defined(HAL_MODULE_ENABLED) && defined(HARDFAULT_SERIAL_TRACE)
|
||
|
||
#ifndef HF_RTT_TAIL_SIZE
|
||
#define HF_RTT_TAIL_SIZE RTT_FLASH_BUFFER_SIZE ///< Размер буфера RTT, который сохранится при Hardfault
|
||
#endif
|
||
|
||
/**
|
||
* @brief Контекст стек-фрейма процессора при HardFault
|
||
* @details Сохраняет регистры R0-R3, R12, LR, PC, PSR для последующего анализа.
|
||
*/
|
||
typedef struct {
|
||
uint32_t r0; ///< Регистр R0
|
||
uint32_t r1; ///< Регистр R1
|
||
uint32_t r2; ///< Регистр R2
|
||
uint32_t r3; ///< Регистр R3
|
||
uint32_t r12; ///< Регистр R12
|
||
uint32_t lr; ///< Link Register
|
||
uint32_t pc; ///< Program Counter
|
||
uint32_t psr; ///< Program Status Register
|
||
} HF_StackFrame_t;
|
||
|
||
/**
|
||
* @brief Проверка и вывод последнего HardFault-трейса из Flash
|
||
* @details
|
||
* Функция ищет последнюю запись HardFault по базовому тегу HF_RTT_TAG_BASE
|
||
* и выводит её содержимое в консоль. После успешного вывода Flash можно опционально очистить.
|
||
*
|
||
* @return int
|
||
* - 1 — данные HardFault найдены и выведены
|
||
* - 0 — данные отсутствуют или тег не найден
|
||
*
|
||
* @note Вызов рекомендуется при инициализации приложения для анализа предыдущего сбоя.
|
||
*/
|
||
__STATIC_FORCEINLINE int HF_CheckRecovered(int erase)
|
||
{
|
||
char buffer[RTT_FLASH_BUFFER_SIZE];
|
||
uint32_t read_size = 0;
|
||
int n_hardfault = RTT_ReadFromFlash(HF_RTT_TAG_BASE, buffer, HF_RTT_TAIL_SIZE, &read_size);
|
||
if (n_hardfault > 0)
|
||
{
|
||
my_printf("\n--- Recovered HardFault RTT buffer #%u ---\n", n_hardfault);
|
||
for (int i = 0; i < read_size; i++)
|
||
{
|
||
char c = buffer[i];
|
||
if (c == 0 || c == (char)0xFF) break;
|
||
my_printf("%c", c);
|
||
}
|
||
|
||
if(erase)
|
||
RTT_EraseFlash();
|
||
my_printf("\n--------- HardFault Dump End ---------\n");
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
static HF_StackFrame_t *stack_frame;
|
||
static uint32_t stack_dump[HF_STACK_DUMP_WORDS];
|
||
static void *ret_adr[10] = {0};
|
||
/**
|
||
* @brief Обработчик HardFault
|
||
* @details
|
||
* Вызывается из прерывания HardFault или в любом месте где понятно что ошибка критическая.
|
||
* Последовательно выполняет:
|
||
* 1. Определяет активный стек (MSP или PSP) на момент сбоя.
|
||
* 2. Сохраняет значения регистров R0-R3, R12, LR, PC, PSR.
|
||
* 3. Выводит системные регистры CFSR, HFSR, DFSR, AFSR, MMFAR, BFAR.
|
||
* 4. Формирует stack trace с 3 уровнями возврата.
|
||
* 5. Копирует последние символы RTT буфера.
|
||
* 6. Сохраняет все данные во Flash через RTT_SaveToFlash с базовым тегом HF_RTT_TAG_BASE.
|
||
*
|
||
* @note Функция защищена, так как вызывается в контексте сбоя — минимизирует использование вызовов HAL.
|
||
*/
|
||
__STATIC_FORCEINLINE void HF_HandleFault(void)
|
||
{
|
||
// Получаем указатель на стек, где произошёл HardFault
|
||
__ASM volatile(
|
||
"TST lr, #4 \n"
|
||
"ITE EQ \n"
|
||
"MRSEQ %[ptr], MSP\n"
|
||
"MRSNE %[ptr], PSP\n"
|
||
: [ptr] "=r"(stack_frame)
|
||
);
|
||
|
||
my_printf("\n===== HardFault occurred! =====\n");
|
||
my_printf("R0 = 0x%08X\n", stack_frame->r0);
|
||
my_printf("R1 = 0x%08X\n", stack_frame->r1);
|
||
my_printf("R2 = 0x%08X\n", stack_frame->r2);
|
||
my_printf("R3 = 0x%08X\n", stack_frame->r3);
|
||
my_printf("R12 = 0x%08X\n", stack_frame->r12);
|
||
my_printf("LR = 0x%08X\n", stack_frame->lr);
|
||
my_printf("PC = 0x%08X\n", stack_frame->pc);
|
||
my_printf("PSR = 0x%08X\n", stack_frame->psr);
|
||
|
||
my_printf("CFSR = 0x%08X\n", SCB->CFSR);
|
||
my_printf("HFSR = 0x%08X\n", SCB->HFSR);
|
||
my_printf("DFSR = 0x%08X\n", SCB->DFSR);
|
||
my_printf("AFSR = 0x%08X\n", SCB->AFSR);
|
||
my_printf("MMFAR = 0x%08X\n", SCB->MMFAR);
|
||
my_printf("BFAR = 0x%08X\n", SCB->BFAR);
|
||
|
||
// --- Stack trace ---
|
||
my_printf("--- Stack trace ---\n");
|
||
ret_adr[0] = __builtin_return_address(0);
|
||
ret_adr[1] = __builtin_return_address(1);
|
||
ret_adr[2] = __builtin_return_address(2);
|
||
|
||
for (int i = 0; i < 3; i++) // развернуть n уровней
|
||
{
|
||
if(ret_adr[i])
|
||
my_printf(" #%d: 0x%08lX\r\n", i, ret_adr[i]); // -1 для Thumb
|
||
}
|
||
RTT_SaveToFlash(HF_RTT_TAG_BASE, HF_RTT_TAIL_SIZE);
|
||
}
|
||
#else // HAL_MODULE_ENABLED && HARDFAULT_SERIAL_TRACE
|
||
#define HF_CheckRecovered(...) 0
|
||
#define HF_HandleFault(...)
|
||
#endif // HAL_MODULE_ENABLED && HARDFAULT_SERIAL_TRACE
|
||
/** TRACE_HARDFAULT
|
||
* @}
|
||
*/
|
||
|
||
|
||
|
||
#endif //__TRACE_H_
|