Courtesy to: http://jeremyherbert.net/get/stm32f4_getting_started
Overview of this guide
This guide explains how to get a fully working ARM-GCC toolchain working under Ubuntu Linux, and provides makefiles that are specifically targeted towards the STM32F4 series of microcontrollers. Unfortunately, to load the code on the board, a windows computer (or VM) is needed. I am investigating linux based tools but I am yet to get one working.
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
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.
A special note for those who have not previously used ARM microcontrollers
Most microcontroller systems have very tight integration between the CPU and the on-chip peripherals due to the manufacturer designing and producing both subsystems. In the case of ARM-based microcontrollers though, a company namedARM Holdings designs the core and licenses it to manufacturers like ST (or NXP, Apple, Samsung, Qualcomm, HP, etc). This means that the manufacturer can spend time on making the on-chip peripherals powerful and reliable while ARM deals with things like power efficiency and instruction set design. On top of this, if the manufacturer follows the CMSIS guidelines, porting software between chips from different manufacturers is a piece of cake. Although peripheral use can be a touch more complicated than in a microcontroller developed completely by one manufacturer, using ARM-based microcontrollers is a much more cost-effective solution if you need high clock speeds and low power (YMMV, of course).
Stage 1: Build a Toolchain
Our build environment will be based on Ubuntu 11.10, so if you don’t already have itgrab the Ubuntu ISO now and install it on a PC or in a virtual machine.
The toolchain we will be using is a modified version of Summon-Arm-Toolchain. Summon-Arm-Toolchain is a shell script which downloads, builds and installs a fully working ARM toolchain for the Cortex-M3 (nice!). On top of this, the amazingMikeSmith has already performed the necessary modifications to build for the Cortex-M4 with hardfloat support with no extra work on our end (double nice!). To get started, we need to install all of its dependencies:
$ sudo apt-get install git zlib1g-dev libtool flex \
bison libgmp3-dev libmpfr-dev libncurses5-dev libmpc-dev \
autoconf texinfo build-essential libftdi-dev
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 that is complete, let’s clone the Summon-Arm-Toolchain repository:
$ git clone https://github.com/MikeSmith/summon-arm-toolchain.git
Now enter the directory and start the build process:
$ cd summon-arm-toolchain
$ ./summon-arm-toolchain
This will take a while, so go take a nap.
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
You can reload the file by running the command:
$ . ~/.profile
Check if it works by running the following command:
$ arm-none-eabi-gcc --version
And if you see something like,
arm-none-eabi-gcc (Linaro GCC 4.6-2011.10) 4.6.2 20111004 (prerelease)
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
you have an ARM toolchain capable of building binaries for the Cortex-M4!
Stage 2: Uh, so what do I do now?
It’s time to start writing some code! If you are at all familiar with embedded development, you might be familiar with the Microcontroller Programming Paradigm(tm):
1. Decide what peripheral you want to use
2. Look in datasheet for the registers to enable and configure it
3. Set bits in the registers to make peripheral behave the way you want
4. GOTO 1
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.
Stage 2A: Building a Blinky
Note: This section asks you initially to compile code that is syntactically correct but functionally incorrect. This is intentional; I am hoping to demonstrate the sort of traps that you can fall into when developing for these chips. If you get easily frustrated, consider reading the whole section before compiling anything yourself.
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:
$ 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.
Before we start writing code, we need to clean up the template. It is set up by default to build the IOToggle example from ST, but we would much prefer to write our own code. So from
blinky/
, run the following commands to remove the files we are not interested in:$ rm inc/stm32f4xx_it.h src/stm32f4xx_it.c src/main.c
$ touch src/main.c
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
Ah, now we have a nice clean build environment.
Stage 2B: Actually writing some code, for reals this time
Let’s now open up
src/main.c
and set it up for some serious C coding:
main-1.c:
1
2
3
4
5
6
| #include "stm32f4xx_conf.h" int main( void ) { } |
Now let’s start at step 1 of the Microcontroller Programming Paradigm(tm) 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
andGPIOx_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
2
3
4
5
6
| #include "stm32f4xx_conf.h" int main( void ) { GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output } |
If you have done a bit of microcontroller programming before, you might be surprised to see the “->” (otherwise known as the structure dereference operator) in the left half of the new statement. We use it because ST organises registers by defining them as structures. Looking at
stm32f4xx.h
should make more sense:
stm32f4xx-truncated.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| /* Lots up here */ typedef struct { __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */ __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */ __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ } GPIO_TypeDef; /* Lots in between */ #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) /* and heaps more down here too */ |
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
2
3
4
5
6
7
8
| #include "stm32f4xx_conf.h" int main( void ) { GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output while (1) GPIOD->ODR ^= (1 << 13); } |
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.
Stage 2C: Enabling peripheral clocks
Over the last half-decade, dramatically lowering current draw has been a goal for most microcontroller manufacturers. One of the techniques used to achieve this is to switch off on-chip peripherals by removing access to their master clocks. On the STM32 devices, these clocks are known as the hardware and peripheral clocks and are controlled by the RCC (Reset and Clock Control) group of registers. Since there are more than 32 on chip peripherals, there are actually two registers used to switch on a clock:
RCC_AHB1ENR
and RCC_AHB2ENR
(for the Hardware clock, APB
for thePeripheral clock). The clock is controlled by set/reset registers, so to turn a system on you set a bit in the ENR register, and to turn that same peripheral off you set the bit in the corresponding RCC_AHBxRSTR
register. Go and have a read of the register descriptions now, they start on page 93 (section 5.3) of the STM32F4 Reference Manual. To switch GPIOD on, we do something like this:
main-4.c:
1
2
3
4
5
6
7
8
9
10
| #include "stm32f4xx_conf.h" int main( void ) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output while (1) GPIOD->ODR ^= (1 << 13); } |
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:
__
. 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.
No comments:
Post a Comment