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!
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
Hi, this function need to be included in your code. Take a look at example implementation here – https://github.com/lpodkalicki/stm32f042f6p6/blob/master/blinky/syscalls.c
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?
Hi,
stdlib reqiure a user-defined function _exit. Take a look at the end of this file – https://github.com/lpodkalicki/stm32f072rbt6/blob/master/blinky/main.c
/L
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
Hi, it should go as it is, I think.
/L
Hi okay thanks I got it.
Do you know which libraries I need to get an serial output in putty by using printf or something like this?
Thanks, Daniel
Hi,
you can have serial output using UART peripheral. No extra libraries needed. Here is an example – https://github.com/lpodkalicki/stm32f401vct6u/tree/master/debug_logging_via_uart
/L
Thanks for your answer.
Is it write that I have to use the “log_info()” command to something to serial output?
In which file is this function defined or which files do I have to include to my project?
Thanks Daniel
I tried to compile it for the STM32F746 discovery board. Compiling was succesfull but when i flash it on the board there is nothing to read in putty.
Do you know why it doesn’t work?
Thanks, Daniel
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?
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
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.
Thank you, I work on windows and know nothing about GCC and makefile command
Then, maybe you should look at Keil
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
Hello, very good project. But everyone doesn’t know gcc 🙁 . I’m beginning in programation, and I have very big problem to run makefile. I don’t know how to start with it. I want to use this project files but I don’t know how to run command mentioned. Please help me, this now 3 days I work on it but nothing 🙁 = https://github.com/dmitrystu/sboot_stm32/blob/master/README.md
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
Great Article. But Sir. Would you lease explain why did you use “0x8000000”?
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.
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?
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
$source ~/.bashrc
above command would execute the .bashrc script again, adding the st-flash binary to your PATH
Good hint! I’ve added it to installation process.