STM32 Value Line Discovery experiences

This board is an STM32F100RBT6B (32-bit ARM SoC, 128kB flash, 8kB SRAM, a "medium-density device") with an STM32F103 to provide a USB interface to its "SWD" or "ST-LINK" programming/debugging protocol. I ordered mine from Mouser.

April 18, 2011.

I'm a to-the-metal kind of guy, especially when I'm working with embedded stuff. I hate big programs that just wrap simple ones. So here is what I went through to make a blinky using this incredibly convenient and inexpensive little board from ST Micro.

Email me if any of these links go stale:

First, use Capt'ns Missing Link program: stlink. It is available at:

git://github.com/texane/stlink.git

It provides a daemon that you run which talks to the USB<=>SWD gateway chip on the Discovery. GDB connects to this program, and is really good at it! It has breakpoints and single-stepping, flash writing, etc. Pretty handy!

Anyways, onward and forward. I built binutils:

$ tar xjvf binutils-2.21.tar.bz2
$ mkdir binutils-build
$ cd binutils-build
$ ../binutils-2.21/configure --target=arm-none-elf --prefix=/home/greg/stm32 --enable-interwork --enable-multilib --with-gnu-as --with-gnu-ld --disable-nls
$ make -j 4 && make install

I fixed a bug in gdb. A corrupted stack on the Discovery would cause it to error out of every command before executing the command, because it would try to unravel the function prologue to provide an accurate stack trace. ASM programmers like myself don't need no stinkin stack traces. I recommend this patch, which maybe I should also submit upstream: gdb-20110418.patch.

I built gdb:

$ tar xjvf gdb-7.2.tar.bz2
$ mkdir gdb-build
$ cd gdb-build
$ ../gdb-7.2/configure --target=arm-none-elf --prefix=/home/greg/stm32 --enable-interwork --enable-multilib --without-headers --disable-shared --with-gnu-as --with-gnu-ld
$ make -j 4 && make install

(yeah I think the gdb configure line is probably excessive, I just copied the binutils one really)

I tried to build gcc but it was a pain. My regular host gcc, provided by debian, forgets where its own damn headers are when I use it to build another gcc. I hate tools that are too smart for my good. I'm programming assembly anyways.

I accumulated a bunch of documentation, which was poorly indexed by the manufacturer so I provide it here:

I made an ldscript. I don't know what the "normal" way to do this is, but I made it so that there is a section .vectors which comes at 0x08000000 (the start of flash), because the ISR vectors have to come first in this thing. Then .text and .data come next. So as long as only one object tries to define the .vectors section, everything should be fine.

Then I wrote a blinky. blinky-20110418.tar.bz2 has the blinker source, the makefile, and the ldscript, so you can see the shape of it.

To load it onto the STM32, I just run:

$ modprobe sg
$ st-util 1234 /dev/stlink &
$ arm-none-elf-gdb
(gdb) tar ext :1234
(gdb) load blink.elf
(gdb) run
(gdb) cont

Two surprises when programming it. First, to enable the GPIO pins, you need to enable the Reset/Clock Control (RCC) GPIO clock. It seems that there is a clock which tells the GPIO pin latches to copy their states from the peripheral memory area and vice versa, which must be enabled.

And the other surprise was quite pleasant: the 24MHz chip comes with an 8MHz external crystal ("HSE"), but it has an internal clock-multiplying PLL that can be trivially configured to get real 24MHz behavior out of it. The delay loop in the blinky is about 24M cycles, and takes about 1 second, success!

I think this webpage now has everything I wished had come in the box with the STM32, so let me know if it's useful or if you need help.

June 26, 2011.

I had a breakthrough today. I have been using arm-none-elf-as with -mcpu=cortex-m32 -march=armv7-m, which had the effect of making it only able to use old thumb (Thumb16) instructions. I don't really understand any of this but apparently adding .syntax unified at the start of your .s file saves the day! Now it is able to use all of the Thumb-2 (new 32-bit thumb) instructions (especially the IT combinations). For extra fun, add -mimplicit-it=always, and you do not need to explicitly specify the IT instructions.

And similarly, to make gdb useful:

set arm fallback-mode thumb

And this shortcut makes single-stepping ASM much easier:

define si
stepi
x/1i $pc
end

July 1, 2011.

Now that I'm linking between multiple objects, I am seeing:

arm-none-elf-ld: font.o(render_char): warning: interworking not enabled.
  first occurrence: sign.o: Thumb call to ARM

There's some sort of delicate dance where 32-bit ARM functions are even-aligned and 16-bit Thumb functions are odd-aligned. It's irrelevant and confusing on this chip, since it only runs in Thumb-2 mode (hybrid 16- and 32-bit Thumb instructions). But whatever. Here's the answer:

.global render_char
.thumb_func
render_char:

The important part is the .thumb_func. I've only tried placing it between the .global and the label. I don't know if it affects the next label or the previous .global, but it works there. Somehow it sets a magic linker flag, probably in the ELF .ARM.attributes section.

There is an analogous situation with branch tables (like the interrupt vector table, and hand-coded ones). You must have ".word foo+1" to indicate that it is thumb code, or you will get an exception for trying to execute non-thumb code on the STM32.

July 23, 2011.

There are some characteristic fails of the people over at ST Micro, especially in the documentation department. What I'm trying to tell you is that no amount of documentation reading will answer the question of "what value goes into FLASH_AR?" Basically it is either a page number (N) or a page address (N*1024). I think it is the latter.

July 24, 2011.

Another eng-STM-lish failure is HSE Bypass. As near as I can tell the "HSEBYP" bit, which is "for bypassing the oscillator with an external clock," does not bypass the HSE, but rather bypasses the HSI. I think so you can have the HSE and HSI enabled at the same time.

But I was wrong. HSEBYP bypasses the HSE, using the HSI if it is enabled. And for some reason it can't be disabled without hard reset. The sort of things you can only learn from experimentation when there isn't such a thing as human language.

April 18, 2012.

I finished my STM project, so I haven't touched these in a while, but this link passed my way, and I think it may be valuable so let's add it: http://code.google.com/p/arm-utilities/.

June 4, 2016.

I notice Capt'ns Missing Link's git repository has changed quite a bit since 2011. Now they use libusb instead of the "sg" kernel driver (because the STM32 debug interface is a hack that doesn't always play well with usb-storage). So you need to take a few different steps. First, install libusb-1.0-0-dev, which, notably, is not the same as libusb-dev on Debian. Then blacklist the STM32 dev board so that the kernel usb-storage driver won't get in the way of USB. I added:

echo "0483:3744:i" >/sys/module/usb_storage/parameters/quirks
to /etc/rc.local, which will work if usb-storage is built into your kernel (and may not work if it is a module -- it will forget every time it is unloaded). I also added:
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3744", MODE="0660", GROUP="staff"
to /etc/udev/rules.d/99-greg.rules, so that st-util would not need to run as root.

Well, stlink didn't work out of the box this time -- they added target voltage detection with libusb, and that crashes my STM32F100 dev board, probably because it is too old. 2011 is a long time ago. See pull request #424, which they will probably merge before you read this.

Also, I had to explicitly enable expat (XML) support when I built gdb, because st-util uses XML to share configuration information with gdb (install libexpat1-dev first):

$ ../gdb-7.7.1/configure --target=arm-none-elf --enable-interwork --enable-multilib --without-headers --disable-shared --with-gnu-as --with-gnu-ld --disable-nls --enable-expat --with-expat=yes

Build and install Capt'ns Missing Link, then this worked for me:

$ st-util &
$ arm-none-elf-gdb
(gdb) tar ext:4242
(gdb) ...

Also, I forgot to provide this earlier, but this Thumb-16 reference card is much better than the documentation in the Cortex manual. This does not have the Thumb-2 instructions, but the reduced complexity is worth it IMO.

June 15, 2018.

I have an STM32 permanently connected to my computer and I noticed it really needs to disable the USB mass storage driver much earlier in the boot process. So I added to /etc/default/grub:

GRUB_CMDLINE_LINUX_DEFAULT="usb-storage.quirks=0483:3744:i"

Then I ran update-grub.

November 17, 2020.

It's come to my attention that my "si" macro in gdb is unnecessary and there's an easier way to view the next instruction with "stepi":

display/i $pc
stepi
stepi
...