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


Project motivation

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).

    Some hints

  • 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.
  • Some internals

    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 "HAL_IncTick()".
  • 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
  • Caveats

    Mixing STM HAL, ChibiOS and your own interrupt drivers (as I do in the "nucleo-f746-chibios-bsp1" example) creates a lot of pitfalls:
  • 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).

    Links

    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.
    Table of contents ... und ein Zaehlpixel hab ich auch :-)