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