Controller

      ARM7-Plattform mit STM32


Home ARM-Projekt Startupcode Bootloader myprintf() ECLIPSE Code::Blocks Funksteck- dosen DCF77 Funkuhr


 Startupcode

Zunächst werden ein paar Files aufgerufen, bevor auf C-Programm-Ebene main() aufgerufen wird:

1. makefile
Makefile-Script mit Multi thread
3. stm32f101vb_rom.ld
Linker script
3. startup.c
Vektortabelle und Variablen-Initialisierung
springt am Ende nach main()

  Im Makefile müssen alle Pfade zu C-Sourcen und Headern aufgeführt sein. Da das uralte make.exe sich bei der Parallelverarbeitung immer vertuttelt, wird make vom makefile - Script zwei mal hintereinander aufgerufen - ein mal mit Multi thread und ein mal ohne.

  Das Linkerscript legt die Chip-abhängigen Speicherbereiche und die Stackgröße  __main_stack_size fest. Im Linkerscript wird die Vektortabelle direkt auf den Reset-Einsprung ab Adresse 0x08000000 eingetragen.

  Die eigentliche Vektortabelle steht im startup.c.Die Reihenfolge der Funktionsaufrufe in der Vektortabelle muß genau zum Prozessortyp passen!  Das C-Programm beginnt erst nach der Initialisierung der Datenbereiche mit dem Aufruf von main().


 Erster C code für den STM32

 Library-Konzept:

  Ich benutze grundsätzlich nicht die komplette STM Standard_Peripherals_Library, sondern lediglich zwei originale STM Register-Definitionsdateien aus diesem Paket.
stm32f10x.h und core_cm3.h bzw. stm32f37x.h und core_cm4.h oder ähnlich.
Das hat den Vorteil, dass man anhand der Registernamen sofort das entsprechende Kapitel in der SW-Doku findet.

 Headerdateien


#include "config.h"
#include "stm32f10x.h"
// ruft core_cm3.h auf

In der config.h finden wir eine Zeile


#define STM32F10X_MD

die dem stm32f10x.h file den aktuellen Prozessortyp verrät. Das stm32f10x.h wählt die Registerdefinitionen des Prozessors abhängig von STM32F10X_MD. Prinzipiell sind diese beiden Files in dieser Reihenfolge in jedem HW-Treiber vorhanden.

 Funktion main()

main(){
pll_start(); //init phase
init_();
...

while (1){ //endlessloop
mainloop_timer(); //timer call back functions are called from here
...
if(keypressed()) parser();
if(isMSG(ANY)!=0){
...
}
}
}
main() besteht aus zwei großen Programmblöcken, der Initialisierungsphase und der Endlosschleife. main() muß als erstes mit pll_start() den PLL-Frequenzplan für den Prozessor einstellen, der in config.h konfiguriert wurde.

Der nächste wichtige Schritt während der Initialisierung ist, mit
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
               RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN |
               RCC_APB2ENR_IOPEEN;
die Peripherie des Prozessors einzuschalten, sonst geht gar nichts.

  Einfaches Task-Konzept:

  Es gibt zwei Programmebenen, den Normal-Kontext (init und endlessloop der main() Funktion) und den Interrupt-Kontext. Die eine Ebene darf auf keinen Fall eine Ressource der anderen Ebene verändern.
Soll eine Funktion, die globale Variablen oder HW verändert, von beiden Ebenen angesprungen werden, dann muß mindestens eine Sperre wie folgt eingebaut werden:
function(){
static int reentry = 0;
if (reentry==0){
reentry = 1;
...

reentry = 0;
return;
}
}
Dasselbe gilt, wenn verschiedene Interruptebenen verwendet werden. Wichtigstes Beispiel dafür ist das printf(), das für's Debuggen aus beiden Kontexten verwendet wird.

 Messages

  Messages und Status-Flags werden in messages.h in jeweils einer 32-Bit-Variable msg bzw. flg definiert. Sie werden in der Mainloop gepollt. Laufende Vorgänge und Stati können so leicht in der Mainloop kontrolliert werden und  für Stromsparzwecke überwacht und zentral geändert werden.

 Einfache Timer

  Die 20 Timer basieren auf dem Systick-Timer-Interrupt, der die Variable millisec hochzählt. Ein Timer wird aufgezogen mittels
newtimer(msec, (callbackfunction), REPETITIVE/CALLONCE)          

  In der mainloop() wird der Timer ausgewertet. Ein abgelaufener timer ruft die entsprechende Callbackfunktion im Normal-(Main)-Kontext auf. Achtung! Während der Initphase laufen die Timer noch nicht.
Verbraucht eine Task der Mailnloop zu viel Rechenzeit, dann kommt der Timer entsprechend später.

 C Standard Libraries

Um beispielsweise ein printf() aus der GCC-Library auf die Serielle Schnittstelle schreiben zu lassen, benötigt man das folgende File, das den HW-Zugriff abbildet:


Ich empfehle aber, sich die einfachen String- oder Print-Funktionen schon selbst im Netz zusammenzusuchen, anstatt die überladenen Newlib-C-Library-Funktionen zu benutzen. Beispiele s.u.

 Downloads


Hallo World Projekt
erzeugt ein 1.5kbyte Binary mit Printf und LED-Blinken
(fast so einfach wie Arduino)
download
stdlib_.c
enthält einige häufig gebrauchte String-Funktionen und anderes
download

Siehe auch small printf().

 Links

[1] Beispiele von Frederic Chopin
[2] STM32 Resources\Firmware\Standard Peripheral Library
[3] Cortex-M3 Interrupt Vector Table
[4] Artikel auf Microcontroller.de
[5] Startupcode für STM32-Discovery
[6] STM SW manual