When developing for the STM32F4, setting up a new project can be a pain. Without some kind of wizard or configuration tool, there are always loads of obscure settings that have to me made each time. The easiest way around this is to make a simple template for the STM32F4 that has all the basic features set up already and then copy or clone it every time you start a new project.

For my STM32F4 development I use Rowley’s Crossworks for ARM. This is a commercial, fully supported environment that is available for non-profit use at a reasonable price. All the other commercial products are either too expensive or too constrained for me. The free environments are just too much like hard work. Ideally, some kind soul would build some equivalent of the Microchip MPLABX environment around Netbeans. Meantime, I need to build my projects in Crossworks.


I have made several attempts at a proper template in the past but always got lazy and messed around with the template until I no longer knew what was what. This time, I decided to create a project and have it managed by the git source control software. Furthermore, I have made it available in a public repository so that others can use it and so that I can easily clone or download a copy as a starter. Upgrading settings or library versions is now trackable and I will be able to go back and check out previous versions if needed.

The starter projects was inspired by the way that the CooCox IDE creates STM32F4 projects. Rather than retain the complicated folder structure used by the ST Standard Peripheral Libraries, it just copies all of the CMSIS headers into one place and all the library files into another. Now there are fewer paths to keep track of and everything is much neater.

Crossworks version

The version of Crossworks used for this project is 2.3 which is current at the time of writing. Crossworks does not use a makefile or linker script for its builds. This is a little annoying and reduces the general usefulness of the starter project for other development environments. Still, I have listed the project settings below so that you can see how everything is configured. The Crossworks project file holds all this information in XML format so that it is easily edited even without using the CrossStudio IDE tools.

The STM32F4 processor

The template is intended to run on the STM32F4Discovery board although the processor is set as the larger STM32F405RG for compatibility with my micromouse. This just gives more memory so it is unlikely to be a big problem in most cases. The STM32F4 devices are all reasonably similar.

The internal oscillator on the STM32F4 is used because I want the project to be more portable and there is no need for any better accuracy in my project. The processor is running at 72MHz as a reasonable compromise between performance and current consumption.

Code is included to set up and blink the four coloured LEDs found on the STM32F4Discovery so that there is an immediate indication that it is running. My board sits on a prototyping board with an extra LED connected to PB.2 which is also the BOOT1 pin. Unless you want to boot your processor to RAM, this pin can always be held low and an LED does that nicely. As that pin has no other function that I want, I use the LED as a status indicator. On the micromouse, it is turned on at the start of every systick event and turned off at the end. That way the brightness of the LED serves as a guide to system load and if it is ever on (or off) continuously, I know the code has crashed. Here I just toggle the LED at each tick. A global boolean, crashed, lets me override the status LED behaviour and will be useful elsewhere in the micromouse code.


The DEBUG configuration (only) has USE_FULL_ASSERT defined and the Rowley cross_studio_io.h header is included. There is also a software timing-loop delay function that can count off microseconds. With these features, I can make full use of the ASSERT macros and, when there is an ASSERT failure, a message is printed to the debug console and the status LED blinks distinctively – even if systick is still trying to flash it normally. In fact, if the target is not actively being debugged, the debug_printf() function seems not to return so this could do with some more thought

Inside the systick event, a millisecond counter is maintained – to be used by a millisecond delay function, delay_ms(). This is a 32-bit counter so it will not overflow for over 1000 hours. Plenty for my purposes.

By default, Crossworks eliminates unused code so it is safe to allow all the libraries to be built. Only the functions used by the program will be included in the binary image downloaded to the target.

The stack for the STM32F4 is set to a size of 2048. The default stack size in Crossworks is only 128 bytes which is pretty small. These STM32F4 devices have a lot of available memory so there is no reason to be mean. As a generally useful extra check, the preprocessor define INITIALIZE_STACK ensures that the startup code fills the stack with the value 0xCC so that we can always look and see how much of the stack has been used when debugging.

I have also set a heap of 4096 bytes. Just in case I want one. Again, there is plenty of space.


The preprocessor define USE_STDPERIPH_DRIVER is used by the CMSIS and library code so it is included for correct behaviour.

Another Rowley feature is the preprocessor define STARTUP_FROM_RESET. This is needed in any production code or the startup routines will sit in an endless loop, waiting for the debugger to connect. I define it for both debug and release configurations just because it annoys me otherwise. However, doing do may be the reason why, sometimes, I am unable to get easy control of the target. I cannot help but feel this is a bit of a hack but I am sure there is good reason for it.

Floating Point

Since the STM32F4 devices have a hardware FPU unit, that is turned on and active. It would be a shame not to use it or, worse, to forget to turn it on and wonder why the code ran so poorly.

On the subject of floating point, I have enabled the use of floating point in printf(). While many frown upon printf() and its related functions for their bloat and slowness, this is a fast processor with plenty of code memory. You can easily afford the convenience that standard I/O functions can bring to the program. Note that printf() is useless without a way to get characters off the chip but that is a problem for another post. This template does not set up the USART. By the time you read this, I may have sorted that out. The template now has initialisation for USART1 so that printf() will work. As will getchar(), putchar() and others.

Code formatting

I like my code to be kept reasonably neat and tidy so I have a copy of astyle installed on my desktop machine. Crossworks allows an external executable to be called at several stages during the build. I call astyle just before the link. It runs through all the files and reformats them according to my preferred style. This is very handy when you are using a source control system as it is pretty annotying to have the repository filled with whitespace changes. So long as I have completed a build just before a commit (and why wouldn’t you), then the code will be neat and tidy. Of course, you probably do not have astyle installed so you will want to remove this option. Sorry for your loss. The command is held in the linker_pre_build_command configuration variable and the line entered is:

/usr/local/bin/astyle “$(ProjectDir)/*.h” “$(ProjectDir)/*.c”

For some reason, Crossworks does not assume anything at all about project include directories and will not be able to find header files even if they are in the same folder as the source files. To make all that happy, the following three paths are added to the user include directories:


Get Your Own Copy

To get a copy of the complete project, you can clone or download it from github:


Let me know if you find any serious errors.

There is more about the USART on the STM32 processors here:


There should be only minor differences between the USART on the STM32F4xx and on the STM32F1xx processors.

This Post Has 9 Comments

  1. Green Ye

    Thanks for sharing, Peter, very well done!
    I had a pain when I was establishing my first template for STM32 project on Keil. As long as we get job done for the first time, I guess it will be easy and repeatable for the rest of the times. I feel F4 has less files needed to add than F103 since ST simplified it’s library, however, there are still less tutorial online to create a template for F4 instead of F103. The template from official library wasn’t that friendly to use if you want do some modification.
    I have question about the way you did for delay_us(). Why did you use 24 counts per us? Since you mixed with assembly, and I am not sure how it works even though I know assembly, Is it same with the way I used here? http://micromouseusa.com/?p=296 by reading the timer counts in systick for microseconds?
    which file did you put the setup for FPU?
    thank you.

  2. Peter Harrison

    The delay_us() function is a purely software busy-wait loop. The number 24 is just the empirically determined value needed to get close to the right timing.

    Although software timing loops are pretty useless if the system clock changes, I still like to have one available. I can use it even inside an interrupt routine if I want a short but fairly accurate delay.

    It would probably be better to use a hardware counter overall but this is simple and easy. My favourites.

    The FPU setup is done in the Crossworks project file. I did not include in this project the proprietary Rowley startup and C runtime files. The actual activation of the FPU is done in their startup file.

  3. Green

    Oh, I get, the reason you get the wait loop still being accurate for delay_us is because you wrote it in assembly. I’ve heard people was trying to write this in C, ended up being so inaccurate since the compiler “optimize” the code for you which is not exactly you wanted.
    I guess the setting for Keil will be different for FPU, thanks for you update.

  4. Sanjay Mishra

    Thanks! This is very helpful

  5. Jason M

    This is an absolute stroke of genius. CrossStudio tends to want you to use “their” version of CMSIS and the ST std_peripheral_library. I tried to install the official version with no success, used your template and the problem is solved. With this template its a snap to update CMSIS and the ST peripheral library, no need to “install packages” with the risk that it will break, as has already happened.

    I struggle because of so many IDEs in my line of work and trying to keep track of how each works is a challenge in itself. Such is life when you need to know how to code for so many different microcontrollers.

  6. Marzi

    Could you please tell me how you change the stack size? I already have problem with that

  7. Peter Harrison

    In Crossworks, if you look in the project properties, there is a Stack Size setting. Simply change that to whatever you like.

  8. Nirmala

    Thanks Peter for putting out the template.
    I am new to Crossworks and trying to understand where is the clock configuration done. Things like telling MCU to use internal/external clock source for PLL and other PLLx values to be set to achieve required sysclk.

    All the examples I have seen just start using clock values using RCC_GetClocksFreq, without configuring the clocks. I can not figure out where is the configuration happening.

  9. Peter Harrison


    Unfortunately, it has been quite a log time since I used Crossworks so I forget exactly how the clock setup is done there.

    Clock configuration is normally done in the file system_stm32f4xx.c. If you generate the basic project files with the STM32CubeIDE, it will write al this code for you.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.