Getting Started with the STM32F4 and GCC
Getting Started with the STM32F4 and GCC
One important aspect of this guide is that the compiler is built with hardfloat
support. One of the major features of the ARM Cortex-M4 series is the
hardware acceleration of floating point operations; however, most free
toolchains and compilers don’t provide support for it (you need to cough up
some dough for the non-free compiler).
Assumed background
To follow this guide, you should already know how to navigate and run
commands within the linux terminal. Commands will look like this:
$ apt-get moo
means put “apt-get moo” in your favourite terminal software (or any terminal
software really…). You should also know some basic C programming.
1 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
Astute readers will note that a few of these aren’t listed as dependencies on
the Summon-Arm-Toolchain page. I am not sure why. All of these are
essential for building the toolchain.
Once this is complete, add the “~/sat/bin” directory to your path. I did this by
adding the following line to my ~/.profile file:
export PATH=$PATH:/home/jeremy/sat/bin
2 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
you have an ARM toolchain capable of building binaries for the Cortex-M4!
In the case of the STM32F4, the datasheet has very little information on
which registers to use. You can find it all tucked away in ST’s RM0090:
STM32F4xx Advanced ARM-based 32-bit MCUs Reference Manual. There is
also some information on the features of the ARM core in the ARM Cortex-
M4F Technical Reference Manual. Finally, ST have published UM1472:
STM32F4 High-Performance Discovery Board User Manual which tells you
how devices are connected together on the board. Make sure you have these
three saved somewhere so you can refer to them later.
To start a project from scratch, the first thing we normally do is work out
how to use the compiler to compile our code. Unfortunately, it’s a little
complicated and so for now we will be jumping straight to the code writing
stage. To do this, we will be using my stm32-template project. So the first
thing we need to do is get a copy of it:
$ mkdir ~/stm32_code
$ cd ~/stm32_code
$ git clone git://github.com/jeremyherbert/stm32-templates.git
Once the cloning is complete, take a look in the directory. You should find two
directories, both of which contain a template build environment for two
different ST development kits. Since we are working with the F4, we
obviously want the “stm32f4-discovery” template. So let’s copy it to a new
place so we can use it:
3 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
$ cp -r stm32-templates/stm32f4-discovery blinky
Now let’s have a look at the directory structure of the template project:
inc/
lib/
src/
Makefile
stm32_flash.ld
The inc/ folder is there for you to put your *.h files in. Likewise, the src/
folder is for your *.c files. The lib/ folder is where we store all of the support
files that ST have provided for the STM32F4 family of microcontrollers; you
shouldn’t need to change anything in here, but have a look if you are curious.
The Makefile instructs the make command on how to build our project (more on
this later) and the stm32_flash.ld file tells the compiler how to arrange the
compiled information.
Now we need to change Makefile to tell the compiler we are only compiling
main.c. Change this:
SRCS = main.c stm32f4xx_it.c system_stm32f4xx.c
to this:
SRCS = main.c system_stm32f4xx.c
main-1.c:
?
1#include "stm32f4xx_conf.h"
2
3int main(void)
4{
5
6}
4 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
and look up how to control the GPIOs (General Purpose Input/Output) on the
STM32F4. Looking in the contents, we can see that the GPIO-related
information starts on page 136 in section 6, so open up your reference
manual to that page and have a quick glance through. If you are used to 8 bit
microcontrollers, you might be surprised as to how much more complex
these chips are.
If you have decided that it looks too complicated and don’t want to continue,
try watching this video. Otherwise, let’s open up the document to the GPIO
register listing (section 6.4/page 148). Read the whole thing if you like, but
we will cheat for now and I will tell you that the registers we are interested
in are GPIOx_MODER and GPIOx_ODR which will set the set the direction and output
value respectively.
To set GPIOx_MODER, let’s take a look at the table. There are 16 pins on each
GPIO output port, so 16 two bit groups are used to configure the pin
direction. Looking through the description below the table, it should be clear
that we want “01: General purpose output mode” so we can turn the LED on
and off. But which bit-pair do we want? The answer is in the STM32F4-
Discovery User Manual (see above for the link), in section 4.4/page 16. It
says:
User LD3: orange LED is a user LED connected to the I/O PD13 of
the STM32F407VGT6.
The PD13 means that we want pin 13 of GPIOD and thus we want MODER13;
the 13th pair slot. Or to put it another way, we want the 26th bit of GPIOD_MODER
to be 1. Let’s put that in our code:
main-2.c:
?
1#include "stm32f4xx_conf.h"
2
3int main(void)
4{
5 GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
6}
stm32f4xx-truncated.h:
?
1 /* Lots up here */
2
3 typedef struct
4 {
5 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
Given that the registers are located sequentially in memory, this structure
simply maps the registers to human readable names.
So now that we know how to use registers, let’s turn on and off the LED
using the ODR register (and a XOR trick).
main-3.c:
?
1#include "stm32f4xx_conf.h"
2
3int main(void)
4{
5 GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
6
7 while (1) GPIOD->ODR ^= (1 << 13);
8}
Now if you build this code, you can load it onto your device using the ST-
LINK Utility under a Windows VM/system. Unfortunately though, it won’t
work.
6 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
main-4.c:
?
1 #include "stm32f4xx_conf.h"
2
3 int main(void)
4 {
5 RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD
6
7 GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
8
9 while (1) GPIOD->ODR ^= (1 << 13);
10}
Other on-chip systems use the peripheral bus, so be careful when checking
whether you are using AHB or APB registers. You are only one keystroke away
from a well hidden bug.
You should also notice the register define I used to set the bit. ST has kindly
written out human-readable names for each bit in configuration registers.
The pattern should be fairly obvious: <register group>_<register name>_<bit name>.
You should always use these defines when configuring your device so that
you don’t need to continuously need to refer to the datasheet to look up the
register structure.
Now our code is ready for primetime! Load it up on the chip and you will
see…
…nothing.
7 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
you look closely in the datasheet the hardware clock only ever reaches
100MHz! Although we could just put a very slow piece of code in our loop, I
would like to take this opportunity to introduce another on-chip peripheral:
the general purpose timer. This peripheral allows us to count up to a value
and then generate an Interrupt ReQuest (IRQ) which then triggers the
execution of a corresponding Interrupt Service Routine (ISR). There are
actually many other modes of operation which you can read about in the
datasheet, but this is how we will be using it. In this case we will be using
TIM2, so first of all let’s enable the peripheral clock for it.
main-5.c:
?
1 #include "stm32f4xx_conf.h"
2
3 int main(void)
4 {
5 RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD
6 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM2 clock
7
8 GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
9
10 while (1) GPIOD->ODR ^= (1 << 13);
11}
Now we need to look at the set registers that configure the timer. The
comments should briefly explain what each register does; make sure you
read the datasheet if you need more information. One important thing to note
is that you need to tell the timer that you’ve changed its configuration. This
is done by setting the first bit of the EGR register to 1.
main-6.c:
?
1 #include "stm32f4xx_conf.h"
2
3 int main(void)
4 {
5 RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD
6 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM2 clock
7
8 GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
9
10 TIM2->PSC = 0x0; // no prescaler, timer counts up in sync with the peripheral
11 clock
12 TIM2->DIER |= TIM_DIER_UIE; // enable update interrupt
13 TIM2->ARR = 0x01; // count to 1 (autoreload value 1)
14 TIM2->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN; // autoreload on, counter enabled
15 TIM2->EGR = 1; // trigger update event to reload timer registers
16
17 while (1);
8 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
Then we enable the timer interrupt by setting the correct bit in the NVIC
(Nested Vectored Interrupt Controller). You can find the complete list of
these on page 197 (section 9.1) of the STM32F4 Reference Manual and you
can find more information on the ISERs (Interrupt SEt Register) in the
Cortex-M4F manual. The _IRQn suffix is added in the define to mean “IRQ
number”.
main-7.c:
?
#include "stm32f4xx_conf.h"
1
2
int main(void)
3
{
4
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD
5
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM2 clock
6
7
GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
8
9
NVIC->ISER[0] |= 1<< (TIM2_IRQn); // enable the TIM2 IRQ
10
11
TIM2->PSC = 0x0; // no prescaler, timer counts up in sync with the peripheral
12
clock
13
TIM2->DIER |= TIM_DIER_UIE; // enable update interrupt
14
TIM2->ARR = 0x01; // count to 1 (autoreload value 1)
15
TIM2->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN; // autoreload on, counter enabled
16
TIM2->EGR = 1; // trigger update event to reload timer registers
17
18
while (1);
19
}
And now we can finally write our interrupt service routine. The interrupt
service routine is just a regular C function with a special name that
corresponds to an IRQ. You can see the full list of function names in
lib/startup_stm32f4xx.s, but I have reproduced them here to make it a little
easier. In this case, we want to use TIM2_IRQHandler, so let’s add it:
main-8.c:
?
1 #include "stm32f4xx_conf.h"
2
3 void TIM2_IRQHandler(void) {
4 // flash on update event
5 if (TIM2->SR & TIM_SR_UIF) GPIOD->ODR ^= (1 << 13);
6
7 TIM2->SR = 0x0; // reset the status register
8 }
9
9 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
int main(void)
10
{
11
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD
12
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM2 clock
13
14
GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output
15
16
NVIC->ISER[0] |= 1<< (TIM2_IRQn); // enable the TIM2 IRQ
17
18
TIM2->PSC = 0x0; // no prescaler, timer counts up in sync with the peripheral
19
clock
20
TIM2->DIER |= TIM_DIER_UIE; // enable update interrupt
21
TIM2->ARR = 0x01; // count to 1 (autoreload value 1)
22
TIM2->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN; // autoreload on, counter enabled
23
TIM2->EGR = 1; // trigger update event to reload timer registers
24
25
while (1);
26
}
There are two things you usually need to do in an ISR; you need to check
which event occurred and respond, as well as reset the corresponding
status register. Seriously, you need to RESET THE STATUS REGISTER!
The chip doesn’t do this automatically and it will be a ridiculously annoying
bug to track down.
Now, build your code and put it on the chip. The light will be blinking!
The light is blinking at 2.6MHz! That’s over 45,000 times faster than your
eyes can perceive!
&
Happy programming, I will try to put up some information on how to use the
on-chip DAC next!
&
10 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
11 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
47 Comments jeremyherbert
1 Login
Sort by Best
Recommend 5 ⤤ Share
Name
You said that you have not been able to get any linux flashing tools working with the
STM32F4. I'm not sure if you have seen stlink for linux (https://github.com/texane/s..., but I
have it working based on your template, with only a couple of tiny modifications. You can try
it with my fork of your code, https://github.com/prattmic... . (I linked to an old commit because
I am messing with not using STM's code at HEAD.) You should be able to add your stlink
folder in the Makefile, then "make && make burn" to flash the Discovery. I have been using
this tool since I got my board, and I actually haven't even used Windows at all.
SRC += lib/startup_stm32f4xx.s
so that it was the second line of code and it worked like a dream. A note to those struggling
with similar problems.
12 of 13 2017/09/12, 18:02
Getting Started with the STM32F4 and GCC - j... http://jeremyherbert.net/get/stm32f4_getting_st...
13 of 13 2017/09/12, 18:02