|
|
ARM-elf-GCC-TutorialDieses Tutorial behandelt die Programmierung von ARM Mikrocontrollern mithilfe des ARM-elf-GCC Compilers. Die meisten Codebeispiele wurden mit WinARM übersetzt. Vorerst wird sich dieses Tutorial an die LPC-Reihe von NXP richten.
[Bearbeiten] BezugsquellenKomplette Boards mit ARM7-Kern kann man von folgenden Webseiten beziehen: Wenn man allerdings selbst ein ARM7-Board herstellen möchte kann man die Schaltpläne der Olimex-Boards als gute Grundlage nehmen. [Bearbeiten] Benötigte Programme
[Bearbeiten] StartproblemeWenn man vor den ersten Versuchen mit AVR-Mikrocontrollern mit WinAVR und ähnlichen Entwicklungsumgebungen programmiert hat, musste man außer seinem C-Code und dem daraus entstehenden HEX-File nicht viel beachten. Bei der Programmierung von ARM-Mikrocontrollern muss man aber bedenken, dass es (noch? ;-) ) keine Standard-Linkerscripte und -Startupcodes in WinARM gibt. WinAVR nimmt einem diese Arbeit mit Standarddateien ab, so dass man bei WinAVR meistens nicht in Berührung damit kommt. Im Netz kursieren viele dieser Linkerscripte und Startupcodes. Wir empfehlen die von uns getesteten Scripte zu verwenden, damit keine unschönen Phänomene auftreten (zum Beispiel fehlende Interrupts). // Scripte befinden sich vorerst in den WinARM-Examples. [Bearbeiten] Bevor wir startenIm AVR-GCC-Tutorial werden Grundlagen erklärt, die sicherlich nützlich sind (Makefiles, Programmablauf in einem Mikrocontroller, etc.). Wir möchten hiermit auf das AVR-GCC-Tutorial verweisen und gehen nicht nochmal auf diese Themen ein. Außerdem sollte man sich vergewissern dass der Controller mit Hilfe des Bootloaders angesprochen werden kann. Hierzu verwendet man am besten das Flash-Tool von Philips um die DEVICE PART ID auszulesen. Falls dies fehlschlägt, sollte man die Verbindung zum Mikrocontroller überprüfen. [Bearbeiten] Länge von VariablentypenDie Länge der jeweiligen Variablentypen unterscheidet sich durchaus von denen des AVRs. Folgendes ist beim ARM-elf-GCC Compiler gültig:
[Bearbeiten] Das erste ProgrammDie meisten Sourcecodes wurden für einen LPC2124 mit WinARM compiliert. Hierbei muss man beachten, dass in einigen Header-Dateien die Register (z. B. IOSET, IOCLR etc.) unterschiedliche Namen haben. Ein Blick in die einzubindende Header-Datei ist daher ratsam. Beispiel: Ein Ausschnitt aus der Header-Datei lpc2114.h des GNUARM-Projekts (bei WinARM in <arch/philips/lpc2114.h>) (auch gültig für LPC2124):
Verwendet man z. B. einen LPC2106 und die von der Keil GmbH bereitgestellte Header-Datei für diesen Controller, weichen die Bezeichnungen von denen in der GNUARM-Definition zum Teil deutlich ab. In WinARM ist die Datei von Keil enhalten und entsprechend benannt ("lpc210x_keil.h" aus <arch/philips/lpc210x_keil.h>).
Daher: Wenn Registernamen beim Compilieren nicht bekannt sind, hilft ein Blick in die Header-Datei des Controllers weiter, oder man passt eine projektspezifische Kopie der Datei an. [Bearbeiten] Nutzung der I/O PortsZum Behandeln von I/O-Ports sind die Register "IOPIN", "IOSET", "IOCLR" und "IODIR" nötig.
In dem folgenden Code wird Pin 25 von PORT0 auf Ausgang geschaltet und danach auf HIGH gelegt:
Wenn man einen Pin wieder auf LOW schalten will, setzt man das entsprechende Bit in IOCLR.
Doch wie geht man nun mit dem Setzen und Löschen einzelner Bits um? In Variablen/Register benutzt man am besten logische Verknüpfungen, wie sie bereits im AVR-GCC-Tutorial beschrieben werden:
Wenn allerdings mit den I/O-Ports gearbeitet wird, sollten die IOSET- und IOCLR-Register benutzt werden:
Die Technik der IOSET-/IOCLR-Register an Stelle klassischer Port-Register vermeidet das in Interrupt-sichere Programmierung von I/O-Ports beschriebene Problem. [Bearbeiten] Systemeinstellungen (System Control Block)Dass ein ARM generell komplexer als ein handelsüblicher AVR oder PIC ist, sollte jedem geläufig sein. Hier kann man am Controller jede Menge (falsch) einstellen. In diesem Abschnitt werden die verschiedenen Register und ihre Bedeutungen beim LPC2xxx erklärt: [Bearbeiten] Phase locked loop
Hier ein Codebeispiel zur Initialisierung:
Warum schreiben wir als Multiplikator (PLL_M-1) und (PLL_P-1) in SCB_PLLCFG? Der Multiplikator 1 wird mit 0 dargestellt. Ein 2x Multiplikator wäre 1 , ein 3x Multiplikator wäre 2, usw. Man muss also immer "1" von dem gewünschten PLL-Wert abziehen. Weitere Informationen zur PLL befinden sich im Controller-Handbuch (beim LPC2124 ab Seite 60 und beim LPC2106 ab Seite 43). [Bearbeiten] VPBDividerDie gesamte Peripherie ( SPI, UART, etc. ) des ARMs hängt am sogenannten "VLSI Peripheral Bus". Mithilfe des VPBDIV-Registers kann man die Taktfrequenz dieses Busses einstellen.
Gebrauchen kann man das, wenn man die gesamte Peripherie des Systems drosseln möchte. Der Peripherie-Takt kann ohne Probleme so schnell sein wie der Prozessor-Takt. [Bearbeiten] ZwischenstandGenerell sollte man Dinge wie Multiplikator und die Quarz-Taktfrequenz am Anfang seines Programms definieren, z. B. so:
[Bearbeiten] Memory Accelerator ModuleMit den MAM-Registern lässt sich der Speicherzugriff des LPC noch etwas optimieren. Die nötigen Register:
Die Einstellungen an den MAM-Register könnten in eurem Code beispielsweise so aussehen:
Für die korrekte Einstellung von MAMTIM gibt es auf Seite 77 im LPC2124-Handbuch einen Hinweis, der die Einstellungen erklärt. Kurze Zusammenfassung:
[Bearbeiten] UARTUm den UART zu aktivieren, sind in der Minimalkonfiguration folgende Register nötig (n steht für den jeweiligen UART):
Für den Datenverkehr sind folgende Register definiert:
Folgende Funktion initialisiert den UART0 des LPC:
Nachdem man die Funktion aufgerufen hat, kann man ganz einfach ein Byte senden, der Code dafür ist so ähnlich wie beim AVR:
Um ein Byte zu empfangen, muss erst einmal überprüft werden, ob sich ein ungelesenes Byte am Anfang des FIFO-Stacks befindet:
[Bearbeiten] Realtime Clock (RTC)Die RTC der LPC-Controller ist eines der am einfachsten zu nutzenden Peripherie-Bestandteile. Mit ein paar Registerzugriffen lässt sie sich aktivieren und einstellen. Um sie zu aktivieren, muss man zunächst einen Teiler für die Systemfrequenz ermitteln. CCLK ist der aktuelle CPU-Takt in Hertz.
Jetzt kann man die RTC ganz einfach aktivieren:
Nun läuft die RTC schon! Nur müssen wir natürlich noch eine andere Zeit und ein anderes Datum einstellen:
Die RTC hat noch weitere Register zum Auslesen weiterer Werte wie z. B. dem Tag des Jahres, dem Tag der Woche usw. Ausserdem bietet die RTC viele Interrupt-Funktionen, die z. B. dazu genutzt werden können, den Controller nach einer bestimmten Zeit aus dem Ruhezustand zu wecken. Weitere Informationen gibt es im LPC2106-Benutzerhandbuch ab Seite 157. [Bearbeiten] InterruptsIn diesem Kapitel wird das Interruptsystem der LPCs erklärt. Die wichtigsten Komponenten sind: Vectored Interrupt Controller (VIC) Interrupt-Register und -Bits der jeweiligen Peripherie Im VIC werden die generellen Einstellungen vorgenommen, die alle Interrupts betreffen. Außerdem gibt es bei der meisten Peripherie auch ein Register, welches ein Interrupt Clear Bit beinhaltet; hierzu später mehr. [Bearbeiten] InterruptartenGrundsätzlich unterscheidet man hier zwischen IRQ und FIQ (Fast Interrupt Request). Diese unterscheiden sich darin, wie schnell in die ISR gesprungen wird. //TODO: Die benötigte Zeit wiederfinden, Quelle leider nicht mehr auffindbar. Ob ein Interrupt ein IRQ oder ein FIQ ist wird in dem Register "VICIntSelect" deklariert. [Bearbeiten] InterruptcontrollerGrundsätzlich muss neben der Interruptart nur noch die Adresse und die dazugehörige Peripherie eingestellt werden. In einem der VICVectAddrn-Register wird die Adresse der jeweiligen Interrupt-Serviceroutine angegeben. Im passenden VICVectCntln-Register gibt man die Peripherie an, die diesen Interrupt auslösen soll ( z. B. ist in VICVectCntln ein UART-Interrupt ?). Um alle eingestellten Interrupts zu aktivieren, benutzt man das VICIntEnable-Register. Hier etwas Beispielcode:
Was bedeutet VIC_UART0? Alle Interrupts können von einer anderen Quelle ihren "Auslöser" bekommen. Damit ein Interrupt weiss, welche Quelle er nutzen soll, gibt man ihm die Peripherie an.
Für weitere Informationen hilft ein Blick ins Datenblatt. Die ISR wird wie folgt deklariert:
[Bearbeiten] SPISPI lässt sich ähnlich simpel wie bei einem AVR initialisieren.
Ein Codebeispiel für die Initialisierung:
Das SPCCR-Register hält die Taktfrequenz des jeweiligen SPI bereit. Grundsätzlich gilt: Jeden n. Takt vom Prozessortakt kommt ein SPI-Takt. Beispiel: 60 MHz Systemclock / 16 = 3,75 MHz SPI-Takt. Allerdings muss der Teiler größer oder gleich 8 sein. Das Senden eines Bytes per SPI sieht so aus:
[Bearbeiten] I2CIn der "Codesammlung" befindet sich eine I2C-Master-Bibliothek für den Polling-Betrieb. Diese Bibliothek findet ihr unter http://www.mikrocontroller.net/forum/read-4-281865.html. Die Bedeutung der Status-Codes findet ihr unter http://www.semiconductors.philips.com/acrobat_download/various/8XC552_562OVERVIEW_2.pdf [Bearbeiten] VerwendungIm folgenden Codebeispiel wird die Verwendung der Bibliothek erklärt:
[Bearbeiten] Weitere Informationsquellen
[Bearbeiten] Nützliche ThreadsHier landen Threads aus dem Mikrocontroller.net-Forum, die sich mit dem Thema ARM beschäftigen. Zum größten Teil sind dies besonders nützliche Threads oder solche, die über den Threadtitel nicht als ARM-Thread identifiziert werden können
|