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
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
*.ofiles) 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 (that can be then run as a container) from a script called
Dockerfile. I’ve prepared a Docker that contains a lightweight Linux distribution
Alpinie which is ideal for using with
Go to my Github repo and download/clone the
Dockerfile to a directory of your choosing. If you have
git the you can type in the following command:
git clone https://github.com/MightyDevices/docker-gcc-arm-none-eabi-builder
Next, build the image by issuing the following command:
docker build -t gcc-arm-none-eabi .
The image will exist under the name of
gcc-arm-none-eabi. You can check all images that you currently have by executing:
docker image ls
So far, so good, let’s leave the image as it is for now – we’ll return to it later.
Side note: I wasn’t able to get the
arm-none-eabi-gdb debugger running due to some issues with shared libraries (
libtinfo.so.5), but not to worry: the image provided will have the
gdb from the Alpine’s repositories installed which is perfectly suitable for our needs.
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 gcc-arm-none-eabi sh
Now let’s try to connect the GDB with the OpenOCD. In the shell start the
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
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-gdbinstalled 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
#endifheader file guard
- Linker Script – Language support (syntax coloring) for the linker scripts
- TODO Highlight – Highlights comments that contain words like
FIXME:, 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
.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
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.