STM32 - Einstieg mit Em::Blocks

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

In diesem Tutorial werde ich versuchen, möglichst einfach, die ersten Schritte zur Entwicklung von STM32 Anwendungen mit der Em::Blocks IDE zu erklären. Hier soll nicht so stark auf die einzelnen Peripherie-Module der Controller eingegangen werden, sondern mehr auf die IDE und die verschiedenen Libraries, wie die CMSIS Library, die DSP-Library, die Standard-Peripheral-Library, die USB-Library und vllt. über Ethernet. Ich denke ein Kapitel zu FreeRTOS wird es auch geben. Außerdem stehen im Vordergrund Sachen wie z.B. booten aus dem SRAM, In-Application-Programming, FPU, Core-Coupled-RAM usw. Mal sehn wie weit ich komme ;)

Die Entwicklungsumgebung

Hardware

Zum testen der Anwendungen, benutze ich das STM32F4-Discovery Board, welches einen Debugger, Peripherie und natürlich einen STM32 Controller (STM32F407VGT6) enthält. Deshalb sind alle Beispiele hier für den STM32F407VG Controller ausgelegt, können aber meisten 1:1 auf andere Controller portiert werden. Ich werde auch möglichsts keine STM32F4-Discovery Board spezifischen Libraries verwenden, damit der Portierungs-Vorgang möglichsts einfach bleibt.

Software

In diesem Tutorial verwende ich Em::Blocks als IDE. Em::Blocks ist eine kostenlose, nur für Windows verfügbare IDE, die alle STM32 Mikrcontroller unterstützt und sehr einfach zu bedienen ist. Nach dem Download kann die IDE per Dialog, sehr einfach, installiert werden. Als Compiler beinhaltet Em::Blocks den ARM GCC Compiler. Die IDE unterstützt zurzeit den ST-Link, den J-Link und es gibt eine Generic-Vorlage, durch welche andere Debugger konfiguriert werden können.

Außerdem muss noch der passende Treiber für den Debugger installiert werden. Verwendet man ein Discovery-Board muss der ST-Link/V2-Treiber heruntergeladen und installiert werden. (Die Installation muss evtl. als Administrator ausgeführt werden, da sonst die Installation unvollständig wird.) Nach der Installation des Treibers kann man den Debugger über die entsprechende USB Schnittstelle an den PC anschließen und es sollte die Meldung von Windows kommen, dass der richtige Treiber gefunden wurde.

Libraries

Zum programmieren benötigt man noch die CMSIS und die Standard-Peripheral-Library (z.B. für die F4 Controller Standard-Peripheral-Library-F4), die man am besten in einen Ordner (z.B. Dokumente\STM32) entpackt

Basis-Wissen zur IDE

Hinzufügen von Dateien zum Projekt

Im Projekt-Manager Rechts Klick auf <ProjektName> -> Add files...

Enfernen von Dateien aus dem Projekt

Im Projekt-Manager Rechts Klick auf die Datei / mehrere Dateien auswählen -> Remove file from project Die Dateien werden lediglich aus dem Projekt entfernt und nicht von der Festplatte gelöscht.

Hinzufügen von Include-Pfaden

Sofern die Include-Datei(en) nicht im standard Include-Ordner inc des Projektes liegen, muss man den Pfad zum Projekt hinzufügen, damit der Compiler die Include-Dateien später auch finden kann. Man geht wie folgt vor: Im Projekt-Manager <ProjektName> -> Rechts Klick -> Build options... -> Search Directories -> Add

Achtung: Es ist standardmäßig im Fenster links Debug ausgewählt. Hier muss darüber das Projekt ausgewählt werden um global die Include-Dateien hinzuzufügen.

Hinzufügen von globalen Defines zum Build

Unter <ProjektName> -> Rechts Klick -> Build options... -> #defines können globale Defines hinzugefügt werden.


Achtung: Hier gilt ebenfalls wie bei den Include-Dateien dass standardmäßig im Fenster links Debug ausgewählt ist. Hier muss darüber das Projekt ausgewählt werden um global die Defines hinzuzufügen.

Compilieren

Entweder über das Build Menü oder über F7

Um eine genau Ausgabe des Compiler-Aufrufes mit allen Argumenten zu bekommen, geht man im Settings Menü auf Tools... und dann auf Toolchain executables. Ganz unten, unter Logging kann man dann Full command line auswählen.

Compiler Optionen

Optimierung

Standardmäßig verwendet Em::Blocks keine Optimierung. Unter <ProjektName> -> Rechts Klick -> Build options... -> Categories: Optimization kann zwischen verschieden Optimierungen ausgewählt werden.

Verschiedene C-Standards

Unter <ProjektName> -> Rechts Klick -> Build options... -> Categories: Language standard kann man zwischen verschieden C-Standard auswählen, wie z.B, C99. (Mit C99 können auch die Lauf-Variable in der for-Schleife deklariert werden: for(int i=0;..))

Debuggen

  • Als erstes den Debugger starten über das Debug Menü und Start/stop Debug Session oder über F8
  • Danach läuft die Anwendung auf dem Controller und warten im Reset_Handler auf Einzelschritt- bzw. Run- Befehle (F5, F10, F11 usw..(siehe Debug-Menü für alle Möglichkeiten))
  • Mit F8 wird die Debug-Session auch wieder beendet
  • Unter Debug -> Interfaces -> Target settings können Eigenschaften, wie z.B Run to main() aktiviert werden.

Register View

Damit man sich beim Debuggen bequem die Werte der Register (der Peripherie-Register) ansehen kann, braucht man eine, zum Controller passende System View Description-Datei (*.svd), die die verschiedenen Register beschreibt (Name, Adresse, usw..). In Em::Blocks gibt es dazu ein Tool, womit man solche Dateien herunterladen und verwenden kann.

Um sich nur die Core-Register anzusehen / zu bearbeiten braucht man keine SVD-Datei.

  • Debug -> Plugins -> SVD repository
  • Controller-Hersteller und Controller-Reihe auswählen
  • Speicherort auswählen (Standard ist das aktuelle Projekt-Verzeichnis)
  • Wenn das Häkchen bei Set and launch target debug configuration gesetzt ist, öffnen sich die Debug-Einstellungen automatisch, und das neue SVD-File wird verwenden.
  • Debug-Einstellungen mit OK schließen

Projekt als Template abspeichern

Unter File -> Save project as template.... Das Template erscheint dann im Projekt-Dialog unter User templates

Das erste Projekt erstellen

Hier erkläre ich, wie man am Besten (aus meiner Sicht), ein neues Projekt von Grund auf erstellt, sodass man hinterher, den Controller-Kern über die CMSIS-Library und die Peripherie über die Register steuern kann.

1. Projekt mit der IDE erstellen

Nach der Installation der oben beschriebenen Komponenten erstellt man am besten ein Verzeichnis (z.B. Dokumente\EmBlocks), indem später alle Projekte gespeichert werden. Für Libraries kann man auch noch ein Verzeichnis (z.B. Dokumente\STM32) erstellen, wo alle Libraries gespeichert werden, die man für das Projekt braucht.

Als nächstes startet man EmBlocks. Bei dem ersten Start öffnet sich ein Fenster, wo verschiedene Compiler angezeigt werden, die Em::Blocks findet. Dort wählt man den ARM GCC Compiler aus und klickt OK. Nun sollte sich das Hauptfenster von Em::Blocks öffnen. Über Create a new Project oder File -> New -> Project ruft man den Projekt-Dialog auf und kann ein neues Projekt erzeugen.

Auf der Ersten Seite wählen man den STmicro-ARM Projekt-Typ aus.

Neues Projekt erstellen


Auf der nächsten Seite wird man darauf hingewiesen, dass das der entsprechende Projekt-Typ-Dialog ausgeführt wird. Hier kann man ein Häkchen bei Skip this page next time setzen und auf OK klicken.

Im nächsten Schritt wählen man das zuvor erstellt Verzeichnis unter Folder to create Project in aus, indem später alle Projekt gespeichert werden. Außerdem geben wir dem Projekt einen Namen (z.B. FirstProject).

Neues Projekt erstellen


Danach muss man einen Compiler auswählen und bestimmen, welche Projekt-Konfigurationen erszeugt werden sollen. Unter Compiler wählt man ARM GCC Compiler und man kann das Häkchen bei Create "Release" configuration entfernen, da diese unnötig ist, die Debug-Konfiguration sollte eig. immer ausreichen.

Neues Projekt erstellen


Auf den folgenden Seite wird der Controller ausgewählt. Verwendet man ein STM32F4-Discovery Board müssen folgende Dinge gewählt werden: Cortex_M4 (F3xx - F4xx) -> STM32F4xx (Cortex M4 with FPU) -> STM32F407VG. Leider sind die Auswahl Möglichkeiten nicht ganz richtig, da auch die F3 Controller einen Cortex M4 Kern und eine FPU haben (Ist aber nicht so wichtig, ist nur eine formale Sache). Außerdem entfernt man das Häkchen bei Standard Peripherals Library (Wie man die Standard-Peripheral-Library einbindet und verwendet erkläre ich später). Man kann auch das Häkchen bei Create hex file for Realease target entfernen. Unter Stack Size und Heap Size wird die Größe von Stack und Heap festgelegt. Für eine erste Anwendung kann man die Einstellungen so lassen (Stack-Size: 0x0100 => 256 Byte Stack und Heap-Size: 0x0000 => kein Heap). Mit Finish ist der Projekt Dialog abgeschlossen.

Neues Projekt erstellen


Danach sollten sich zwei neue Fenster öffnen, um die Debug-Eigenschaften fest zu legen. Verwendet man ein Discovery Board/ST-Link müssen die Einstellungen wie auf dem Bild gewählt werden. Über den Button Settings öffnet sich das zweite Fenster, falls sich dieses nicht schon von alleine öffnen sollte. Nach den Schließen der Fenster über OK ist der Projekt Dialog beendet und ein neues Projekt steht zur Verfügung.

Debug Eigenschaften 1. Fenster


Debug Eigenschaften 2. Fenster

2. CMSIS-Library und Startup-Code hinzufügen

Wenn man sich, nachdem man ein neues Projekt in der IDE erstellt hat, die bereits vorhandenen Dateien im Projekt ansieht, stellt man fest, das bereits ein Ordner cmsis existiert, der einige wichtige Include-Dateien der CMSIS-Library enthält. Allerdings sind das wirklich nur die wichtigsten Include-Dateien der CMSIS-Library und dazu noch die aus einer älteren Version. Ich ziehe es deshalb vor die "Mini-CMSIS-Libraray" und die system_stm32f4xx.c/.h Dateien des Projektes durch aktuellere Versionen zu ersetzen. So ist später auch die Integration von anderen Libraries, wie z.B. der CMSIS-DSP-Libarary einfacher.

Zunächst geht man in das Projekt-Verzeichnis und löscht:

  • Den Ordner cmsis
  • Alle Dateien im Include Verzeichnis inc
  • Die Datei system_stm32f4xx.c im src Ordner

Außerdem entfernt man auch alle gelöschten Dateien in der IDE aus dem Projekt mit FirstProject -> Rechts Klick -> Remove file from project

Es sollte folgende Verzeichnis Struktur überbleiben:

Überbleibende Dateien


Jetzt müssen die gelöschten Dateien durch die aktuelleren ersetzt werden.

  • Dazu kopiert man den gesamten Ordner CMSIS aus dem CMSIS Download in das Projekt Verzeichnis.
  • Aus dem Standard-Peripheral-Library Download kopiert man von Libraries\CMSIS\Device\ST\STM32F4xx\Include alle Dateien in den inc Ordner des Projekt Verzeichnisses
  • Aus der Standard-Peripheral-Library kopiert man ebenfalls von Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates die Datei system_stm32f4xx.c in den src Ordner des Projekts

Anschließend müssen die neuen Dateien in der IDE dem Projekt hinzugefügt werden. Dazu benutzt man FirstProject -> Rechts Klick -> Add files.

  • Aus dem CMSIS Ordner im Projekt Verzeichnis werden aus den Unterorder Include folgende Dateien dem Projekt hinzugefügt:
    • arm_common_tables.h
    • arm_const_structs.h
    • arm_math.h
    • core_cm4.h
    • core_cm4_simd.h
    • core_cmFunc.h
    • core_cmInstr.h
  • Alle Dateien aus den inc Ordner des Projektes
  • Die Datei system_stm32f4xx.c aus dem src Ordner

So sollte die Struktur dann aussehen:

Anwenungs-Grundgerüst


Da einige Dateien der Libraries später bearbeitet werden müssen und schreibgeschützt sind, weise ich darauf hin, dass der Schreibschutz in der IDE über Rechts Klick auf die Datei -> Properties -> Häkchen bei "File is read only" entfernen entfernt werden kann.

Damit die IDE die Header-Dateien der CMSIS Library auch finden kann, weil diese ja nicht im standard Include Ordner inc liegen, muss unter FirstProject -> Build Options -> Search Directories der Pfad CMSIS\Include hinzugefügt werden.

Compiliert man jetzt das Projekt mit F7 gibt es ein paar Fehlermeldungen, weil keine Controller-Reihe ausgewählt wurde. Ebenfalls unter FirstProject -> Build Options -> Compiler Settings -> #defines wird der Define STM32F40_41xxx hinzugefügt.

Beim erneuten Compilieren sollten sich nun keine neuen Fehler ergeben.

3. Das erste Programm

Nachdem ein grundsätzliches "Gerüst" fertig ist, kann man den ersten Code schreiben. main.c:

#include "stm32f4xx.h"

int main(void)
{
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;    //Takt für GPIO aktivieren
    GPIOD->MODER |= GPIO_MODER_MODER15_0;   //GPIOD Pin15 als Ausgang
    GPIOD->BSRRL |= GPIO_BSRR_BS_15;        //GPIOD Pin15 auf High ziehen

    while(1)
    {
    }
}

Mit F8 wird dann der Debugger gestartet. Hat der Debugger das Projekt erfolgreich übertragen kann die Ausführung mit F5 begonnen werden.

Libraries einbinden/verwenden

Um Libraries, wie hier im Tutorial, einzubinden, wird ein existierendes Projekt, wie ein im Kapitel Das Erste Projekt erstellen beschrieben wird, vorausgesetzt.

Einbinden der Standard-Peripheral-Library

Um nicht immer die einzelnen Register, wie im ersten "bare-metal" Beispiel, aus dem Reference-Manual zu suchen und zu beschreiben, gibt es von ST die Standard Peripheral Library, die diese Aufgaben übernimmt und mit der sich die "normale/grundlegende" Peripherie (alles außer USB und Ethernet) recht einfach steuern lässt. Ich kopiere die Standard-Peripheral-Library immer per Hand in das Projekt. Dadurch hat man auch gleich die aktuellste Version (in den älteren Versionen gibt es einige Bugs).

  • 1. Den Ordner Libraries\STM32F4xx_StdPeriph_Driver aus dem Standard-Peripheral-Library Download in das Projekt-Verzeichnis kopieren
  • 2. Die Datei Project\STM32F4xx_StdPeriph_Templates\stm32f4xx_conf.h ebenfalls aus dem Standard-Peripheral-Library Download in den inc Ordner des Projektes kopieren.
  • 3. In der IDE den Include-Path STM32F4xx_StdPeriph_Driver\inc hinzufügen
  • 4. Und das/den Define USE_STDPERIPH_DRIVER hinzufügen
  • 4. Source- und Include-Dateien zum Projekt hinzufügen
    • Achtung: Es dürfen nur die Dateien hinzugefügt werden, für die der Controller auch die entsprechende Peripherie bereit stellt. Diese Dateien dürfen nicht beim STM32F4-Discovery/STM32F407VG hinzugefügt werden.
      • stm32f4xx_dma2d.c/.h
      • stm32f4xx_fmc.c/.h
      • stm32f4xx_ltdc.c/.h
      • stm32f4xx_sai.c/.h

Daraus resultiert folgende Struktur:

Header-Struktur Source-Struktur


Anschließend kann die Standard-Peripheral-Library benutzt werden. main.c:

#include "stm32f4xx.h"

int main(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    GPIO_InitTypeDef GPIOD_InitStructure;
    GPIOD_InitStructure.GPIO_Mode = GPIO_Mode_OUT;     //Als Ausgang
    GPIOD_InitStructure.GPIO_OType = GPIO_OType_PP;    //Push-Pull Betrieb
    GPIOD_InitStructure.GPIO_Pin = GPIO_Pin_15;        //Pin 15 (STM32F4-Discovery: Blaue LED)
    GPIOD_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  //Kein Pull-Up oder Pull-Down Widerstand aktiviert
    GPIOD_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; //25MHz Update-Rate
    GPIO_Init(GPIOD, &GPIOD_InitStructure);

    GPIO_SetBits(GPIOD, GPIO_Pin_15);

    while(1)
    {
    }
}

Hier ist noch ein "BasicFramework", dass die komplette CMSIS- und Standard-Peripheral-Library enthält. Es kann quasi als Basis für alle Projekte verwendet werden. Allerdings ist das Framework primär auf die STM32F405/7 und STM32F415/7 ausgelegt. Die neueren STM32F401, STM32F42X und STM32F43X haben noch etwas andere Peripherie, das Framework müsste dann entsprechend angepasst werden. Download: Datei:StdPeriphDriver BasicFramework.zip

Einbinden der DSP-Library

STM32F3 / STM32F4 Microcontroller haben neben der FPU noch einen Digital-Signal-Processor (DSP). Die DSP dient hauptsächlich dazu, viele Daten schnell zu verarbeiten (z.B. Video- oder Audiosignal). Der CMSIS-Download beinhaltet dazu neben der eigentlichen CMSIS-Library auch noch eine DSP-Library. Diese Library stellt sowohl einfache, mathematische Funktionen wie addieren, multiplizieren usw. als auch komplexe Funktionen, wie z.B einen IIR-Filter, zur Verfügung. Die DSP-Library ist weiter aufgeteilt in kleinere Module, wie z.B. BasicMathFunctions, FilteringFunctions usw. (siehe CMSIS\DSP-Library\Source).

  • 1. Source-Dateien des benötigten Moduls zum Projekt hinzufügen. (<ProjektName> -> Rechts Klick -> Add files...)
  • 2. arm_math.h in der main.c einbinden
  • 3. Damit die Warnung "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" verschwindet, muss der Define __FPU_PRESENT hinzugefügt werden. (Keine Ahnung, wieso es die Warnung gibt)

Jetzt kann die DSP-Library verwedet werden. Das Beispiel multipliziert zwei Buffer miteinander. main.c:

#include "stm32f4xx.h"
#include "arm_math.h"

int main(void)
{
    float32_t x[1024];
    float32_t y[1024];
    float32_t z[1024];

    for(uint16_t i = 0; i < 1024; i++)
    {
        x[i] = i;

    }

    for(uint16_t i = 0; i < 1024; i++)
    {
        y[i] = PI;
    }

    arm_mult_f32(x, y, z, 1024);

    while(1)
    {
    }
}

Startup-Code, Linker-Script, Takt, FPU usw.

Startup-Code

Em::Blocks fügt standardmäßig einen Startup-Code mit in das Projekt ein. Die wichtigsten Dinge die darin erledigt werden sind folgende:

  • Stack und Heap wird angelgt
  • Die Vector-Table wird angelegt
  • Ein Default Reset-Handler wird implementiert, dieser erledigt folgende aufgaben:
    • Kopieren der definierten Daten aus dem Flash in den SRAM
    • SystemInit() wird aufgerufen, wenn __NO_SYSTEM_INIT nicht definiert ist
    • Sprung in die main() Funktion
  • Allen anderen Interrupt-Handler wird eine Default-Funktion def_irq_handler mit einer while(1)-Schleife zugewiesen, falls kein anderer Interrupt-Handler existiert.

Normalerweise muss am Startup-Code nichts verändert werden.

Linker-Script

Em::Blocks stellt zwei fertige Linker-Scripts bereit, eins für die Ausführung des Programmes aus dem Flash (*_flash.ld), das andere für die Ausführung aus dem SRAM (*_sram.ld).

Normalerweise muss auch hier nichts geändert werden, es sei den Daten sollen in bestimmten Adressbereichen abgelegt werden (z.B. im Core-Coupled RAM), was ich später noch zum Tutorial hinzufüge.

Der Takt

Jeder Controller oder Prozessor braucht einen Takt, indem er die einzelnen Maschinen-Befehle abarbeiten kann. Bei den STM32 Mikrocontrollern kann dieser Takt aus verschiedenen Quellen kommen:

1. High Speed Internal Oszillator (HSI)

2. High Speed External Oszillator (HSE)

3. Low Speed Internal Oszillator (LSI)

4. Low Speed External Oszillator (LSE)

5. Phase-Locked Loop (PLL) (generiert den Takt aus den ersten vier Taktquellen durch Teiler und Faktoren)

Nach einem Reset wird der Controller aus der HSI Taktquelle versorgt (16MHz beim STM32F407VG). Um die voll Performance des Controllers zu erreichen muss die PLL verwendet werden, welche den Takt durch geschickte Teiler und Faktoren vervielfältigt, sodass am Ende der Controller mit der maximalen Taktfrequenz läuft. Die notwendige Initialisierung der PLL findet in der Funktion SystemInit() statt. Die PLL erzeugt zunächst einen Basistakt:

PLL_VCO = (F_HSE / PLL_M) * PLL_N

Aus diesem Takt wird dann der Takt für den Controller-Kern abgeleitet:

SYSCLK = PLL_VCO / PLL_P

Und der Takt für SDIO / USB / RNG (48MHz):

48MHz = PLL_VCO / PLL_Q


Durch weiter Teiler werden, die Takte für die einzelnen Busse erzeugt, da diese nicht alle beliebig schnell betrieben werden dürfen.

Genaue Angaben findet man zu dem jeweiligen Clock-System des Controllers im Reference Manual.

Ein Blick in den "Kommentar-Header" verrät, dass von einem externen (HSE) Oszillator von 25MHz ausgegangen wird, der die PLL versorgen soll.

SYSCLK = ((25MHz / PLL_M(25)) * PLL_N(336)) / PLL_P(2) = 168MHz

Allerdings stimmt diese Rechnung natürlich nur, wenn wirklich ein 25MHz Quarz am Controller angeschlossen ist. Auf dem STM32F4-Discovery Board befindet sich aber nur ein 8MHz Quarz. Demnach gilt:

SYSCLK = ((8MHz / PLL_M(25)) * PLL_N(336)) / PLL_P(2) = 53.76MHz

Der Controller läuft also nur mit 53.76MHz, wenn wie ober beschrieben ein Projekt erstellt wird. Allerdings kann dieses Problem durch ändern von den betroffen Defines behoben werden:

  • Dazu geht man in die system_stm32f4xx.c Datei und passt zur Vollständigkeit erstmal die zum Controller passende Tabelle im "Kommentar-Header" an (Z.57f):
*-----------------------------------------------------------------------------
*      HSE Frequency(Hz)                      | 8000000
*-----------------------------------------------------------------------------
*      PLL_M                                  | 8
*-----------------------------------------------------------------------------


  • Danach passt man den PPL_M Define (Z.254) an:
/************************* PLL Parameters *************************************/
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M      8


  • Außerdem muss man den HSE_VALUE Define in der inc/stm32f4xx.h angepasst werden (Z.122):
#if !defined  (HSE_VALUE)
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */

#endif /* HSE_VALUE */

Eine neue Berechnung zeigt:

SYSCLK = ((8MHz / PLL_M(8)) * PLL_N(336)) / PLL_P(2) = 168MHz

Jetzt läuft der Controller mit maximaler Geschwindigkeit.

Die Theorie wird durch folgendes Programm verifiziert. Der geviertelte System-Takt SYSCLK wir am Pin PC9 ausgegeben. main.c:

#include "stm32f4xx.h"

int main(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

    GPIO_InitTypeDef GPIOC_InitStructure;
    GPIOC_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIOC_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIOC_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIOC_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIOC_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOC, &GPIOC_InitStructure);

    RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO2Div_4);

    while(1)
    {
    }
}

Die FPU

Allgemeines

STM32F3 und STM32F4 Controller haben eine Floating Point Unit (FPU). Damit die FPU richtig verwendet wird, müssen normalerweise zwei Bedingungen erfüllt sein:

1. Der Controller muss die FPU Hardware aktivieren

2. Damit die FPU auch was zu tun bekommt, müssen vom Compiler FPU-Instructions erzeugt werden.

Die Em::Blocks IDE verwendet standardmäßig FPU-Befehle, sofern eine FPU vorhanden ist. Hier wird die FPU Hardware aktiviert (system_stm32f4xx.c Z.341):

/* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif

__FPU_PRESENT wird in der stm32f4xx.h definiert. __FPU_USED wird von der IDE übernommen (siehe Build-Log): -D__FPU_USED

Außerdem wird der Compiler von der IDE standardmäßig mit dem Argument -mfloat-abi=hard aufgerufen, wodurch FPU-Instructions erzeugt werden.

FPU abschalten

Um die FPU ab zu schalten (Mir würde jetzt nur Strom sparen als Grund einfallen) geht man wie folgt vor:

Unter <ProjektName> -> Rechts Klick -> Build options... -> Device -> Policy wählt man Use target settings und über nimmt die Einstellungen, die vorher dort eingetragen waren, allerdings lässt man das Feld FP-hardware frei.

FPU Abschalten Schritt 1


Unter Compiler Settings -> Policy wählt man dann Use target options only. Dann kann man unter Categories die Option ARM FPU architecture auswählen. Dort stehen dann drei Möglichkeiten für die FPU zur Verfügung:

1. Software-FPU-Library für Gleitkomma-Berechnung verwenden - Keine FPU-Instructions werden generiert (Compiler-Argument: -mfloat-abi=soft)

2. Eine Mischung aus Software- und Hardware-FPU wird verwendet (Compiler-Argument: -mfloat-abi=softfp)

3. Hardware-FPU wird für Gleitkomma-Berechnungen verwendet (Compiler-Argument: -mfloat-abi=hard)

FPU Abschalten Schritt 2


Um die FPU ab zu schalten wählt man natürlich die erste Möglichkeit. Bei der 2. und 3. Möglichkeit fügt die IDE auch den Define __FPU_USED hinzu. Bei Der ersten Möglichkeit nicht. Der Code muss also nicht verändert / auskommentiert werden.

Um die FPU wieder zu aktivieren müssen alle Schritte wieder rückgängig gemacht werden.

Am besten ist es einfach mal alle Möglichkeiten im Einzelschritt durch zu probieren. Dann stellt man schnell fest, ob die FPU aktiviert wir oder nicht. Ein Blick in den erweiterten Build-Log gibt auch schnell Aufschluss darüber, wie der Compiler aufgerufen wird (siehe Kapitel Compilieren).

Test Programm für die FPU

Hier ein kleines Testprogramm, dass misst wie viele Taktzyklen für eine Gleitkomma-Multiplikation gebraucht werden. main.c:

#include "stm32f4xx.h"

#define CORE_SysTickEn()    (*((u32*)0xE0001000)) = 0x40000001
#define CORE_SysTickDis()   (*((u32*)0xE0001000)) = 0x40000000
#define CORE_GetSysTick()   (*((u32*)0xE0001004))

uint32_t t1, t2, dt;
float x, y, z;

int main(void)
{
    x = 53.64f;
    y = 0.27f;

    CORE_SysTickEn();
    t1 = CORE_GetSysTick();

    z = x * y;

    t2 = CORE_GetSysTick();
    CORE_SysTickDis();

    dt = (t2 - t1) - 9; //9 Takt-Zyklen werden für die Messung gebraucht 

    while(1)
    {
    }
}

Core-Coupled-RAM verwenden

Auf den normale SRAM kann durch den Core und die verschiedenen DMA-Controller zugegriffen werden. Jedoch können Core und DMA nicht gleichzeitig auf den SRAM zugreifen. Deswegen gibt es einen zweiten kleinen RAM, den Core-Coupled-RAM (CCRAM), auf den nur vom Core zugegriffen werden kann und nicht von einem DMA-Controller. Der CCRAM ist über einen anderen Bus an dem Cortex angeschlossen. Daduch wird ermöglicht, dass der Core sinnvoll weiterarbeiten kann, wenn ein DMA-Controller Daten aus den "Haupt"-RAM ließt/schreibt, indem der Core Daten aus dem CCRAM verarbeitet. Um Daten in den CCRAM zu legen, geht man wie folgt vor:

1. Eine neue Section im Linkerscript anlegen:

Sections:
{
  ...

  .ccram :
  {
    *(.ccram)
  } > CCRAM

  ...
}

2. Um die neue Section zu verwenden benutzt man diesen Define:

#define CCRAM __attribute__((section(".ccram")))

...

int CCRAM dataInCCRAM;

Beim Debuggen stellt man fest, das die Variable an der Adresse 0x10000000 liegt, der ersten Adresse das CCRAM's.

Booten aus dem SRAM

STM32 Controller haben die Möglichkeit Code aus dem Flash als auch aus dem SRAM auszuführen. Um eine Anwenung per Debugger in des SRAM zu laden sind nur wenige Schritte erforderlich:

  • Um das Linkerscript für den SRAM zu verwenden geht man zu: Build options... -> Device dort wählt man unter Policy Use target settings aus und übernimmt die vorherigen Einstellungen, nur unter Linker script* gibt man .\stm32f407vg_sram.ld an
  • Dann muss man noch unter Debug -> Interfaces -> Settings Vector table start auf 0x20000000 ändern und Execute from RAM auswählen.
  • Bei Verwendung von Interrupts muss weiterhin das Symbol VECT_TAB_SRAM definiert werden. (Project -> Build Options -> Debug -> Compiler Settings -> Reiter #defines -> Dort VECT_TAB_SRAM einfügen)

Bootsram 1.png Booten aus dem SRAM Bootsram 2.png Booten aus dem SRAM

Anschließend kann man die Anwendung ganz normal debuggen. Nach einem Reset muss die Anwenung erneut mit dem Debugger gestartet werden.

Fortsetzung folgt..

Kritik, Wünsche usw. sind gerne erwünscht, am besten hier in den Thread posten: Neues STM32 Tutorial