|
|
STM32F10x Standard Peripherals Library
[Bearbeiten] AllgemeinesDie STM32F10x Standard Peripherals Library ist eine umfangreiche komfortable C-Bibliothek, die den Zugriff auf alle Funktionen der STM32F10x Familie erlaubt. Dabei ist für die verschiedenen Peripheriekomponenten jeweils ein eigenes Modul verfügbar. Die Bibliothek kann hier bei ST kostenlos heruntergeladen werden. Dazu auf der ST-Webseite:
Alternativ ist hier ein Direktlink. In dem Archiv ist die STM32F10x Standard Peripherals Library. Zu jeder Peripherie gibt es diverse Beispiele 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 [Bearbeiten] HinweiseDie 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 Peripheriekomponenten 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 Ausschnitte des Reference Manual RM0008 in den Artikel kopiert. (Bevor hier die Copyright Discussion entbrennt: Das Landgericht 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.) [Bearbeiten] DokumentationenUm mit der Library zu arbeiten sind folgende Dokumentationen empfehlenswert:
Beschreibung und Registerdoku der Peripherie (GPIO, Timer ...)
Cortex-M3 eigene Peripherie (wichtig zb. NVIC)
Pinout des µC und alternative Verwendbarkeit der PINs Download from [Documents and files for STM32F family] [Bearbeiten] Die ST Standard Peripheral Lib in CrossWorksAufgrund mehrfacher Anfragen, hier ein Archiv, das meine Arbeitsumgebung Datei:CrossWorks StandardLib Setup.zip mit zwei Crossworks Projekten (ARM_LED_TEST und ARM_USB_Test) enthält. Einfach alles in ein Verzeichnis entpacken und schon sollte es Laufen. [Bearbeiten] Einführungsbeispiel Blinking LEDEinführungsbeispiel STM32 LEDBlinken AtollicTrueStudio [Bearbeiten] Die Idee hinter der STM32 Standard Peripherals LibrarySo stellt sich ARM und ST-Microelectronics die Library vor. [Bearbeiten] Clocks - Der Herzschlag unseres MikrocontrollersInterner Aufbau eines STM32F103 Unser Mikrocontroller hat viele interne Takte. Diese müssen unbedingt konfiguriert werden. Dazu verwenden wir die RCC (Reset and Clock Control). Dazu gibt es Funktionen in der ST-Library. Welche Takte an welchen Bussen liegen, siehst du im Bild. Die Controller verfügen über zwei getrennte Datenbusse für die langsameren Teile der Periphierie. 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, dass der APB1 "nur" mit maximal 36MHz getaktet werden darf, 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. Bei Nichtbeachten führt dies 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). Jeder benötigte Takt wird vom Haupttakt abgeleitet. Die Controller verfügen auch über interne RC-Oszillatoren (typischerweise ein 8 MHz RC-Oszillator mit einer Genauigkeit von 1 % sowie einem 40 kHz RC-Oszillator). Zumindest der interne 8 MHz-Oszillator ist für die meisten Anwendungen genau genug. Für z.B. USB oder CAN mit Taktraten > 100 kbit/s ist er aber nicht genau genug. Der interne 40 kHz-Oszillator hingegen ist sehr ungenau (zwischen 30 und 60 kHz). Hardwaretechnisch sollte darauf geachtet werden, dass bei Verwendung eines externen Quarzes dieser eine Frequenz von 8 MHz besitzt. Die gesamten Defines in der Library beziehen sich darauf. Ansonsten ist eine Anpassung der PLL Multiplikatoren in der Datei "system_stm32f10x.c" nötig. [Bearbeiten] Taktquelle auswählenNach dem Reset wird automatisch immer der interne HSI-Takt genutzt, um einen definierten Zustand zu haben (also der interne 8 MHz RC-Oszillator). Das ist z.B. erforderlich, damit der integrierte Bootloader mit einer bekannten Frequenz versorgt wird (sofern dieser über die Boot-Pins überhaupt gewünscht ist). Zum Glück gibt es ein automatisches Sicherheitsfeature, sodass man die aktuelle Taktquelle des Controllers nicht abschalten kann, da der Controller sonst einfach stehen bleiben würde! [Bearbeiten] HSI - Highspeed Internal OscillatorMan kann den internen Oszillator nach dem Reset umkonfigurieren. [Hier fehlt noch etwas] Zum Ein- und Ausschalten der HSI-Taktquelle verwendet man RCC_HSICmd(). Als Parameter werden entweder ENABLE oder DISABLE erwartet.
[Bearbeiten] Takte ResetenNach dem Reset befindet sich die gesamte Taktkonfiguration in einem definierten Zustand. Macht man später irgendwelche Änderungen an der Taktkonfiguration kann es sinnvoll sein, vorher alle Takte auf ihre Standardwerte zurückzusetzen. Dies schafft wieder eine definierte zentrale Taktkonfiguration. Man muß sich nicht überlegen, wo man noch was zuvor eingestellt hat. Hat man allerdings schon z.B. irgendwelche Peripheriemodule konfiguriert, muß man natürlich aufpassen, welche Folgen die zentrale Taktänderung hier haben kann! Nach RCC_DeInit() sind alle Takte resettet. Übergeben wird nichts!
[Bearbeiten] HSE - Highspeed External Oscillator (Quarz)Es kann auch eine externe Taktquelle ausgewählt werden. Um dem Controller dies mitzuteilen, 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 Taktsignal. Dieses darf bis zu 25MHz schnell sein und kann Rechteck, Sinus oder Dreieck Spannung mit einem Duty Cycle von 50% sein.
[Bearbeiten] GPIOS - Wie greife ich auf einzelne Pins zur Ein/Ausgabe zu[Bearbeiten] GrundlagenUnser 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
Pointer auf die entsprechenden GPIO-Ports sind hier ebenfalls definiert:
Welche GPIO-Ports tatsächlich verfügbar sind ist abhängig vom verwendeten Controller! In stm32f10x_gpio.h sind darüber hinaus Definitionen für alle Pins vorhanden:
Will man also von Hand auf die Bits dieser Register zugreifen, so ist das denkbar einfach:
setzt das Bit für Pin 13 im Port C.
oder
löscht dieses Bit wieder. Mit
kann man die Pins 4..7 auf einen beliebigen Wert setzen, ohne die übrigen Pins zu beeinflussen. Die oberen 16 Bits des geschriebenen Wertes definieren die betroffenen Pins, die unteren 16 Bits den Wert. In der Standard Peripheral Library ist keine Funktion enthalten, die diese Fähigkeit des Ports unterstützt. Die äquivalente Operation über das ODR ist nicht atomar und somit in Verbindung mit Interrupts problematisch.
[Bearbeiten] InitialisierungWICHTIG: Bevor man Pins eines GPIO-Ports benutzen kann, muss man die Clock des entsprechenden Ports mit der Funktion RCC_APB2PeriphClockCmd aktivieren , da diese nach einem Reset immer ausgeschaltet ist. Dies ist ein sehr beliebter Fehler beim Arbeiten mit Ports. Mehr dazu bei Clocks Bevor man einen Pin benutzen kann, muss dieser Initialisiert werden. Die Funktion GPIO_Init() ermöglicht es einen oder mehere Pins auf einmal zu konfigurieren. Hierzu muss eine struct ausgefüllt und GPIO_Init übergeben werden:
GPIO_Pin definiert den Pin welcher konfiguriert werden soll. Um den zu konfigurierenden Pin anzugeben, genügt es GPIO_Pin_X zu schreiben.
Wobei "X" durch die entsprechende Pinnummer zu ersetzen ist.
Hier ein Beispiel mit mehreren Pins
GPIO_Init() Nachdem wir nun alle relevanten Parameter gesetzt haben, müssen wir den Port nur noch mit GPIO_Init() initialisieren.
GPIOx ist der Pointer auf den den zu initialisierenden Port. Wobei x einfach mit dem entsprechenden Buchstaben zu ersetzen ist. GPIO_InitStruct ist der Pointer auf die soeben von uns ausgefüllte struct. Ein Beispiel für GPIOA
[Bearbeiten] Etwas auf den Port ausgebenUm etwas an einem GPIO auszugeben, gibt es drei Möglichkeiten (Funktionen). Entweder man schreibt den gesamten Port oder nur ein einzelnes Bit. Die STMs bieten eine Bitbanding-Funktion, die in ARM Bitbanding genauer erklärt ist.
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 ( | )
Die zweite Funktion lautet GPIO_WriteBit() Diese Funktion kann sowohl einen oder mehrere Pins setzen als 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 ( | ).
Funktion 3 Die dritte und somit letzte 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! Hier wird der ganze Port mit einem 16 bit Wert beschrieben. Viel leserlicher als mit magic numbers im Hex-Format zu arbeiten, sind natürlich auch hier die defines: GPIO_Pin_1 | GPIO_Pin_5
[Bearbeiten] Pin SperrenDer STM32 bietet die Möglichkeit, einen Pin zu sperren. Ist ein Pin gesperrt, so kann dessen Zustand (High / Low) bis zu einem Reset nicht mehr geändert werden! Wie wir es 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.
[Bearbeiten] Eingänge einlesenUm Daten in den STM32 einzulesen, gibt es wieder ein paar ST Funktionen. Diese möchten wir hier vorstellen. Die dafür verwendbaren Funktionen sind: GPIO_ReadInputDataBit() sowie GPIO_ReadInputData(). Erster Parameter ist der Port, der zweite sind die Pins, die man einlesen möchte. Bei GPIO_ReadInputData() wird jedoch nur der Port übergeben, da diese Funktion den gesamten Port zurück liefert! Hier wird der tatsächliche logische Pegel am Pin eingelesen!
[Bearbeiten] Ausgänge einlesenMan 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!Hier wird nicht der tatsächliche logische Pegel am Pin eingelesen, sondern was im Ausgangsregister eingestellt wurde (also eigentlich anliegen sollte)! Diese beiden Werte können sich auf Grund externer Einflüsse aber unterscheiden!!!
[Bearbeiten] Deinitialisieren von PortsEs gibt auch die Möglichkeit, den Port zu deinitialisieren. Dann wird er mit seinen Standardwerten konfiguriert. Die Funktion dazu lautet GPIO_DeInit(). Erster und einziger Parameter ist der Port, den man deinitialisieren möchte.
[Bearbeiten] Weblinks, Foren, Communities
|