Cortex-M bring up. Post#2: this time with simple software infrastructure

I have found that basic example code from MCU suppliers is often too complex. Vendors want to write one example for their entire product range, so you often end up with a spaghetti of code and linker script that weaves in CMSIS, USB, drivers for fancy peripherals and whatever else. Even if you wanted to strip it down to basics, you would have to deal with multiple file dependencies in a web of directories that make the job harder than it should be.

For new board bring up, once I have shown sign of life using low level JTAG (see this post), the next thing I like is the simplest possible software set-up that I can get working.

Nice theory. Where do I download some code? I was surprised at how much I had to search to find anything useful. So this post has a simple example I made for a new STM32F100C8 board. It should work or at least help for many CortexM0 or M3 MCUs.

In the below diagram, the pieces of code we need to supply for this simple approach are in blue:

Screen Shot 2014-08-11 at 9.09.39 PM

There are countless write ups on the anatomy of makefiles, link scripts and startup code which I am not going to duplicate that here. Instead I’ll just comment on some key pieces of my solution that you can download from GitHub here.


Even though Eclipse offers automatically generated makefiles, I like to write my own for better control. It’s great to be able to switch in/out various libraries, symbol and map analysis tools etc.


In the above:

  • $(PATH) is the file path to your GNU tools install. Set it in a declaration like PATH = /my/path/
  • STM32F100 is a Cortex-M3 MCU. Modify to Cortex-M0 as appropriate
  • nostartfiles enables standalone code by stopping the linker from bringing in C library code such as malloc. This makes for smaller code size.
  • The last line calls a GNU utility nm which lists all the symbols in the executable. Really useful.


I define the memory map for the STM32F100C8 with the correct flash and RAM sizes along with the top of stack location.

Since the main() code that follows is so simple that there are no constants or variables, there are no data sections in the link script. Just the bare minimum .text to place the program code is all that is needed. Don’t try and use this for a real application…

The .vectors come first to make sure that the stack and reset vector locations are placed as required for Cortex-M3 at the bottom of the memory map at zero hex:



With a Cortex-M series device, the bare minimum startup is just to set the reset vector and stack pointer and then branch to main. By ignoring vectors such as NMI and HardFault there would be trouble in normal applications….but we can get away with it here. Also since there are no constants or zero initialized data in main(), no need either for the traditional copying of data or zero initialized variables from Flash to RAM.

In the below, there isn’t even any assembly. Simply placeholders for the stack address set in the link script and the reset vector branch to main:


main.c and STM32F.h

Sometimes MCU clocking will default to an internal RC oscillator at some predetermined frequency on power up. This is useful since there is no need even to have an external crystal working nor to write code to set up an internal PLL system. This is the case for the STM32F100. In the Reset Clock Controller block, there is a way to route this internal RC clock directly to a pin. This is a simple method to provide an output signal “sign of life” from the MCU. I also show the more traditional toggling of a GPIO pin:

Set up the project in Eclipse

Two items worth pointing out:

(a) When setting up the new project under Eclipse, you need to select “new Makefile project with existing code:

Screen Shot 2014-08-11 at 11.52.17 PM

(b) There is only one project setting that needs to be made in the Eclipse GUI is the Makefile location. This is a hidden Eclipse “feature”. Highlight the project name in Project Explorer like this:

Screen Shot 2014-08-11 at 11.50.00 PM

Then go into Project:Properties: and highlight the C/C++ Build line item.

In the Builder group box, deselect “Use default build command” and instead supply the name of your Makefile like this: make -f Makefile

Screen Shot 2014-08-11 at 11.50.38 PM


Build and run

All being well you should see an 8MHz output on pin 29 (on the 48pin LQFP package):



You May Also Like

About the Author: Liam

Leave a Reply

Your email address will not be published. Required fields are marked *