stm32f4discovery: Up and running with the ARM Cortex M4

The stm32f4discovery is the ARM Cortex M4 evaluation board from ST Microelectronics. I’ve been following the progress of the Cortex M4 since its launch, looking for the right time to dip in give the new MCU a test drive.

That opportunity came with the launch of the stm32f4discovery board. I got mine from Farnell Electronics where, at the time of writing, they are on sale for £9.96 + VAT.

For just over a tenner you get rather a lot on board. To quote the good bits from the ST blurb:

  • 168Mhz STM32F407VGT6 microcontroller featuring 32-bit ARM Cortex-M4F core, 1 MB Flash, 192 KB RAM in an LQFP100 package
  • On-board ST-LINK/V2 with selection mode switch to use the kit as a standalone ST-LINK/V2 (with SWD connector for programming and debugging). Not sure about this one, JTAG is my tried and trusted debugging method, we’ll have to see what we can do with this.
  • LIS302DL, ST MEMS motion sensor, 3-axis digital output accelerometer
  • MP45DT02, ST MEMS audio sensor, omni-directional digital microphone
  • CS43L22, audio DAC with integrated class D speaker driver
  • Four user LEDs, LD3 (orange), LD4 (green), LD5 (red) and LD6 (blue)
  • Two push buttons (user and reset)
  • USB OTG FS with micro-AB connector
  • Extension header for all LQFP100 I/Os for quick connection to prototyping board and easy probing

Setting up a free toolchain

This is going to be explained from the point-of-view of a windows user. Linux users should find the procedure broadly similar.

Here’s my workflow. I’m going to walk through the steps required to set up the whole thing.


I do all my project editing in Eclipse. The current version of Eclipse is Indigo and it’s now fully compatible with the ARM toolchain plugin. Get Eclipse from here and make sure you download the edition for C++ developers (CDT).

The GNU ARM Eclipse plugin

Before you can get going with Eclipse you’re going to need the GNU ARM Eclipse plugin. Here’s the official site with instructions about how to install it.

Sourcery Codebench Lite Edition

This used to be called CodeSourcery but evidently they’ve been bought by Mentor Graphics and some re-branding has been going on.

Go to their site and download the latest EABI lite edition. This is the GNU GCC package set up for ARM cross-compilation.

stm32f4 peripheral library

It’s possible to develop applications for the STM32 without the peripheral library, and many people do just that. Me, I rather like it and clearly ST Microelectronics have put significant effort into making it work well. Click here to download the 31.5Mb package from the ST Microelectronics site.

Unzip the package and you’ll find the library in the LibrariesSTM32F4xx_StdPeriph_Driver folder.

CMSIS library

This one is supplied by ARM. It gives you functions for accessing the basics of the Cortex M4 that are the same across all manufacturers. It goes hand in hand with the standard peripheral library. You can find it in the same firmware zip as the standard peripheral library in the LibrariesCMSIS folder.

I make the following changes to CMSIS before using it.

  1. Exclude all of STSTM32F4xxSourceTemplates from any build. This folder contains example startup assembly code for different compilers. Instead of building this into CMSIS I take a copy of the correct startup source (the one in the TrueSTUDIO folder) and I add it to my project.
  2. I prepend the following line to CMSISSTSTM32F4xxIncludestm32f4xx.h:

    #define assert_param(a)((void)0)

    This has the effect of disabling parameter validity checking in the peripheral library, something which I don’t want.

Atollic true stdio lite

The lite edition of this commercial package is available for free download. There are a couple of useful items in this package that we’ll take advantage of to get up and running. The linker (.ld) script will help us create a runnable image of our project and the GDB server will arbitrate our debug sessions between Eclipse and the ST-Link hardware.

Test Application

With all the tools in place I’m all set to create a test application. I decided to dive straight in and try driving one of the little Nokia 2730 QVGA LCDs that I’ve been tinkering with of late. After extracting the code from stm32plus and porting it to the F4 firmware library I came up with the following test code:

* This file is a part of the open source stm32plus library.
* Copyright (c) 2012 Andy Brown <>
* Please see website for licensing terms.

#include <cstdlib>
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_fsmc.h"

#define NOKIA_6300
#undef NOKIA_2730

// initialisation

static void initSystick();
static void initGPIO();
static void initFSMC();
static void initLCD();
static void checkWiring();

// demo

static void showDemo();
static void lineDemo();
static void rectDemo();

// timing

static void delayMillis(uint32_t timedelay);
static volatile uint32_t millisecondCounter;

// fsmc access

static void writeCommand(uint16_t command);
static void writeCommand(uint16_t command,uint16_t parameter);
static void writeData(uint16_t data);

static volatile uint16_t *fsmcData=reinterpret_cast<volatile uint16_t *>(0x60020000);
static volatile uint16_t *fsmcRegister=reinterpret_cast<volatile uint16_t *>(0x60000000);

// graphics operations

static void beginWriting();
static void moveTo(int16_t x1,int16_t y1,int16_t x2=239,int16_t y2=319);
static void clearScreen(uint8_t r,uint8_t g,uint8_t b);
static void drawLine(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t r,uint8_t g,uint8_t b);
static void plotPoint(int16_t x,int16_t y,uint8_t r,uint8_t g,uint8_t b);
static void fillRectangle(int16_t x,int16_t y,int16_t width,int16_t height,uint8_t r,uint8_t g,uint8_t b);

* The command set as documented in v0.99 of the MC2PA8201 datasheet

enum E {

* Main entry point

int main(void) {

  // initialisations


  // run the demo


  // not reached
  return 0;

* Check wiring by reading the device code

void checkWiring() {

  uint8_t data[4];
  int i;



  // check for the correct response and hangup if it's
  // not there

  if(data[1]!=0x54 || data[2]!=0x80)

* Run a basic demo

void showDemo() {

  for(;;) {



* Clear down the screen

void clearScreen(uint8_t r,uint8_t g,uint8_t b) {

  int32_t i;


  while(i--) {

* Run a demo drawing random coloured lines

void lineDemo() {

  int16_t x1,y1,x2,y2;
  int i;
  uint32_t cr;

  for(i=0;i<5000;i++) {
    x1=rand() % 240;
    y1=rand() % 320;
    x2=rand() % 240;
    y2=rand() % 320;

    drawLine(x1,y1,x2,y2,(cr >> 16) & 0xfc,(cr >> 8 ) & 0xfc,cr & 0xfc);

* Show a rectangle demo

void rectDemo() {

  int i;
  int16_t x,y,w,h;
  uint32_t cr;

  for(i=0;i<2000;i++) {

    x=(rand() % 240/2);
    y=(rand() % 320/2);
    w=rand() % (240-x);
    h=rand() % (320-y);

    fillRectangle(x,y,w,h,(cr >> 16) & 0xfc,(cr >> 8 ) & 0xfc,cr & 0xfc);

* Draw a line in the requested colour

void drawLine(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t r,uint8_t g,uint8_t b) {

  bool yLonger=false;
  int incrementVal,endVal;
  int shortLen=y2-y1;
  int longLen=x2-x1;

  if(abs(shortLen) > abs(longLen)) {
    int swap=shortLen;


  if(longLen < 0) {
  } else {

  int decInc;

  if(longLen == 0)
    decInc=(shortLen << 16) / longLen;

  int j=0;

  if(yLonger) {
    for(int i=0;i != endVal;i+=incrementVal) {
      plotPoint(x1 + (j >> 16),y1 + i,r,g,b);
  } else {
    for(int i=0;i != endVal;i+=incrementVal) {
      plotPoint(x1 + i,y1 + (j >> 16),r,g,b);


* plot a single point

void plotPoint(int16_t x,int16_t y,uint8_t r,uint8_t g,uint8_t b) {

  // move to the point and start writing


  // we set it up in BGR colour order


* Fill a rectangle

void fillRectangle(int16_t x,int16_t y,int16_t width,int16_t height,uint8_t r,uint8_t g,uint8_t b) {

  int32_t count;

  // number of pixels to fill


  // set the display window and prepare for writing


  // blit the pixels

  while(count--) {

* Begin writing

void beginWriting() {

* Move to pixel position (or rectangle if x2 and y2 are specified)

void moveTo(int16_t x1,int16_t y1,int16_t x2,int16_t y2) {


  writeData(0);								// x=0..239


  writeData(y1 >> 8);					// y=0..319
  writeData(y1 & 0xff);
  writeData(y2 >> 8);
  writeData(y2 & 0xff);

* Reset and initialise the LCD

void initLCD() {

  // reset


  // init sequence


  writeCommand(MEMORY_ACCESS_CONTROL,0xc8);			// BGR

  // wait


  // turn the display on


  // select 262K colour mode


* Write to the LCD

void writeCommand(uint16_t reg,uint16_t data) {

void writeCommand(uint16_t reg) {

void writeData(uint16_t data) {

* Initialise GPIO ports D and E for FSMC use
* Also initialise PE1 for RESET
* RS will be on A16

void initGPIO() {

  GPIO_InitTypeDef init={0};


  // reset



  GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);			// D2
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);			// D3
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);			// NOE -> RD
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);			// NWE -> WR
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource7, GPIO_AF_FSMC);			// NE1 -> CS
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC);			// D13
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC);			// D14
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC);		// D15
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);		// A16 -> RS
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);		// D0
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);		// D1

  // PORTE

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FSMC);			// D4
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FSMC);			// D5
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FSMC);			// D6
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FSMC);		// D7
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FSMC);		// D8
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FSMC);		// D9
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FSMC);		// D10
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FSMC);		// D11
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FSMC);		// D12

  init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 |
                                  GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |
                                  GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15;
  init.GPIO_Mode = GPIO_Mode_AF;
  init.GPIO_Speed = GPIO_Speed_100MHz;
  init.GPIO_OType = GPIO_OType_PP;
  init.GPIO_PuPd  = GPIO_PuPd_NOPULL;

  GPIO_Init(GPIOD, &init);

  // PORTE
  init.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |
                                  GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 |
  init.GPIO_Mode = GPIO_Mode_AF;
  init.GPIO_Speed = GPIO_Speed_100MHz;
  init.GPIO_OType = GPIO_OType_PP;
  init.GPIO_PuPd  = GPIO_PuPd_NOPULL;

  GPIO_Init(GPIOE, &init);

* Initialise NOR/SRAM bank 1

void initFSMC() {

  FSMC_NORSRAMTimingInitTypeDef timing={0};
  FSMC_NORSRAMInitTypeDef init={0};



  // initialise how the FSMC will work and then enable it



* start counting millis

void initSystick() {

* Delay for the given milliseconds

void delayMillis(uint32_t millis) {
  uint32_t target;


* SysTick interrupt handler

extern "C" {
  void SysTick_Handler(void) {

This example application will run a continuous loop showing random coloured lines being drawn followed by random sized and coloured rectangles.

The test application

Flashing and debugging the compiled code

Traditionally this is the part that causes the most trouble for developers, probably because it’s the first time in the development cycle that we have to work with the actual hardware.

The stm32f4discovery board comes with ST’s proprietary debugging hardware ST-Link built in. ST-Link offers the advantage of using far fewer pins than the traditional JTAG connector but the downside is that the Serial Wire Debug (SWD) protocol that it uses does not enjoy the same support as JTAG in the open source community.

OpenOCD ST-Link support is not quite there

I checked out the latest openocd source code, compiled it up and tested it against the stm32f4discovery board. Within a few minutes I was able to use openocd to flash my code to the board and it looked like debugging was working too.

First startup is encouraging. The debugger flashes the image and connects for a debug session.

Unfortunately debugging was not working well enough for me to consider it as suitable for regular development. After reset I could connect Eclipse to openocd and a debug session was established.

Unfortunately it all too often ends up like this.

Sadly though, this only lasted for the single session. When I made changes to my code and reflashed it was as though the debugger did not see the code change and the program crashed. The only way to get it working again was to power-cycle the board and restart openocd. Having to do this each time I want to debug is not good enough, so I had to try another option.

Atollic GDB server

This one worked better than openocd in that I can do multiple repeated debug sessions without having to power-cycle the board. However I do have to Ctrl-C and restart the server before each debug session. This isn’t ideal but it’s just a minor inconvenience and doesn’t stand in the way of productive development.

The GDB server in the Atollic distribution is almost ready to use out of the box. Here’s how to get it going.

  1. Make sure your board is connected via the USB port and that the ST-Link drivers have been installed so that it shows up as a named device in your computer’s device manager.
  2. Fire up cygwin or a command prompt and change directory to your PC’s equivalent of C:\Program Files (x86)\Atollic\TrueSTUDIO for ARM Lite 3.0.0\Servers\ST-LINK_gdbserver
  3. Edit config.txt and towards the bottom of the file make sure the ‘-d’ option is enabled. By default it’s documented in there but not enabled. The -d option enables the SWD protocol. Without it the gdb server will not see the board.
  4. Fire up the server with ST-LINK_gdbserver.exe -c config.txt

If you got it right then the server should now be waiting for commands from Eclipse.

Debugging with Eclipse

The Eclipse hardware debugging facility gives you the ability to visually step through the code running on the device, set breakpoints and inspect variables and memory. Absolutely vital in my opinion and something that anyone who develops on a full size computer will expect as a matter of course.

When creating a debug launch configuration you need to specify a valid initialisation sequence. Here’s mine:

target remote localhost:61234
monitor reset init
monitor debug_level 2
monitor soft_reset_halt
set mem inaccessible-by-default off
monitor debug_level 0
file c:/users/andy/src/stm32f407test/Debug/stm32f407test.elf
monitor reset init

Watch the demo video

Here’s a demo giving a brief tour around the development board. The main components are highlighted and you can see the Nokia LCD getting a workout.

stm32plus for the Cortex m4?

Yes, at long last it’s here.

  • Yuri

    Just a question ……does this board actually have the processor working at 8Mhz or is it at 168Mhz…cos the crystal is 8Mhz

    • It's 168Mhz. If you download the standard peripheral library source code and look at system_stm32f4xx.c you'll see the initialisation code that sets up the system of multipliers and dividers used to initialise the core clock from the external crystal source.

  • Xuiop

    Great post… thanks !

  • Exactly the article I was looking for – have the same dev board & looking to do screens.

    I was looking to buy a screen to use with your stm32plus library but I guess now I have to go with Nokia 2730 LCD. What is this board under the screen you use for pin out?

    • It's this one:

      I don't have any boards left for the 2730 but I do have some bare dev board PCBs for the 6300 which is basically the same controller but with a different pinout. Drop me a line using my "Contact" form if you're interested and I'll mail you a few photos so you can decide.

      By the way, stm32plus is not STM32F4 compatible yet. There are significant differences to the ST firmware library…

  • francesco

    thanks for the toutorial. I find it complete for the part on the availability of the material.
    I tried to follow her but but I have some difficulty in installing packages.
    Since you have used this time it would be wonderful to complete the work by indicating the steps for installation.
    thanks Francesco

  • Aleksandr

    Thanks for the tutorial.
    I install Atollic true stdio lite v3.2.0, Debugging with Eclipse not work with this ST-LINK_gdbserver ((
    Please send me ST-LINK_gdbserver from version 3.0.0 or 3.1.0 if this works.
    P.S.: Register Lite version not works for old version.

  • david simmonett

    Brilliant work
    Can you help me understand why you use c++ and not C?
    Thanks Dave

  • Mark

    Any chance you'd be willing to share your startup code and linker script, along with the machinery necessary to get C++ working (syscalls, library hacks, etc, assuming they're different than the standard stm32plus ones)?

    • Sure no problem. You can download my entire Eclipse project from here.

      You may have to fix up a few pathnames to get a clean compile and any compiler/linker references to "stm32f4peripheral" are references to ST's standard peripheral library that I keep in a separate project.

      • Hi, I tried to run build your example but with no sucess.
        I'm using:
        -Kubuntu (ubuntu)
        -Eclipse IDE for C/C++ Developers JUNO
        -Sourcery Codebench Lite Edition
        -GNU ARM Eclipse Plug-in.

        I insert some images in the following google docs to illustrate what I've done, it keeps giving an make error.

        Can you make an article explaining that steps? why we need the GNU ARM Eclipse plugin, why we need each librarie.
        -The GNU ARM Eclipse plugin
        -Sourcery Codebench Lite Edition
        -stm32f4 peripheral library
        -CMSIS library

        I'm learning a lot with your blog 🙂

        PS: there are a great tool to use with ST-LINK and how to use here

        • PS2: the doc

          • It would appear to be a lack of the ST peripheral library in the location where it's expected – you can tell from the warnings where it wants the library to be and you can get the library from ST.

            Annoyingly, and not for the first time, ST have broken all the links into their website. I'm going to have to go through my articles and reset all the hyperlinks.

          • forgot to change the includes path for g++ :s
            Now I'm getting a make error:

            PS: new link for ST peripheral library:

          • can you help me with that 🙂

          • Do you have the GNU Arm plugin installed and was this project created like this:

            File -> New C++ Project -> ARM Cross Target Application (Empty Project) -> ARM Windows GCC (Sourcery G++ Lite)

            Not having a rule to build the .ELF file sounds like the toolchain is not configured within the project indicating that the project was not created as an ARM Cross Target Application.

        • Liviu Ionescu (ilg)

          Please note that recently the GNU ARM Eclipse Plug-in was completely redesigned and a new version (1.x) is available.

          It includes not only an improved interface, but also functional project templates, including a template for STM32Fx projects.

          I would be interested to know your comments on the new version.

  • mat03

    I try use gdb in eclipse but I only see:
    symbol-file D:\thundercyer-the-alarm-clock\Debug\ThunderCryer.elf
    load D:\thundercyer-the-alarm-clock\Debug\ThunderCryer.elf
    You can't do that when your target is `None'
    tbreak main
    Cannot access memory at address 0x801dc1e
    jump ResetHandler
    The program is not being run.

  • I was looking to buy a screen to use with your stm32plus library

  • Matt

    Have you ever tried using texane's stlink-master in cygwin for debugging sessions?

    • Hi Matt, no I haven't tried it. Since I wrote this article OpenOCD (now version 0.6.1) has become stable with its ST-Link Support. I can now debug all day long with OpenOCD on the F4 discovery board without ever having to manually intervene with resets or power on/off cycles.