Saturday, September 1, 2012

STM32F4 Discovery Quick Start Guide

I finally got my hands on an STM32F4 Discovery board, those are super cheap STM32F407 ARM Cortex-M4 boards from ST. The Cortex-M4 is perfect for DSP applications as it has SIMD and an FPU and it also runs at 168Mhz. As for the board, it seems okay given its price, I really hate the headers though and it only has a few components namely, an audio DAC, a chip microphone, an accelerometer and a few LEDs.


There's also an STLINK debugger on board, which means you don't need anything else to program and debug the chip. This is a quick tutorial on how to setup a toolchain and the necessary software.

Setting up a toolchain
First thing you need to do is build or install a toolchain, I couldn't get myself to use any of the toolchains/IDEs that work with the board, as they are all propriety and they all have some sort of limitations on the free editions.

There's always the CodeSourcry toolchain, while it works fine, however, it was not built with hard-float enabled only soft and softfp, which means you have a choice between slow FP and really slow FP emulation. I also tried the summon-arm-toolchain, but it seems to have the same problem no hard-float...

I then accidentally stumbled upon a gcc toolchain which seems to be maintained by ARM and it supports soft, softfp and hard-float, I think I will be using this toolchain for every ARM Cortex I have from now on, anyway, now that you have a working toolchain you can start compiling programs but you still need drivers.

Building the drivers library:
ST provides a package that includes drivers for all the peripherals, a DSP library, API documentation and lots of examples, if you look at the examples you will see that you have to copy all the *.c files you need to your project directory along with the linker script, system initialization and startup code, personally, I like to build a monolithic library and just link with that, so you can skip this step if you want:

First download and extract the STM32F4 DSP and standard peripherals Library then copy the startup and initialization code to the src directory:
$ cp Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c Libraries/STM32F4xx_StdPeriph_Driver/src/ 
$ cp Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s Libraries/STM32F4xx_StdPeriph_Driver/src/
Before you build the library, note that most of the drivers use a macro called assert_param, this macro is defined in stm32fxx_conf.h when you build a project you can enable/disable assertions, but since we're compiling this into a library the macro has to be seen at compile time, otherwise the compiler will just assume it's an external symbol and you'll get undefined references later, so you need to add this macro to the stm32f4xx header
#Libraries/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h
#ifdef  USE_FULL_ASSERT
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */
if you #define USE_FULL_ASSERT it will enable assertions or you can compile two libraries one for normal use and the other for debugging.

Finally, copy this Makefile into the src directory and compile the library, I'm assuming here that you have the new toolchain in the path:
#Libraries/STM32F4xx_StdPeriph_Driver/src/Makefile
LIB     =  libcm4.a 
SRCS    = $(wildcard *.c) 
LIB_OBJS= $(SRCS:.c=.o)
AFLAGS  = -mcpu=cortex-m4 -mthumb -mthumb-interwork -mlittle-endian -mfloat-abi=hard -mfpu=fpv4-sp-d16 
CFLAGS  = -mcpu=cortex-m4 -mthumb -mthumb-interwork -mlittle-endian -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O2 \
-I../inc -I../../CMSIS/Include/ -I../../CMSIS/Device/ST/STM32F4xx/Include/

CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
AR = arm-none-eabi-ar

all:: $(LIB)

$(LIB): $(LIB_OBJS)
    $(AR) -r $(LIB) $(LIB_OBJS)
    echo $(LIB_OBJ)

clean:
    $(RM) *.o *.a

.c.o :
    $(CC) $(CFLAGS) -c $<

.s.o :
    $(AS) $(AFLAGS) $< -o $@
When it's done, copy all the headers (including the CMSIS headers) and the library to somewhere like /opt or /usr/local. There's just one last piece of the puzzle missing, the linker script, you can find one in the many templates included with the library or use the one here, it also includes a Makefile template.

Blinky... Well not exactly!
To keep this simple, this example just lights up one of the LEDs on the board

#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
GPIO_InitTypeDef  GPIO_InitStructure;
int main(void)
{
    /* GPIOG Periph clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    /* Configure PD12 in output mode */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* Set PD12 high */
    GPIO_SetBits(GPIOD, GPIO_Pin_12);

    /* Do nothing */
    while (1) {
    }
}
Programming and debugging
There's no "official" stlink utility for Linux, so I used this one instead:
$ git clone https://github.com/texane/stlink.git
$ cd stlink
$ ./autogen.sh
$ ./configure
$ make
This will build two binaries, the st-util a gdb server, and st-flash which is supposed to let you write the binary to flash but it doesn't really work! anyway, the project includes a udev rules, you should use that instead of sudo each time:
$ cp 49-stlinkv2.rules /etc/udev/rules.d/
$ udevadm control --reload-rules

Now just run st-util and in another terminal run gdb:
$ arm-none-eabi-gdb blinky.elf
$ tar ext :4242
$ load
$ continue

Notes
Take care not to remap the USB pins used by the STLINK debugger, if you do so the debugger won't be able to talk to the chip and you could actually brick the board :) if you do find yourself in this situation, pull the BOOT0 pin high, there's a VDD pin right next to it so just place a jumper on those pins, when you do that, the board will run the bootloader instead and you can erase the entire flash.

The library is configured for a 25MHz oscillator and the Discovery board has an 8MHz oscillator, you will need to change two values to fix that, the HSE_VALUE in stm32f4xx.h set that to (8000000) and the PLL_M in system_stm32f4xx.c to (8).

6 comments:

  1. Nice writeup! I've had one of these for awhile -- seems I keep adding MCUs to the pile faster than I get around to learning them.

    ReplyDelete
    Replies
    1. Thanks, this one is really worth learning, it has some nice features especially the DCMI and DSP which open up lots of possibilities. The down side is that the libraries are much more difficult to use compared to NXP libraries, you need a lot of code to get something running.

      Delete
  2. Using a static library is perfect solution but there is not really much people talking about it in the blogs. It save you a lot of compile time :)

    I am also trying to compile standart library in eclipse. I put the full_assert code block to stm32f4xx header as you said. Library compiles both with assert and without assert, no problem. In my real project I include the library and compile as well, until I do not set assert. In this case chip is programming and working. But if I try to debug everything locks.

    If I try to compile the project with assert enabled then it is not compiling and gives the error:

    (very long path)/src/stm32f4xx_usart.c:187: undefined reference to `assert_failed'

    I really did not understand why giving this error.

    ReplyDelete
    Replies
    1. Hi, sorry for the late response, I'm not sure why it locks when you try to debug, I've debugged my code a lot with this setup, but you need to define assert_failed to do something useful (like blink leds in a loop) in your project.

      Delete
  3. I recently got this board and have a few dumb questions I would like to ask.
    I need to give the board power supply via some battery etc (not the laptop), for a while I had been giving nearly 5V to the VDD pin and the board worked fine...recently I came across a few documents where I learned that Vdd range is for 0-3.6V.
    Which pin is for power supply input?
    What is the purpose of Vdd?
    Is there any pin for analog reference voltage?

    ReplyDelete
    Replies
    1. I can't really remember, I think it's for regulated input/output, take a look at the schematics.

      Delete