Переструктурирование:
- MyLibs - максимально платформонезависимые библиотеки (кроме разве что RTT) - RTT - STM32_General - библиотеки для периферии stm32
This commit is contained in:
96
MyLibs/Inc/__mylibs_config.h
Normal file
96
MyLibs/Inc/__mylibs_config.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file mylibs_config.h
|
||||
* @brief Конфигурации для библиотек MyLibs
|
||||
**************************************************************************
|
||||
* @defgroup MYLIBS_CONFIG Configs
|
||||
* @ingroup MYLIBS_ALL
|
||||
* @brief Конфигурации для библиотек MyLibs
|
||||
* @{
|
||||
*************************************************************************/
|
||||
#ifndef __MYLIBS_CONFIG_H_
|
||||
#define __MYLIBS_CONFIG_H_
|
||||
|
||||
#include "stm32f4xx_hal.h"
|
||||
|
||||
// user includes
|
||||
|
||||
/**
|
||||
* @addtogroup TRACE_CONFIG Trace configs
|
||||
* @ingroup MYLIBS_CONFIG
|
||||
* @brief Конфигурация трекеров и трассировки
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define TRACKERS_ENABLE ///< Включить трекеры
|
||||
#define SERIAL_TRACE_ENABLE ///< Включить serial трассировку
|
||||
#define RTT_TRACE_ENABLE ///< Включить serial трассировку через RTT
|
||||
#define SWO_TRACE_ENABLE ///< Включить serial трассировку через SWO
|
||||
/**
|
||||
* @brief Уровень log serial трассировки @ref log_printf
|
||||
* - LOG_LEVEL == 0 - логирование отключено (макрос пустой)
|
||||
* - LOG_LEVEL == 1 - выводится время и TAG
|
||||
* - LOG_LEVEL >= 2 - выводится время, TAG, имя файла и номер строки
|
||||
*/
|
||||
#define LOG_LEVEL 1
|
||||
|
||||
#define RTT_FLASH_BUFFER_SIZE 1024 ///< Размер буфера RTT в Flash
|
||||
#define RTT_FLASH_SECTOR FLASH_SECTOR_11 ///< Сектор FLASH куда положится RTT буфер
|
||||
#define RTT_FLASH_SECTOR_START 0x080E0000 ///< Начало сектора RTT_FLASH_SECTOR
|
||||
#define RTT_FLASH_SECTOR_END 0x080FFFFF ///< Конец сектора RTT_FLASH_SECTOR
|
||||
|
||||
|
||||
#define HARDFAULT_SERIAL_TRACE ///< Включить обработку и serial трассировку Hardfault
|
||||
#define HF_RTT_TAG_BASE 0xDEAD0000 ///< базовый тег для HardFault
|
||||
#define HF_RTT_TAIL_SIZE RTT_FLASH_BUFFER_SIZE ///< Размер буфера RTT, который сохранится при Hardfault
|
||||
#define HF_STACK_DUMP_WORDS 32 ///< Сколько слов стека будет проанализировано во время Hardfault
|
||||
#define HF_FLASH_ADDR ((uint32_t)0x080FF000) ///< Адрес FLASH куда положится RTT буфер
|
||||
#define HF_RAM_END 0x20030000 ///< Конец RAM памяти (чтобы во время анализа стека не выйти за пределы)
|
||||
|
||||
#define GPIO_TRACE_ENABLE ///< Включить GPIO трассировку
|
||||
|
||||
/** TRACE_CONFIG
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup EVOLVE_CONFIG Evolve configs
|
||||
* @ingroup MYLIBS_CONFIG
|
||||
* @brief Конфигурация однослойного персептрона и алгоритма обучения
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define ENABLE_EVOLVE_OPTIMIZATION ///< Включить оптимизацию параметров
|
||||
#define EVOLVE_MAX_PARAMS 20 ///< Максимальное количество параметров
|
||||
#define EVOLVE_MAX_CANDIDATES 100 ///< Максимальное количество кандидатов для обучения
|
||||
|
||||
/** EVOLVE_CONFIG
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup LIBS_CONFIG Libraries configs
|
||||
* @ingroup MYLIBS_CONFIG
|
||||
* @brief Подключение различных модулей библиотеки
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define local_time() uwTick ///< Локальное время
|
||||
|
||||
#define INCLUDE_EVOLVE_OPTIMIZER ///< Подключить библиотеку для оптимизации параметров
|
||||
#define INCLUDE_BIT_ACCESS_LIB ///< Подключить библиотеку с typedef с битовыми полями
|
||||
#define INCLUDE_TRACKERS_LIB ///< Подключить библиотеку с трекерами
|
||||
#define INCLUDE_TRACE_LIB ///< Подключить библиотеку с трейсами
|
||||
#define INCLUDE_GENERAL_PERIPH_LIBS ///< Подключить библиотеку с периферией
|
||||
#define FREERTOS_DELAY ///< Использовать FreeRTOS задержку, вместо HAL
|
||||
|
||||
/** LIBS_CONFIG
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** MYLIBS_CONFIG
|
||||
* @}
|
||||
*/
|
||||
#endif //__MYLIBS_CONFIG_H_
|
||||
125
MyLibs/Inc/__mylibs_include.h
Normal file
125
MyLibs/Inc/__mylibs_include.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file mylibs_include.h
|
||||
* @brief Заголочный файл для всех библиотек
|
||||
**************************************************************************
|
||||
* @details
|
||||
Здесь нужно собрать библиотеки и дефайны, которые должны быть видны во всем проекте,
|
||||
чтобы не подключать 100 инклюдов в каждом ".c" файле
|
||||
**************************************************************************
|
||||
* @defgroup MYLIBS_ALL My Libs
|
||||
* @brief Все используемые MyLibs библиотеки
|
||||
* @details
|
||||
Для подключения библиотеки необходимо:
|
||||
- Сконфигурировать mylibs_config.h:
|
||||
- Подключить заголовочный файл HAL библиотеки конкретного МК (напр. stm32f4xx_hal.h)
|
||||
- Подключить другие заголовочные файлы которые общие для всего проекта и должны быть видны
|
||||
-
|
||||
- Подключить mylibs_include.h туда, где необходим доступ к библиотекам.
|
||||
|
||||
* @defgroup MYLIBS_PERIPHERAL Peripheral
|
||||
* @ingroup MYLIBS_ALL
|
||||
* @brief Модули для управления периферией
|
||||
*
|
||||
*************************************************************************/
|
||||
#ifndef __MYLIBS_INCLUDE_H_
|
||||
#define __MYLIBS_INCLUDE_H_
|
||||
|
||||
#include "mylibs_defs.h"
|
||||
|
||||
|
||||
#ifdef ARM_MATH_CM4
|
||||
#include "arm_math.h"
|
||||
#else
|
||||
#include "math.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef INCLUDE_BIT_ACCESS_LIB
|
||||
#include "bit_access.h"
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_TRACKERS_LIB
|
||||
#include "trackers.h"
|
||||
#else
|
||||
#define TrackerTypeDef(num_user_vars) void *
|
||||
#define num_of_usercnts(_user_) 0
|
||||
#define assert_tracecnt(_cntstruct_, _uservarnumb_) 0
|
||||
#define if_assert_usertracker(_cntstruct_, _uservarnumb_) if(0)
|
||||
#define tern_assert_usertracker(_cntstruct_, _uservarnumb_) 0
|
||||
#define TrackerGet_Ok(_cntstruct_) dummy
|
||||
#define TrackerGet_Err(_cntstruct_) dummy
|
||||
#define TrackerGet_Warn(_cntstruct_) dummy
|
||||
#define TrackerGet_User(_cntstruct_, _uservarnumb_) dummy
|
||||
#define TrackerCnt_Ok(_cntstruct_)
|
||||
#define TrackerCnt_Err(_cntstruct_)
|
||||
#define TrackerCnt_Warn(_cntstruct_)
|
||||
#define TrackerCnt_User(_cntstruct_, _uservarnumb_)
|
||||
#define TrackerWrite_User(_cntstruct_, _uservarnumb_, _val_)
|
||||
#define TrackerClear_All(_cntstruct_)
|
||||
#define TrackerClear_Ok(_cntstruct_)
|
||||
#define TrackerClear_Err(_cntstruct_)
|
||||
#define TrackerClear_Warn(_cntstruct_)
|
||||
#define TrackerClear_User(_cntstruct_)
|
||||
#define TrackerClear_UserAll(_cntstruct_)
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_TRACE_LIB
|
||||
#include "trace.h"
|
||||
#else
|
||||
#define my_printf(...)
|
||||
#define log_printf(TAG, fmt, ...)
|
||||
#define TRACE_GPIO_SET(_gpio_,_pin_)
|
||||
#define TRACE_GPIO_RESET(_gpio_,_pin_)
|
||||
#define RTT_FlashPrepare(...)
|
||||
#define RTT_EraseFlash(...) 0
|
||||
#define RTT_SaveToFlash(...) 0
|
||||
#define RTT_ReadFromFlash(...) 0
|
||||
#define HF_CheckRecovered(...) 0
|
||||
#define HF_HandleFault(...)
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_EVOLVE_OPTIMIZER
|
||||
#include "evolve_optimizer.h"
|
||||
#else
|
||||
typedef struct {
|
||||
uint16_t n_params;
|
||||
uint16_t n_cand;
|
||||
uint16_t n_best;
|
||||
uint16_t iq_mutation;
|
||||
int32_t loss[0];
|
||||
int32_t candidates[0][0];
|
||||
} EvolveOptimizer_t;
|
||||
#define EvolveOptimizer_Init(opt, n_params, n_cand, n_best, iq_mutation, start_params)
|
||||
#define EvolveOptimizer_Step(opt, params, LossFunc)
|
||||
#define PARAM_SCALE_Q16(x, min_val, max_val) (x)
|
||||
#define PARAM_UNSCALE_Q16(q16_val, min_val, max_val) (q16_val)
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_GENERAL_PERIPH_LIBS
|
||||
|
||||
#include "__general_flash.h"
|
||||
#include "general_gpio.h"
|
||||
#ifdef HAL_SPI_MODULE_ENABLED
|
||||
#include "general_spi.h"
|
||||
#endif
|
||||
#ifdef HAL_UART_MODULE_ENABLED
|
||||
#include "general_uart.h"
|
||||
#endif
|
||||
#ifdef HAL_TIM_MODULE_ENABLED
|
||||
#include "general_tim.h"
|
||||
#endif
|
||||
|
||||
#endif //INCLUDE_GENERAL_PERIPH_LIBS
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////---USER SETTINGS---/////////////////////////
|
||||
// user includes
|
||||
|
||||
// user settings
|
||||
/////////////////////////---USER SETTINGS---/////////////////////////
|
||||
|
||||
|
||||
#endif // __MYLIBS_INCLUDE_H_
|
||||
159
MyLibs/Inc/bit_access.h
Normal file
159
MyLibs/Inc/bit_access.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file bit_access.h
|
||||
* @brief Заголочный файл для дефайнов битового доступа.
|
||||
**************************************************************************
|
||||
* @defgroup BIT_ACCESS_DEFINES Bit access defines
|
||||
* @ingroup MYLIBS_DEFINES
|
||||
* @brief Макросы и typedef'ы для работы с битами в unsigned типах.
|
||||
* @details
|
||||
В этом файле определены макросы для получения значения конкретного бита^
|
||||
- @ref uint8_bit
|
||||
- @ref uint16_bit
|
||||
- @ref uint32_bit
|
||||
- @ref uint64_bit
|
||||
|
||||
Особенности использования:
|
||||
- Индекс бита должен быть **константой на этапе компиляции**.
|
||||
Пример верного использования:
|
||||
@code
|
||||
uint8_t val = 0x05;
|
||||
uint8_t b2 = uint8_bit(val, 2); // Получить бит 2
|
||||
uint8_bit(val, 6) = 1; // Записать бит 6
|
||||
@endcode
|
||||
- Нельзя использовать переменные в качестве индекса:
|
||||
@code
|
||||
uint8_t i = 2;
|
||||
uint8_bit(val, i); // Не сработает!
|
||||
@endcode
|
||||
- Макросы возвращают 0 или 1.
|
||||
- Доступ реализован через приведение к `union` с битовыми полями, поэтому это
|
||||
безопасный способ работы с отдельными битами без ручного сдвига и маскирования.
|
||||
* @{
|
||||
*************************************************************************/
|
||||
#ifndef __BIT_ACCESS_H_
|
||||
#define __BIT_ACCESS_H_
|
||||
#include "mylibs_defs.h"
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint8_t all;
|
||||
struct
|
||||
{
|
||||
unsigned bit0:1;
|
||||
unsigned bit1:1;
|
||||
unsigned bit2:1;
|
||||
unsigned bit3:1;
|
||||
unsigned bit4:1;
|
||||
unsigned bit5:1;
|
||||
unsigned bit6:1;
|
||||
unsigned bit7:1;
|
||||
}bit;
|
||||
}uint8_BitTypeDef;
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint16_t all;
|
||||
struct
|
||||
{
|
||||
unsigned bit0:1;
|
||||
unsigned bit1:1;
|
||||
unsigned bit2:1;
|
||||
unsigned bit3:1;
|
||||
unsigned bit4:1;
|
||||
unsigned bit5:1;
|
||||
unsigned bit6:1;
|
||||
unsigned bit7:1;
|
||||
unsigned bit8:1;
|
||||
unsigned bit9:1;
|
||||
unsigned bit10:1;
|
||||
unsigned bit11:1;
|
||||
unsigned bit12:1;
|
||||
unsigned bit13:1;
|
||||
unsigned bit14:1;
|
||||
unsigned bit15:1;
|
||||
}bit;
|
||||
}uint16_BitTypeDef;
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint32_t all;
|
||||
struct
|
||||
{
|
||||
unsigned bit0:1; unsigned bit1:1; unsigned bit2:1; unsigned bit3:1;
|
||||
unsigned bit4:1; unsigned bit5:1; unsigned bit6:1; unsigned bit7:1;
|
||||
unsigned bit8:1; unsigned bit9:1; unsigned bit10:1; unsigned bit11:1;
|
||||
unsigned bit12:1; unsigned bit13:1; unsigned bit14:1; unsigned bit15:1;
|
||||
unsigned bit16:1; unsigned bit17:1; unsigned bit18:1; unsigned bit19:1;
|
||||
unsigned bit20:1; unsigned bit21:1; unsigned bit22:1; unsigned bit23:1;
|
||||
unsigned bit24:1; unsigned bit25:1; unsigned bit26:1; unsigned bit27:1;
|
||||
unsigned bit28:1; unsigned bit29:1; unsigned bit30:1; unsigned bit31:1;
|
||||
}bit;
|
||||
}uint32_BitTypeDef;
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint64_t all;
|
||||
struct
|
||||
{
|
||||
unsigned bit0:1; unsigned bit1:1; unsigned bit2:1; unsigned bit3:1;
|
||||
unsigned bit4:1; unsigned bit5:1; unsigned bit6:1; unsigned bit7:1;
|
||||
unsigned bit8:1; unsigned bit9:1; unsigned bit10:1; unsigned bit11:1;
|
||||
unsigned bit12:1; unsigned bit13:1; unsigned bit14:1; unsigned bit15:1;
|
||||
unsigned bit16:1; unsigned bit17:1; unsigned bit18:1; unsigned bit19:1;
|
||||
unsigned bit20:1; unsigned bit21:1; unsigned bit22:1; unsigned bit23:1;
|
||||
unsigned bit24:1; unsigned bit25:1; unsigned bit26:1; unsigned bit27:1;
|
||||
unsigned bit28:1; unsigned bit29:1; unsigned bit30:1; unsigned bit31:1;
|
||||
unsigned bit32:1; unsigned bit33:1; unsigned bit34:1; unsigned bit35:1;
|
||||
unsigned bit36:1; unsigned bit37:1; unsigned bit38:1; unsigned bit39:1;
|
||||
unsigned bit40:1; unsigned bit41:1; unsigned bit42:1; unsigned bit43:1;
|
||||
unsigned bit44:1; unsigned bit45:1; unsigned bit46:1; unsigned bit47:1;
|
||||
unsigned bit48:1; unsigned bit49:1; unsigned bit50:1; unsigned bit51:1;
|
||||
unsigned bit52:1; unsigned bit53:1; unsigned bit54:1; unsigned bit55:1;
|
||||
unsigned bit56:1; unsigned bit57:1; unsigned bit58:1; unsigned bit59:1;
|
||||
unsigned bit60:1; unsigned bit61:1; unsigned bit62:1; unsigned bit63:1;
|
||||
}bit;
|
||||
}uint64_BitTypeDef;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Получить n-й бит из uint8_t
|
||||
* @param _uint8_ Переменная типа uint8_t
|
||||
* @param _bit_ Константный номер бита (0..7)
|
||||
* @return Значение выбранного бита (0 или 1)
|
||||
* @note Индекс бита должен быть известен на этапе компиляции!
|
||||
*/
|
||||
#define uint8_bit(_uint8_, _bit_) (*(uint8_BitTypeDef *)(&(_uint8_))).bit.bit##_bit_
|
||||
/**
|
||||
* @brief Получить n-й бит из uint16_t
|
||||
* @param _uint16_ Переменная типа uint16_t
|
||||
* @param _bit_ Константный номер бита (0..15)
|
||||
* @return Значение выбранного бита (0 или 1)
|
||||
* @note Индекс бита должен быть известен на этапе компиляции!
|
||||
*/
|
||||
#define uint16_bit(_uint8_, _bit_) (*(uint16_BitTypeDef *)(&(_uint8_))).bit.bit##_bit_
|
||||
/**
|
||||
* @brief Получить n-й бит из uint32_t
|
||||
* @param _uint32_ Переменная типа uint32_t
|
||||
* @param _bit_ Константный номер бита (0..31)
|
||||
* @return Значение выбранного бита (0 или 1)
|
||||
* @note Индекс бита должен быть известен на этапе компиляции!
|
||||
*/
|
||||
#define uint32_bit(_uint8_, _bit_) (*(uint32_BitTypeDef *)(&(_uint8_))).bit.bit##_bit_
|
||||
/**
|
||||
* @brief Получить n-й бит из uint64_t
|
||||
* @param _uint64_ Переменная типа uint64_t
|
||||
* @param _bit_ Константный номер бита (0..63)
|
||||
* @return Значение выбранного бита (0 или 1)
|
||||
* @note Индекс бита должен быть известен на этапе компиляции!
|
||||
*/
|
||||
#define uint64_bit(_uint8_, _bit_) (*(uint64_BitTypeDef *)(&(_uint8_))).bit.bit##_bit_
|
||||
|
||||
#endif //__BIT_ACCESS_H_
|
||||
|
||||
/** BIT_ACCESS_DEFINES
|
||||
* @}
|
||||
*/
|
||||
338
MyLibs/Inc/evolve_optimizer.h
Normal file
338
MyLibs/Inc/evolve_optimizer.h
Normal file
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file evolve_optimizer.h
|
||||
* @brief Заголовочный файл для адаптивного подбора параметров
|
||||
******************************************************************************
|
||||
* @addtogroup EVOLVE_OPTIMIZER Evolve optimizer
|
||||
* @ingroup MYLIBS_DEFINES
|
||||
* @brief Библиотека для эволюционного подбора параметров
|
||||
* @details
|
||||
Поддерживает:
|
||||
- Любое количество параметров
|
||||
- Генерацию новых параметров на основе лучших кандидатов
|
||||
- Мутацию для поиска оптимальных параметров
|
||||
- Несколько независимых оптимизаторов в одной программе
|
||||
|
||||
|
||||
Параметры для конфигурации:
|
||||
- @ref ENABLE_EVOLVE_OPTIMIZATION - Включить оптимизацию параметров
|
||||
Если библиотека отключена @ref ENABLE_EVOLVE_OPTIMIZATION, то вставляются
|
||||
заглушки, никак не влияющие на параметры и остальную программу
|
||||
- @ref EVOLVE_MAX_PARAMS - Максимальное количество параметров
|
||||
- @ref EVOLVE_MAX_CANDIDATES - Максимальное количество кандидатов для обучения
|
||||
- (опционально) @ref EVOLVE_MUTATION_MIN_PCT - Минимальная мутация в процентах от Loss (по умолчанию 10%)
|
||||
- (опционально) @ref EVOLVE_MUTATION_MAX_PCT - Максимальная мутация в процентах от Loss (по умолчанию 100%)
|
||||
- (опционально) @ref ELOVLE_N_ELITE_CANDIDATE - Количество кандидатов, которые проходят в поколение без изменений
|
||||
|
||||
@par Пример использования:
|
||||
@code
|
||||
#include "evolve_optimizer.h"
|
||||
#define N_PARAMS 4
|
||||
#define N_CANDIDATES 100
|
||||
#define N_BEST 10
|
||||
#define MUTATION 0.1f
|
||||
float params[N_PARAMS];
|
||||
EvolveOptimizer_t optimizer;
|
||||
|
||||
// Формирование параметров
|
||||
uint16_t param_u16 = 800;
|
||||
float param_f = 0.01f;
|
||||
uint8_t param_u8 = 40;
|
||||
int16_t param_i16 = 1600;
|
||||
params[0] = PARAM_SCALE(param_u16, 0.0f, 1000.0f);
|
||||
params[1] = PARAM_SCALE(param_f, 0.001f, 0.1f);
|
||||
params[2] = PARAM_SCALE(param_u8, 10.0f, 100.0f);
|
||||
params[3] = PARAM_SCALE(param_i16, 500.0f, 5000.0f);
|
||||
|
||||
// Инициалиазция
|
||||
EvolveOptimizer_Init(&optimizer, N_PARAMS, N_CANDIDATES, N_BEST, MUTATION, params);
|
||||
|
||||
// Шаг эволюции
|
||||
float loss = calc_loss(); // расчет эффективности параметров (от 0 до 1)
|
||||
EvolveOptimizer_Step(&optimizer, params, loss);
|
||||
|
||||
// Взятие следующих для эволюции параметров
|
||||
param_u16 = PARAM_UNSCALE(params[0], 0.0f, 1000.0f);
|
||||
param_f = PARAM_UNSCALE(params[1], 0.001f, 0.1f);
|
||||
param_u8 = PARAM_UNSCALE(params[2], 10.0f, 100.0f);
|
||||
param_i16 = PARAM_UNSCALE(params[3], 500.0f, 5000.0f);
|
||||
@endcode
|
||||
* @{
|
||||
*****************************************************************************/
|
||||
#ifndef __EVOLVE_OPTIMIZER_H_
|
||||
#define __EVOLVE_OPTIMIZER_H_
|
||||
|
||||
#include "mylibs_defs.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef ENABLE_EVOLVE_OPTIMIZATION
|
||||
/**
|
||||
* @brief Линейное масштабирование x из диапазона [min_val, max_val] в диапазон [0, 1)
|
||||
*/
|
||||
#define PARAM_SCALE(x, min_val, max_val) \
|
||||
(((float)(x) - (float)(min_val)) / ((float)(max_val) - (float)(min_val)))
|
||||
|
||||
/**
|
||||
* @brief Обратное линейное масштабирование значения из [0, 1) в диапазон [min_val, max_val]
|
||||
*/
|
||||
#define PARAM_UNSCALE(val, min_val, max_val) \
|
||||
(((float)(val)) * ((float)(max_val) - (float)(min_val)) + (float)(min_val))
|
||||
|
||||
#ifndef local_time
|
||||
#define local_time() HAL_GetTick() ///< Локальное время
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef EVOLVE_MUTATION_MIN_PCT
|
||||
#define EVOLVE_MUTATION_MIN_PCT 10 ///< Минимальная мутация (в процентах от Loss)
|
||||
#endif
|
||||
#ifndef EVOLVE_MUTATION_MAX_PCT
|
||||
#define EVOLVE_MUTATION_MAX_PCT 100 ///< Максимальная мутация (в процентах от Loss)
|
||||
#endif
|
||||
#ifndef ELOVLE_N_ELITE_CANDIDATE
|
||||
#define ELOVLE_N_ELITE_CANDIDATE 2 ///< Количество кандидатов, которые проходят в поколение без изменений (по умолчанию 2)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Структура эволюционного оптимизатора
|
||||
*/
|
||||
typedef struct {
|
||||
float stability; ///< Коэффициент насколько стабильная популяция (0..1)(@ref n_cand)
|
||||
|
||||
uint16_t n_params; ///< Количество параметров
|
||||
uint16_t n_cand; ///< Количество кандидатов в популяции
|
||||
uint16_t n_best; ///< Количество лучших, усредняемых
|
||||
float mutation_amp; ///< Амплитуда мутации (0..1)
|
||||
|
||||
uint16_t cand_index; ///< Индекс кандидата для обработки
|
||||
uint16_t gen_index; ///< Индекс популяции
|
||||
|
||||
//INTERNAL
|
||||
float gen_mut; ///< Амплитуда мутации у текущей популяции
|
||||
|
||||
float loss[EVOLVE_MAX_CANDIDATES]; ///< Loss для каждого кандидата
|
||||
float candidates[EVOLVE_MAX_CANDIDATES][EVOLVE_MAX_PARAMS]; ///< Параметры кандидатов
|
||||
uint16_t sorted_idx[EVOLVE_MAX_CANDIDATES]; ///< Индексы отсортированных кандидатов
|
||||
} EvolveOptimizer_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @cond EVOLVE_INTERNAL
|
||||
*/
|
||||
|
||||
// Вспомогательный указатель для сортировки
|
||||
static EvolveOptimizer_t *g_sort_opt; // глобальный указатель на текущий оптимизатор
|
||||
|
||||
// функция условия сортировки
|
||||
static int cmp_idx(const void *a, const void *b) {
|
||||
if (g_sort_opt->loss[*(const uint16_t*)a] < g_sort_opt->loss[*(const uint16_t*)b])
|
||||
return -1;
|
||||
if (g_sort_opt->loss[*(const uint16_t*)a] > g_sort_opt->loss[*(const uint16_t*)b])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
/** @endcond */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Инициализация эволюционного оптимизатора.
|
||||
* @param opt Указатель на структуру оптимизатора
|
||||
* @param n_params Количество параметров в одном кандидате
|
||||
* @param n_cand Количество кандидатов
|
||||
* @param n_best Количество лучших, усредняемых
|
||||
* @param mutation_amp Амплитуда мутации (в диапазоне 0.0–1.0)
|
||||
* @param start_params Начальные параметры (в диапазоне 0.0–1.0)
|
||||
* @return 0 — если окей,
|
||||
* -1 — если ошибка
|
||||
*/
|
||||
__STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t* opt,
|
||||
uint16_t n_params,
|
||||
uint16_t n_cand,
|
||||
uint16_t n_best,
|
||||
float mutation_amp,
|
||||
float* start_params)
|
||||
{
|
||||
if((opt == NULL) || (start_params == NULL))
|
||||
return -1;
|
||||
|
||||
if(n_params > EVOLVE_MAX_PARAMS)
|
||||
return -1;
|
||||
opt->n_params = n_params;
|
||||
|
||||
if(n_cand > EVOLVE_MAX_CANDIDATES)
|
||||
return -1;
|
||||
opt->n_cand = n_cand;
|
||||
|
||||
if(n_best > EVOLVE_MAX_CANDIDATES/2)
|
||||
return -1;
|
||||
opt->n_best = n_best;
|
||||
|
||||
|
||||
if((mutation_amp > 1) || (mutation_amp < 0))
|
||||
return -1;
|
||||
if(mutation_amp <= 0.001f)
|
||||
mutation_amp = 0.001f;
|
||||
opt->mutation_amp = mutation_amp;
|
||||
|
||||
uint32_t seed = local_time();
|
||||
#ifdef ADC1
|
||||
seed += (ADC1->DR & 0xFF);
|
||||
#endif
|
||||
srand(seed);
|
||||
|
||||
for (uint16_t i = 0; i < n_cand; i++) {
|
||||
for (uint16_t j = 0; j < n_params; j++) {
|
||||
// Добавляем случайную мутацию вокруг стартового параметра
|
||||
float base = start_params[j];
|
||||
float inv_randmax = 1.0f / (float)RAND_MAX;
|
||||
float noise = ((float)rand() * inv_randmax * 2.0f - 1.0f) * mutation_amp;
|
||||
opt->candidates[i][j] = base + noise;
|
||||
if (opt->candidates[i][j] < 0.0f) opt->candidates[i][j] = 0.0f;
|
||||
if (opt->candidates[i][j] > 1.0f) opt->candidates[i][j] = 1.0f;
|
||||
}
|
||||
opt->loss[i] = 0.0f;
|
||||
}
|
||||
|
||||
opt->cand_index = 0;
|
||||
opt->gen_index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Один шаг эволюционного оптимизатора.
|
||||
* @param opt Указатель на структуру оптимизатора
|
||||
* @param params Массив параметров, которые будут обновлены (на выходе — новые параметры)
|
||||
* @param loss Loss текущего кандидата
|
||||
* @return 0 — если окей,
|
||||
* -1 — если ошибка
|
||||
* @details
|
||||
* Сохраняет loss текущего кандидата и формирует параметры следующего кандидата.
|
||||
* Если накоплено n_cand кандидатов, генерируется новое поколение.
|
||||
* Новое поколение формируется случайным выбором из n_best лучших с добавлением мутации.
|
||||
*
|
||||
* На выходе params содержит параметры следующего кандидата для измерений.
|
||||
* @note Функция использует глобальную внутреннюю переменную для сортировки.
|
||||
* Надо убедится что только один экземпляр функции запущен в момент времени
|
||||
*/
|
||||
__STATIC_INLINE int EvolveOptimizer_Step(EvolveOptimizer_t* opt,
|
||||
float* params,
|
||||
float loss)
|
||||
{
|
||||
if((opt == NULL) || (params == NULL))
|
||||
return -1;
|
||||
|
||||
uint16_t n_params = opt->n_params;
|
||||
if(n_params > EVOLVE_MAX_PARAMS)
|
||||
return -1;
|
||||
|
||||
uint16_t n_cand = opt->n_cand;
|
||||
if(n_cand > EVOLVE_MAX_CANDIDATES)
|
||||
return -1;
|
||||
|
||||
uint16_t n_best = opt->n_best;
|
||||
if(n_best > EVOLVE_MAX_CANDIDATES/2)
|
||||
return -1;
|
||||
|
||||
float mut = opt->mutation_amp;
|
||||
if((mut > 1) ||(mut < 0))
|
||||
return -1;
|
||||
|
||||
// 1. Сохраняем loss текущего кандидата
|
||||
opt->loss[opt->cand_index] = loss;
|
||||
opt->cand_index++;
|
||||
|
||||
if (opt->cand_index >= n_cand) {
|
||||
// 2. Сортируем текущее поколение по loss
|
||||
for(uint16_t i = 0; i < opt->n_cand; i++)
|
||||
opt->sorted_idx[i] = i;
|
||||
|
||||
g_sort_opt = opt;
|
||||
qsort(opt->sorted_idx, opt->n_cand, sizeof(uint16_t), cmp_idx);
|
||||
g_sort_opt = NULL;
|
||||
|
||||
// --- Адаптивная мутация в зависимости от Loss ---
|
||||
float best_loss = opt->loss[opt->sorted_idx[0]];
|
||||
float worst_loss = opt->loss[opt->sorted_idx[opt->n_cand - 1]];
|
||||
float diff = worst_loss - best_loss;
|
||||
|
||||
float sum_loss = 0.0f;
|
||||
for (uint16_t i = 0; i < n_cand; i++)
|
||||
sum_loss += opt->loss[i];
|
||||
float avg_loss = sum_loss / (float)n_cand;
|
||||
|
||||
|
||||
float loss_ratio = (diff > 0.0f) ? ((avg_loss - best_loss) / diff) : 0.5f;
|
||||
if (loss_ratio < 0.0f) loss_ratio = 0.0f;
|
||||
if (loss_ratio > 1.0f) loss_ratio = 1.0f;
|
||||
|
||||
// Записываем стабильность популяции в структуру
|
||||
if(diff < 0.0f) diff = 0.0f;
|
||||
if(diff > 1.0f) diff = 1.0f;
|
||||
opt->stability = (1.0f - worst_loss) * (1.0f - (worst_loss - best_loss));
|
||||
if(opt->stability < 0.0f) opt->stability = 0.0f;
|
||||
if(opt->stability > 1.0f) opt->stability = 1.0f;
|
||||
|
||||
float mut_pct = EVOLVE_MUTATION_MIN_PCT +
|
||||
(EVOLVE_MUTATION_MAX_PCT - EVOLVE_MUTATION_MIN_PCT) * loss_ratio;
|
||||
float adaptive_mut = mut * (mut_pct / 100.0f);
|
||||
if (adaptive_mut < 0.0001f) adaptive_mut = 0.0001f;
|
||||
opt->gen_mut = adaptive_mut;
|
||||
|
||||
// 3. Генерируем новое поколение
|
||||
uint16_t n_elite = ELOVLE_N_ELITE_CANDIDATE;
|
||||
for (uint16_t c = 0; c < n_cand; c++) {
|
||||
if (c < n_elite) {
|
||||
for (uint16_t i = 0; i < n_params; i++)
|
||||
opt->candidates[c][i] = opt->candidates[opt->sorted_idx[c]][i];
|
||||
opt->loss[c] = 0.0f;
|
||||
} else {
|
||||
for (uint16_t i = 0; i < n_params; i++) {
|
||||
float inv_randmax = 1.0f / (float)RAND_MAX;
|
||||
float noise = ((float)rand() * inv_randmax * 2.0f - 1.0f) * adaptive_mut;
|
||||
uint16_t parent = opt->sorted_idx[rand() % opt->n_best];
|
||||
opt->candidates[c][i] = opt->candidates[parent][i] + noise;
|
||||
if (opt->candidates[c][i] < 0.0f) opt->candidates[c][i] = 0.0f;
|
||||
if (opt->candidates[c][i] > 1.0f) opt->candidates[c][i] = 1.0f;
|
||||
}
|
||||
opt->loss[c] = 0.0f;
|
||||
}
|
||||
}
|
||||
opt->cand_index = 0;
|
||||
opt->gen_index++;
|
||||
}
|
||||
|
||||
// 4. Возвращаем параметры следующего кандидата
|
||||
for (uint16_t i = 0; i < opt->n_params; i++)
|
||||
params[i] = opt->candidates[opt->cand_index][i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else // ENABLE_EVOLVE_OPTIMIZATION
|
||||
//заглушки
|
||||
typedef struct {
|
||||
uint16_t n_params;
|
||||
uint16_t n_cand;
|
||||
uint16_t n_best;
|
||||
float mutation_amp;
|
||||
float loss[0];
|
||||
float candidates[0][0];
|
||||
} EvolveOptimizer_t;
|
||||
#define EvolveOptimizer_Init(opt, n_params, n_cand, n_best, mutation_amp, start_params)
|
||||
#define EvolveOptimizer_Step(opt, params, LossFunc)
|
||||
#define PARAM_SCALE(x, min_val, max_val) (x)
|
||||
#define PARAM_UNSCALE(val, min_val, max_val) (val)
|
||||
#endif // ENABLE_EVOLVE_OPTIMIZATION
|
||||
|
||||
#endif // __EVOLVE_OPTIMIZER_H_
|
||||
|
||||
/** EVOLVE_OPTIMIZER
|
||||
* @}
|
||||
*/
|
||||
206
MyLibs/Inc/mylibs_defs.h
Normal file
206
MyLibs/Inc/mylibs_defs.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file mylibs_defs.h
|
||||
* @brief Заголочный файл для дефайнов библиотеки MyLibsGeneral.
|
||||
**************************************************************************
|
||||
* @defgroup MYLIBS_DEFINES General Tools
|
||||
* @ingroup MYLIBS_ALL
|
||||
* @brief Общие макросы и typedef'ы, используемые по всему проекту
|
||||
*
|
||||
*************************************************************************/
|
||||
#ifndef __MYLIBS_DEFINES_H_
|
||||
#define __MYLIBS_DEFINES_H_
|
||||
|
||||
#include "mylibs_config.h"
|
||||
|
||||
/***************************************************************************
|
||||
******************************ERROR_HANDLER********************************/
|
||||
/**
|
||||
* @addtogroup ERROR_HANDLER_DEFINES Error Handler defines
|
||||
* @ingroup MYLIBS_DEFINES
|
||||
* @brief Дефайны для обработки ошибок
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* extern Error_Handler from main.h */
|
||||
extern void Error_Handler(void);
|
||||
|
||||
/**
|
||||
* @brief Error_Handler который будет вызыватся в библиотеке
|
||||
*/
|
||||
#define MyLibs_Error_Handler(params) Error_Handler(params)
|
||||
/* If error handler not defined - set void */
|
||||
#ifndef MyLibs_Error_Handler
|
||||
#define MyLibs_Error_Handler(...)
|
||||
#endif // MyLibs_Error_Handler
|
||||
|
||||
/** @brief Проверить один указатель на NULL */
|
||||
#define check_null_ptr_1(p1) (p1 == NULL)
|
||||
|
||||
/** @brief Проверить два указателя на NULL */
|
||||
#define check_null_ptr_2(p1, p2) ((p1 == NULL) || (p1 != NULL && p2 == NULL))
|
||||
|
||||
/** @brief Проверить три указателя на NULL */
|
||||
#define check_null_ptr_3(p1, p2, p3) ((p1 == NULL) || (p1 != NULL && ((p2 == NULL) || (p2 != NULL && p3 == NULL))))
|
||||
|
||||
/** @brief Проверить четыре указателя на NULL */
|
||||
#define check_null_ptr_4(p1, p2, p3, p4) ((p1 == NULL) || (p1 != NULL && ((p2 == NULL) || (p2 != NULL && ((p3 == NULL) || (p3 != NULL && p4 == NULL))))))
|
||||
|
||||
/** @brief Проверить пять указателей на NULL */
|
||||
#define check_null_ptr_5(p1, p2, p3, p4, p5) ((p1 == NULL) || (p1 != NULL && ((p2 == NULL) || (p2 != NULL && ((p3 == NULL) || (p3 != NULL && ((p4 == NULL) || (p4 != NULL && p5 == NULL))))))))
|
||||
|
||||
/** ERROR_HANDLER_DEFINES
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
******************************DELAYS_DEFINES*******************************/
|
||||
/**
|
||||
* @addtogroup DELAYS_DEFINES Delays defines
|
||||
* @ingroup MYLIBS_DEFINES
|
||||
* @brief Макросы и определения для работы с задержками в миллисекундах.
|
||||
* @details
|
||||
* Этот блок содержит макросы для реализации задержек с использованием HAL или FreeRTOS:
|
||||
* - @ref msDelay — простая задержка заданной длительности;
|
||||
* - @ref msDelayStart — сохранение текущего времени начала задержки;
|
||||
* - @ref msDelayWhileActive — проверка, активна ли задержка;
|
||||
* - @ref msDelayWaitDone — проверка, завершена ли задержка.
|
||||
* Эти макросы удобны для реализации неблокирующих задержек.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @def msDelay(_ms_)
|
||||
* @brief Блокирующая задержка на указанное количество миллисекунд.
|
||||
* @param _ms_ Время задержки в миллисекундах.
|
||||
* @note Использует задержку через @ref local_time или osDelay в зависимости от @ref FREERTOS_DELAY.
|
||||
*/
|
||||
#ifdef FREERTOS_DELAY
|
||||
#define msDelay(_ms_) osDelay(_ms_)
|
||||
#else
|
||||
#define msDelay(_ms_) \
|
||||
do { \
|
||||
uint32_t _start_ = local_time(); \
|
||||
while (local_time() - _start_ < (_ms_)) {} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Начать отсчет задержки.
|
||||
* @param _pvar_ Указатель на переменную типа uint32_t для хранения времени старта.
|
||||
* @details После вызова этого макроса переменная _pvar_ содержит текущее количество миллисекунд
|
||||
* с момента запуска системы (@ref local_time).
|
||||
*
|
||||
* Используется для реализации неблокирующих задержек.
|
||||
*/
|
||||
#define msDelayStart(_pvar_) *(_pvar_) = local_time()
|
||||
|
||||
/**
|
||||
* @brief Проверяет, активна ли задержка.
|
||||
* @param _ms_ Длительность задержки в миллисекундах.
|
||||
* @param _pvar_ Указатель на переменную, в которой сохранено время начала (@ref msDelayStart).
|
||||
* @retval 1 Задержка еще активна.
|
||||
* @retval 0 Задержка завершена.
|
||||
* @details
|
||||
* Возвращает true, пока время задержки не истекло. Используется в проверках,
|
||||
* когда нужно **действовать, пока задержка выполняется**. Пример:
|
||||
* @code
|
||||
* while(msDelayWhileActive(1000, &tick)) {
|
||||
* // выполняем другие задачи, задержка не блокирует поток
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define msDelayWhileActive(_ms_, _pvar_) (local_time() - *(_pvar_) < _ms_)
|
||||
|
||||
/**
|
||||
* @brief Проверяет, завершилась ли задержка.
|
||||
* @param _ms_ Длительность задержки в миллисекундах.
|
||||
* @param _pvar_ Указатель на переменную, в которой сохранено время начала (msDelayStart).
|
||||
* @retval 1 Задержка завершена.
|
||||
* @retval 0 Задержка еще активна.
|
||||
* @details
|
||||
* Возвращает true, когда задержка уже завершена. Используется в проверках,
|
||||
* когда нужно **выполнить действие только после окончания задержки**. Пример:
|
||||
* @code
|
||||
* if(msDelayWaitDone(1000, &tick)) {
|
||||
* // выполняем действие после завершения задержки
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define msDelayWaitDone(_ms_, _pvar_) (local_time() - *(_pvar_) >= _ms_)
|
||||
|
||||
/** DELAYS_DEFINES
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*******************************UTIL_DEFINES********************************/
|
||||
static int dummy;
|
||||
/**
|
||||
* @addtogroup UTILS_DEFINES Utils defines
|
||||
* @ingroup MYLIBS_DEFINES
|
||||
* @brief Общие вспомогательные макросы
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Обнуление структуры.
|
||||
* @param _struct_ Структура, которую нужно обнулить.
|
||||
* @details Макрос использует memset для обнуления всей памяти структуры.
|
||||
* Используется для быстрой и безопасной инициализации переменных структур до нуля.
|
||||
*/
|
||||
#define ClearStruct(_struct_) memset(&(_struct_), 0, sizeof(_struct_))
|
||||
|
||||
/**
|
||||
* @brief Деление с округлением вверх
|
||||
* @param _val_ Делимое.
|
||||
* @param _div_ Делитель.
|
||||
* @return Результат деления, округленный вверх.
|
||||
* @details Если результат деления без остатка: он возвращается как есть
|
||||
Если с остатком - округляется вверх
|
||||
*/
|
||||
//#define Divide_Up(_val_, _div_) (((_val_)%(_div_))? (_val_)/(_div_)+1 : (_val_)/_div_) /* через тернарный оператор */
|
||||
#define Divide_Up(_val_, _div_) ((_val_ - 1) / _div_) + 1 /* через мат выражение */
|
||||
|
||||
/**
|
||||
* @brief Swap between Little Endian and Big Endian
|
||||
* @param v Исходное 16-битное значение.
|
||||
* @return Результат с поменяными местами старшим и младшим байтом.
|
||||
* @details Переключения между двумя типами хранения слова: HI-LO байты и LO-HI байты.
|
||||
*/
|
||||
#define ByteSwap16(v) (((v&0xFF00) >> (8)) | ((v&0x00FF) << (8)))
|
||||
|
||||
/**
|
||||
* @brief Абсолютное значение числа
|
||||
* @param x Число.
|
||||
* @return Абсолютное значение числа x.
|
||||
* @details Берет число по модулю. Хз как работает библиотечный abs в stdlib.h, мб это быстрее, но вряд ли конечно.
|
||||
*/
|
||||
#define ABS(x) ( ((x) > 0)? (x) : -(x))
|
||||
|
||||
/** UTILS_DEFINES
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @cond LIBS_INTERNAL
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Аналог HAL макроса для привязки DMA к UART.
|
||||
* @note @ref __HAL_LINKDMA.
|
||||
*/
|
||||
#define __USER_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \
|
||||
do{ \
|
||||
(__HANDLE__)->__PPP_DMA_FIELD__ = (__DMA_HANDLE__); \
|
||||
(__DMA_HANDLE__)->Parent = (__HANDLE__);} while(0U)
|
||||
|
||||
|
||||
/** @endcond */
|
||||
#endif //__MYLIBS_DEFINES_H_
|
||||
591
MyLibs/Inc/trace.h
Normal file
591
MyLibs/Inc/trace.h
Normal file
@@ -0,0 +1,591 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @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_
|
||||
163
MyLibs/Inc/trackers.h
Normal file
163
MyLibs/Inc/trackers.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file trackers.h
|
||||
* @brief Заголочный файл для работы с трекерами @ref TRACKERS.
|
||||
**************************************************************************
|
||||
* @addtogroup TRACKERS Trackers defines
|
||||
* @ingroup MYLIBS_DEFINES
|
||||
* @brief Дефайны для работы с трекерами
|
||||
* @details
|
||||
Есть дефайн для объявления структуры трекера: TrackerTypeDef(num_user_vars).
|
||||
Структура состоит из следующих элементов:
|
||||
- cnt_ok
|
||||
- cnt_err
|
||||
- cnt_warn
|
||||
- user[num_user_vars]
|
||||
Также есть ряд функций (дефайнов) для обращения к элементам этой структуры.
|
||||
|
||||
Параметры для конфигурации:
|
||||
- @ref TRACKERS_ENABLE - Включить трекеры
|
||||
Если трекеры @ref TRACKERS_ENABLE отключены, то все дефайны определяются как ничего
|
||||
и на производительность кода не влияют
|
||||
|
||||
@par Пример:
|
||||
|
||||
Определяем typedef трекера измерений Measure_TrackerTypeDef
|
||||
|
||||
@verbatim
|
||||
typedef TrackerTypeDef(MEASURE_USER_VARS_NUMB) Measure_TrackerTypeDef;
|
||||
@endverbatim
|
||||
|
||||
И через @ref Measure_TrackerTypeDef структура подключается в другие структуры
|
||||
|
||||
Для работы с структурой можно использовать функции:
|
||||
- Для получения значения:
|
||||
- TrackerGet_Ok()
|
||||
- TrackerGet_Err()
|
||||
- TrackerGet_Warn()
|
||||
- TrackerGet_User(n)
|
||||
|
||||
- Для записи значения:
|
||||
- TrackerCnt_Ok()
|
||||
- TrackerCnt_Err()
|
||||
- TrackerCnt_Warn()
|
||||
- TrackerCnt_User()
|
||||
- TrackerWrite_User(n)
|
||||
|
||||
- Для очищения значения:
|
||||
- TrackerClear_All()
|
||||
- TrackerClear_Ok()
|
||||
- TrackerClear_Err()
|
||||
- TrackerClear_Warn()
|
||||
- TrackerClear_User(n)
|
||||
- TrackerClear_UserAll()
|
||||
* @{
|
||||
*************************************************************************/
|
||||
#ifndef __TRACKERS_H_
|
||||
#define __TRACKERS_H_
|
||||
#include "mylibs_defs.h"
|
||||
|
||||
#ifdef TRACKERS_ENABLE
|
||||
/**
|
||||
* @brief Структура для счетчиков отладки
|
||||
* @param num_user_vars - количество пользовательских переменных
|
||||
* @details Содержит счетчик для успешных событый (cnt_ok),
|
||||
* счетчик для ошибок (cnt_err), счетчик для предупреждений (cnt_warn).
|
||||
*
|
||||
* Также есть возможность объявить пользовательские переменные в
|
||||
* количестве <num_user_vars> штук.
|
||||
*/
|
||||
#define TrackerTypeDef(num_user_vars) \
|
||||
struct \
|
||||
{ \
|
||||
uint32_t cnt_ok; \
|
||||
uint32_t cnt_err; \
|
||||
uint32_t cnt_warn; \
|
||||
uint32_t user[num_user_vars]; \
|
||||
}
|
||||
|
||||
/** @brief Получить количетство пользовательских переменных */
|
||||
#define num_of_usercnts(_user_) (sizeof(_user_) / sizeof(uint32_t))
|
||||
/** @brief Проверка существует ли указанная пользовательская переменная */
|
||||
#define assert_usertracker(_cntstruct_, _uservarnumb_) ((_uservarnumb_) < num_of_usercnts((_cntstruct_).user))
|
||||
/** @brief Условие для проверки существует ли указанная пользовательская переменная */
|
||||
#define if_assert_usertracker(_cntstruct_, _uservarnumb_) if(assert_usertracker(_cntstruct_, _uservarnumb_))
|
||||
/** @brief Тернарный оператор для проверки существует ли указанная пользовательская переменная */
|
||||
#define tern_assert_usertracker(_cntstruct_, _uservarnumb_) (assert_usertracker(_cntstruct_, _uservarnumb_)) ? _uservarnumb_ : 0
|
||||
|
||||
|
||||
/** @brief Считать счетчик успешных событий */
|
||||
#define TrackerGet_Ok(_cntstruct_) (_cntstruct_).cnt_ok
|
||||
/** @brief Считать счетчик ошибок */
|
||||
#define TrackerGet_Err(_cntstruct_) (_cntstruct_).cnt_err
|
||||
/** @brief Считать счетчик предупреждений */
|
||||
#define TrackerGet_Warn(_cntstruct_) (_cntstruct_).cnt_warn
|
||||
/**
|
||||
* @brief Считать пользовательскую переменную
|
||||
* @note Здесь нет проверки - существует ли пользовательская переменная!
|
||||
* Есть возможность выйти за границы структуры!!!
|
||||
* Чтобы этого избежать можно использовать дефайн #ref assert_usertracker()
|
||||
@verbatim
|
||||
if(assert_usertracker(struct, 0)) {
|
||||
TrackerGet_User(struct, 0)
|
||||
}
|
||||
@endverbatim
|
||||
*/
|
||||
#define TrackerGet_User(_cntstruct_, _uservarnumb_) (_cntstruct_).user[tern_assert_usertracker(_cntstruct_, _uservarnumb_)]
|
||||
|
||||
|
||||
|
||||
/** @brief Инкрементирование счетчика успешных событий */
|
||||
#define TrackerCnt_Ok(_cntstruct_) (_cntstruct_).cnt_ok++
|
||||
/** @brief Инкрементирование счетчика ошибок */
|
||||
#define TrackerCnt_Err(_cntstruct_) (_cntstruct_).cnt_err++
|
||||
/** @brief Инкрементирование счетчика предупреждений */
|
||||
#define TrackerCnt_Warn(_cntstruct_) (_cntstruct_).cnt_warn++
|
||||
/** @brief Инкрементирование пользовательской переменной */
|
||||
#define TrackerCnt_User(_cntstruct_, _uservarnumb_) if_assert_usertracker(_cntstruct_, _uservarnumb_) (_cntstruct_).user[_uservarnumb_]++;
|
||||
/** @brief Запись числа в пользовательскую переменную */
|
||||
#define TrackerWrite_User(_cntstruct_, _uservarnumb_, _val_) if_assert_usertracker(_cntstruct_, _uservarnumb_) (_cntstruct_).user[_uservarnumb_] = (_val_)
|
||||
|
||||
/** @brief Очистка всей структуры */
|
||||
#define TrackerClear_All(_cntstruct_) memset(&(_cntstruct_), 0, sizeof(_cntstruct_))
|
||||
/** @brief Очистка счетчика успешных событий */
|
||||
#define TrackerClear_Ok(_cntstruct_) (_cntstruct_).cnt_ok = 0
|
||||
/** @brief Очистка счетчика ошибок */
|
||||
#define TrackerClear_Err(_cntstruct_) (_cntstruct_).cnt_err = 0
|
||||
/** @brief Очистка счетчика предупреждений */
|
||||
#define TrackerClear_Warn(_cntstruct_) (_cntstruct_).cnt_warn = 0
|
||||
/** @brief Очистка пользовательской переменной */
|
||||
#define TrackerClear_User(_cntstruct_, _uservarnumb_) if_assert_usertracker(_cntstruct_, _uservarnumb_) (_cntstruct_).user[_uservarnumb_] = 0;
|
||||
/** @brief Очистка всех пользовательских переменных */
|
||||
#define TrackerClear_UserAll(_cntstruct_) memset(&(_cntstruct_).user, 0, sizeof((_cntstruct_).user))
|
||||
|
||||
#else //TRACKERS_ENABLE
|
||||
|
||||
#define TrackerTypeDef(num_user_vars) void *
|
||||
|
||||
#define num_of_usercnts(_user_) 0
|
||||
#define assert_tracecnt(_cntstruct_, _uservarnumb_) 0
|
||||
#define if_assert_usertracker(_cntstruct_, _uservarnumb_) if(0)
|
||||
#define tern_assert_usertracker(_cntstruct_, _uservarnumb_) 0
|
||||
|
||||
#define TrackerGet_Ok(_cntstruct_) dummy
|
||||
#define TrackerGet_Err(_cntstruct_) dummy
|
||||
#define TrackerGet_Warn(_cntstruct_) dummy
|
||||
#define TrackerGet_User(_cntstruct_, _uservarnumb_) dummy
|
||||
|
||||
#define TrackerCnt_Ok(_cntstruct_)
|
||||
#define TrackerCnt_Err(_cntstruct_)
|
||||
#define TrackerCnt_Warn(_cntstruct_)
|
||||
#define TrackerCnt_User(_cntstruct_, _uservarnumb_)
|
||||
#define TrackerWrite_User(_cntstruct_, _uservarnumb_, _val_)
|
||||
|
||||
#define TrackerClear_All(_cntstruct_)
|
||||
#define TrackerClear_Ok(_cntstruct_)
|
||||
#define TrackerClear_Err(_cntstruct_)
|
||||
#define TrackerClear_Warn(_cntstruct_)
|
||||
#define TrackerClear_User(_cntstruct_)
|
||||
#define TrackerClear_UserAll(_cntstruct_)
|
||||
|
||||
#endif //TRACKERS_ENABLE
|
||||
|
||||
#endif //__TRACKERS_H_
|
||||
Reference in New Issue
Block a user