This repo demonstrates a strategy for working with STM32CubeIDE-based projects. It preserves ST's .ioc workflow with autogeneration and easy pin reconfiguration in a git-friendly way, and also showcases:
- High-performance LL-DMA UART and USB peripheral abstractions (see the loopback project)
- Convenience wrappers for static allocation of FreeRTOS components (see static_rtos.h)
- C++ used appropriately in a constrained embedded environment (no dynamic allocations - see no_new.cpp).
- Code deduplication:
- Linking to versioned vendor firmware
- Common code shared across projects
- Unit testing with CppUTest (example)
- Code coverage with lcov
- Autoformatting with clang-format
- ITM debug logging
- FreeRTOS task profiling
CI checks on each pull request ensure that all projects compile, pass unit tests, and are formatted correctly. Here are example PRs demonstrating:
common
- Common code shared among all projects.loopback
- Demonstrates sending binary packets across USB and UART peripherals.vfd_bench
- A more real-world project demonstrating control of VFDs (variable-frequency drives) via modbus commands.host_apps
- Applications which run on the host PC for communicating with the target microcontroller.
These instructions were verified on a fresh install of Ubuntu 20.04.
Download the "Generic Linux Installer" (NOT the debian installer, which requires root to update) from: https://www.st.com/en/development-tools/stm32cubeide.html
Extract. Then install by running:
sh st-stm32cubeide_1.4.0_7511_20200720_0928_amd64.sh
Note that ctrl-C
will let you jump to the bottom of the licenses.
When updating the IDE, it will continue to use the original install path. So rather than requiring constant path editing or full IDE reinstalls for CI script compatibility, we're using a versionless symlinked path that's also compatible with the docker image directory structure. Set up that symlink by running (substitute 1.5.1
for your particular IDE version):
sudo mkdir /opt/st
sudo ln -frs ~/st/stm32cubeide_1.5.1 /opt/st/stm32cubeide
Download ST-Link Server from: https://www.st.com/en/development-tools/st-link-server.html
Extract. Then install with:
sudo dpkg -i st-stlink-server-1.3.0-4-linux-amd64.deb
Open launcher with super-key (windows key), then start typing "STM32CubeIDE". There might be two results. I go for the one with the blue background.
Select a "dummy" workspace for this initial setup (e.g. ~/projects/dummy
).
Then update from 1.4.0 to 1.4.2:
Help > Check for updates
Don't worry about the unsigned content warnings.
Create a new project in your dummy workspace:
File > New -> STM32 Project
Select our chip:
STM32F413ZH
Click through the prompts.
Build and flash this dummy project to ensure that tooling is setup correctly so far. You can just click "Ok" to accept the default debug config.
If you encounter a arm / libcurses error, try:
sudo apt install libncurses5
We need to setup a symlink to the STM FW repo to ensure we all share the same absolute path to shared project assets.
sudo ln -s ~/STM32Cube/Repository /usr/local/share/stm_repo
Switch your workspace to the checked-out repo.
Files > Switch Workspace
Use the symlinked firmware location in this workspace.
Window > Preferences > STM32Cube > Firmware Updater > Firmware Repository
Set to /usr/local/share/stm_repo
.
There was a bug in FreeRTOS message buffers that has recently been fixed; however, this fix has not yet been incorporated into ST's F4 firmware package. As of writing, ST's F4 firmware V1.26.1 uses FreeRTOS V10.3.1. The FreeRTOS bug affects at least V10.4.1 and below.
In the meantime, the simplest solution is to overwrite Middlewares/Third_Party/FreeRTOS/Source
with a clone of this patched repo.
Add each folder to this workspace.
File > Open Projects from Files System
You should be able to just point to the repo directory and then un-check all non-projects (likely just the first directory). As of this writing, there should only be one project selected:
loopback
Now try building and flashing each project. Sometimes you'll need to expand the project folder in order for the build hammer to work.
Here are some notes on the steps to create a new C++ project. You can either start a fresh project, or import an existing .ioc. This guide shows the "from .ioc" method.
File > New > STM32 Project ... from .ioc
Change Targeted Language
to C++
, then click Next
.
Change Code Generator Options
to ... as reference ...
, then click Finish
.
See screenshots for relevant settings.
Some .ioc settings to double check are:
Project Manager > Project
checkDo not generate the main()
Project Manager > Code Generator
selectAdd ... as reference ...
Project Manager > Code Generator > Generated files
checkGenerate ... as a pair ...
In the Project Explorer
, right click on the active project and select Convert to C++
.
Copy the custom
directory over from the loopback
example. Note that you'll likely need to right-click on your project in the Project Explorer
and select Refresh
for this new folder to appear.
In the Project Explorer
, right click on the active project, New > Folder > Advanced > Link to ...
, paste:
WORKSPACE_LOC/common
In the Project Explorer
, right click on custom / tests
, select properties
then C/C++ General > Paths and Symbols
, select [All configurations]
from the Configuration
dropdown, and check Exclude resource from build
. We don't want the IDE to build our unit tests (those are instead run independently on the host machine).
Repeat the same steps for excluding common / tests
.
Project > Properties > C/C++ General > Paths and Symbols
From Configuration
dropdown select [All configurations]
In Includes
tab, for languages GNU C
and GNU C++
, add these paths:
custom/inc
${WorkspaceDirPath}/common/inc
For this next step the Debug
, and then Release
configurations must be selected individually. In the Source Location
tab Add Folder
, select common
and custom
.
The STM32CubeIDE is not fully baked, and there are lots of annoying workarounds required to get things running smoothly in a team setting. Here are the workarounds you'll need to keep in mind during development:
The IDE likes to replace the absolute paths in .project
with relative paths containing stuff like PARENT-5-Project_LOC
, etc. when regenerating code from .ioc. This can break the build for other developers and CI. More details about this issue can be found in this post.
Use the fix-paths.sh
script to restore these absolute paths.
common # Code that's 100% user-generated and shared across all projects
├── inc
├── src
└── tests # Unit tests for common code
some_project
├── Core # autogenerated, but editable
│ ├── Inc
│ ├── Src
│ └── Startup
├── custom # 100% user-generated code that's project-specific
│ ├── inc
│ ├── src
│ └── tests # Unit tests for custom project code
│ # The following directories and files are autogenerated
├── Drivers # Just empty directories for links to common ST FW
│ └── STM32F4xx_HAL_Driver
├── Middlewares # Just empty directories for links to common ST FW
│ ├── FreeRTOS
│ └── USB_Device_Library
└── USB_DEVICE # autogenerated, but editable
├── App
└── Target
some_other_project
├── ...
...
For each PR, the following is checked:
- All STM32CubeIDE projects must compile
- Unit tests must pass
- Code must be formatted correctly
- Code coverage report generated
It uses this GitHub action: https://github.com/milesfrain/stm32-action
Which uses this docker image: https://github.com/milesfrain/stm32-docker
This repo uses the CppUTest unit testing framework. These tests are automatically run as part of CI for each pull request. To run locally, install the dependency, and run the test script. Projects / directories to test are tracked in the test-dirs.txt
configuration file.
sudo apt install cpputest
./unit.sh
Coding style for this repo is managed with clang-format
.
This tool is not mandatory for development, but your life will be easier if you install it:
sudo apt install clang-format
Then you can check if you're messing anything up by running:
format/check.sh
If you'd like to automatically fix these formatting mismatches, run:
format/do.sh
Autoformatting in eclipse/stm32cubeIDE that is close to (but not exactly the same as) the .clang-format
config, you can import format/eclipse_formatting.xml
into Window > Preferences > C/C++ > Code Style > Formatter > Import
.
Note that we're only autoformatting code that is 100% user-created, so we're not constantly fighting with ST's autogeneration tools. You can add new directories to track to format/targets.txt
.
If you're not happy with the formatting rules, you can adjust the top-level .clang-format
config file, and then we can adopt this new style.