0% found this document useful (0 votes)
13 views24 pages

STM32 Testing with QEMU and Docker

The document provides a tutorial on testing and debugging STM32 embedded systems using QEMU emulator and Docker, aimed at automating the testing process for developers. It covers setting up an isolated build environment, preparing test projects with STM32CubeMX, and automating tests using Docker and GitHub Actions. Additionally, it discusses debugging techniques using GDB and QEMU, including the structure of Docker containers and the integration of FreeRTOS for multithreaded testing.

Uploaded by

Moon Nguyen
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views24 pages

STM32 Testing with QEMU and Docker

The document provides a tutorial on testing and debugging STM32 embedded systems using QEMU emulator and Docker, aimed at automating the testing process for developers. It covers setting up an isolated build environment, preparing test projects with STM32CubeMX, and automating tests using Docker and GitHub Actions. Additionally, it discusses debugging techniques using GDB and QEMU, including the structure of Docker containers and the integration of FreeRTOS for multithreaded testing.

Uploaded by

Moon Nguyen
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Testing and Debugging STM32 Embedded Systems Using QEMU Emulator and Docker

Average

13 min

7.7K

C*GitHub*Virtualization*Programming microcontrollers*IT systems testing*

Tutorial

Recommendations

The article will be useful for embedded system developers who want to automate the
testing process of their projects. A separate block is devoted to debugging gdbin the
QEMU emulator. The logging library is used as an example ( GitHub , Habr )

What will you learn from the article?

1. How to set up a build and emulation system using Docker and QEMU for testing
without physical hardware

2. How to Prepare a Test Project Based on STM32CubeMX and Integrate It with


CI/CD

3. How to Automate Testing of STM32 Projects to Ensure Reproducibility

4. How to debug an application in an emulator using familiar tools

Docker build system and QEMU emulator

Why Docker?

When developing embedded systems, we often encounter the problem of "it doesn't
work for me". This happens because of di erences in:

 Compiler versions

 Installed libraries

 Environment settings

Docker solves these problems by providing an isolated environment with precisely


defined versions of all components.

Structure of Docker containers

To create a full-fledged development environment, three containers are used:

1. Cross-compilation container ( Dockerfi[Link] )


o Contains ARM GCC toolchain downloaded directly from
[Link]

o Provides access to the latest version of arm-none-eabi-gcc

o Includes make and other build tools

2. Container with QEMU ( Dockerfile.qemu_stm32 )

o Builds QEMU from source beckus/qemu_stm32

o QEMU compilation only occurs the first time the container is built

o Emulates microcontroller peripherals

> Important: The version of QEMU used is outdated and only supports old STM32
models. There is a need to update the emulator to support modern STM32
microcontrollers. This could be an interesting project for the development community.

3. Container for running tests ( Dockerfile.run_tests )

o Combines the results of previous containers

o Runs tests and collects results

o Generates test reports

QEMU for STM32

QEMU is a powerful emulator capable of emulating various processor architectures. For


working with STM32, a special version of QEMU with support for STM32 peripherals is
used.

Supported Peripherals

At the time of writing, the following are supported for STM32F103C8:

 UART

 Timers

 GPIO

 Interruptions

 Flash

 Watchdog

 ADC, DAC

 Real time clock


 Basic system functions

> Tip: Despite the limitations of the current version of QEMU, this testing approach
can be adapted to other emulators or newer versions of QEMU as they become
available.

Test project

Generating a project in STM32CubeMX

The project is created using STM32CubeMX, which ensures correct initial configuration
of the microcontroller and peripherals.

STM32CubeMX Clock Configuration

Key project settings:

 Clocking: HSE 8 MHz with PLL up to 72 MHz

 UART1 for log output

 GPIO PC13 for LED indication

 System Timers for FreeRTOS


FreeRTOS Configuration Window

> FreeRTOS Configuration Using CMSIS-RTOS2 API

When generating code, it is important to select the correct parameters:

Project settings window

> Copying libraries to a project. Using .c and .h files for peripherals.


Code generation settings window

You need to select Makefile as the build system for Docker integration. Increase the
heap size, as dynamic memory is used in FreeRTOS. This is necessary because 10
threads are created during testing, when checking for bu er locks.

Project structure

The project uses the standard STM32CubeMX structure, which is located in the
directory test_project. Scripts for building and testing are located in the directory test.
The library under test is located in the directory liband is copied to the project during
assembly.

├── lib

│ ├── logging.c

│ ├── logging.h

│ ├── logging_usb.c

│ └── logging_usb.h

├── LICENSE

├── [Link]

└── test

├── [Link]
├── Dockerfile.qemu_stm32

├── Dockerfile.run_tests

├── Dockerfi[Link]

├── start_qemu_gdb_mode.sh

├── test_in_docker.sh

├── [Link]

├── test_project

│ ├── Core

│ ├── Drivers

│ ├── logging_cmsis_rtos2

│ ├── Makefile

│ ├── Middlewares

│ ├── startup_stm32f103xb.s

│ ├── STM32F103C8Tx_FLASH.ld

│ └── test_project.ioc

└── verify_output.py

When building, [Link] the logging library to the test project and starts the
build. MakefileAdditional flags have been added for building the library and tests.

FreeRTOS integration

FreeRTOS is configured via STM32CubeMX with the following parameters:

 DefaultTask enabled

 Dynamic memory allocation is used

 Increased heap size to support multi-threaded test where 10 additional threads


are created. With default FreeRTOS configuration there is only enough memory
to create 2 additional threads

 Tracing functions are enabled (more details in the debugging section)

All FreeRTOS user code is located in 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();
// ...
}

This stream is used as an entry point for running tests and initializing the logging library.

> Tip: STM32CubeMX allows you to easily modify the project configuration via a
graphical interface with subsequent code regeneration. However, this also in some
sense obliges the developer to always change the configuration via cubemx, so as not
to break the project structure.

Testing the Logging Library

The tests are implemented in [Link] check various modes of operation of the
logging library:

void logging_test()
{
logging_basic_test("basic_test"); // Базовые тесты
osDelay(200); // Ожидание вывода логов
logging_test_pack("pack_of_ten_ten_times"); // Пакетная запись
osDelay(200); // Ожидание вывода логов
logging_test_di erent_levels("di erent_levels"); // Разные уровни логов
osDelay(200); // Ожидание вывода логов
logging_test_fatal("fatal"); // Фатальные ошибки
osDelay(200); // Ожидание вывода логов
logging_interrupt(); // Логи из прерываний
osDelay(200); // Ожидание вывода логов
logging_test_multiple_threads(); // Многопоточный тест
}
> Note: Delays between tests ( osDelay) allow the logging thread to empty its bu ers
and cleanly separate the output of di erent test cases.

Key Test Scenarios

1. Basic testing

2. [INFO ][1s.1]: basic_test_0

3. [INFO ][1s.12]: basic_test_1

[INFO ][1s.22]: basic_test_2

o Checking message formatting

o Timestamps with millisecond precision

o Sequential logging

The logging library uses FreeRTOS kernel ticks to obtain the time. The time is captured
when the logging function is called and stored in a bu er before being transmitted over
the interface, thus reflecting the time of the event, not the time of transmission over the
interface.

4. Logging levels

5. [DEBUG_ALL][2s.112]: di erent_levels_DEBUG_ALL

6. [DEBUG_MIN][2s.112]: di erent_levels_DEBUG_MIN

7. [INFO ][2s.112]: di erent_levels_INFO

8. [WARNING ][2s.112]: di erent_levels_WARNING

[ERROR ][2s.112]: di erent_levels_ERR

9. Special modes

10. # Фатальные ошибки (прямой вывод в UART)

11. fatal_0

12. fatal_1

13.

14. # Логи из прерываний (без буферизации)

[INFO ][2s.512]ISR: LOG_ISR_9

o LOG_FATAL- direct recording to UART, without bu ering

o LOG_ISR- logging from interrupts, without using a bu er


void logging_interrupt()
{
// только последний лог будет напечатан
for (int i = 0; i < LOGS_AT_ONCE; i++) {
LOG_ISR(INFO, "LOG_ISR_%d", i);
}
}

> Important about ISR: Only the last message ( LOG_ISR_9) is visible in the output,
and this is expected behavior. In the interrupt context:
> - Blocking operations cannot be used
> - Bu ering is not applied
> - The logging thread has a lower priority.
> As a result, while the logging thread is processing one message, the next
call LOG_ISRis already overwriting the data. This is a tradeo between correctness of
operation in an ISR and the guarantee of delivery of all messages.

15. Multithreaded test

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

o 10 threads write logs simultaneously

o Tests the circular bu er under load

o The logging thread has normal priority

o Test threads block when bu er is full

> Feature: The library uses delayed log output via UART. If there are few logs, they are
output in the background. If the bu er is full, the streams are blocked until space is
freed. The modes LOG_FATALand LOG_ISRprovide instant output.

Verification of output
The correctness is checked by a script verify_output.py. QEMU creates a virtual
terminal, to which the emulated STM32 outputs data. The Python script connects to this
terminal and checks the correctness of the output.

# Пример проверки формата INFO сообщения


pattern = r"\[INFO\s*\]\[(\d+)s\.(\d+)\]: basic_test_(\d+)"
match = [Link](pattern, output)
if match:
seconds = int([Link](1))
milliseconds = int([Link](2))
test_number = int([Link](3))

The script completes successfully if all messages are correct. This section of the code
needs to be modified depending on the requirements of the project being tested. For
example, if the project hangs or there is no full set of messages, the script will hang in
standby mode. This can be fixed by adding timeouts and more complex logic that will
trigger the internal QEMU "shutdown" mechanism when such situations are detected,
for example, when a HardFault or similar errors occur.

Automated testing

Once the test cases are implemented, the next step is to automate their execution. This
is done using a combination of Docker and GitHub Actions.

Docker containerization

Testing is performed in the same three containers described earlier:

services:
tests:
build:
context: .
dockerfile: Dockerfi[Link]
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

Each container plays its own role in the testing process:

1. tests— compiles firmware with tests.

2. qemu_stm32- provides an emulation environment.

3. run_tests— runs tests and verifies results.

Running tests

Automation of launch is implemented through a script [Link]:

#!/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 '{{.[Link]}}' $test_container_name)

> Feature: The script not only runs tests, but also ensures correct copying of the
library and saving of test results.

Integration with CI/CD

Tests are automatically run on every push and pull request via 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 && ./[Link]
- name: Archive test results
uses: actions/upload-artifact@v4
with:
name: combined_logs
path: |
./test/logs/[Link]
./test/logs/serial_output.txt

> Tip: For corporate use, you can set up a self-hosted runner on your own build
server. The approach is compatible with GitLab CI and other CI systems. In this case,
GitHub Actions is used, which is provided free of charge for open-source projects.

Analysis of results

The tests generate two files with results:

1. [Link]— debug output of the testing process

2. serial_output.txt— logs from the library being tested

These files are saved as build artifacts and can be used to:

 Analysis of the causes of test failures

 Checking the correctness of the library operation

 Documentation of the system operation

Debugging using GDB and QEMU

Debugging embedded systems often feels like a detective investigation. Fortunately,


QEMU and GDB provide a complete set of tools for this.

How does this work?


QEMU, as a true GNU project, supports all standard debugging tools. For tests, we
specify the serial interface name pty. It is possible to run qemu in the mode where the
serial interface is output to the qemu console.

QEMU_FLAGS=(

"-nographic"

"-M" "stm32-f103c8"

"-kernel" "/app/fi[Link]"

"-serial" "pty"

> For the curious: The mapping between STM32 USARTs and QEMU PTYs is defined
in the emulator source code. If you need something special, you'll have to study the
source. RTSL — Read The Source, Luke!

Debug mode

The script test_in_docker.shsupports two operating modes:

1. Test mode - normal test run

2. Debug mode - run with GDB support:

if [ "$1" == "gdb" ]; then

QEMU_FLAGS+=(

"-gdb" "tcp::3333" # GDB-сервер на порту 3333

"-S" # Остановка при старте

echo "Starting QEMU in GDB debug mode..."

Connecting the debugger

To connect to QEMU, use arm-none-eabi-gdb. The test project has a ready-made target:

gdb:

arm-none-eabi-gdb ../elf/fi[Link] -ex "target extended-remote :3333"

> Important: There is a little magic here! The firmware for GDB is taken from the
Docker container via volume:
volumes:
- ./elf/:/elf # Файлы для GDB &gt;

Starting debugging (GDB)

To start QEMU in debug mode use start_qemu_gdb_mode.sh:

#!/bin/bash

docker compose --profile gdb up --build

It uses a profile gdbin Docker Compose:

gdb:
&lt;&lt;: *run_tests # Наследуем настройки от run_tests
ports:
- "3333:3333" # Открываем порт для GDB
profiles:
- gdb # Отдельный профиль
entrypoint: ["./test_in_docker.sh", "gdb"]

> Profiles in Docker Compose allow you to have di erent configurations in a single
file. A profile testis for testing, and gdba profile is for debugging. This allows you to
choose which configuration to run depending on your current needs.

Debugging process

1. Running QEMU in debug mode:

./start_qemu_gdb_mode.sh

2. Connecting to GDB in another terminal:

cd test_project && make gdb

3. Waiting for GDB connection:


QEMU waits for connection and keeps the firmware in a paused state, allowing:

o Set breakpoints

o Study the initial state of the system

o Debug system initialization

Practice working with GDB

Once debugging starts, you will see several terminals:

1. QEMU terminal:
The terminal where the script was run start_qemu_gdb_mode.shwill display a
message that QEMU is waiting for a connection:
QEMU terminal waiting for GDB connection

2. GDB Terminal:

test/test_project$ make gdb


arm-none-eabi-gdb ../elf/fi[Link] -ex "target extended-remote :3333"
GNU gdb (Arm GNU Toolchain [Link]-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--

3. GDB in TUI (Text User Interface) mode:

Enabling TUI mode allows you to visualize the debugging process:

# Включить TUI режим


(gdb) tui enable
GDB in TUI mode

Basic debugging commands

1. Setting a breakpoint:

Let's put a breakpoint in the function that runs the tests:

(gdb) break logging_test

2. Launching the program:

Let's enable the program execution. The execution will stop when the set breakpoint is
reached:

(gdb) continue
Running program until breakpoint

3. Step by step execution:

Allows you to execute a program line by line or step inside functions:

(gdb) next # Выполнить строку (n)


(gdb) step # Зайти внутрь функции (s)

4. View variables:

To view the value of a variable, use:

(gdb) print name # Вывести значение переменной


(gdb) info locals # Вывести все локальные переменные
GDB print variables

GDB print variables> Tip: GDB supports shortcuts for commonly used commands:
> - c= continue
> - n= next
> - s= step
> - p= print
> - bt= backtrace (call stack)

Typical problems

1. Compiler optimizations can "hide" variables:

(gdb) print i
No symbol "i" in current context.

Solution: Use flags -O0together with -g(debug symbols) when compiling for debugging.
The Makefile already has these options set.

2. Loss of context on interrupts:

(gdb) where
#0 HardFault_Handler () at ../Core/Src/stm32f1xx_it.c:89
#1
#2 ?? () at ??:?

Solution: Set a breakpoint in the interrupt handler.


> Important: Debugging embedded systems is an art. GDB may seem complicated,
but it is a powerful tool, especially when combined with integrated development
environments (IDEs).

Debugging in VS Code

Visual Studio Code provides a graphical interface for GDB that greatly simplifies the
debugging process. It is an alternative to the command line make gdb, where under the
hood, the same GDB protocol is used to connect to the debug server, but instead of a
CLI, a convenient graphical interface is o ered.

Setting up VS Code

To work with QEMU and GDB, a special configuration is required in the [Link]. VS
Code can generate a basic template for this file (F1 -> "Debug: Add Configuration"), but
it needs to be adapted to our project:

{
"configurations": [
{
"name": "QEMU ARM Debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/test/elf/fi[Link]",
"miDebuggerServerAddress": "localhost:3333",
"cwd": "${workspaceFolder}/test/test_project",
"targetArchitecture": "arm",
"MIMode": "gdb",
"stopAtEntry": true
}
]
}

Debugging process in VS Code

1. Start QEMU in debug mode:

./start_qemu_gdb_mode.sh

2. Open VS Code and go into debug mode (Ctrl+Shift+D). If QEMU is running, VS


Code will automatically connect to it and open the project files pointed to by the
"PC" register. Make sure you have the C/C++ extension for VS Code installed,
which allows you to view variables and manipulate the call stack.
VS Code Debug Overview

3. Set breakpoints by clicking to the left of the line number. A red dot indicator
will appear.

Setting Breakpoint in VS Code

4. Click the "Start Debugging" button (F5):

The program execution will be interrupted when the breakpoint is reached. You can then
use all GDB commands through the VS Code graphical interface.

> VS Code Benefits:


> - Visual breakpoint management
> - Real-time variable view
> - Code navigation while debugging
> - Built-in terminal for parallel work with QEMU
> - Convenient work with variables and call stack
> - Multithreading support
Note: Although VS Code provides a convenient interface, knowledge of basic GDB
commands remains useful for debugging on slow machines, where frequent requests to
the GDB server over the network can significantly slow down the process. It is also a key
skill for automating the testing process and understanding the GNU GCC tools.

Profiling FreeRTOS in VS Code

One of the most powerful debugging capabilities is visualizing the RTOS. Let's set up
tools that allow us to "look inside" the operating system and establish communication
with our device to understand where it spends time and memory.

> Note: More details about profiling capabilities can be found in the o icial FreeRTOS
documentation .

Enabling FreeRTOS Statistics

1. In STM32CubeMX, enable the required options for the RTOS on the main
page:

FreeRTOS Configuration in CubeMX

FreeRTOS Configuration in CubeMX> Important: Don't


forget RECORD_STACK_HIGH_ADDRESSto enable to track stack usage per thread.

Setting up a timer for profiling

To measure the execution time of threads, a timer is needed. Let's set up TIM2 to run
slightly faster than the system timer, but slow enough to give about 5 seconds before
overflow:
Timer Configuration

Implementation of functions for working with statistics:

Let's add them to the end of the file tim.c:

unsigned long getRunTimeCounterValue(void) {


return __HAL_TIM_GET_COUNTER(&htim2);
}

void configureTimerForRunTimeStats(void)
{
// Таймер уже настроен HAL, просто запускаем
HAL_TIM_Base_Start(&htim2);
}

> Note: These functions are declared as weakin CMSIS-RTOS2. We provide our own
implementation according to our timer configuration.

RTOS Views in VS Code

1. Install the RTOS Views extension .

2. Start debugging as described earlier.


3. Let the system run for a few seconds (to allow the threads to be created).

4. Open the RTOS tab next to the terminal:

RTOS Views in VS Code

In the table you will see:

 List of all streams

 The state of each thread (active/blocked/suspended)

 Using a stack

 Percentage of CPU time spent on each thread

> Personal experience: To be honest, I spent a lot of time trying to set this all up on real
hardware when I was just starting out in open-source STM32 development. But it was
worth it - now I have a full understanding of how the system works at any point during
debugging.

Conclusion

This article is a compilation of my experience as an embedded systems developer.


Previously, my work mainly involved interacting with hardware: an oscilloscope, a
soldering iron, and endless firmware updates for debug boards. But over time, I realized
that the key to comfortable development is a properly configured environment.

> The main lesson: When your editor is fast, debugging is launched with one button,
and the process is identical for both virtual and real hardware - development becomes a
pleasure!

In today's world, where remote work is becoming the norm, the ability to emulate
hardware opens up new horizons.

What's next?

I hope this short introduction to the world of open-source tools, emulation, automation
and debugging using STM32 and my logging library as an example was useful. All code is
available under the MIT license - feel free to use it in your projects and create forks!
( GitHub )
> Finally: Don't be afraid to experiment and create your own scripts for comfortable
development!

Common questions

Powered by AI

QEMU acts as an emulator that simulates the STM32 microcontroller environment, allowing for tests and debugging without the need for physical hardware. GDB is used to perform debugging operations such as setting breakpoints, stepping through code, and inspecting variables. The Docker profiles 'test' and 'gdb' define separate configurations for executing tests normally or in debug mode, respectively. This modularity allows developers to easily switch between running tests and performing detailed debugging tasks within the containerized environment, providing flexible and powerful debugging capabilities .

The use of osDelay between tests allows the logging thread to empty its buffers, ensuring that the output of different test cases is cleanly separated. This is crucial in avoiding overlaps or mixed outputs from concurrent operations, which could otherwise lead to incorrect assessments of each test case's output. By ensuring that logs from one test finish processing before the next begins, the test results' validity improves because each test's performance and correctness can be accurately evaluated .

The Python script verify_output.py ensures correctness by matching the logged output against predefined patterns using regular expressions. It confirms that the timestamps and formats are accurate, which allows verification of both the logging library's functionality and its time-stamping accuracy . The script anticipates potential hangups, such as project hangs or incomplete message sets, by implementing strategies like timeouts and logic to trigger the shutdown mechanism of QEMU during errors, providing robustness against prolonged execution without output .

The integration of Docker and GitHub Actions facilitates automated testing by containerizing the necessary components and providing a consistent testing environment. Docker ensures that tests, emulation via QEMU, and result verification occur in isolated environments, replicating conditions consistently across different setups. GitHub Actions automates the running of these tests on every push or pull request, enabling continuous integration with immediate feedback. This setup allows for automated, reliable test execution and artifact collection, ensuring that code changes can be tested promptly across environments without manual intervention .

In the interrupt context, blocking operations cannot be used and buffering is not applied because the logging thread operates at a lower priority. As a result, while the logging thread is processing one message, subsequent calls to the logging function overwrite the data, meaning only the last message in rapid succession (LOG_ISR_9) is visible in output. This approach balances the need for real-time response in ISRs with the practicality of processing complete information, even though not all messages may be retained .

Using FreeRTOS's dynamic memory allocation introduces flexibility in resource allocation, allowing systems to adjust dynamically to varying demands, such as creating additional threads for logging. However, it presents tradeoffs, including increased complexity in managing memory leaks and fragmentation, which can lead to unpredictable memory usage patterns in real-time environments. While dynamic allocation supports scalability, it potentially affects system reliability and real-time performance when memory pressure causes allocations to fail or delays occur in a time-critical logging process, necessitating robust design practices to mitigate these risks .

The Makefile in the project is set up with additional flags to accommodate the building of both the library and the tests, ensuring that dependencies and configurations specific to multithreading are captured during compilation. Dynamic memory enhances this by allowing for an increased heap size, which is critical in FreeRTOS for supporting multiple threads beyond the base configuration. The default setup only allows for a small number of threads, but the project's adapted configuration can handle additional threads due to dynamic allocation, which is essential for multithreaded tests where the heap management needs adjust based on runtime thread creation .

Multithreaded logging with a circular buffer implies robust handling of concurrent log entries from multiple threads. The circular buffer efficiently manages memory by overwriting the oldest data when full, enabling continuous operation without halting program execution . However, it also necessitates careful synchronization to prevent data corruption, as threads may intersect when accessing the buffer. The project addresses these challenges by ensuring that log threads block until space is freed or use immediate output modes, like LOG_FATAL and LOG_ISR, for critical logs, which bypass buffering for real-time needs, showcasing a precise design trade-off between resilience and real-time efficiency .

Visual Studio Code enhances the debugging experience by providing a user-friendly graphical interface that simplifies breakpoint management, real-time variable viewing, and code navigation, which are less intuitive in a traditional command line GDB setup. It allows for an integrated terminal, multithreading support, and instant access to GDB commands through the graphical user interface, significantly reducing the learning curve and effort when navigating through code and debugging complex systems .

Modifying settings only through the STM32CubeMX interface is important because it ensures the project configuration remains consistent and intact. STM32CubeMX not only helps in generating code but also maintains an internal structure that, if altered externally, could break the project configuration. This integrated approach reduces errors and discrepancies that might arise if settings were manually altered, preserving the project's integrity .

You might also like