Seabright Technology Header Image

Hello STM32

The  STM32 is a decent chip but, unlike the  AVR or traditional microcontrollers, fairly hard to get up and going. There's quite a bit of code out there using the  ST standard libraries, non-free software, or bigger systems like  eLua or  FreeRTOS, but I prefer things closer to the metal.

This example includes a Makefile, startup script, linker script, and simple 'hello' example that sets up the basic hardware enough to flash a LED and write some characters out the serial port.

Want to get going? Download,  CodeSourcery's Lite toolchain, and give it a 'make'.


# Makefile for building basic STM32 projects
# Michael Hope <>, 2010

# Using the CodeSourcery 2009q3 Lite ARM EABI tools
CROSS_COMPILE = arm-none-eabi-

# Name of the startup file.  'hd' for high density
STARTUP = startup_stm32f10x_hd.s

CFLAGS = -mcpu=cortex-m3 -mthumb -O -g

# List of all binaries to build
all: hello.bin

# Create a raw binary file from the ELF version
hello.bin: hello.elf
        $(OBJCOPY) -O binary $< $@

# Create the ELF version by mixing together the startup file,
# application, and linker file
hello.elf: $(STARTUP) hello.c
        $(CC) -o $@ $(CFLAGS) -nostartfiles -Wl,-Tstm32.ld $^

# Program the binary to the board using the builtin serial bootloader
program: -p /dev/ttyUSB0 -ewv hello.bin

# Remove the temporary files
        rm -f *.o *.elf *.bin

The Makefile is nice and simple and skips most of the intermediate steps that others do. The flow is (startup.s & hello.c & stm32.ld) -> hello.elf -> hello.bin.


/// A very basic STM32 demo that runs of the internal oscillator,
/// flashes a LED, and writes out some serial data.
/// Michael Hope <>, 2010
#include "stm32f10x.h"

/// Spin delay
void delay(int count)
    // volatile so that the compiler doesn't optimise it out
    volatile int i;

    for (i = 0; i < 100000; i++)

/// Main function.  Called by the startup code.
int main(void)
    // Most of the peripherals are connected to APB2.  Turn on the
    // clocks for the interesting peripherals
    RCC->APB2ENR = 0
        // Turn on USART1
        // Turn on IO Port A
        // Turn on IO Port B
        // Turn on the alternate function block

    // Put Port A pins 8 through 15 into alternate function/50 MHz
    // mode.

    // Put PB0 into push pull 50 MHz mode
    GPIOB->CRL = 0x03;

    // The internal RC oscillator runs at 8 MHz.  The divide by 16
    // combined with the fractional divider means we just divide the
    // bus clock by the baud rate
    USART1->BRR = 8000000/38400;
    // Enable the UART, TX, and RX circuit

    // Nothing else /needs/ to be setup but you could go further.  The
    // chip defaults to 8 MHz RC with the busses running at the same
    // speed as the core.

    for (;;)
        // Around 1/4 of a second

        // Write out a character
        USART1->DR = 'H';
        // Turn all pins on
        GPIOB->ODR = ~0;


        // Write out another.  Note that the delay takes long enough
        // so we know that TX is empty
        USART1->DR = 'i';
        // Turn all pins off
        GPIOB->ODR = 0;

I haven't used the ST standard libraries as they reduce the readability of the code and greatly increase the code size. Sitting right on top of the registers is more efficient, readable, and transparent.

Other files

The startup script, linker script, and header files are all from the ST standard libraries. Note that the linker script (stm32.ld) and startup script are for the 512 k high density device used in the wiki:ETSTM32Stamp.


  • Don't forget the -mcpu=cortex-m3. GCC generates code for the incompatible ARMv5 by default
  • You can program the binary images from Linux and probably anything with Python using  stm32loader
  • Don't forget to turn on the clocks for the module - check out the RCC registers
  • Keep an eye out for sub enable registers. The UART needs the module clock turned on, the UART enabled, the UART TX enabled, and UART TX enabled
  • See the wiki:ETSTM32Stamp page for links to the manuals for the STM32