STM32CubeMX HAL for make and gcc
An improved makefile template for STM32CubeMX 4.13.0, HAL code, ChibiOS, LwIP and the Nucleo-F746 board
Starting to get used to HAL drivers
The manufacturer of my favourite microcontrollers (ST micro) updated
their code generation tool STM32CubeMX several times since I made my second example.
Now I'm starting to integrate the HAL code into my projects. I've made my makefile framework to import the generated
HAL based code.
Now there are three (or even more) great and useful things that I'd like to combine:
The easy way of configuring the microcontroller and its peripherals using the CubeMX tool
The nice, clean and simple ChibiOS
My own interrupt service routines (for time critical tasks or just my very simple uart stdout console for printf() debugging
My own proven-in-use makefile framework
More great open source libraries, like the well-known LwIP stack
There's the way I'm approaching this goal:
How to make a big mess
Get and install the latest STM32CubeMX (4.13.0) tool from ST micro. They claim it works on linux now, which is true. From
within the tool, download the most recent Cube HAL firmware for STM32F7 chips (Version 1.3.1).
Install your favourite Makefile based ARM bare metal toolchain. At the time of writing, mine comes from here:
GCC ARM Embedded on launchpad.net. To be able to use several versions
in parallel (depending on the project), I install them (just unpack the downloaded tarball) to /usr/local/gcc-arm-none-eabi-*.
If no particular version is specified in the project Makefile, the makefile framework automatically uses the most recent one
from there, falling back to arm-none-eabi installed to the system.
Get and install a recent openocd, at least a version 0.10.0-dev-snapshot to include STM32F7 support
Use your favourite IDE / text editor. Mine is still Eclipse CDT, but there's more available on the mighty Internet.
Download my example: STM32F746 Nucleo board Makefile example
Untar the examples to an appropriate place
You should now be able to open the STM32Cube project files (e.g. "nucleo-f746-blinky/cube/nucleo-f746-blinky/nucleo-f746-blinky.ioc")
with the latest STM32CubeMX tool. Having installed the appropriate Cube HAL, you should be able to re-generate the code from there. You
don't need to do that right now, everything required to compile is included in the tarball.
If you want to edit the code using Eclipse CDT:
Import the folders "nucleo-f746-*" as Existing Code as Makefile project (each one at a time, as a separate project)
Import the folder "com" as Existing Code as Makefile project
Add "com" to the Project References of the demos
If you like to compile the projects from within eclipse, add "all" as Make target
Just run "make" from the top directory ("nucleo-f746-examples") to compile all of the examples, or cd into one of the example projects
to compile this particular one. Use "make flash" from the project directory to flash the binary into your Nucleo-F746ZG board and watch
the LEDs blinking. Connect to the ST-LinkV2.1 built-in virtual comport at 115200 baud to see the messages sent over the uart.
In case you are testing the LwIP example: connect your nucleo board to your local network. Your network must provide a DHCP server,
I've configured LwIP to use it. Check your DHCP server for the IP address it has given out to the nucleo board. You should be able to
ping the board using this address. There's nothing more provided (no fancy web pages etc.) in my demo. For some more advanced demo projects
using LwIP, look here.
Use "make gdb" to start openocd as gdb server. Note that this target does not include compiling, downloading or flashing. You should
then be able to debug the target using your favourite gdb frontend (e.g. the zylin embedded plugin for eclipse).
I've included the HAL source code as a subdirectory to "com/lib". So do not export the library code from the code generator tool, instead
use the setting "Add neccessary library files as references ...".
Use "SW4STM32" as the target toolchain/IDE
For the ethernet peripheral: be sure to check "Not to be generated" for "MX_ETH_Init" (located at "Project -> Settings ... -> Advanced Settings").
Activate "Generate peripheral initialization as a pair of .c/.h files per IP"
Many linux distributions now offer their own "gcc-arm-none-eabi" packages. Easy to install, but I do not recommend to use them.
The main reason is, you don't know which exact version of arm-none-eabi-gcc you get, and you'll eventually get a newer version installed
when you upgrade your system. This may break your embedded application. There's one major rule in embedded development: never
change your toolchain version. If you need to, do extensive regression testing.
There are some scripts working inside to makefiles to convert the generated code to work together with my modular library approach.
You don't have to manually edit the generated code, and if you didn't, the whole generated code can be overwritten while generating fresh code without
loosing your own code. Your own code is kept completely separated from the generated code.
The only important thing to know is: Rename your own "main()" function to "main1()", since a "main()" function is generated by CubeMX.
A call to main1() is inserted in the generated "main.c"
Additionally, main() in "main.c" is made "__weak", so you could override this by your own main(). In this case, you'll have
to do some initialization by yourself, which is otherwise done by the generated main().
A header containing some #defines of the used resources is generated
The interrupt service routines in "stm32f7xx_it.c" will be declared "__weak", so you can overwrite them with your own handlers, bypassing
the generated ISR code
The LwIP port uses the STM32 MAC driver from ChibiOS HAL and relies on STM32 HAL code for the first time initialization of the
MAC peripheral and its associated pins
Ethernet MAC initialization code "MX_ETH_Init()" must be called after ChibiOS ("chSysInit()") initialization, the systick handler must call
The scripts do not edit the CubeMX generated files, but create new ones using the prefix "cube_".
Starting a new project
Basically, throwing ChibiOS RT and generated STM CubeMX HAL code together results in a big mess. Both have their own styles of
doing startup, vectors, processor initialization, linker scripts and more. I decided to use the ChibiOS startup, vector and linker
script files. Additionally you'll have to include some of the CMSIS and HAL code to make the STM HAL work properly. Beware of
enabling the caches more than once, this will lead to strange and difficult to debug results. My makefiles take care of these
issues, so if everything works as desired (which I cannot warrant for), setting up a new project should be a rather simple task now:
Just copy the appropriate example directory tree
Important: rename the directory under "cube" to exactly match the newly created directory name
Do the same for the "*.ioc" file there, or create a new one using the same file name
(If used) Edit the "simple-bsp" settings in "inc/bsp-conf.h"
(If used) Edit the chibios configuration file "port/chibios/chconf.h"
Place your own source (.c / .h) files to "src/"
Put globally (outside your sources, e.g. library configration files) required headers to "inc/"
Edit the Makefile to match your controller / board / debugger. In these examples, the STM Nucleo-F746ZG board is available, nothing else
Mixing STM HAL, ChibiOS and your own interrupt drivers (as I do in the "nucleo-f746-chibios-bsp1" example) creates a lot of
Double check (e.g. by looking into the .map file) the actually linked interrupt service routines (__weak symbols might be evil)
The HAL Systick ISR won't get called in this example configuration, so any HAL routine using the systick for delay or timeout
will not work properly
As I do not use the ChibiOS HAL right now, ChibiOS tickless mode is not supported (yet)
Double check processor and cache initialization. Not everything set up in CubeMX will be applied, instead the ChibiOS initialization
is used for basic processor and cache setup. RCC (clock tree and PLL initialization) is supposed to be done by CubeMX settings.
Watch your arm-none-eabi-gcc version. Especially considering time critical interrupt service routines, there are differences
in code size and execution time, even using the same -O option. Keep your toolchain for each project constant, do not upgrade
if you have timing or size critical code.
Check for correct handling of DOS / Unix line handlings, sometimes mixing DOS / Unix files results in silent failures.
This happened to me using gawk to create a header.
Watch out: Cache and DMA
STM32F7 D-Cache and DMA don't work well together. Actually, they do not know anything about each other. So it is perfectly possible
to write some data to the RAM, then transfer these data using a DMA channel to some peripheral and the data seen by the peripheral aren't the
data you've just written. The same thing can happen in the other direction, reading from a peripheral register to RAM by DMA. The data seen by
the CPU may be different.
This is a result of the architecture: Cache lines are not updated by DMA transfers and Cache lines may be written delayed to RAM.
For simple tasks, the ChibiOS memory layout takes care of this issue by placing all DATA and BSS into a non-cached RAM block (DTCM). But as
soon as you start to use memory allocated from the heap as DMA buffers, you'll run into strange problems that aren't easy to debug.
One possible workaround would be to completely disable the D-Cache. A more sophisticated way would be to flush or invalidate the cache lines by
the device driver. A better way would be to have some kind of cache snooping protocols in hardware (the STM32F7 doesn't).
Download the full ChibiOS source code here: Chibios Homepage
Get the most recent version of LwIP here: LwIP project homepage
Look here: The STM32 files for my other
STM32 related pages.