In this tutorial I’ll shortly describe how I setup my development environment for the STM32 micros. STM32L433 will be used as an example but the following process shall be applicable to any other STM32 micro.
Step 1: Get the Toolchain
In order to make the whole development process portable (i.e. to allow other parties to build the same output binary images from the same source files) I use Docker
build container
. If you aren’t familiar to Docker then go ahead and read this article. After that install Docker Desktop on your computer.
Although not strictly necessary three advantages come with the Docker approach:
- Docker offers virtualization that ensures that other people who use the same Docker image will get she same results no matter what underlying (Host) OS they use, etc..
- Docker (if well configured) can make the firmware building process to go *much* faster than building within the HostOS due to the fact that we’ll be storing the build process half-products (like
*.o
files) within the file system of the container that resides in RAM - You don’t have to contaminate your Host OS with tools for building ARM projects
Next step would be to build the docker image
on your machine. Since i’ve put the docker image to the dockerhub.io
(which is an image storing repository, something like git
) the whole build process can be started by typing in:
docker pull twatorowski/gcc-arm-none-eabi
If you are interested in the details of this image then take a peek at https://github.com/MightyDevices/docker-gcc-arm-none-eabi-builder
Step 2: We need something for debugging and uploading the firmware – OpenOCD.
Sadly enough If you, (like me) use Windows as your Host OS this step cannot be done in a Docker as Docker Desktop on Windows has no capability to forward the host’s USB to the containers so it can’t use the programming adapters like (STLink or any of the FTDI based JTAGs/SWDs). We need to install the OpenOCD within the Host OS.
Go to the https://github.com/gnu-mcu-eclipse/openocd/releases and look for a release that’s applicable for you operating system (I chose this). Next, extract the zip contents to the directory of your choosing. The main executable is in the directory bin
. Add this directory to the system PATH
so that the openocd
is accessible from command line in any directory.
Since this tutorial uses the STM32L433 as an example we may as well do a quick check whether the OpenOCD works. Find yourself a Nucleo board with any MCU from the STM32L4 familiy and type the following command:
openocd -f board/st_nucleo_l4.cfg
If the OpenOCD did not exit and your output looks something like this then you are good to go:
GNU MCU Eclipse OpenOCD, 64-bitOpen On-Chip Debugger 0.10.0+dev-00593-g23ad80df4 (2019-04-22-20:25)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 500 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 500 kHz
Info : STLINK V2J33M25 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.244649
Info : stm32l4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : Listening on port 3333 for gdb connections
OpenOCD does not limit you to the STM32L4 family: if you are planning a project using other micro then take a peek under the scripts/boards
directory and choose a script that suits you the most.
Step 3: Test connection between the OpenOCD and the GDB running inside a Docker container
This is just a test to tell whether the networking between the Docker containers ad you Host OS is working as expected. From the OpenOCD’s output provided above you can tell that the OpenOCD expects a connection from GDB on port 3333. That’s the default configuration and I’ll stick to it for the rest of this article.
Let’s begin with a fresh start of the OpenOCD, just like we did before (make sure that the target board you wish to develop on is connected!):
openocd -f board/st_nucleo_l4.cfg
Follow that by starting the container using the following command. You should be left with container’s shell:
docker run --rm --name test -it twatorowski/gcc-arm-none-eabi bash
Now let’s try to connect the GDB with the OpenOCD. In the shell start the gdb
process:
arm-none-eabi-gdb
Then within the running GDB type the following command that will establish the connection to the gdb-server
part of the OpenOCD:
target remote host.docker.internal:3333
The address host.docker.internal
points to your Host OS. If the output from all above looks something like this then everything is fine!
c:\>docker run --rm --name test -it gcc-arm-none-eabi sh
/ # arm-none-eabi-gdb
GNU gdb (GDB) 8.3
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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-musl --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) target remote host.docker.internal:3333
Remote debugging using host.docker.internal:3333
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x08006c40 in ?? ()
(gdb)
You can test some basic commands here. For example, let’s try to crank up the Nucleo SWD interface clock speed to it’s maximum using the following command:
(gdb) monitor adapter_khz 4000
adapter speed: 4000 kHz
Step 4: Get the IDE – VSCode
This is as simple as obtaining installing the Visual Studio Code from here: https://code.visualstudio.com/. Start the VSCode editor.
It is worth to install couple of plugins that make the development process pleasant. Here’s my list in the alphabetical order:
- C/C++ – syntax highlighting and debugging capabilities.
- Code Spell Checker – helps to avoid spelling mistakes in code and comments.
- Cortex-Debug – ARM Cortex-M GDB Debugger support for VSCode, can display Peripherals, Core registers, Function Disassembly, Memory View and so on, BUT useful only when you have the
arm-none-eabi-gdb
installed in your host OS. Sadly enough does not support the gdb pipe launch which means that it cannot communicate with the gdb residing in Docker container. - Doxygen Documentation Generator – helps to create Doxygen headers for new files, functions, etc…
- Head-File-Guard – Generator of the infamous
#ifndef ...
#define ...
#endif
header file guard - Linker Script – Language support (syntax coloring) for the linker scripts
- TODO Highlight – Highlights comments that contain words like
TODO:
orFIXME:
, can generate a list of TODOs and FIXMEs.
Step 5: Prepare a project within the VSCode that uses all we did above
Start by downloading the source code that you want to build. Let’s proceed with the STM32L433 startup project. Then go to it’s main directory.
git clone https://github.com/MightyDevices/startup-stm32l433
cd startup-stm32l433
NOTE: Make sure that your project directory has no whitespace characters.
VSCode uses .vscode
folder inside the main project directory to store all tasks (like Build, Clean) and launch configurations (like Debug). I’ve prepared all the files needed so let’s start with a clean directory and get these:
git clone https://github.com/MightyDevices/vscode-project-settings-docker-arm-none-eabi-gcc .vscode
Now we are officially ready to start coding in VSCode. Start the VSCode in the project directory. After the VSCode has launched. If you press shift+ctrl+b
you’ll see the picker with four options. Two of these must be invoked before anything gets uploaded (but only once in a life cycle of the VSCode) into the target device: Start OpenOCD
and Start Docker
.
In order to have the includes from the docker’s arm-none-eabi-gcc toolchain within the reach of VSCode a special task was prepared: Copy Includes
This is to be executed only once per project and will extract all the *.h
files to the project’s .includes
directory. For the sake of Intellisense settings.json
file was prepared so that files from the .includes
can be resolved within the project.
After that whenever you’ll press F5 the firmware will get build, uploaded and the execution will start. Don’t forget to add breakpoints to see the debugging in action 🙂
If you want to change the name of the output *.elf
file then don’t forget to update your .vscode/launch.json
as well.
Happy debugging!