STM32 Entwicklung unter Linux

Mein Einstieg in die Programmierung der STM32 Mikrocontroller.

Das Board

Zum Einsatz kommt ein STM32-H103 Development Board von Olimex.

../../../_images/STM32-H103.png

Erster Test - Blinkende LED

Olimex bietet ein bereits compiliertes Beispiel Programm, das kann mit OpenOCD auf das Board geflasht werden, später gehe ich noch genauer auf OpenOCD ein.

Zum flashen benutze ich einen ARM USB JTAG Adapter, ebenfalls von Olimex.

../../../_images/jtag.png

So wenn das Board über ein USB Kabel vom PC mit Strom versorgt wird und der JTAG Adapter angeschlossen ist kann es losgehen.

Wir wechseln in das Verzeichnis mit dem entpackten Beispielprogramm:

[chris@thinkpad ~]$ cd projects_STM32/STM32-H103_flash/
[chris@thinkpad STM32-H103_flash]$ ls
arm_comm.h          main.c     stm32f10x_gpio.o
armusbocd.cfg       main.list  stm32f10x_rcc.o
armusbocd_tiny.cfg  main.o     stm_h103_blink_rom.cmd
lib                 main.out
main.bin            makefile

Da das Beispielprogramm einen ältere Version von OpenOCD benutzt müssen wir die Datei armusbocd_tiny.cfg editieren, so dass sie folgender maßen aussieht:

#daemon configuration
telnet_port 4444
gdb_port 3333

source [find interface/olimex-jtag-tiny.cfg]
source [find target/stm32f1x.cfg]

Der Rest in der Datei kann entweder auskommentiert oder gelöscht werden.

Jetzt verbinden wir uns via Telnet mit dem Debugger und können einen Reset ausführen und das Programm auf den Chip flashen. Bei den Befehlen muss sudo verwendet werden, da normale Nutzer unter Linux nicht genug Rechte haben um auf die USB Schnittstelle zu zugreifen, später können wir noch eine udev-Regel erstellen, die uns das ermöglicht.

[chris@thinkpad STM32-H103_flash]$ sudo openocd
sudo: openocd: command not found
[chris@thinkpad STM32-H103_flash]$ which openocd
~/tools/summon-arm-toolchain/bin/openocd
[chris@thinkpad STM32-H103_flash]$ sudo ~/tools/summon-arm-toolchain/bin/openocd -f armusbocd_tiny.cfg
Open On-Chip Debugger 0.5.0 (2011-12-05-11:10)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.berlios.de/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
1000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
cortex_m3 reset_config sysresetreq
Info : clock speed 1000 kHz
Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
Info : JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
Info : stm32.cpu: hardware has 6 breakpoints, 4 watchpoints

Nun kann in einer anderen Konsole eine Telnet-Verbindung hergestellt werden:

[chris@thinkpad STM32-H103_flash]$ telnet 127.0.0.1 4444
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Open On-Chip Debugger
> reset halt
JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000010 msp: 0x20000800
> flash probe 0
device id = 0x20036410
flash size = 128kbytes
device id = 0x20036410
flash size = 128kbytes
flash 'stm32f1x' found at 0x08000000
> stm32f1x mass_erase 0
stm32x mass erase complete
> flash write_bank 0 main.bin 0
wrote 4640 bytes from file main.bin to flash bank 0 at offset 0x00000000 in 0.342668s (13.223 KiB/s)
> reset run
JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
> exit
Connection closed by foreign host.
[chris@thinkpad STM32-H103_flash]$

Alle Zeilen die mit > beginnen sind dabei Eingaben, der Befehl flash write_bank 0 main.bin 0 schreibt zum Beispiel unser Beispielprogramm auf den Chip, anschließend wird die Telnet-Verbindung mit exit beendet, wenn alles funktioniert hat sollte die Status LED jetzt blinken.

Das Beispielprogramm selber compilieren

Fertige Compiler gibt es von Code Sourcery für die verschiedensten Betriebssysteme, allerdings nicht für 64-Bit Linux Betriebssysteme, und da Fedora auch keinen ARM BARE METAL CROSS-COMPILER im Repository hat (nur OpenOCD ist vorhanden: sudo yum install openocd), hab ich mir meinen Compiler und OpenOCD einfach selber compiliert, das HOWTO findet sich hier.

Ein Aufruf von make clean in der Konsole sollte die die Datei main.bin löschen und wir können gucken ob sich das Programm compilieren lässt:

[chris@thinkpad STM32-H103_flash]$ make
.compiling
arm-none-eabi-gcc -I./ -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb  main.c
make: *** No rule to make target `lib\stm32f10x_rcc.c', needed by `stm32f10x_rcc.o'.  Stop.
[chris@thinkpad STM32-H103_flash]$

OK ein Fehler, es sieht aus als wurde das Makefile für Windows geschrieben, also ersetzen wir mal die Backslashes im Makefile durch normale Schrägstriche, und siehe da es funktioniert:

[chris@thinkpad STM32-H103_flash]$ make
.compiling
arm-none-eabi-gcc -I./ -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb  lib/stm32f10x_rcc.c
.compiling
arm-none-eabi-gcc -I./ -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb  lib/stm32f10x_gpio.c
..linking
arm-none-eabi-ld -v -Tstm_h103_blink_rom.cmd -nostartfiles -o main.out main.o stm32f10x_rcc.o stm32f10x_gpio.o
GNU ld (GNU Binutils) 2.21.1
...copying
arm-none-eabi-objcopy -Obinary main.out main.bin
arm-none-eabi-objdump -S main.out > main.list
[chris@thinkpad STM32-H103_flash]$

Eine neue udev-Regel erstellen

Um es OpenOCD zu ermöglichen ohne Root-Rechte mit dem Board zu kommunizieren erstellen wir eine udev-Regel, die uns den einfachen Zugriff auf den JTAG-Adapter gestattet.

Als erstes holen wir uns mit lsusb die Device IDs:

[chris@thinkpad ~]$ lsusb
Bus 002 Device 015: ID 0bdb:1911 Ericsson Business Mobile Networks BV
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 003: ID 147e:2016 Upek Biometric Touchchip/Touchstrip Fingerprint Sensor
Bus 001 Device 005: ID 04f2:b217 Chicony Electronics Co., Ltd
Bus 002 Device 004: ID 056a:00e6 Wacom Co., Ltd
Bus 001 Device 012: ID 0a5c:217f Broadcom Corp. Bluetooth Controller
Bus 002 Device 016: ID 15ba:0004 Olimex Ltd. OpenOCD JTAG TINY

Das letzte Gerät entspricht unserem JTAG-Adapter.

Ohne diese Regel bringt uns OpenOCD folgenden Fehler wenn wir versuchen ohne Root-Rechte auf den JTAG-Adapter zuzugreifen:

[chris@thinkpad STM32-H103_flash]$ openocd -f armusbocd_tiny.cfg
Open On-Chip Debugger 0.5.0 (2011-12-05-11:10)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.berlios.de/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
1000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
cortex_m3 reset_config sysresetreq
Error: unable to open ftdi device: usb_open() failed
in procedure 'init'

Jetzt öffnen wir die Datei /etc/udev/rules.d/10-local.rules mit Root-Rechten:

[chris@thinkpad ~]$ sudo vim /etc/udev/rules.d/10-local.rules

und fügen folgende Zeile ein:

ATTR{idVendor}=="15ba", ATTR{idProduct}=="0004", GROUP="plugdev", MODE="0660" # Olimex Ltd. OpenOCD JTAG TINY

Damit das Ganze funktioniert sollten noch folgende Befehle ausgeführt werden, dann kann Rebooted werden und es sollte funktionieren:

[chris@thinkpad ~]$ sudo groupadd plugdev
[chris@thinkpad ~]$ sudo usermod -aG plugdev chris # Hier sollte chris mit deinem eigenen Benutzernamen ersetzt werden
[chris@thinkpad ~]$ sudo usermod -aG dialout chris
[chris@thinkpad ~]$ # sudo reboot

Und siehe da: OpenOCD funktioniert ohne Root-Rechte:

[chris@thinkpad STM32-H103_flash]$ openocd -f armusbocd_tiny.cfg
Open On-Chip Debugger 0.5.0 (2011-12-05-11:10)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.berlios.de/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
1000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
cortex_m3 reset_config sysresetreq
Info : clock speed 1000 kHz
Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
Info : JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
Info : stm32.cpu: hardware has 6 breakpoints, 4 watchpoints

Eclipse zum Debuggen einrichten

Zuerst müssen Eclipse und die C/C++ Development Tools installiert werden, Unter Fedora geht das am einfachsten über den Package Manager:

[chris@thinkpad ~]$ sudo yum install eclipse eclipse-cdt

Ein neues Projekt erstellen

Nun starten wir Eclipse, mein Betriebssystem ist auf Englisch eingestellt, daher sind die Menüeinträge in Eclipse auch auf Englisch. Zum Einsatz kommt Version 3.7 (Projektname “Eclipse Indigo”).

Schritt für Schritt:

  • Wir erstellen ein neues Arbeitsverzeichnich für die STM32 Entwicklung:

    • File -> Switch Workspace -> Other ...
    • Und nennen es z.B. eclipse_stm32_workspace /home/chris/stuff/eclipse_stm32_workspace
  • Jetzt erstellen wir ein neues C-Project

    • File -> New -> Project
    • Und wählen C/C++ C-Project aus
    • Next >
    • Project Name: STM32-HelloWorld
    • Wir wählen Makefile Project und dort Empty Project und unter Toolchain: Cross GCC
    • Next >
    • Finish
    • This kind of project is associated with the C/C++ perspective. Do you want to open this perspective now?: Yes
  • Wir importieren unser Beispielprojekt von Oben

    • Wir kopieren einfach alle Dateien aus unserem Beispielprogramm projects_STM32/STM32-H103_flash in das Verzeichnis eclipse_stm32_workspace/STM32-HelloWorld/

    • Im Project Explorer Rechtsklick auf STM32-HelloWorld und Refresh

    • Wenn ich jetzt auf das Hammer Symbol drücke compiliert das Programm ohne Probleme, da der Compiler schon bei mir im Pfad ist.

      [chris@thinkpad ~]$ cat .bashrc |grep arm
      export PATH=$PATH:/home/chris/tools/summon-arm-toolchain/bin
      

OpenOCD zu den External Tools hinzufügen

  • Einen Befehl zum Flashen hinzufügen:
    • Run -> External Tools -> External Tools Configurations...
    • Rechtsklick auf Programm und dann New
    • Name: OpenOCD Programmer
    • Location: /home/chris/tools/summon-arm-toolchain/bin/openocd
    • Working Directory: ${project_loc}
    • Arguments: -f ${project_loc}/flash.cfg
    • Im Tab Common noch ein Häkchen unter Display in favortites menue bei External Tools setzen
Screenshot Settings
  • Hier die Datei flash.cfg, einfach mit in das Projektverzeichnis kopieren:

    #daemon configuration
    telnet_port 4444
    gdb_port 3333
    source [find interface/olimex-jtag-tiny.cfg]
    source [find target/stm32f1x.cfg]
    init
    reset halt
    stm32f1x mass_erase 0
    flash write_bank 0 main.bin 0
    reset run
    shutdown
    
  • Das Output in Eclipse sieht dann folgendermaßen aus:

    Open On-Chip Debugger 0.5.0 (2011-12-05-11:10)
    Licensed under GNU GPL v2
    For bug reports, read
            http://openocd.berlios.de/doc/doxygen/bugs.html
    Info : only one transport option; autoselect 'jtag'
    1000 kHz
    adapter_nsrst_delay: 100
    jtag_ntrst_delay: 100
    cortex_m3 reset_config sysresetreq
    Info : clock speed 1000 kHz
    Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
    Info : JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
    Info : stm32.cpu: hardware has 6 breakpoints, 4 watchpoints
    Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
    Info : JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
    target state: halted
    target halted due to debug-request, current mode: Thread
    xPSR: 0x01000000 pc: 0x00000010 msp: 0x20000800
    Info : device id = 0x20036410
    Info : flash size = 128kbytes
    stm32x mass erase complete
    wrote 4640 bytes from file main.bin to flash bank 0 at offset 0x00000000 in 0.336948s (13.448 KiB/s)
    Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
    Info : JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
    shutdown command invoked
    
  • Einen Befehl zum Starten des GDB Servers hinzufügen:

    • Run -> External Tools -> External Tools Configurations...
    • Rechtsklick auf Programm und dann New
    • Name: OpenOCD Server
    • Location: /home/chris/tools/summon-arm-toolchain/bin/openocd
    • Working Directory: ${project_loc}
    • Arguments: -f ${project_loc}/armusbocd_tiny.cfg
    • Im Tab Common noch ein Häkchen unter Display in favortites menue bei External Tools setzen

Mit OpenOCD Debuggen

  • Eclipse zum Debuggen mit OpenOCD konfigurieren

    • Run -> Debug configurations...

    • Rechtsklick auf GDB Hardware Debugging

    • New

    • Im Tab Debugger

      • GDB Command: /home/chris/tools/summon-arm-toolchain/bin/arm-none-eabi-gdb
      • Port number auf 3333 setzen
    • Im Tab Startup

      • In das Textfeld unter Halt kommt folgendes rein zur Initialisierung:
      monitor soft_reset_halt
      monitor wait_halt
      monitor poll
      monitor flash probe 0
      monitor stm32f1x mass_erase 0
      monitor flash write_bank 0 main.bin 0
      monitor soft_reset_halt
      thbreak main
      continue
      
  • So fertig, zum Debuggen muss jetzt nur noch der OpenOCD über das External Tools Menü gestartet werden und dann kann durch drücken auf das “Käfer” Symbol in der Toolbar das Programm auf der Hardware im Debug Modus ausgeführt werden.

  • Es kann Probleme geben wenn vor dem Klicken auf das External Tools Menü oder auf das Debug Symbol nicht das Projekt im Project Explorer ausgewählt ist, eine mögliche Fehlermeldung ist z.B. Variable references empty selection: ${project_loc}. Zudem kann es beim erstem mal notwendig sein den Debugger aus dem Einstellungsfenster zu starten, damit ein neuer Debug Menüeintrag in der Toolbar erscheint.

Screenshot Debugging

Das erste eigene Programm

Einige Informationen

Einen Übersicht über die verschiedenen STM32 F1 Controller gibt es hier, und hier die Seite mit allen Datenblättern etc. zum STM32F103RBT6.

Nützliche Dokumente:

Setup

Zuerst laden wir uns mal die STM32F10x standard peripheral library herunter. Oh je, das ist ja ganze Menge Code, nichts mit 10 Header Dateien, mal sehen.

Notiz

Es gibt auch eine GPL lizenzierte Alternative: libopencm3 (hieß früher libopenstm32).

Die Datei stm32f10x_stdperiph_lib_um.chm enthält dabei ein ziemlich umfangreiches Benutzerhandbuch und lässt sich z.B. mit dem KchmViewer (sudo yum install kchmviewer) öffnen.

Erstellen wir erst mal einen Projekt Ordner, entpacken die STM32F10x_Standard_Peripherals_Library dort hin und legen einen Ordner STM32_GCC_Template für unser Programm an:

[chris@thinkpad STM32]$ tree stm32-projects -L 3
stm32-projects
├── STM32_GCC_Template
└── STM32F10x_StdPeriph_Lib_V3.5.0
    ├── _htmresc
    │   ├── CMSIS_Logo_Final.jpg
    │   └── logo.bmp
    ├── Libraries
    │   ├── CMSIS
    │   └── STM32F10x_StdPeriph_Driver
    ├── Project
    │   ├── STM32F10x_StdPeriph_Examples
    │   └── STM32F10x_StdPeriph_Template
    ├── Release_Notes.html
    ├── stm32f10x_stdperiph_lib_um.chm
    └── Utilities
        └── STM32_EVAL

Das Template

Hier gibt es ein sehr schönes STM32VLDiscovery project template for GCC, das auf dem Beispiel STM32Test.zip von http://www.emb4fun.de/arm/examples/index.html basiert.

Nach ein paar kleinen Anpassungen (Groß/Kleinschreibung von Dateinamen, vergessene Punkte im Makefile compiliert es dann auch unter Linux, hier die reparierte Version.

Leider landet der Controller beim STM32Test.zip Beispiel als auch im Template von oben immer im HardFault_Handler selbst wenn sich in der Main-Funktion nur eine Endlosschleife befindet, irgendwas stimmt wohl mit der Initialisierung nicht aber momentan habe ich keine Ahnung wo der Fehler genau liegt.

Sehen wir mal was die libopencm3 zu bieten hat, der Download funktioniert über Git:

[chris@thinkpad stm32-projects]$ git clone git://libopencm3.git.sourceforge.net/gitroot/libopencm3/libopencm3

Wow, ein ganzer Ordner mit Beispielen für mein Board:

[chris@thinkpad stm32-projects]$ tree -L 1 libopencm3/examples/stm32/f1/stm32-h103/
libopencm3/examples/stm32/f1/stm32-h103/
├── button
├── exti_both
├── exti_rising_falling
├── fancyblink
├── led_stripe
├── miniblink
├── pwm_6step
├── spi
├── stm32-h103.ld
├── timer
├── traceswo
├── usart
├── usart_irq
├── usart_irq_printf
├── usart_printf
├── usb_cdcacm
├── usb_dfu
├── usb_hid
└── usb_iap

Na dann compilieren wir das ganze mal:

[chris@thinkpad stm32-projects]$ cd libopencm3
[chris@thinkpad libopencm3]$ make
...
[chris@thinkpad libopencm3]$ cd examples/stm32/f1/stm32-h103/miniblink/
[chris@thinkpad miniblink]$ ls
flash.cfg  Makefile  miniblink.bin  miniblink.c  miniblink.d  miniblink.elf  miniblink.hex  miniblink.list  miniblink.o  miniblink.srec  README
[chris@thinkpad miniblink]$

OK das hat doch schon mal wunderbar funktioniert, die Datei flash.cfg habe ich erstellt und sieht folgendermaßen aus:

#daemon configuration
telnet_port 4444
gdb_port 3333
source [find interface/olimex-jtag-tiny.cfg]
source [find target/stm32f1x.cfg]
init
reset halt
flash probe 0
stm32f1x mass_erase 0
flash write_bank 0 miniblink.bin 0
#flash write_image erase miniblink.bin
reset run
shutdown

Flashen:

[chris@thinkpad miniblink]$ openocd -f flash.cfg

Nach ersetzen der Zeile OOCD_INTERFACE ?= flossjtag in libopencm3/examples/stm32/f1/Makefile.include durch OOCD_INTERFACE ?= olimex-jtag-tiny funktioniert auch das flashen über make flash und die Datei flash.cfg kann wieder gelöscht werden:

[chris@thinkpad miniblink]$ rm flash.cfg
[chris@thinkpad miniblink]$ make flash
FLASH   miniblink.hex
[chris@thinkpad miniblink]$

Und siehe da, es funktioniert, ich denke das ist erst mal ein guter Startpunkt um sich tiefer in die STM32 Entwicklung einzuarbeiten.

Die Serielle Schnittstelle

Versuchsaufbau

Auf die Stiftleisten an der Unterseite der Platine passt ein abgeschliffener Stecker von einem 20 poligen Flachbandkabel wie er auch bei den JTAG Kabeln benutzt wird (etwas anderes passendes hatte ich gerade nicht zur Hand, und auf eine Platine löten wollte ich das Ding nicht).

../../../_images/STM32-H103-unterseite.png

Die Belegung des Flachbandkabels ist dann im nächsten Bild zu sehen, die Zahlen entsprechen dabei den Pinnummern des zweiten Extension Headers auf der Unterseite des Boards:

../../../_images/flachband_kabel_belegung.png

Aus der Anleitung STM32-H103.pdf von Olimex geht hervor das Pin 14 PB10/UART3.TX und Pin 15 P11/UART3.RX entspricht, also verbinden wir UART3.RX mit TX von unserem 5V USB Serial Wandler und UART3.TX mit RX. Das fertig verkabelte Board ist im nächsten Bild zu sehen:

Foto Aufbau

Als Terminal Programme für den Serialport unter Linux eigenen sich z.B. GtkTerm:

../../../_images/gtkterm.png

Oder CuteCom:

../../../_images/cutecom.png

Beide lassen sich unter Fedora einfach mit diesem Befehl installieren:

[chris@thinkpad ~]$ sudo yum install gtkterm cutecom

Das komplette Programm:

#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/nvic.h>
#include <stdio.h>
#include <errno.h>
void clock_setup(void)
{
    rcc_clock_setup_in_hse_8mhz_out_72mhz();
    // Enable GPIOC clock.
    rcc_peripheral_enable_clock(&RCC_APB2ENR,
               RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
               RCC_APB2ENR_IOPCEN);
    // Enable clocks for GPIO port B
    // (for GPIO_USART3_TX) and USART3.
    rcc_peripheral_enable_clock(&RCC_APB2ENR,
               RCC_APB2ENR_USART1EN);
    rcc_peripheral_enable_clock(&RCC_APB1ENR,
                                RCC_APB1ENR_USART2EN |
                                RCC_APB1ENR_USART3EN);
}
void usart_setup(void)
{
    // Setup GPIO pin GPIO_USART3_TX/GPIO10
    // on GPIO port B for transmit.
    gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ,
                  GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART3_TX);
    // Setup UART parameters.
    usart_set_baudrate(USART3, 38400);
    usart_set_databits(USART3, 8);
    usart_set_stopbits(USART3, USART_STOPBITS_1);
    usart_set_parity(USART3, USART_PARITY_NONE);
    usart_set_flow_control(USART3, USART_FLOWCONTROL_NONE);
    usart_set_mode(USART3, USART_MODE_TX);
    // Finally enable the USART.
    usart_enable(USART3);
}
void gpio_setup(void)
{
    gpio_set(GPIOC, GPIO12);
    // Setup GPIO6 and 7 (in GPIO port A) for LED use.
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ,
                  GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
}
int _write(int file, char *ptr, int len)
{
    int i;
    if (file == 1)
    {
        for (i = 0; i < len; i++)
            usart_send_blocking(USART3, ptr[i]);
        return i;
    }
    errno = EIO;
    return -1;
}
int main(void)
{
    int counter = 0;
    float fcounter = 0.0;
    double dcounter = 0.0;
    clock_setup();
    gpio_setup();
    usart_setup();
    // Write Hello World, an integer, float and double all over
    // again while incrementing the numbers.
    while (1)
    {
        gpio_toggle(GPIOC, GPIO12);
        printf("Hello World! %i %f %f\r\n", counter, fcounter,
              dcounter);
        counter++;
        fcounter += 0.01;
        dcounter += 0.01;
        int i;
        for (i = 0; i < 8000000; i++) // Wait a bit.
        __asm__("nop");
    }
    return 0;
}

Das Programm befindet sich hier zum Download.