Getting started using the STM32 platform
See this GitHub repo for the related code (and a copy of this post).
I have an Arch Max STM32F4 development board. Although it supports mbed I like to understand how things work...
The datasheet and reference manual have all the required low-level hardware info. This guide will get you started compiling C programs to directly twiddle with the stuff from the reference manual.
I experimented with two ways to get started on the board: CMSIS and libopencm3. I preferred libopencm3 and that's what I'm using now, but have included examples using both. The toolchain and debugging stuff is the same between the two.
On Arch Linux, just install the
packages. Most other distros probably offer similarly named packages.
openocd config file is provided in the Arch Max git repo.
openocd -f Arch_Max/arch_max.cfg with the board connected to a
USB port should pick it up and provide a GDB server on port
3333. I have to
run OpenOCD as root as it needs access to the
/dev/hidraw0 device node to
communicate with the board.
Once OpenOCD is running and connected to the board via USB
gdb can be
connected and used to run and debug binaries.
arm-none-eabi-gdb -x load.gdb binary.elf
The gdb script
load.gdb will connect to the OpenOCD gdb server, reset the
board and load the elf file into memory. You can continue (
c) at the GDB
prompt to just run the program. For debugging you can set breakpoints etc before
- how to program an image into flash
Both the libopencm3 and the CMSIS examples will produce a binary which when run on the Arch Max board will blink the LED next to the ethernet port (GPIO B3).
The Makefiles in both examples will link an elf executable file, which is
suitable for debugging and loading via GDB (see above). A further
objcopy -O binary step (also shown in the Makefiles) provides a file suitable for flashing
onto the chip's flash memory (I think - I've not got around to actually trying
The libopencm3 project provide an free software library which is in many ways similar to CMSIS, but I found it easier to get started with and it is what I use for projects now. Note that despite the name it also supports a large range of Cortex-M4 devices!
There is an awk script (
libopencm3/scripts/gnlink.awk) which can generate the
required C flags and linker script for a device, see
example-libopencm3/Makefile for usage.
The generated linker script sets the executable entry point to
which can be found in
libopencm3/lib/cm3/vector.c. This is pretty minimal:
- clears the BSS
- makes sure the stack is aligned correctly
- calls pre_main, which may do chip-specific init. For the STM32F4 boards it just enabled access to the floating point unit
- calls global constructors
- calls main() - provided by you!
- calls global destructors
Note that the clocks are left in their reset state. In the example I have
set them up (using the
rcc_clock_setup_hse_3v3 function) to run at 168MHz. The
settings there are correct for the Arch Max (I hope!) as it has a high-speed
external (hse) 8MHz oscillator.
Using the functions in libopencm3 to do simple things like toggling the GPIO is probably rather wasteful as they each incur a function call overhead. If you are worried about this then you can build the libopencm3 library and your binary with link-time optimisation (LTO), which will inline any small functions the compiler thinks is wise.
I found it very difficult to find the CMSIS code. It turns out the best way to
get hold of it for a platform with mbed support (which is quite a lot, including
the Arch Max) is to just grab the mbed source code from mbed-os GitHub
repo. The repository contains lots of stuff for mbed and mbed-os, but the
interesting CMSIS code is in the
Each supported chip has a folder containing a chip-specific startup assembly
file. For the Arch Max this is in
This assembly file contains the entry point (
Reset_Handler) and quite well
documented with comments - it:
- Sets up the stack
- Copies data segment from flash to RAM
SystemInitto init clock and other basic hardware init
_start(which is the libc entry point that will call main)
- Provides the interrupt vector table
- Default handler just enters an infinite loop
- Set up to have a weak symbol for each interrupt, aliased to the default one
- Can easily provide an interrupt handler by just creating a function of the
correct name (e.g.
In the same folder as the startup
.S file there should be a linker script
.ld) which should be passed to the linker with the
-T option. This is also
quite well commented.
An implementation of
SystemInit for the Arch Max is provided
which sets up the system clock and various other bits and bobs. This
implementation calls into the HAL library (sources for which can be found at
mbed-os/targets/TARGET_STM/TARGET_STM32F4/device/). I have provided a chopped
system_light.c which does not depend on the HAL library.
The mbed files for your board should have some clues as to what pins are
connected to the odd LEDs etc. which are probably on your board. For the Arch
Max that was in
- at the bottom
LED1is set to
Makefile shows the compiler/linker options and include paths required.
See example in the
example-cmsis folder (note you'll need that checkout of
mbed-os - see the readme in the folder).
Good overview of CMSIS on the LPC devices
Very useful tutorial
Useful tutorial on making a blinky
Tutorial not really checked out yet