- PVSM.RU - https://www.pvsm.ru -
Встроенные системы на базе микроконтроллеров STM32 широко применяются в различных областях — от бытовой электроники до промышленного оборудования и IoT-устройств. Одной из ключевых задач при разработке таких систем является отладка и мониторинг работы приложений. Эффективное логирование существенно облегчает эти процессы, позволяя разработчикам быстро выявлять и устранять ошибки, а также анализировать поведение системы в реальном времени.
Цель данной статьи — познакомить вас с библиотекой логирования для STM32, разработанной с использованием RTOS FreeRTOS. Эта библиотека упрощает процесс отладки RTOS-приложений, предоставляя удобные инструменты для вывода и управления логами. Хотя решение не рассчитано на корпоративный уровень, оно уже доказало свою эффективность в различных проектах, ускоряя процесс разработки и повышая качество конечного продукта. Благодаря использованию функций FreeRTOS, библиотеку легко адаптировать под другие микроконтроллеры, изменив методы взаимодействия с периферией.
Мы рассмотрим основные возможности библиотеки, её архитектуру и интеграцию с FreeRTOS, а также преимущества использования данного решения в ваших проектах. Примеры кода и подробные объяснения помогут легко интегрировать библиотеку в проекты на STM32 и использовать её функционал для эффективной отладки и мониторинга приложений.
Для лучшего понимания работы библиотеки логирования приведем пример вывода логов и соответствующую функцию, генерирующую эти логи.
[DEBUG_ALL][0s.922]: Logging inited
[INFO ][5s.922]: I'm alive every 5 seconds from default task
[INFO ][10s.922]: I'm alive every 5 seconds from default task
[INFO ][15s.922]: I'm alive every 5 seconds from default task
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* init code for USB_DEVICE */
MX_USB_DEVICE_Init();
/* USER CODE BEGIN StartDefaultTask */
logging_init();
LOG(DEBUG_ALL, "Logging inited");
/* Infinite loop */
for (;;)
{
osDelay(DEFAULT_TASK_DELAY);
// мигание зеленым светодиодом
HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
LOG(INFO, "I'm alive every 5 seconds from default task");
}
/* USER CODE END StartDefaultTask */
}
В данном примере:
Инициализация логирования: Вызов logging_init()
подготавливает библиотеку к работе.
Логирование инициализации: LOG(DEBUG_ALL, "Logging inited");
выводит сообщение о инициализации системы логирования.
Бесконечный цикл: Каждые 5 секунд происходит задержка с помощью osDelay(DEFAULT_TASK_DELAY)
, мигание светодиода и вывод информационного сообщения о состоянии задачи.
Этот пример демонстрирует, как легко интегрировать библиотеку логирования в основную задачу приложения и получать понятные и структурированные лог-сообщения для мониторинга работы системы.
Библиотека логирования для STM32 с использованием FreeRTOS представляет собой компактное и настраиваемое решение для отладки и мониторинга приложений. Основная цель — предоставить разработчикам простой инструмент для вывода логов различных уровней с возможностью расширения функционала по необходимости.
Различные уровни логирования: Поддержка уровней DEBUG_ALL
, DEBUG_MIN
, INFO
, WARNING
, ERR
позволяет фильтровать сообщения в зависимости от потребностей отладки.
Буферизация сообщений: Циклические буферы обеспечивают эффективное управление памятью и предотвращают потерю данных при высоком объёме логов.
Поддержка различных интерфейсов вывода: Возможность выбора между UART и USB интерфейсами делает библиотеку универсальной для различных проектов.
Потокобезопасность: Использование мьютексов и семафоров FreeRTOS гарантирует корректную работу в многопоточной среде, предотвращая конфликты при одновременном доступе к ресурсам.
Библиотека организована для максимальной простоты интеграции и модификации. Основные компоненты находятся в директории lib
, а тестовый проект — в test/test_stm32
.
lib/
logging.h
: Заголовочный файл с определениями, макросами и декларациями функций для логирования.
logging.c
: Реализация функций логирования, включая инициализацию и передачу логов через выбранный интерфейс.
logging_usb.h
: Расширение для поддержки логирования через USB.
test/test_stm32/
Пример инициализации и использования логирования в реальном проекте.
Скрипты для сборки и тестирования проекта с использованием Docker и QEMU виртуальной машины, обеспечивающие автоматизированное тестирование библиотеки.
Для демонстрации простоты использования библиотеки приведём пример записи логов из задачи FreeRTOS.
#include "logging.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
// Инициализация логирования
void start_logging_task(void)
{
logging_init();
// Создание задачи логирования
osThreadId_t loggingTask = osThreadNew(logging_thread, "Logger", NULL);
if (loggingTask == NULL)
{
LOG_FATAL("Failed to create logging taskn");
Error_Handler();
}
}
// Пример логирования из задачи
void logging_thread(void *arg)
{
const char *taskName = (const char *)arg;
for (int i = 0; i < 100; i++)
{
LOG(INFO, "Task %s: message %d", taskName, i);
osDelay(100);
}
osThreadExit();
}
// Основная функция приложения
int main(void)
{
// Инициализация аппаратных компонентов
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_FREERTOS_Init();
// Запуск задачи логирования
start_logging_task();
// Запуск планировщика FreeRTOS
osKernelStart();
// Основной цикл не должен достигаться
while (1)
{
}
}
В этом примере:
Инициализация логирования: Функция logging_init()
подготавливает библиотеку к работе, настраивая необходимые ресурсы и задачи.
Создание задачи логирования: Задача logging_thread
отвечает за запись логов, принимая имя задачи в качестве аргумента и периодически записывая сообщения.
Запуск планировщика: После инициализации и создания задач запускается планировщик FreeRTOS с помощью osKernelStart()
, который управляет задачами.
Этот пример демонстрирует, насколько легко интегрировать библиотеку логирования в проект. Библиотека готова к использованию и позволяет быстро адаптироваться под специфические требования приложения.
Библиотека логирования для STM32, интегрированная с FreeRTOS, обладает продуманной архитектурой, обеспечивающей эффективность и надежность в многозадачной среде.
Для потокобезопасности при записи и чтении логов библиотека использует мьютексы и семафоры FreeRTOS. Это гарантирует, что доступ к общим ресурсам не приведет к конфликтам или повреждению данных при одновременном обращении из разных задач или прерываний.
Мьютексы (interface_mutex
, logs_mutex
):
interface_mutex
защищает доступ к интерфейсам вывода логов (например, UART или USB), обеспечивая, что только одна задача или прерывание может одновременно отправлять данные.
logs_mutex
защищает внутренние структуры данных библиотеки, такие как буферы логов, предотвращая одновременное изменение данных несколькими задачами.
Семафоры (logs_semaphore_store
, logs_semaphore_print
):
logs_semaphore_store
синхронизирует процесс записи логов в буфер, обеспечивая последовательную запись без пропусков.
logs_semaphore_print
управляет выводом логов через выбранный интерфейс, гарантируя корректный и упорядоченный вывод сообщений.
#include "logging.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os2.h"
#include "string.h"
char LOGGING_BUF[LOG_BUFFER_SIZE] = "";
char LOGGING_ISR_BUF[LOG_BUFFER_SIZE] = "";
char INTERFACE_BUFFER[INTERFACE_BUFFER_SIZE] = "";
char log_circular_buf[LOG_QUEUE_ROWS][LOG_BUFFER_SIZE] = {0};
uint32_t log_time_buf[LOG_QUEUE_ROWS] = {0};
uint32_t log_time_ms_buf[LOG_QUEUE_ROWS] = {0};
int log_level_buf[LOG_QUEUE_ROWS] = {0};
int logs_tail = 0;
int logs_head = 0;
int log_isr_set = 0;
uint32_t log_isr_time = 0;
uint32_t log_isr_time_ms = 0;
int log_isr_level = 0;
int init_packege_received = 0;
char log_names[ERR + 1][LOG_TYPE_SIZE] = {"DEBUG_ALL", "DEBUG_MIN", "INFO ", "WARNING ", "ERROR "};
osMutexId_t interface_mutex;
osMutexId_t logs_mutex;
/* Definitions for logs semaphore */
osSemaphoreId_t logs_semaphore_store;
osSemaphoreId_t logs_semaphore_print;
typedef StaticTask_t osStaticThreadDef_t;
osThreadId_t loggingTaskHandle;
osThreadAttr_t loggingTask_attributes = {
.name = "logging",
.priority = osPriorityBelowNormal
};
Библиотека создает отдельную задачу для обработки логов, разгружая основные задачи приложения и обеспечивая своевременный вывод логов без задержек.
Задача логирования (loggingTaskHandle
):
Имя задачи: "logging"
Приоритет: osPriorityBelowNormal
Эта задача отвечает за прием логовых сообщений, их буферизацию и вывод через выбранный интерфейс. Низкий приоритет задачи гарантирует, что она не будет мешать критическим задачам приложения, обеспечивая стабильную работу логирования.
typedef StaticTask_t osStaticThreadDef_t;
osThreadId_t loggingTaskHandle;
osThreadAttr_t loggingTask_attributes = {
.name = "logging",
.priority = osPriorityBelowNormal
};
Библиотека использует циклические буферы для эффективного управления памятью и предотвращения потери данных при высоком объёме логов.
char log_circular_buf[LOG_QUEUE_ROWS][LOG_BUFFER_SIZE] = {0};
uint32_t log_time_buf[LOG_QUEUE_ROWS] = {0};
uint32_t log_time_ms_buf[LOG_QUEUE_ROWS] = {0};
int log_level_buf[LOG_QUEUE_ROWS] = {0};
int logs_tail = 0;
int logs_head = 0;
Логирование из прерываний (ISR) требует особого подхода. Библиотека предоставляет механизмы для безопасной записи логов из ISR.
Переменные для ISR:
log_isr_set
: Флаг наличия новых логовых сообщений из ISR.
log_isr_time
, log_isr_time_ms
: Время создания логовых сообщений из ISR.
log_isr_level
: Уровень логирования для сообщений из ISR.
int log_isr_set = 0;
uint32_t log_isr_time = 0;
uint32_t log_isr_time_ms = 0;
int log_isr_level = 0;
Функции логирования из ISR:
#define LOG_ISR(level, ...)
if (level >= LOGGING_LEVEL)
{
snprintf(LOGGING_ISR_BUF, LOG_BUFFER_SIZE, __VA_ARGS__);
log_ISR(LOGGING_ISR_BUF, UPTIME_S, UPTIME_MS, level);
}
Библиотека предоставляет встроенное расширение для логирования с использованием виртуального COM-порта или CDC-драйвера. Это значительно упрощает процесс отправки логов через USB-кабель на практически любую операционную систему. Для облегчения отладки через USB CDC в расширение включены несколько важных функций:
Ожидание открытия виртуального COM-порта на стороне ПК
Программное обеспечение микроконтроллера ожидает открытия виртуального COM-порта на ПК, а также готовности CDC-драйвера к приему данных. Это гарантирует, что логи, генерируемые в первые миллисекунды работы прошивки, будут захвачены и доставлены. В противном случае, из-за задержки инициализации виртуального COM-порта USB, соответствующие логи могут быть потеряны.
Ожидание чтения ПК
При высоком объёме логирования функция логирования будет блокироваться до тех пор, пока в циклическом буфере не освободится место. Это обеспечивает сохранность всех логов и предотвращает их потерю даже при интенсивном журналировании.
// additional include for USB logging
# include "usbd_cdc_if.h"
# include "usb_device.h"
# define INTERFACE_CDC_BUFFER_SIZE 200
extern char CDC_USB_RX_BUF[INTERFACE_CDC_BUFFER_SIZE];
extern char CDC_USB_TX_BUF[INTERFACE_CDC_BUFFER_SIZE];
# define CDC_WAIT_FOR_PC_TO_READ 1
# define INTERFACE_printf(FATAL_FLAG, ...)
while (CDC_IsBusy() == USBD_BUSY && CDC_WAIT_FOR_PC_TO_READ == 1)
{
osThreadYield();
}
CDC_Transmit_FS((uint8_t *)CDC_USB_TX_BUF,
snprintf(CDC_USB_TX_BUF, INTERFACE_CDC_BUFFER_SIZE, **VA_ARGS**));
Эти функции совместно обеспечивают надёжное и эффективное логирование через USB CDC, позволяя разработчикам легко отслеживать работу системы в реальном времени без риска потери данных.
По умолчанию, задача логирования выполняется с минимальным приоритетом и активируется только в случае отсутствия более приоритетных задач. Это позволяет основной нагрузке приложения получать необходимое CPU время, а логированию не мешать критическим операциям. Для оптимальной работы библиотеки необходимо правильное использование функций RTOS, что позволяет освободить время процессора для фактического логирования.
Библиотека поддерживает несколько режимов логирования, обеспечивающих гибкость и надежность:
ISR-режим: Предназначен для логирования внутри прерываний. Этот режим позволяет быстро записывать логи из ISR, однако не поддерживает буферизацию, что может привести к потере данных при интенсивном журналировании.
# define LOG_ISR(level, ...)
if (level >= LOGGING_LEVEL)
{
snprintf(LOGGING_ISR_BUF, LOG_BUFFER_SIZE, **VA_ARGS**);
log_ISR(LOGGING_ISR_BUF, UPTIME_S, UPTIME_MS, level);
}
Фатальный режим: Предоставляет механизм логирования в случае критических ошибок, когда RTOS не может продолжать работу. В этом режиме логирование служит последним средством для вывода информации перед перезагрузкой или остановкой системы.
# define LOG_FATAL(...) INTERFACE_printf(FATAL_FLAG_SET, **VA_ARGS**)
Эти режимы обеспечивают надежное логирование в различных сценариях эксплуатации системы, позволяя сохранять критическую информацию даже в условиях ограниченных ресурсов или при отказе основных компонентов.
Эффективность и минимизация задержек: Использование RTOS позволяет минимизировать время блокировок и обеспечить быстрый обмен данными между задачами.
Гибкость и расширяемость:
Поддержка различных интерфейсов: Возможность выбора интерфейса вывода логов, например, UART или USB.
Различные уровни логирования: Поддержка уровней DEBUG_ALL
, DEBUG_MIN
, INFO
, WARNING
, ERROR
для гибкой настройки вывода информации.
Удобство использования: Простота интеграции библиотеки в существующие проекты на STM32 с FreeRTOS и легкость настройки параметров логирования через конфигурационные файлы.
Потокобезопасность и надежность: Использование мьютексов и семафоров обеспечивает корректную работу библиотеки в многопоточной среде, предотвращая конфликты при одновременном доступе к ресурсам.
Расширяемость через USB CDC: Возможность логирования через USB CDC позволяет легко отправлять логи на ПК без необходимости наличия дополнительных физических интерфейсов.
Библиотека логирования для STM32 с использованием FreeRTOS — это компактное, гибкое и легко интегрируемое решение для отладки и мониторинга встроенных систем. Её простота в использовании и возможность расширения делают её идеальным выбором как для хобби-разработчиков, так и для профессионалов, стремящихся повысить эффективность рабочего процесса.
Ссылки на исходный код: Репозиторий проекта [1] для заинтересованных читателей.
Рекомендации по использованию: Советы по оптимизации и настройке библиотеки для различных типов проектов на STM32.
В ближайшей следующей статье мы подробно рассмотрим процесс тестирования данной библиотеки логирования. Будем использовать Docker для автоматизированной сборки и QEMU для эмуляции микроконтроллера, что позволит протестировать логирование через USART без необходимости наличия физического оборудования. Этот подход обеспечивает гибкость и повторяемость тестов, позволяя быстро выявлять и устранять возможные проблемы в процессе разработки.
В библиотеке логирования используется библиотека cmsis_os2
, которая представляет собой абстракцию RTOS (Real-Time Operating System) на основе стандартов CMSIS (Cortex Microcontroller Software Interface Standard). Подробности можно найти в документации CMSIS-RTOS2 [2].
cmsis_os2
предоставляет единый интерфейс для различных RTOS, упрощая переносимость кода между разными операционными системами реального времени. Это позволяет разработчикам использовать общие API для управления задачами, мьютексами, семафорами и другими средствами синхронизации, независимо от выбранного RTOS. В контексте нашей библиотеки логирования cmsis_os2
обеспечивает надежное управление многозадачностью и синхронизацией доступа к ресурсам, что является критически важным для корректного функционирования системы логирования в многопоточной среде.
Автор: AleksandrVi
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/stm32/401429
Ссылки в тексте:
[1] Репозиторий проекта: https://github.com/AleksandrVin/logging_cmsis_rtos2
[2] документации CMSIS-RTOS2: https://www.keil.com/pack/doc/CMSIS_Dev/RTOS2/html/group__CMSIS__RTOS.html
[3] Источник: https://habr.com/ru/articles/814745/?utm_campaign=814745&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.