Discovering the STM32F3 Discovery

Posted by pulkomandy on Thu Sep 27 18:05:21 2012  •  Comments (5)  • 

So I just got this STM32F3 Discovery board. This is a development board from ST with a Cortex M4 chip, the mandatory leds and buttons, and some accelerometers and sensors.

The main problem is, as often, ST only offers code samples for commercial development environments. And I'm more of an open-source kind of guy. So let's setup some toolchain with free software.

First of all, get the files, this will be more convenient. This archive has the OpenOCD config file, the linker script, startup code, Makefile, and a stupid C code to show it all works (not even blinking a LED!).

Programming the board

That's the easy part. The STM32F3 Discovery has an embedded STLink/V2 interface. A nice thing about it is there are jumpers that allow to use the STLink part to program another device. However, there's a catch: the STLink can only be use to program STM32 and STM8 chips. It's not as flexible as other JTAG adapters.

Unfortunately, STM32F3 support is not part of OpenOCD sources yet. They have a patch set waiting, however:

Do not use OpenOCD 0.6.0 release packages, they don't have these patches in. You will need to get the sources from git and apply the patches above. Then, building OpenOCD is a rather straight-forward process (at least under Linux):

./bootstrap
./configure --enable-maintainer-mode --use-stlink
make
sudo make install
you need to enable access to the STLink hardware for openocd. Create the file /etc/udev/rules.d/99-stlink.rules and add :
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"
Then test everything is allright. We use the config file for the STM32F4 since OpenOCD doesn't provide one yet for the STM32F3 discovery. They are close enough for this small test, but we'll need a more specific one later on.

Enter the following commands:

openocd -f /usr/local/share/openocd/scripts/board/stm32f4discovery.cfg &
telnet localhost 4444
reset init
reset run
When you enter 'reset init', the STLink should reset the CPU and the ledswill stop blinking. 'reset run' will make it run again and the led should resume blinking. If everything is ok, you're (almost) ready to debug!

Compiling

The Cortex M4 chip is some kind of ARM CPU, so you need an ARM toolchain. There are several to chose from: Linaro, Sourcery CodeBench Lite, a plain vanilla gcc, and so on. I used Sourcery Codebench, because I already have it installed. But it shouldn't matter anyway: we're not going to use their linker scripts or standard library at all. Just make sure you have some arm-none-eabi-gcc or similar compiler available in your PATH.

Linker script

To make it easier to use different toolchains, it's better to use our own linker script. I derived one from here.

The linker script tells the linker about several things. It defines the memory areas available in the chip, then tells where each section generated by the compiler must go. For example, all code sections are usually allocated in flash memory, while the variables are put in the RAM alongside the stack. Reset vectors usually need to be put at the start of the flash so the CPU knows what to do when booting.

Writing a linker script is not as hard as some toolchain-provided ones make it sound. If you only want to write plain C code, you can get away with something very simple. I plan to use C++ as well, but that doesn't add that much more stuff. I added comments to the linker script, so you should understand what's going on.

Startup code

An usual C program starts with a main() function. However, when working with microcontrollers, there are some things to do before you can reach it. These tasks are performed by the boot code, usually in a function named _start or something similar. This includes setting up some things required by the C language:

  • Setup some hardware so the C code can be used at all (on more complex devices this could include initializing SDRAM controllers or so, on CortexM3 there shouldn't be much to do)
  • A stack, so we can call functions, pass parameters and return (on Cortex M this is done by the CPU hardware as part of the reset handling)
  • Initialize all the global and static variables (so you thought that was done magically?)
  • For C++, call global constructors so the global objects (if any) are in a coherent state,
  • And finally call the main() function, eventually with the argc and argv parameters (but most of the time you don't need that)

Check everything is ok

With a fairly simple program it's easy to look at the final elf file using the

arm-none-eabi-objdump -d
command. This will dump a complete disassembly of the code, so you can check the reset vectors are at the right place as well as the startup code.

The code should start with _start, which will point first to the stack (somewhere in RAM), then to the reset vector which is stored right after it. See the startup.s file to see how that's made.

Debugging

We'll be using OpenOCD again. While we used the TCL interface to quickly try it out before, OpenOCD is really meant to be used together with gdb. This way, it's possible to debug your code on the chip just like youwould debug any unix application on your PC. How nice is that ?

So, let's load our executable now !

openocd -f /usr/local/share/openocd/scripts/board/stm32f3discovery.cfg &
telnet localhost 4444
reset halt

flash erase_sectors 0 0 127
flash write a.out 0x8000000

So, the first step is to reset the CPU and halt it to make sure it isn't runnning code from the flash while we're erasing it. Then, we erase the flash, bank 0, sectors 0 to 127 (that's the whole flash of this chip). Finally, we write our new code. Notice the 0x8000000 offset: while the code is executed from address 0, the flash must be written from address 0x8000000. This is related to the way the STM32 chip can boot. Depending on the state of the BOOT0 and BOOT1 pins, the address 0 will be mapped to eitehr the flash, the SRAM, or the system ROM holding ST bootloader. But writing can only occur at the "e;real"e; flash address.

Now that our program is flashed, let's attempt to debug it. Exit the openOCD telnet prompt, but leave OpenOCD runnning. Now connect to it with GDB:

arm-none-eabi-gdb
target remote localhost 3333
file a.out
stepi
break main
cont
step
First we connect to the OpenOCD server through the GDB port. Then, we load the file to get the debugging symbols from it. The first stepi command is to sync gdb with OpenOCD. From then on, we can set breakpoints, and run/debug the program as usual. Happy hacking!

gravatar Comment

Posted by beungoud on Fri Sep 28 11:16:06 2012

Nice work. This seems a very promising board.
Where did you get your hand on it? I can't find any available.

Thanks.

gravatar Comment

Posted by tkuschel on Tue Oct 2 08:57:54 2012

Hi, I got boards from rutronik.com, I paid 18 Euro, but now they are about 10 Euro.
Thomas

gravatar Comment

Posted by zdav on Wed Dec 12 01:22:42 2012

Hi,

Now that OpenOCD v0.6.1 is out, you don't have to apply the said patches. You still have to enable stlink support at configure time, but you have to use "--enable-stlink" and not "--use-stlink".

Thanks for the write-up.

gravatar Comment

Posted by Pierre on Thu Dec 19 19:04:25 2013

Hey! Nice article! Complete, clear, efficient.

I used OpenOCD along with gdb and ST-Link this week to debug the application you let me before leaving (you know what I am talking about ^^) and Julien and I wondered where the port 3333 is configured with OpenOCD. Do you have any idea?

gravatar Comment

Posted by PulkoMandy on Mon Jan 6 09:18:25 2014

The TCP ports can be configured from the configuration file:
http://openocd.sourceforge.net/doc/html/Daemon-Configuration.html#Daemon-Configuration

Use the commands gdb_port, telnet_port, and tcp_port to set the 3 ports. The pipe mode is nice and avoids messing with TCP ports. It is possible to have GDB start OpenOCD itself this way.

Leave a comment

Name: Mail: