STM32F10x Standard Peripherals Library
Allgemeines
Die STM32F10x Standard Peripherals Library ist eine umfangreiche komfortable C-Bibliothek die den Zugriff auf alle Funktionen der STM32F10x Familie erlaubt.
Dabei sind für die verschiedenen Peripherien jeweils ein eigenes Modul verfügbar.
Die Bibliothek kann hier bei ST kostenlos heruntergeladen werden download
In dem Archiv ist die STM32F10x Standard Peripherals Library, zu jeder Peripherie gibt es bis zu 12 Demos und eine Beschreibung als CHM Datei "stm32f10x_stdperiph_lib_um.chm"
Bevor Ihr lange sucht, die USB-Schnittstelle ist nicht in der STM32F10x Standard Peripherals Library enthalten. Die hierfür verfügbare Firmware sowie Beispielcode befindet sich in der STM32_USB-FS-Device_Lib
Hinweise
Die notwendigen Informationen, um die Funktionen der Standard Peripherals Library zu verstehen, muss man sich leider aus verschiedenen Quellen zusammensuchen. Daher soll mit diesem Artikel versucht werden, deren Benutzung für die verschiedenen Peripherien zu erläutern.
Leider beziehen sich die von ST gelieferten Beispiele sehr auf die von ST verfügbaren Evalboards und der Code wimmelt von #defines, so dass man sich erst mühsam durchhangeln muss um zu verstehen was da eigentlich passiert. Dies geht zwar durch die gute Verlinkung in der Hilfsdatei ganz gut, erschwert aber den Einstieg unnötig.
In diesem Artikel soll daher die Anwendung bare bones ähnlich den Beispielen in den AVR Datenblättern erfolgen. Wo nötig werden die zum Verständnis relevanten Auschnitte des Reference Manual RM0008 in den Artikel kopiert.
(Bevor hier die Copyright Discussion entbrennt: Das Langericht München hat 1996 festgestellt, dass die Beschreibung von elektronischen Schaltungen nicht dem Urheberrecht unterliegen. Außerdem sollte ST sehr daran gelegen sein mehr Entwickler für Ihre µC zu begeistern.)
GPIOS - Wie greife ich auf einzelne Pins zur Ein/Ausgabe zu
Unser Mikrocontroller hat ja einige Beinchen, diese können wir als Eingänge sowie als Ausgänge verwenden. Dazu müssen wir unserem Käfer jedoch erst sagen welcher Pin was machen soll. Wie dies geht wird hier beschrieben.
Der STM32F10x verfügt zur Manipulation der IO-Pins über ein sehr raffiniertes Feature, das es erlaubt Bits für die IO-Pins zu setzen / löschen ohne vorher deren aktuellen Zustand auslesen zu müssen (üblicherweise Read-Modify-Write). Dadurch ist gewährleistet, dass beim Setzen/Löschen von Bits kein Interrupt dies stören kann.
Each of the general-purpose I/O ports has two 32-bit configuration registers (GPIOx_CRL, GPIOx_CRH), two 32-bit data registers (GPIOx_IDR, GPIOx_ODR), a 32-bit set/reset register (GPIOx_BSRR), a 16-bit reset register (GPIOx_BRR) and a 32-bit locking register (GPIOx_LCKR).
Each I/O port bit is freely programmable, however the I/O port registers have to be accessed as 32-bit words (half-word or byte accesses are not allowed). The purpose of the GPIOx_BSRR and GPIOx_BRR registers is to allow atomic read/modify accesses to any of the GPIO registers. This way, there is no risk that an IRQ occurs between the read and the modify access.
Auf die BRR und BSRR register greift man über die entsprechenden GPIO-Port zu.
Hierzu existieren in <c>\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\stm32f10x.h</c> folgende Definition:
<c> typedef struct {
__IO uint32_t CRL; __IO uint32_t CRH; __IO uint32_t IDR; __IO uint32_t ODR; __IO uint32_t BSRR; __IO uint32_t BRR; __IO uint32_t LCKR;
} GPIO_TypeDef; </c>
Grundlegende Funktionen | stm32f10x_gpio.c
GPIO Ports GPIOA GPIOB GPIOC GPIOD GPIOE GPIOF GPIOG GPIO Pin's GPIO_Pin_0 GPIO_Pin_1 GPIO_Pin_2 GPIO_Pin_3 GPIO_Pin_4 GPIO_Pin_5 GPIO_Pin_6 GPIO_Pin_7 GPIO_Pin_8 GPIO_Pin_9 GPIO_Pin_10 GPIO_Pin_11 GPIO_Pin_12 GPIO_Pin_13 GPIO_Pin_14 GPIO_Pin_15 |
Die GPIO Ports sind abhängig vom verwendeten Controller!
Initialisierung
Beim Konfigurieren der Ports wird der funktion GPIO_Init() ein Initialisierungs Strukt übergeben. Dieses Strukt ist wie folgt aufgebaut
<c> typedef struct {
u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef; </c>
Definieren müssen wird dieses zuvor jedoch auch. Dies geschieht mit
<c> GPIO_InitTypeDef GPIO_InitStructure; </c>
Danach können wir bequem auf die einzelnen Einträge aus dem Strukt zugreifen.
Der nachfolgende Code zeigt eine Beispiel Konfiguration.
<c>
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
</c>
GPIO_Speed definiert den Maximalen Bus Speed welcher der APB2 Liefert.
Mögliche Werte für GPIO_Speed
| Wert | Beschreibung |
| GPIO_Speed_2MHz | Der APB2 gibt einen 2MHz clock zum GPIO |
| GPIO_Speed_10MHz | Der APB2 gibt einen 10MHz Clock zum GPIO |
| GPIO_Speed_50MHz | Der APB2 gibt einen 50MHz Clock zum GPIO. Dies ist das Maximum |
GPIO_Mode_Out_PP konfiguriert den Port oder den Port Pin.
Mögliche Werte für GPIO_Mode_Out_PP
| Wert | Beschreibung |
| GPIO_Mode_Out_PP | Der Pin wird als Ausgang im Push Pull Modus konfiguriert. Dies bedeutet, der ausgang kann sowohl Positive als auch Negative Ströme liefern |
| GPIO_Mode_Out_OD | Der Pin wird als Ausgang im Open Drain Modus konfiguriert. |
| GPIO_Mode_IN_FLOATING | Der Pin wird als Eingang im Floating modus konfiguriert. Dies bedeutet, das der Pin kein Niveau hat. Er "schwebt" |
| GPIO_Mode_AIN | Der Pin wird als Analoger Eingang konfiguriert |
| GPIO_Mode_IPD | Der Pin wird als Eingang konfiguriert mit internem Pull Down widerstand |
| GPIO_Mode_IPU | Der Pin wird als Eingang konfiguriert mit internem Pull Up widerstand |
| GPIO_Mode_AF_OD | Der Pin wird mit Alternativer Funktion (SPI, I2C..) konfiguriert im Open Drain Modus |
| GPIO_Mode_AF_PP | Der Pin wird mit Alternativer Funktion (SPI, I2C..) konfiguriert im Push Pull Modus |
GPIO_Mode_Pin definiert den Pin welcher konfiguriert werden soll. Mögliche Werte für GPIO_Mode_Pin
Um den zu konfigurierenden Pin anzugeben, genügt es GPIO_Pin_X zu schreiben.
Wobei "X" durch die entsprechende Portnummer zu ersetzen ist.
Es können auch mehrere Pins gleichzeitig Konfoguriert werden.
Dazu wird einfach logische ODER verknüfung verwendet ( | )
Möchte man den gesamten Port konfigurieren, so genügt es wenn man GPIO_Pin_All angibt.
Hier ein Beispiel mit mehreren Pins
<c>
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_7
</c>
GPIO_Init()
Nachdem wir nun alle relevanten Parameter gesetzt haben, müssen wir den Port nur noch Initialisieren. Die geschieht in dem wir der Funktion GPIO_Init() zwei Parameter übergeben.
GPIOX steht hierbei für den zu Initialisierenden Port. Wobei X mit dem Buchstaben zu ersetzen ist. der zweite Parameter übergibt das Strukt.
Ein Beispiel für GPIOA <c> GPIO_Init(GPIOA, &GPIO_InitStructure); </c>
Etwas auf den Port geben
Um etwas an einem GPIO auszugeben gibt es drei möglichkeiten (funktionen). Entweder man schreibt den gesamten Port oder nur ein einzelnes Bit. Die STM's bieten eine Bitbanding funktion welche hier nicht genauer erklärt werden soll.
Funktion 1
Die erste Funktion lautet GPIO_SetBits() und GPIO_ResetBits() Erster Parameter ist der GPIO Port der zweite ist der Pin oder eine kombination daraus. Kombinationen sind wieder Logisch zu verknüpfen mit ODER ( | )
<c>
GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_5); //Setzt die Bits 1 und 5 am GPIOA auf high
GPIO_ResetBits(GPIOA,GPIO_Pin_2 | GPIO_Pin_9); //Setzt die Bits 2 und 9 am GPIOA auf low </c>
Funktion 2
Die zweite Funktion lautet GPIO_WriteBit() Diese Funktion kann den oder die Pins setzen sowohl auch löschen
Erster Parameter ist der GPIO Port der zweite ist der Pin oder eine kombination daraus und der dritte sagt aus ob gesetzt oder gelöscht wird!
Kombinationen sind auch hier Logisch zu verknüpfen mit ODER ( | )
<c>
GPIO_WriteBit(GPIOA,GPIO_Pin_1 | GPIO_Pin_5, Bit_SET); //Setzt die Bits 1 und 5 am GPIOA auf high
GPIO_ResetBits(GPIOA,GPIO_Pin_2 | GPIO_Pin_9, Bit_RESET); //Setzt die Bits 2 und 9 am GPIOA auf low </c>
Funktion 3
Die dritte und somit letste Funktion lautet GPIO_Write() Diese Funktion beschreibt den gesamten Port! Erster Parameter ist der GPIO Port der zweite ist der an dem Port auszugebende Wert.
Wichtig! da wir 16 Pins je Port haben, kann es zu verwirrungen mit 8bit Variablen kommen. Korrektes Format ist 0xFFFF!
<c>
GPIO_Write(GPIOA,0x0011); //Setzt die Bits 1 und 5 am GPIOA auf high
GPIO_ResetBits(GPIOA,0x0000); //Setzt die Bits 2 und 9 am GPIOA auf low </c>
Pin Sperren
Der STM32 bietet die Möglichkeit einn Pin zu sprren. Ist ein Pin gesperrt, so kann dessen Zustand (High / Low) bis zu einem Reset nicht mehr geändert werden!
Wie wir es uns nun gewohnt sind, hat ST dafür eine eigene Funktion geschrieben. Diese lautet GPIO_PinLockConfig()
Erster Parameter ist der Port, der zweite sind die Pins welche man Sperren möchte. Diese kann man wieder mit der ODER verknüpfung kombinieren.
<c>
GPIO_PinLockConfig(GPIOA,GPIO_Pin_1 | GPIO_Pin_5); //Sperrt die Pins bis zu einem Reset
</c>
Wie kommen die Daten in den Mikrocontroller
Um Daten in den STM32 einzulesen gibt es wieder ein paar ST Funktionen. Diese möchten wir dir hier vorstellen.
Die dafür verwendbaren Funktionen sind: GPIO_ReadInputDataBit() sowie GPIO_ReadInputData()
Erster Parameter ist der Port, der zweite sind die Pins welche man Auslesen möchte.
Bei GPIO_ReadInputData() wird jedoch nur der PORT übergeben da diese Funktion den gesamten PORT zurück liefert!
<c>
unsigned char ucStatus = 0;
ucStatus = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5); //Speichert den Zustand von Pin5 am GPIOA in die Variable ucStatus </c>
<c>
unsigned int uiPort = 0;
uiPort = GPIO_ReadInputData(GPIOA); //Speichert den Zustand von GPIOA in die Variable uiPort </c>
Ausgänge einlesen
Man hat ja häufig das Problem, dass man gerne nachsehen möchte was man denn gerade am Ausgang ausgibt.
Dazu kann man den Ausgang wie ein Eingang einlesen. Die dafür verwendbaren Funktionen sind: GPIO_ReadOutputDataBit() sowie GPIO_ReadOutputData()
Erster Parameter ist der Port, der zweite sind die Pins welche man Auslesen möchte.
Bei GPIO_ReadOutputData() wird jedoch nur der PORT übergeben da diese Funktion den gesamten PORT zurück liefert!
<c>
unsigned char ucStatus = 0;
ucStatus = GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5); //Speichert den Zustand von Pin5 am GPIOA in die Variable ucStatus </c>
<c>
unsigned int uiPort = 0;
uiPort = GPIO_ReadOutputData(GPIOA); //Speichert den Zustand von GPIOA in die Variable uiPort </c>
Deinitialisieren von Ports
Es gibt auch die Möglichkeit, den Port zu Deinitialisieren. Dann wird er mit seinem Standard Wert konfiguriert.
Die Funktion dazu lautet GPIO_DeInit() Erster und einziger Parameter ist der Port den man Deinitialisieren möchte.
<c> GPIO_DeInit(GPIOA); //Setzt den GPIOA auf seinen Standard Wert zurück </c>
Der Herzschlag unseres Mikrocontroller
Unser Mikrocontroller hat viele interne Takte. Diese müssen unbeding konfiguriert werden. dazu verwenden wir die RCC (Reset and Clock Control)
Dazu gibt es Funktionen in der ST-Library. Welche Takte an welchen Busen liegen siehst du im Bild
Die Controller verfügen über zwei getrennte Daten Buse. Den APB1 und den APB2 diese sind wiederum über Brücken am Systembus angeschlossen.
Das wären in diesem Fall die AHB1 und AHB2. Man muss beachten, das der APB1 "nur" mit 36MHz getaktet werden kann. der APB2 hingegen mit 72MHz.
Wichtig! Alle unsere Peripherie Teile, die wir verwenden möchten, müssen mit einem Takt versorgt werden bevor man Sie verwenden kann. Dies führt häufig zu langer Fehlersuche.
Der STM32 wird normalerweise mit einem Quarz von 4-16MHz versorgt. Aus diesem wird dann mittels der internen PLL der eigentliche Takt gebildet (bis 72MHz)
Im STM32 wird jeder benötigte Takt vom Haupttakt abgeleitet. Die Controller verfügen auch über interne Oszillatoren, diese sind jedoch
verhältnismässig ungenau und daher nicht zu empfehlen.
Takte Reseten
Zu beginn empfiehlt es sich alle Takte auf ihre Standard Werte zurückzusetzen. Dies schafft eine allgemeine Grundlage für weitere Konfigurationen.
Somit muss man sich nicht überlegen wo man noch was zuvor eingestellt hat. Nach RCC_DeInit() sind alle Takte Resettet. Übergeben wird nichts!
<c> RCC_DeInit(); //Setzt alle Takte auf deren ursprungsszustand zurück. </c>
Taktquelle auswählen
Damit der Controller seine Takte hat, muss man ihm zuerst mitteilen woher er seinen Grundtakt bekommt.
Dies Geschieht mit Funktionen der ST-Library
HSE - Highspeed External Oscillator (Quarz)
Damit der Controller weiss ob man den HSE (Highspeed External Oscillator) verwenden möchte, gibt es die Funktion RCC_HSEConfig()
Als Parameter erwartet sie einen der folgenden werte: RCC_HSE_OFF RCC_HSE_ON oder RCC_HSE_Bypass
Die ersten sind selbsterklärend. Wenn man der Funktion jedoch RCC_HSE_Bypass übergibt, so erwartet der Controller am OSC_IN Pin
ein Clock Signal. Dieses darf bis zu 25MHz schnell sein und kann Rechteck, Sinus oder Dreieck Spannung mit einem Duty/Cycle von 50% sein.
<c> RCC_HSEConfig(RCC_HSE_ON); //Aktiviert den Externen Highspeed Oszillator (Quarz). </c>
HSI - Highspeed Internal Oscillator
Man kann, wenn es auch nicht empfehlenswert ist, den Internen Oszillator verwenden. Dazu verwendet man RCC_HSICmd()
Als Parameter erwartet sie entweder ENABLE oder DISABLE Der Interne Highspeed Oszillator ist nach einem System Reset
automatisch als Taktquelle ausgewählt. Wichtig! wenn man den HSI zusammen mit der PLL verwendet, kann man ihn nicht Stoppen!
<c>
RCC_HSICmd(ENABLE); //Aktiviert den Internen Highspeed Oszillator.
</c>