How to compile and burn the code to STM32 chip on Linux (Ubuntu)

This is tutorial for beginners that shows how to install tools, compile the code with gcc-arm-none-eabi and send it to the STM32 using st-flash. It also introduce basics of automation of this task by putting all instructions into Makefile.

A few, complete code examples can be found on GitHub:

Updates

[28.10.2019] Take a look at docker version of toolchain for STM32

1. Installing compiler and stlink

To compile C and/or C++ source code of your firmware you will need gcc-arm-none-eabi compiler and stlink.

Installing gcc-arm-none-eabi

What is extremely useful, there are complete and easy to install packages for all major platforms (https://launchpad.net/~team-gcc-arm-embedded/+archive/ubuntu/ppa)

sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi

Installing stlink

At first, we need install dependencies then build it from sources (https://github.com/texane/stlink/blob/master/doc/compiling.md#build-from-sources).

sudo apt-get install git build-essential libusb-1.0.0-dev cmake
cd $HOME
git clone git@github.com:texane/stlink.git
cd stlink
make release
cd build/Release && make install DESTDIR=_install
echo "export PATH=\$PATH:$HOME/stlink/build/Release/_install/usr/local/bin" >> $HOME/.bashrc
echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$HOME/stlink/build/Release/_install/usr/local/lib" >> $HOME/.bashrc
source $HOME/.bashrc

2. Compiling and burning the code

Now that you have the toolchain installed, a next step is to compile the source code into a .ELF, then generate .BIN file and finally burn this this binary file to STM32 chip using ST-Link v2 programmer.

Example code

Here is an example content of main.c file. The code does nothing except getting stuck in an endless loop but it’s always something!

int
main(void)
{

        while (1);
}

Compiling

The command below will compile your code. It’s GCC so I assume it looks familiar to you and no additional explanations are needed. If you want perform compilation for some other MCU then you need specify at least appropriate -mcpu, .LD and .S files (not provided in this tutorial)

$ arm-none-eabi-gcc -std=gnu99 -g -O2 -Wall -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m0 -fsingle-precision-constant -Wdouble-promotion main.c -o main.elf

After performing successful compilation, you can check program and data memory size with this command.

$ arm-none-eabi-size -tA main.elf 
main.elf  :
section              size        addr
.isr_vector           192   134217728
.text                6404   134217920
.rodata                60   134224324
.ARM                    8   134224384
.init_array             8   134224392
.fini_array             4   134224400
.data                1092   536870912
.jcr                    4   536872004
.bss                   32   536872008
._user_heap_stack    1536   536872040
.ARM.attributes        40           0
.comment               31           0
.debug_line          7416           0
.debug_info         22917           0
.debug_abbrev        6837           0
.debug_aranges        744           0
.debug_loc           6584           0
.debug_ranges         472           0
.debug_str           5717           0
.debug_frame         2004           0
Total               62102

Generating .BIN

Most programmers will not accept a GNU executable as an input file, so we need to do a little more processing. So, the next step is about converting the information form .ELF into .BIN file. The GNU utility that does this is called arm-none-eabi-objcopy.

$ arm-none-eabi-objcopy -O binary main.elf main.bin

Burning

The utility called st-flash can program processors using the content of the .BIN files specified on the command line. With the command below, the file main.bin will be burned into the flash memory.

$ st-flash write main.bin 0x8000000

Voila! Chip is programmed.

3. Make and Makefiles

Now, we can automate this process by creating a Makefile and putting our commands there. The structure of a Makefile is very simple, and more information about it can be found here. Utility make reads automatically a Makefile file in the folder where you launch it. Take a look at simple Makefile presented bellow.

TARGET=main

CC=arm-none-eabi-gcc
LD=arm-none-eabi-gcc
AR=arm-none-eabi-ar
AS=arm-none-eabi-as
CP=arm-none-eabi-objcopy
OD=arm-none-eabi-objdump
SE=arm-none-eabi-size
SF=st-flash

CFLAGS  = -std=gnu99 -g -O2 -Wall
CFLAGS += -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m0
CFLAGS += -fsingle-precision-constant -Wdouble-promotion

SRCS =  main.c

.PHONY: $(TARGET)

$(TARGET): $(TARGET).elf

$(TARGET).elf: $(SRCS)
        $(CC) $(INCLUDE) $(CFLAGS) $^ -o $@
        $(CP) -O binary $(TARGET).elf $(TARGET).bin

clean:
        rm -f *.o $(TARGET).elf $(TARGET).bin

flash:
        $(SF) write $(TARGET).bin 0x8000000

If you launch a simple make in the terminal, only label “all” will be executed. When you launch make flash label “flash” will be executed, and so on.

4. Summary

Essentially, assuming that our program is in main.c, only those three things are needed to compile and burn the code to STM32 chip.

$ arm-none-eabi-gcc -std=gnu99 -g -O2 -Wall -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m0 -fsingle-precision-constant -Wdouble-promotion main.c -o main.elf
$ arm-none-eabi-objcopy -O binary main.elf main.bin
$ st-flash write main.bin 0x8000000

It’s important to highlight that we can easily automate whole process with Makefiles. Sooner or later you will need it!

27 thoughts on “How to compile and burn the code to STM32 chip on Linux (Ubuntu)

  1. Hi, sorry if it is trivial but as a newbie I don’t understand why I needed to add a user defined _exit function in the main.c file in order to avoid the following error:
    /usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/lib/thumb/v6-m/nofp/libg.a(lib_a-exit.o): in function `exit’: /build/newlib-CVVEyx/newlib-3.3.0/build/arm-none-eabi/thumb/v6-m/nofp/newlib/libc/stdlib/../../../../../../../../newlib/libc/stdlib/exit.c:64: undefined reference to `_exit’ collect2: error: ld returned 1 exit status

  2. Hi there,

    sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
    sudo apt-get update
    sudo apt-get install gcc-arm-none-eabi
    “””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””
    gcc-arm-none-eabi is already the newest version (15:9-2019-q4-0ubuntu1).
    0 upgraded, 0 newly installed, 0 to remove and 44 not upgraded.
    “””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””””
    after i copied and pasted your command:
    arm-none-eabi-gcc -std=gnu99 -g -O2 -Wall -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m0 -fsingle-precision-constant -Wdouble-promotion main.c -o main.elf

    i got this in return:
    usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/lib/thumb/v6-m/nofp/libg.a(lib_a-exit.o): in function `exit’:
    /build/newlib-CVVEyx/newlib-3.3.0/build/arm-none-eabi/thumb/v6-m/nofp/newlib/libc/stdlib/../../../../../../../../newlib/libc/stdlib/exit.c:64: undefined reference to `_exit’
    collect2: error: ld returned 1 exit status
    root@marco-desktop:/home/marco/Documents/c language# arm-none-eabi-gcc -std=gnu99 -g -O2 -Wall -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m0 -fsingle-precision-constant -Wdouble-promotion main.c -o main.elf
    /usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/lib/thumb/v6-m/nofp/libg.a(lib_a-exit.o): in function `exit’:
    /build/newlib-CVVEyx/newlib-3.3.0/build/arm-none-eabi/thumb/v6-m/nofp/newlib/libc/stdlib/../../../../../../../../newlib/libc/stdlib/exit.c:64: undefined reference to `_exit’
    collect2: error: ld returned 1 exit status

    i’m using the new Ubuntu 20.04, went to the “open terminal” via a right click the folder i created where the “main.c” file resides. you see the *.elf appear in the folder while terminal is doing “something”, but directly disappears again.

    what’s the remedy?

  3. Hi nice tutorial but I want to use the STM32F746NG Board.
    Can you tell me what changes I have to do to run it on this board?

    Thanks, Daniel

  4. Needed to add –specs=nosys.specs for main.c -o main.elf compilation,
    else getting error
    collect2: error: ld returned 1 exit status
    (using Ubuntu 18.04 dist upgraded from Ubuntu 16.04)
    Is it okay to complie with –specs=nosys.specs?

  5. Hi,

    I followed your instructions – bin dir is in PATH yet:
    ~/stlink/build/Release$ st-flash
    st-flash: error while loading shared libraries: libstlink.so.1: cannot open shared object file: No such file or directory

    The libraries were installed in ..Release/_install/usr/local/lib

    Should add also in .bashrc:
    export LD_LIBRARY_PATH=…path-to/_install/usr/local/lib

    • Hi, thanks for reporting it. Looks like the installation process can require it. I’ve updated export lines inside the .bashrc.
      /L

  6. So is with keil I could run file of this repository without problems ? Is a way to run makefile like program ?

    • Keil works on Windows. The most common way to run Makefile is do it on Linux using make command.

      • I arrive tu run now to run make on Windows after many research. What i want is to build a secure bootloader from the makefile of this project, but i have error : https://github.com/dmitrystu/sboot_stm32/issues/5

        ACER i5@SORO_KOLO MINGW64 ~/Desktop/sboot_stm32-master
        $ make stm32l052x8
        C:/Program Files/Git/mingw64/bin/make fwclean bootloader FWCPU=’-mcpu=cortex-m0plus’
        FWSTARTUP=’mcu/stm32l0xx.S’
        FWDEFS=’STM32L0 STM32L052xx USBD_ASM_DRIVER’
        LDPARAMS=’ROMLEN=64K RAMLEN=8K’
        make[1]: Entering directory ‘C:/Users/ACER i5/Desktop/sboot_stm32-master’
        compiling src/arc4.c
        compiling src/chacha.c
        compiling src/gost.c
        compiling src/raiden.c
        compiling src/rc5.c
        compiling src/speck.c
        compiling src/xtea.c
        compiling src/blowfish.c
        compiling src/rtea.c
        assembling mcu/stm32l0xx.S
        compiling src/descriptors.c
        src/descriptors.c:19:19: fatal error: stm32.h: No such file or directory
        #include “stm32.h”
        ^
        compilation terminated.
        make[1]: *** [Makefile:110: build/objfw/descriptors.o] Error 1
        make[1]: Leaving directory ‘C:/Users/ACER i5/Desktop/sboot_stm32-master’
        make: *** [Makefile:263: stm32l052x8] Error 2

    • Hi, this how-to guide is for beginners. It shows how to build ARM toolchain (plus st-link) on Linux/Ubuntu. The project you’re trying to build is using the same toolchain so it should be quite easy to build it using Makefile.

      $ cd /path-to-project/ && make

      /L

  7. Thanks this was exactly what I needed. Most people seem to just throw a 300 line long Makefile at you instead of a simple command to get the compiled program into MCU.

  8. Hi,
    I had problem when invoking make:
    arm-none-eabi-gcc -std=gnu99 -g -O2 -Wall -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m4 -fsingle-precision-constant -Wdouble-promotion ../src/main.c -o main.elf
    /usr/lib/gcc/arm-none-eabi/5.4.1/../../../arm-none-eabi/lib/armv7e-m/libg.a(lib_a-exit.o): In function `exit’:
    /build/newlib-OTNHfJ/newlib-2.4.0.20160527/build/arm-none-eabi/armv7e-m/newlib/libc/stdlib/../../../../../../newlib/libc/stdlib/exit.c:70: undefined reference to `_exit’
    collect2: error: ld returned 1 exit status
    makefile:22: recipe for target ‘main.elf’ failed
    make: *** [main.elf] Error 1

    I solved the problem by adding -c flag in $(CC) $(INCLUDE) $(CFLAGS) $^ -o $@.
    Now instruction looks as follow: $(CC) $(INCLUDE) $(CFLAGS) -c $^ -o $@

    Dont you think there should be LD=arm-none-eabi-ld instead of LD=arm-none-eabi-gcc? Am I correct?

  9. Hi Lukasz,
    Great website, some interesting stuff on the ATtiny.
    I’m a bit of a novice so was wondering if you could help me?
    I’m running Linux Mint and have tried to install st-link.

    I’ve done everything you show but when i get to the section on burning it says
    st-flash: command not found.

    john@john-ThinkPad-X200 ~/stlink/build/Release $ echo “export PATH=\$PATH:$HOME/stlink/build/Release/_install/usr/local/bin” >> $HOME/.bashrc
    john@john-ThinkPad-X200 ~/stlink/build/Release $ st-flash erase
    st-flash: command not found
    Thanks in advance

    • Hi, thanks. Sorry for late response. If it says “command not found” then the bash doesn’t know the path of st-flash. As a first, I would check if st-flash is available under destination path ($HOME/stlink/build/Release/_install/usr/local/bin). Sometimes it needs to reopen/refresh terminal to load new set of environment variables. L

Leave a Comment