Рекомендации
Статья будет полезна разработчикам встраиваемых систем, которые хотят автоматизировать процесс тестирования своих проектов. Отдельный блок посвещен отладке gdb
в эмуляторе QEMU. В качестве примера используется библиотека логирования (GitHub, Habr)
Что вы узнаете из статьи
Как настроить систему сборки и эмуляции с использованием Docker и QEMU для тестирования без физического оборудования
Как подготовить тестовый проект на базе STM32CubeMX и интегрировать его с CI/CD
Как автоматизировать тестирование STM32 проектов для обеспечения воспроизводимости
Как отлаживать приложение в эмуляторе с помощью привычных инструментов
Система сборки в Docker и эмулятор QEMU
Почему Docker?
При разработке встраиваемых систем мы часто сталкиваемся с проблемой "у меня не работает". Это происходит из-за различий в:
Версиях компилятора
Установленных библиотеках
Настройках окружения
Docker решает эти проблемы, предоставляя изолированную среду с точно определенными версиями всех компонентов.
Структура Docker-контейнеров
Для создания полноценной среды разработки используется три контейнера:
-
Контейнер для кросс-компиляции (Dockerfile.tests)
Содержит ARM GCC toolchain, загруженный напрямую с developer.arm.com
Обеспечивает доступ к последней версии arm-none-eabi-gcc
Включает make и другие инструменты сборки
-
Контейнер с QEMU (Dockerfile.qemu_stm32)
Собирает QEMU из исходного кода beckus/qemu_stm32
Компиляция QEMU происходит только при первой сборке контейнера
Эмулирует периферию микроконтроллера
> ⚠️ Важно: Используемая версия QEMU является устаревшей и поддерживает только старые модели STM32. Существует потребность в обновлении эмулятора для поддержки современных микроконтроллеров STM32. Это может быть интересным проектом для сообщества разработчиков.
-
Контейнер для запуска тестов (Dockerfile.run_tests)
Объединяет результаты работы предыдущих контейнеров
Запускает тесты и собирает результаты
Генерирует отчеты о тестировании
QEMU для STM32
QEMU - это мощный эмулятор, способный эмулировать различные архитектуры процессоров. Для работы с STM32 используется специальная версия QEMU с поддержкой периферии STM32.
Поддерживаемая периферия
На момент написания статьи для STM32F103C8 поддерживаются:
UART
Таймеры
GPIO
Прерывания
Flash
Watchdog
АЦП, ЦАП
Часы реального времени
Базовые системные функции
> ? Совет: Несмотря на ограничения текущей версии QEMU, этот подход к тестированию может быть адаптирован под другие эмуляторы или более новые версии QEMU по мере их появления.
Тестовый проект
Генерация проекта в STM32CubeMX
Проект создан с помощью STM32CubeMX, что обеспечивает корректную начальную конфигурацию микроконтроллера и периферии.
Ключевые настройки проекта:
Тактирование: HSE 8 МГц с PLL до 72 МГц
UART1 для вывода логов
GPIO PC13 для LED-индикации
Системные таймеры для FreeRTOS
> Конфигурация FreeRTOS с использованием CMSIS-RTOS2 API
При генерации кода важно выбрать правильные параметры:
> Копирование библиотек в проект. Использование .c и .h файлов для периферии.
Нужно выбрать Makefile в качестве системы сборки для интеграции с Docker. Увеличьте размер кучи, так как используется динамическая память во FreeRTOS. Это нужно, т.к. при тестировании создается 10 потоков, при проверке блокировок буфера.
Структура проекта
Проект использует стандартную структуру STM32CubeMX, которая находится в директории test_project
. Скрипты для сборки и тестирования находятся в директории test
. Тестируемая библиотека находится в директории lib
и копируется в проект при сборке.
├── lib
│ ├── logging.c
│ ├── logging.h
│ ├── logging_usb.c
│ └── logging_usb.h
├── LICENSE
├── README.md
└── test
├── compose.yaml
├── Dockerfile.qemu_stm32
├── Dockerfile.run_tests
├── Dockerfile.tests
├── start_qemu_gdb_mode.sh
├── test_in_docker.sh
├── test.sh
├── test_project
│ ├── Core
│ ├── Drivers
│ ├── logging_cmsis_rtos2
│ ├── Makefile
│ ├── Middlewares
│ ├── startup_stm32f103xb.s
│ ├── STM32F103C8Tx_FLASH.ld
│ └── test_project.ioc
└── verify_output.py
При сборке, test.sh
копирует библиотеку логирования в тестовый проект и запускает сборку. В Makefile
добавлены дополнительные флаги для сборки библиотеки и тестов.
FreeRTOS интеграция
FreeRTOS настраивается через STM32CubeMX со следующими параметрами:
Включен DefaultTask
Используется динамическое выделение памяти
Увеличен размер heap для поддержки многопоточного теста, в котором создается 10 дополнительных потоков. При стандартной конфигурации FreeRTOS хватает памяти только на создание 2 дополнительных потоков
Активированы функции трассировки (подробнее в разделе отладки)
Весь пользовательский код FreeRTOS размещается в freertos.c
:
// Определение основного потока
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
// Точка входа для тестов
void StartDefaultTask(void *argument)
{
logging_init();
// Задержка для подключения терминала
osDelay(1000);
// Запуск тестов
logging_test();
// ...
}
Этот поток используется как точка входа для запуска тестов и инициализации библиотеки логирования.
> ? Совет: STM32CubeMX позволяет легко модифицировать конфигурацию проекта через графический интерфейс с последующей регенерацией кода. Однако, это и в некотором смысле обязавает разработчика всегда изменять конфигурацию через cubemx, дабы не нарушить структуру проекта.
Тестирование библиотеки логирования
Тесты реализованы в tests.c
и проверяют различные режимы работы библиотеки логирования:
void logging_test()
{
logging_basic_test("basic_test"); // Базовые тесты
osDelay(200); // Ожидание вывода логов
logging_test_pack("pack_of_ten_ten_times"); // Пакетная запись
osDelay(200); // Ожидание вывода логов
logging_test_different_levels("different_levels"); // Разные уровни логов
osDelay(200); // Ожидание вывода логов
logging_test_fatal("fatal"); // Фатальные ошибки
osDelay(200); // Ожидание вывода логов
logging_interrupt(); // Логи из прерываний
osDelay(200); // Ожидание вывода логов
logging_test_multiple_threads(); // Многопоточный тест
}
> ? Примечание: Задержки между тестами (osDelay
) позволяют потоку логирования опустошить буферы и четко разделить вывод разных тестовых сценариев.
Ключевые тестовые сценарии
-
Базовое тестирование
[INFO ][1s.1]: basic_test_0 [INFO ][1s.12]: basic_test_1 [INFO ][1s.22]: basic_test_2
Проверка форматирования сообщений
Временные метки с миллисекундной точностью
Последовательная запись логов
Библиотека логирования использует тики ядра FreeRTOS для получения времени. Время фиксириуется при вызове функции логирования и хранится в буфере до передачи через интерфейс, таким образом отражает время возникновения события, а не передачи через интерфейс.
-
Уровни логирования
[DEBUG_ALL][2s.112]: different_levels_DEBUG_ALL [DEBUG_MIN][2s.112]: different_levels_DEBUG_MIN [INFO ][2s.112]: different_levels_INFO [WARNING ][2s.112]: different_levels_WARNING [ERROR ][2s.112]: different_levels_ERR
-
Специальные режимы
# Фатальные ошибки (прямой вывод в UART) fatal_0 fatal_1 # Логи из прерываний (без буферизации) [INFO ][2s.512]ISR: LOG_ISR_9
LOG_FATAL
- прямая запись в UART, без буферизацииLOG_ISR
- логирование из прерываний, без использования буфера
void logging_interrupt() { // только последний лог будет напечатан for (int i = 0; i < LOGS_AT_ONCE; i++) { LOG_ISR(INFO, "LOG_ISR_%d", i); } }
> ⚠️ Важно про ISR: В выводе видно только последнее сообщение (
LOG_ISR_9
), и это ожидаемое поведение. В контексте прерывания:
> - Нельзя использовать блокирующие операции
> - Буферизация не применяется
> - Поток логирования имеет более низкий приоритет.
> В результате, пока поток логирования обрабатывает одно сообщение, следующий вызовLOG_ISR
уже перезаписывает данные. Это компромисс между корректностью работы в ISR и гарантией доставки всех сообщений. -
Многопоточный тест
void logging_test_multiple_threads() { for (int i = 0; i < THREADS_AMOUNT; i++) { threads[i] = osThreadNew(logging_thread, names[i], NULL); } }
[INFO ][2s.812]: logging_thread_4_0 [INFO ][2s.812]: logging_thread_4_1 [INFO ][2s.812]: logging_thread_5_1 [INFO ][2s.812]: logging_thread_6_1
10 потоков одновременно пишут логи
Тестирует циклический буфер под нагрузкой
Поток логирования имеет нормальный приоритет
Тестовые потоки блокируются при заполнении буфера
> ? Особенность: Библиотека использует отложенный вывод логов через UART. При малом количестве логов они выводятся в фоновом режиме. При переполнении буфера потоки блокируются до освобождения места. Режимы
LOG_FATAL
иLOG_ISR
обеспечивают мгновенный вывод.
Верификация вывода
Корректность проверяется скриптом verify_output.py
. QEMU создает виртуальный терминал, в который эмулируемая STM32 выводит данные. Скрипт на Python подключается к этому терминалу и проверяет корректность вывода.
# Пример проверки формата INFO сообщения
pattern = r"\[INFO\s*\]\[(\d+)s\.(\d+)\]: basic_test_(\d+)"
match = re.match(pattern, output)
if match:
seconds = int(match.group(1))
milliseconds = int(match.group(2))
test_number = int(match.group(3))
Скрипт завершается успешно, если все сообщения корректны. Данный участок кода необходимо дорабатывать в зависимости от требований тестируемого проекта. Например, в случае зависания проекта или отсутствия полного набора сообщений, скрипт зависнет в режиме ожидания. Это можно исправить, добавив таймауты и более сложную логику, которая при обнаружении таких ситуаций будет запускать внутренний механизм "выключения" QEMU, например, при возникновении HardFault или аналогичных ошибок.
Автоматическое тестирование
После реализации тестовых сценариев следующим шагом является автоматизация их выполнения. Для этого используется комбинация Docker и GitHub Actions.
Docker-контейнеризация
Тестирование выполняется в тех же трех контейнерах, описанных ранее:
services:
tests:
build:
context: .
dockerfile: Dockerfile.tests
image: tests
qemu_stm32:
build:
context: .
dockerfile: Dockerfile.qemu_stm32
depends_on:
- tests
image: qemu_stm32
run_tests: &run_tests
build:
context: .
dockerfile: Dockerfile.run_tests
depends_on:
- qemu_stm32
- tests
volumes:
- ./logs/:/test/logs
- ./elf/:/elf
profiles:
- test
image: run_tests
Каждый контейнер выполняет свою роль в процессе тестирования:
tests
— компилирует прошивку с тестами.qemu_stm32
— предоставляет среду эмуляции.run_tests
— запускает тесты и верифицирует результаты.
Запуск тестов
Автоматизация запуска реализована через скрипт test.sh
:
#!/bin/bash
# Копирование библиотеки в тестовый проект
cp -r ../lib/ test_project/logging_cmsis_rtos2
# Запуск контейнеров
docker compose --profile test up --build
# Сохранение логов
docker compose logs $container > $logs_file
# Получение кода завершения
exit_code=$(docker inspect -f '{{.State.ExitCode}}' $test_container_name)
> ? Особенность: Скрипт не только запускает тесты, но и обеспечивает корректное копирование библиотеки и сохранение результатов тестирования.
Интеграция с CI/CD
Тесты автоматически запускаются при каждом push и pull request через GitHub Actions:
name: CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run test
run: cd test && ./test.sh
- name: Archive test results
uses: actions/upload-artifact@v4
with:
name: combined_logs
path: |
./test/logs/logs.txt
./test/logs/serial_output.txt
> ? Совет: Для корпоративного использования можно настроить self-hosted runner на собственном сервере сборки. Подход совместим с GitLab CI и другими CI-системами. В данном случае используется GitHub Actions, который предоставляется бесплатно для open-source проектов.
Анализ результатов
Тесты генерируют два файла с результатами:
logs.txt
— отладочный вывод процесса тестированияserial_output.txt
— логи от тестируемой библиотеки
Эти файлы сохраняются как артефакты сборки и могут быть использованы для:
Анализа причин неудач тестов
Проверки корректности работы библиотеки
Документации работы системы
Отладка с использованием GDB и QEMU
Отладка встраиваемых систем часто напоминает детективное расследование. К счастью, QEMU и GDB предоставляют полный набор инструментов для этого.
Как это работает?
QEMU, как истинный проект GNU, поддерживает все стандартные инструменты отладки. Для тестов мы указываем имя последовательного интерфейса pty
. Возможно запустить qemu и в режиме когда последовательный интерфейс выводится в консоль qemu.
QEMU_FLAGS=(
"-nographic"
"-M" "stm32-f103c8"
"-kernel" "/app/firmware.bin"
"-serial" "pty"
)
> ? Для любопытных: Соответствие между USART'ами STM32 и PTY в QEMU определяется в исходном коде эмулятора. Если требуется что-то особенное, придется изучать исходники. RTSL — Read The Source, Luke!
Режим отладки
Скрипт test_in_docker.sh
поддерживает два режима работы:
Тестовый режим — обычный запуск тестов
Режим отладки — запуск с поддержкой GDB:
if [ "$1" == "gdb" ]; then
QEMU_FLAGS+=(
"-gdb" "tcp::3333" # GDB-сервер на порту 3333
"-S" # Остановка при старте
)
echo "Starting QEMU in GDB debug mode..."
fi
Подключение отладчика
Для подключения к QEMU используется arm-none-eabi-gdb
. В тестовом проекте есть готовая цель:
gdb:
arm-none-eabi-gdb ../elf/firmware.elf -ex "target extended-remote :3333"
> ⚠️ Важно: Здесь есть небольшая магия! Прошивка для GDB берется из Docker-контейнера через volume:
volumes:
- ./elf/:/elf # Файлы для GDB >
Запуск отладки (GDB)
Для запуска QEMU в режиме отладки используется start_qemu_gdb_mode.sh
:
#!/bin/bash
docker compose --profile gdb up --build
Он использует профиль gdb
в Docker Compose:
gdb:
<<: *run_tests # Наследуем настройки от run_tests
ports:
- "3333:3333" # Открываем порт для GDB
profiles:
- gdb # Отдельный профиль
entrypoint: ["./test_in_docker.sh", "gdb"]
> ? Профили в Docker Compose позволяют иметь различные конфигурации в одном файле. Профиль test
предназначен для тестов, а gdb
— для отладки. Это позволяет выбирать, какую конфигурацию запускать в зависимости от текущих потребностей.
Процесс отладки
-
Запуск QEMU в режиме отладки:
./start_qemu_gdb_mode.sh
-
Подключение к GDB в другом терминале:
cd test_project && make gdb
-
Ожидание подключения GDB:
QEMU ожидает подключения и удерживает прошивку в состоянии паузы, что позволяет:Устанавливать точки останова
Изучать начальное состояние системы
Отлаживать инициализацию системы
Практика работы с GDB
После запуска отладки вы увидите несколько терминалов:
-
Терминал QEMU:
В терминале, где был запущен скриптstart_qemu_gdb_mode.sh
, будет отображено сообщение о том, что QEMU ожидает подключения: -
Терминал GDB:
test/test_project$ make gdb arm-none-eabi-gdb ../elf/firmware.elf -ex "target extended-remote :3333" GNU gdb (Arm GNU Toolchain 12.2.MPACBTI-Rel1 (Build arm-12-mpacbti.34)) 13.1.90.20230307-git Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "--host=x86_64-pc-linux-gnu --target=arm-none-eabi". Type "show configuration" for configuration details. For bug reporting instructions, please see: . --Type for more, q to quit, c to continue without paging--
-
GDB в режиме TUI (Text User Interface):
Включение режима TUI позволяет визуализировать процесс отладки:
# Включить TUI режим (gdb) tui enable
Основные команды отладки
-
Установка точки останова:
Поставим брейкпоинт в функцию, запускающую тесты:
(gdb) break logging_test
-
Запуск программы:
Разрешим выполнение программы. Исполнение остановится при достижении установленного брейкпоинта:
(gdb) continue
-
Пошаговое выполнение:
Позволяет выполнять программу построчно или заходить внутрь функций:
(gdb) next # Выполнить строку (n) (gdb) step # Зайти внутрь функции (s)
-
Просмотр переменных:
Для просмотра значения переменной используйте:
(gdb) print name # Вывести значение переменной (gdb) info locals # Вывести все локальные переменные
GDB print variables> ? Совет: GDB поддерживает сокращения для часто используемых команд:
> -c
= continue
> -n
= next
> -s
= step
> -p
= print
> -bt
= backtrace (стек вызовов)
Типичные проблемы
-
Оптимизация компилятора может "спрятать" переменные:
(gdb) print i No symbol "i" in current context.
Решение: Используйте флаги
-O0
вместе с-g
(отладочные символы) при компиляции для отладки. В Makefile эти опции уже установлены. -
Потеря контекста при прерываниях:
(gdb) where #0 HardFault_Handler () at ../Core/Src/stm32f1xx_it.c:89 #1 #2 ?? () at ??:?
Решение: Установите брейкпоинт в обработчике прерывания.
> ⚠️ Важно: Отладка встраиваемых систем — это искусство. GDB может показаться сложным, но это мощный инструмент, особенно в сочетании с интегрированными средами разработки (IDE).
Отладка в VS Code
Visual Studio Code предоставляет графический интерфейс для GDB, значительно упрощающий процесс отладки. Это альтернатива командной строке make gdb
, где под капотом используется тот же протокол GDB для подключения к серверу отладки, но вместо CLI предлагается удобный графический интерфейс.
Настройка VS Code
Для работы с QEMU и GDB необходима специальная конфигурация в файле launch.json
. VS Code может сгенерировать базовый шаблон этого файла (F1 -> "Debug: Add Configuration"), но его нужно адаптировать под наш проект:
{
"configurations": [
{
"name": "QEMU ARM Debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/test/elf/firmware.elf",
"miDebuggerServerAddress": "localhost:3333",
"cwd": "${workspaceFolder}/test/test_project",
"targetArchitecture": "arm",
"MIMode": "gdb",
"stopAtEntry": true
}
]
}
Процесс отладки в VS Code
-
Запустите QEMU в режиме отладки:
./start_qemu_gdb_mode.sh
-
Откройте VS Code и перейдите в режим отладки (Ctrl+Shift+D). Если QEMU запущен, VS Code автоматически подключится к нему и откроет файлы проекта, на которые указывает регистр "PC". Убедитесь, что установлено расширение C/C++ для VS Code, позволяющее просматривать переменные и управлять стеком вызовов.
-
Установите точки останова, кликнув слева от номера строки. Появится индикатор красной точки.
-
Нажмите кнопку "Start Debugging" (F5):
Исполнение программы прервется при достижении брейкпоинта. Далее вы можете использовать все команды GDB через графический интерфейс VS Code.
> ? Преимущества VS Code:
> - Визуальное управление точками останова
> - Просмотр переменных в реальном времени
> - Навигация по коду во время отладки
> - Встроенный терминал для параллельной работы с QEMU
> - Удобство работы с переменными и стеком вызовов
> - Поддержка многопоточности
Примечание: Хотя VS Code предоставляет удобный интерфейс, знание базовых команд GDB остаётся полезным для отладки на медленных машинах, где частые запросы к серверу GDB по сети могут существенно замедлить процесс. Это также ключевой навык при автоматизации процесса тестирования и понимании инструментов GNU GCC.
Профилирование FreeRTOS в VS Code
Одной из самых мощных возможностей при отладке является визуализация работы RTOS. Настроим инструменты, позволяющие "заглянуть внутрь" операционной системы и установить связь с нашим устройством, чтобы понимать, где оно тратит время и память.
> ? Примечание: Подробнее о возможностях профилирования можно прочитать в официальной документации FreeRTOS.
Включение статистики FreeRTOS
-
В STM32CubeMX включите необходимые опции для RTOS на основной странице:
FreeRTOS Configuration in CubeMX> ? Важно: Не забудьте включить
RECORD_STACK_HIGH_ADDRESS
для отслеживания использования стека каждым потоком.
Настройка таймера для профилирования
Для измерения времени выполнения потоков необходим таймер. Настроим TIM2 так, чтобы он работал чуть быстрее системного таймера, но достаточно медленно, чтобы дать около 5 секунд до переполнения:
Реализация функций для работы со статистикой:
Добавим их в конец файла tim.c
:
unsigned long getRunTimeCounterValue(void) {
return __HAL_TIM_GET_COUNTER(&htim2);
}
void configureTimerForRunTimeStats(void)
{
// Таймер уже настроен HAL, просто запускаем
HAL_TIM_Base_Start(&htim2);
}
> ? Примечание: Эти функции объявлены как weak
в CMSIS-RTOS2. Мы предоставляем собственную реализацию в соответствии с нашей конфигурацией таймера.
RTOS Views в VS Code
Установите расширение RTOS Views.
Запустите отладку, как описано ранее.
Дайте системе поработать несколько секунд (чтобы успели создаться потоки).
-
Откройте вкладку RTOS рядом с терминалом:
В таблице вы увидите:
Список всех потоков
Состояние каждого потока (активный/заблокированный/приостановленный)
Использование стека
Процент времени CPU, затраченного на каждый поток
> Личный опыт: Честно говоря, я потратил немало времени, пытаясь настроить это всё на реальном железе, когда только начинал свой путь в open-source разработке STM32. Но оно того стоило - теперь у меня есть полное понимание работы системы в любой момент отладки.
Заключение
Эта статья - компиляция моего опыта как разработчика встраиваемых систем. Раньше моя работа в основном заключалась во взаимодействии с железом: осциллограф, паяльник и бесконечные прошивки отладочных плат. Но со временем я понял, что ключ к комфортной разработке - это правильно настроенное окружение.
> ? Главный урок: Когда ваш редактор быстр, отладка запускается одной кнопкой, а процесс идентичен что для виртуального, что для реального железа - разработка превращается в удовольствие!
В современном мире, где удаленная работа становится нормой, возможность эмуляции железа открывает новые горизонты.
Что дальше?
Надеюсь, это небольшое введение в мир open-source инструментов, эмуляции, автоматизации и отладки на примере STM32 и моей библиотеки логирования было полезным. Весь код доступен под MIT лицензией - не стесняйтесь использовать его в своих проектах и создавать форки! (GitHub)
> ? Напоследок: Не бойтесь экспериментировать и создавать свои скрипты для комфортной разработки!
Комментарии (20)
ptr128
09.12.2024 14:02Не понял, как эмулировать внешнее по отношению МК окружение? В постейшем виде - зашумленный сигнал АЦП. В более сложном - обмен через NRF24L01 или динамическая графика хотя бы на ST7735
AleksandrVi Автор
09.12.2024 14:02Привется написать эмулятор spi протокола для nrf и дальше к нему отладить прошивку самого мк. Да, это не так просто
maquefel
09.12.2024 14:02В случае STM32 QEMU это просто программа в userspace не более. Зашумленный сигнал АЦП вы можете смоделировать, в принципе, любой. Остальное тоже можете смоделировать, можете реальное устройство подключить. А нужно ли оно вам это другой вопрос.
ptr128
09.12.2024 14:02Зашумленный сигнал АЦП вы можете смоделировать, в принципе, любой
Смоделировать то я смогу. Вот только будет ли он эмулирован так же как в железе? И не будет ли проще использовать синтезатор (генератор) и МК в железе.
можете реальное устройство подключить
Подключать тот же ST7735 через FT2232H и пробрасывать его в QEMU - боюсь еще тот квест.
А нужно ли оно вам это другой вопрос.
Вот как раз на этот вопрос я ищу ответ. С одной стороны, обвешиваться осликами, мультиметрами, логическим анализатором и синтезатором сидя в консоли - муторно. Но будет ли проще в QEMU?
Всё же в JTAG сидеть приходится редко, так как основные алгоритмы можно отладить и на ПК, а тесно связанные с железом - требуют соблюдения жесткой времянки, что не позволяет отлаживать их пошагово.
maquefel
09.12.2024 14:02Вы правильные вопросы задаёте,
Вот только будет ли он эмулирован так же как в железе?
Конечно не будет, модель процесса это не сам процесс.
И не будет ли проще использовать синтезатор (генератор) и МК в железе.
Может будет, а может и не будет. Если у вас всё "под паром", вы владеете хорошо владеете инструментом, то конечно быстрее воспользоваться тем, что есть.
А вот если вы это действие повторяете из раза в раз с тем же самым набором компонентов, тогда чисто "программное" решение, возможно будет выгоднее.
Подключать тот же ST7735 через FT2232H и пробрасывать его в QEMU - боюсь еще тот квест.
Ну как раз USB очень просто пробросить.
Вот как раз на этот вопрос я ищу ответ. С одной стороны, обвешиваться осликами, мультиметрами, логическим анализатором и синтезатором сидя в консоли - муторно. Но будет ли проще в QEMU?
Не зная железа, которое вы используете сложно сказать. Но если брать самый плохой вариант то было бы проще, если бы всё что вам нужно уже было из "коробки". В противном случае это превращается в долгий проект, где вам самому необходимо написать недостающие компоненты.
В QEMU использованном в статье нет и половины всей периферии stm32f103, а в официальном репозитории её еще меньше - но тем не менее того что есть автору для решения своей задачи хватило.
ptr128
09.12.2024 14:02Ну как раз USB очень просто пробросить
Так для ST7735 пробрасывать надо не USB, а SPI с тремя сигнальными линиями GPIO. У ST7735 странный SPI с одной двунаправленной шиной для данных. Так что управлять надо CS, R/W и Reset через GPIO. А то, что FT2232H предоставляет к этому интерфейс через USB, в данном случае, лишь усложняет задачу.
К тому же, мне чаще приходится работать не с STM32, а с ESP32. А с последним в QEMU может оказаться даже тяжелей, чем с STM32. Но тут уже надо пробовать и разбираться. Начну с ESP32-C3 (RISC-V). Если что накопаю - отпишусь.
maquefel
09.12.2024 14:02а SPI с тремя сигнальными линиями GPIO. У ST7735 странный SPI с одной двунаправленной шиной для данных.
А вот это большая проблемка, соблюсти времянку тут тяжело будет (если именно прокидывать, а не делать эмуляцию целиком).
Но тут уже надо пробовать и разбираться. Начну с ESP32-C3 (RISC-V). Если что накопаю - отпишусь.
С такими вещами всегда готов помочь ! Пишите - буду рад ответить.
ptr128
09.12.2024 14:02
Gordon01
09.12.2024 14:02Гораздо проще и правильнее научиться, наконец, писать юнит-тесты https://pragprog.com/titles/jgade/test-driven-development-for-embedded-c/
Все эти "обмены с внешним окружением" - лишь передача данных, не более
ptr128
09.12.2024 14:02Вместо того, чтобы предлагать купить книгу за $30, лучше напишите об этом статью, А после прочтения статьи уже можно будет понять, стоит ли тратить на это время и деньги.
segment
09.12.2024 14:02VS Code пользоваться нормально сложно, постоянно какие-то проблемы с IntelliSense были и отладка это боль. Сам пользуюсь Visual Studio, SEGGER Embedded Studio, CLion.
AleksandrVi Автор
09.12.2024 14:02Да, приходится один раз нормаоьно настроить intelliSence, дальше хорошо работает. Сейчас он умеет использовать cmake или makefile для работы с проектом. Тогда все корректно подсказывает.
Mcublog
09.12.2024 14:02Спасибо за статью. Не знал, что в QEMU есть эмулятор периферии. Хотя как понял, он довольно неполный
Работаю примерно тем же путем, но собираю прошивку просто под десктоп.
Железноспецифичные вещи меняю на аналоги, типа флешку на файл, вход концевика значение в файле. Внешние скрипты просто пишут в файлы эмулируя работу внешних устройств.
Эмуляцию окружения и его детализированность делаю в разумных пределах, где-то чуть подробнее, где-то просто затычка
Подобными способами отлаживаю и проверяю "бизнес" логику. Для железа отдельный hw тесты
Графику просто вывожу фрейм буфер в файлы картинок, с довольно низким ФПС. Можно наверное и с высоким, но не вижу для себя смысла. Все что нужно вижу)
Всю эту требуху держу на tmpfs. Если надо сдампить стейт, то отдельными скриптами сохраняю состание всех нужных файлов, потом также делаю рестор.
Seenkao
09.12.2024 14:02Здесь можно посмотреть обычную эмуляцию используя только Qemu.
Отладчик GDB - это очень мощный инструмент для отладки, но им надо уметь пользоваться. Здесь ссылка по работе с отладчиком, а здесь ссылка как можно отлаживать разные архитектуры на одном компьютере (в Linux в основном, но я думаю если использовать WSL, то тоже можно).
Просто как дополнительная информация. )))
pistoletov
09.12.2024 14:02Не проще ли реальное окружение чем настройка qemu?. Плюс совершенно не факт что баги отлаженные в эмуляторе не появятся потом в железе. Девборды доступные и недорогие. Например сейчас проект с TCP стеком. Устройство приемное в нем стек реализован некорректно. Такое в quemu вряд ли повторишь. А так получается двойное время тратить. Отладил в эмуляторе и потом по новому в железе
maquefel
09.12.2024 14:02Так это каждый для себя сам решает,
Девборды доступные и недорогие.
Это правда.
Устройство приемное в нем стек реализован некорректно. Такое в quemu вряд ли повторишь. А так получается двойное время тратить. Отладил в эмуляторе и потом по новому в железе
А вот это не факт. В QEMU вполне себе повторишь, может даже оказатся, что в одной ревизии кривой, а в новой уже поправили, такое тоже можно учесть.
Возможно, выгоднее иметь какой-никакой смоук-тест на куче QEMU и небольшое кол-во реальных установок.
MrJones
09.12.2024 14:02Использую для работы с МК среды на базе Eclipse. Да не фонтан, но пока что для меня это оптимальный вариант. Тормозит иногда, но работает стабильно, предсказуемо, просто и быстро всё настраивается. Отладкой в эмуляторе думаю стоит заниматься на этапе учёбы, в реальном же проекте проще и быстрее заказать отладку или вообще собирать на конечном устройстве
maquefel
Спасибо за статью!
Не могли бы вы пояснить расхождения использованной версии QEMU с официальной, чтобы всем было понятнее ? Насколько там всё сильно в итоге разошлось ?
Я вижу, что в официальном репозитории реализованы только UART, SPI:
https://elixir.bootlin.com/qemu/v9.2.0-rc3/source/hw/arm/stm32f100_soc.c#L152
AleksandrVi Автор
Ответить на вопрос сложно. Этот форк давно не синхронизован с основным репозиторием. Если посмотреть в https://github.com/qemu/qemu/blob/master/hw/arm/stm32f100_soc.c, то можно найти определение для f100_soc, в котором почти ничего не реализовано кроме самого cortex ядра. Те использованная версия это старый qemu c доработками конкретно для stm32 периферии.
Например, тут добавляются gpio и uart https://github.com/beckus/qemu_stm32/blob/stm32/hw/arm/stm32_f103c8.c. А здесь можно найти имплементацию других функций. https://github.com/beckus/qemu_stm32/tree/stm32/hw/arm.
Те, расхождения только в том, что добавлена реализации периферии stm32 и добавлены несколько soc. Однако, для старой версии.