Ich habe langjährige Erfahrung mit 8-Bit-Kernen verschiedener Hersteller - nämlich 8051, PIC und AVR - und muss jetzt einen Cortex M0 herausfinden. Speziell dieses , aber ich hoffe, wir können allgemeiner sein.
Es stellt sich heraus, dass es ein bisschen mehr ist, als ich erwartet hatte, mit mehreren Dokumenten, die verschiedene Teile des Systems in unterschiedlichen Detailebenen beschreiben, und keines, das ich wirklich gesehen habe, um alles miteinander zu verbinden. Dies im Vergleich zu einem Datenblatt, das alles erklärt. Ich verstehe, dass ich von Anfang an viel mehr zu dokumentieren habe, aber die Änderung des Formats bringt mich aus der Fassung.
Die obige Website enthält ein Dokument, das einen guten Überblick über jedes einzelne Subsystem und Peripheriegerät bietet, und ein weiteres, das jedes Register im Detail beschreibt, und ich habe den gesamten Quellcode für ihr SDK, einschließlich Header-Dateien und einiger komplexer Beispiele, aber ich sehe es immer noch nichts, was beschreibt, wie alles zusammenhängt.
Gibt es eine kurze exemplarische Vorgehensweise der Cortex-Architektur, die die Funktion von Dingen erklärt, die kleinere Controller einfach nicht haben – wie mehrere Ebenen von Bussen von der CPU zu Peripheriegeräten, jede mit ihrem eigenen Watchdog-Timer – und wie sie alle miteinander verbunden sind?
Ich habe sowohl an AVRs als auch an ARM Cortex-M3/M4/R4-basierten MCUs gearbeitet. Ich denke, ich kann einige allgemeine Ratschläge geben. Dies setzt voraus, dass Sie in C programmieren, nicht in Assembler.
Die CPU ist eigentlich der einfache Teil. Die grundlegenden C-Datentypen haben unterschiedliche Größen, aber Sie verwenden trotzdem uint8/16/32_t, richtig? :-) Und jetzt sollten alle Integer-Typen einigermaßen schnell sein, wobei 32-Bit (int) am schnellsten ist. Sie haben wahrscheinlich keine FPU, also vermeiden Sie weiterhin Floats und Doubles.
Arbeiten Sie zunächst an Ihrem Verständnis der Architektur auf Systemebene. Dies bedeutet IOs, Taktung, Speicher, Resets und Interrupts. Außerdem müssen Sie sich an die Idee von speicherabgebildeten Peripheriegeräten gewöhnen. Bei AVR können Sie sich darüber keine Gedanken machen, da die Register eindeutige Namen mit eindeutigen globalen Variablen haben, die für sie definiert sind. Auf komplexeren Systemen ist es üblich, auf Register durch eine Basisadresse und einen Offset zu verweisen. Es läuft alles auf Zeigerarithmetik hinaus. Wenn Sie mit Zeigern nicht vertraut sind, beginnen Sie jetzt mit dem Lernen.
Finden Sie für IOs heraus, wie das periphere Muxing gehandhabt wird. Gibt es eine zentrale Mux-Steuerung, um auszuwählen, welche Pins Peripheriesignale und welche GPIOs sind? Oder setzen Sie die Pins mithilfe der Peripherieregister in den Peripheriemodus? Und natürlich müssen Sie wissen, wie man GPIOs als Ein- und Ausgänge konfiguriert und den Open-Drain-Modus und Pull-Ups/Downs aktiviert. Externe Interrupts fallen normalerweise ebenfalls in diese Kategorie. GPIOs sind ziemlich generisch, daher sollte Ihnen Ihre Erfahrung hier gute Dienste leisten.
Das Takten läuft auf ein paar Dinge hinaus. Sie beginnen mit einer Taktquelle, typischerweise einem Kristall oder einem internen RC-Oszillator. Dies wird verwendet, um eine oder mehrere Taktdomänen auf Systemebene zu erstellen. Chips mit höherer Geschwindigkeit verwenden eine PLL, die Sie sich als Frequenzmultiplikator vorstellen können. An verschiedenen Stellen wird es auch Taktteiler geben. Die wichtigsten Dinge, die Sie berücksichtigen sollten, sind, wie hoch Ihre CPU-Taktfrequenz sein sollte und welche Bitraten Sie für Ihre Kommunikationsperipherie benötigen. Normalerweise ist dies ziemlich flexibel. Wenn Sie fortgeschrittener sind, können Sie Dinge wie Low-Power-Modi lernen, die normalerweise auf Clock-Gating basieren.
Speicher bedeutet Flash und RAM. Wenn Sie über genügend RAM verfügen, ist es oft schneller, Ihr Programm während der frühen Entwicklung dort zu belassen, damit Sie den Flash nicht immer wieder programmieren müssen. Das große Problem hier ist die Speicherverwaltung. Ihr Anbieter sollte Beispiel-Linker-Skripts bereitstellen, aber je nach Art Ihres Programms müssen Sie Code, Konstanten, globalen Variablen oder dem Stack möglicherweise mehr Speicher zuweisen. Fortgeschrittenere Themen umfassen Codesicherheit und Flash-Programmierung zur Laufzeit.
Zurücksetzen ist ziemlich einfach. Normalerweise müssen Sie nur nach dem Watchdog-Timer Ausschau halten, der möglicherweise standardmäßig aktiviert ist. Zurücksetzungen sind während des Debuggens wichtiger, wenn Sie denselben Code immer wieder ausführen. Es ist leicht, einen Fehler aufgrund von Sequenzierungsproblemen auf diese Weise zu übersehen.
Es gibt zwei Dinge, die Sie über Interrupts wissen müssen – wie Sie sie aktivieren und deaktivieren und wie Sie die Interrupt-Vektoren konfigurieren. AVR-GCC erledigt letzteres für Sie mit den ISR()-Makros, aber auf anderen Architekturen müssen Sie möglicherweise eine Funktionsadresse manuell in ein Register schreiben.
Mikrocontroller-Peripheriegeräte sind normalerweise unabhängig voneinander, sodass Sie sie einzeln lernen können. Es kann hilfreich sein, ein Peripheriegerät auszuwählen und es zu verwenden, um einen Teil der Dinge auf Systemebene zu lernen. Comm-Peripheriegeräte und PWMs sind gut für Taktung und IOs, und Timer sind gut für Interrupts.
Lassen Sie sich nicht von der Komplexität einschüchtern. Diese "einfachen" Mikrocontroller haben Ihnen bereits viel von dem beigebracht, was Sie wissen müssen. Bitte lassen Sie mich wissen, wenn Sie mich brauchen, um etwas zu klären.
int
/ int_leastN_T
-Typen für Stack-Variablen zu bevorzugen.int_fastN_t
Sie Typen, nicht int_leastN_t
Typen.int16_t
oft genauso schnell wie int32_t
für im Speicher gespeicherte Werte, aber der Standard erfordert, dass auf Plattformen mit int
17 Bit oder mehr auf -32767 int16_t x=32767; x+=2;
gesetzt werden muss , was häufig Vorzeichenerweiterungsanweisungen erfordert, selbst wenn Code dies tun würde x
Verwenden Sie niemals das Wrapping-Verhalten.x+=2
wäre es also legal, Anweisungen für 16-Bit-Typen zu verwenden, da der Compiler davon ausgehen kann, dass der Wert nicht umgebrochen wird, und daher würde die Verwendung das beobachtbare Verhalten nicht ändern. Aber ich denke , dass ARM keine 16-Bit-ADD-Anweisung hat, die dies ermöglichen würde. (Ich könnte mich irren, mein Wissen über den ARM-Befehlssatz ist nicht so gut.)int
angegeben ist, als Konvertieren des Werts in einen vorzeichenlosen Typ gleicher Größe und anschließendes Konvertieren in eine vorzeichenbehaftete Ganzzahl. Wenn der Wert innerhalb des Bereichs liegt, der für den Typ der vorzeichenbehafteten Ganzzahl angegeben ist, ist das Ergebnis implementierungsdefiniert und nicht undefiniert. Ich finde es albern, dass auf den meisten 32-Bit-Plattformen int16_t i; uint16_t u;
die Anweisung i*=i;
für alle Werte von definiert ist i
, aber u*=u;
nicht für alle Werte von u
, aber ich habe die Regeln nicht geschrieben.int16_t
, aber Compilern die zusätzliche Freiheit gewährt, dass ein Compiler einen Compiler haben darf, wenn ein Wert außerhalb des Bereichs in einer Variablen dieses Typs gespeichert wird Lesevorgänge der Variablen ergeben willkürlich jeden Wert, der mit dem gespeicherten Wert mod 65536 kongruent ist, obwohl Umwandlungen erforderlich wären, um auf den Bereich -32768..32767 zu kürzen ; Umgekehrt würde ich verlangen, dass, wenn das Ergebnis eines additiven, multiplikativen oder linken Verschiebungsoperators in einen vorzeichenlosen Typ umgewandelt oder gezwungen wird, der kleiner als int
...uint16_t
Werten undefiniertes Verhalten in Fällen auslöst, in denen das Ergebnis wieder zugewiesen wird uint16_t
.Es ist nützlich, sich daran zu erinnern, dass ARM das geistige Eigentum für den Mikroprozessor besitzt, aber eigentlich keine Teile herstellt. Stattdessen lizenzieren die Hersteller die verschiedenen ARM-Prozessorversionen und produzieren ihre eigenen einzigartigen Teile mit individuellen Mischungen aus Funktionen und Peripheriegeräten.
Wenn Sie neu in der Architektur sind, wäre es wahrscheinlich sinnvoll, mit der ARM-Dokumentation zu beginnen, die im Wesentlichen die Basisdokumentation für alle diese Mikroprozessoren ist.
Der Cortex-M0 wird beispielsweise auf der Website von ARM beschrieben .
Es gibt auch eine Liste von ARM-bezogenen Büchern , die eine Vielzahl von Bedürfnissen und Interessen abdecken.
Schließlich gibt es noch die Datenblätter der jeweiligen Hersteller. Für den M0 sind Cypress, NXP und STMicroelectronics nur drei der vielen Hersteller von Realteilen auf Basis des Cortex-M0.
(Und nein, ich arbeite nicht für ARM und habe es nie getan.)
Ein großer Unterschied besteht in der Verwendung von vom Anbieter bereitgestellten Bibliotheken. Für die PICs, Atmels usw. wurden die grundlegenden Bibliotheken (für GPIO, Timer, ADC usw.) von den meisten Entwicklern nicht viel verwendet. Meiner Erfahrung nach würden die Leute sie (höchstens) als Leitfaden verwenden, wenn sie ihren eigenen Code schreiben.
Bei ARM werden die Bibliotheken jedoch fast immer verwendet. Es gibt einen Standard, „CMSIS“, den Herstellern empfohlen wird. Die meisten tun es. Es hilft bei der Codeportabilität (zwischen verschiedenen ARMs und zwischen Herstellern) und bietet eine "standardisierte" Methode zum Strukturieren Ihres Codes. Die Leute gewöhnen sich daran, die Bibliotheksfunktionen zu sehen und zu verstehen.
Sicher, es gibt einige Entwickler, die direkt auf die Register zugreifen, aber sie sind die Ausreißer :)
Um Ihre Frage zu beantworten, fand ich es sehr hilfreich, die Bibliotheksdokumentation durchzulesen. ST verfügt über einen gut entwickelten Code mit einer großen, von Doxygen erstellten Hilfedatei. Sie können sehen, welche Optionen es für jedes Hardwaremodul gibt.
Um GPIO als Beispiel zu verwenden, behandelt die Initialisierungsfunktion:
Wenn Sie sich die Optionen ansehen, können Sie sehen, was möglich ist. Und natürlich lernen Sie, wie Sie diese Optionen an die Init-Funktion übergeben!
OK, jetzt, wo ich das gesagt habe, sehe ich, dass Ihr spezifischer ARM keine CMSIS-kompatiblen Bibliotheken hat. Stattdessen steht ihnen ihr proprietäres SDK zum Download zur Verfügung. Ich würde anfangen, durch ihre SDK-Dokumente zu suchen.
Wenn Sie nicht mit diesem speziellen Produkt verheiratet sind, empfehle ich Ihnen möglicherweise, einen anderen Anbieter mit kompatibleren Bibliotheken zu finden. Sie werden sowieso eine Lernkurve erklimmen, also können Sie Ihre Investition genauso gut tragbarer machen ...
Arme machen Spaß! Ich habe nicht zurückgeschaut.
Gute Zeit zum Umziehen; die 8-Bit sterben schnell; Wenn Sie ein 5-Dollar-Board mit (zum Beispiel) einem STM32F103 kaufen können, der ein ziemlich leistungsfähiger 32-Bit-ARM-Mikrocontroller ist (sogar mit USB!), gibt es keinen Zweifel, dass sich die Zeiten geändert haben.
Sie haben bereits einige hervorragende Antworten erhalten, aber in erster Linie würde ich sagen "Vergessen Sie die Montage" und fast "Vergessen Sie, sich darum zu kümmern, wie die CPU auf niedrigem Niveau funktioniert" - eines Tages wird es einen Eckfall geben, in dem Sie sich damit befassen müssen (eine bestimmte Optimierung oder zum Debuggen), aber ARM-Kerne führen C-Code außergewöhnlich gut aus (von Natur aus) und Sie müssen selten tief in die Eingeweide vordringen.
Das bedeutet, dass Sie eine gewisse Zeit damit verbringen werden, sich gegen Probleme mit Compilern (und insbesondere Linkern und Makefiles) zu wehren, die Ihnen obskure Fehler vorwerfen, aber sie sind alle überwindbar.
Die Eingeweide, wie die ARMs funktionieren (dh die ARM-CPU-Bücher), sind dicht und nicht sehr interessant, bis Sie tatsächlich optimieren müssen (und Sie werden erstaunt sein, wie selten das ist, wenn Sie 32-Bit-Register und Ihre PLL haben. d CPU-Takt liegt im Bereich von 100 MHz).
Der ARM-Befehlssatz der "alten Schule" ist viel einfacher zu lesen als der viel neuere "Thumb2" - den Sie auf den meisten modernen ARMs auf Mikrocontroller-Ebene (Cortex) finden - aber wieder die Innereien der Anweisungen in Assemblersprache treten meist in den Hintergrund; Wenn Sie das richtige Toolset haben (insbesondere einen anständigen Debugger auf Quellebene mit Haltepunkten / Einzelschritten usw.), ist es Ihnen einfach egal, ob es sich überhaupt um ARM handelt.
Sobald Sie in der Welt der 32-Bit-Register und 32-Bit-Datenbusbreiten und allem, was Sie sich jemals gewünscht haben, auf dem Chip angekommen sind, werden Sie nie wieder zu einer 8-Bit-CPU zurückkehren wollen; Im Grunde gibt es oft keine Strafe dafür, "es ruhig angehen zu lassen" und Code so zu schreiben, dass er mehr als effizient lesbar ist.
Allerdings ... Peripheriegeräte ... aye und da ist der Haken.
Auf modernen MCUs bekommt man sicher eine Menge Zeug, mit dem man spielen kann, und vieles davon ist ziemlich ausgefallenes Zeug; Sie finden oft eine Welt der Raffinesse, die weit über AVR-, PIC- und 8051-On-Chip-Peripheriegeräte hinausgeht.
Ein programmierbarer Timer? Nee, hab acht! DMA? Wie wäre es mit 12 Kanälen mit programmierbarer Priorität und Burst-Modus und verkettetem Modus und Auto-Reload und ... und ... und ...
I2C? I2S? Dutzende von Pin-Muxing-Optionen? Fünfzehn verschiedene Möglichkeiten, den On-Chip-Flash neu zu programmieren? Sicher!
Es fühlt sich oft so an, als ob Sie mit den Peripheriegeräten von einer Hungersnot zu einem Festmahl übergegangen sind, und es ist üblich, dass es ganze Brocken eines Chips gibt, die Sie bewundern, aber kaum verwenden (daher; Clock-Gating).
Die Menge an On-Chip-Hardware (und Variationen davon in nur einer Chiplinie eines Anbieters) ist heutzutage ziemlich verblüffend. Ein Chip-Anbieter wird natürlich dazu neigen, IP-Blöcke wiederzuverwenden, sobald Sie sich also mit einer bestimmten Marke vertraut gemacht haben, wird es einfacher, aber "Scheiße wird heutzutage verrückt".
Wenn überhaupt, sind die Peripheriegeräte und ihre Interaktionen (und DMA und Interrupts und Buszuweisung und und und ...) so komplex (und gelegentlich nicht genau wie in den Datenblättern beschrieben), dass Ingenieure häufig eine bevorzugte Auswahl an ARM-MCUs haben und neigen dazu, dabei zu bleiben, einfach weil sie mit den Peripheriegeräten und Entwicklungstools vertraut sind.
Gute Bibliotheken und Entwicklungstools (z. B. schneller Kompilier- und Debugzyklus mit einem geeigneten Debugger) und eine große Anzahl funktionierender Beispielcodeprojekte sind heutzutage absolut entscheidend für Ihre ARM-MCU-Wahl. Es scheint, dass die meisten Anbieter jetzt äußerst billige Evaluierungsboards haben (
Wie Sie sicherlich bemerkt haben, ändern sich die Regeln vollständig, sobald Sie mit ARMs über die Mikrocontroller-Ebene hinausgehen und in die SOC-Ebene (z zu rennen, weil Sie - mit verschwindend wenigen Ausnahmen - verrückt bellen würden, wenn Sie etwas anderes versuchen würden.
Grundsätzlich; Unabhängig von der CPU, die (möglicherweise) für Sie bei diesem Auftritt vorab ausgewählt wurde, kaufen Sie sich eine Handvoll supergünstiger Cortex-basierter Evaluierungsboards von ein paar verschiedenen Anbietern (TI, STM, Freescale und mehr kommen mir in den Sinn) und Probieren Sie es mit dem bereitgestellten Beispielcode aus.
Letzter Ratschlag; Sobald Sie die Seite oder drei im Datenblatt gefunden haben, die die Pin-Muxing-Optionen für den genauen Teilenummer- Chip beschreibt, mit dem Sie arbeiten, möchten Sie es vielleicht ausdrucken und an die Wand kleben. Spät in einem Projekt herauszufinden, dass eine bestimmte Kombination von Peripheriegeräten wegen Pin-Muxing unmöglich ist, macht keinen Spaß, und manchmal sind diese Informationen so vergraben, dass man schwören könnte, dass sie versuchen, sie zu verbergen :-)
Ich komme auch von AVR und bleibe jetzt normalerweise bei STM32 (Cortex-M). Folgendes empfehle ich für den Anfang und spiegelt meine eigenen Schwierigkeiten wider, als ich anfing:
Holen Sie sich ein Board mit einem Debugger oder zumindest einem JTAG-Anschluss (und kaufen Sie dann einen JTAG-Debugger). Es gibt viele billige, und Sie werden viel Zeit sparen, wenn Sie sie verwenden.
Holen Sie sich eine gute IDE mit allem, was enthalten ist. Ich habe die CooCox CoIDE schon vor langer Zeit empfohlen. Seitdem wurde die Entwicklung gestoppt und neu gestartet, daher bin ich mir nicht sicher, wie es jetzt ist. "Eine gute IDE" ermöglicht es Ihnen, die grundlegende Hello World-LED im Handumdrehen zum Blinken zu bringen.
"Eine gute IDE" sollte die CMSIS-Header des Herstellers einrichten. Dies sind im Grunde die Registerzuordnungen, die das einfachere Schreiben von C/C++-Programmen ermöglichen, mit Variablennamen anstelle von einfachen Zahlen und Zeigern.
Versuchen Sie, die Peripheriebibliotheken des Herstellers zu verwenden, wenn Sie nicht die absolut beste Leistung benötigen. Das tust du im Moment eigentlich nicht, da du lernst. Wenn Sie später feststellen, dass Sie mehr quetschen müssen, sehen Sie sich den Bibliothekscode an, um zu sehen, wie er was macht. Das Gute an Bibliotheken ist auch, dass Sie normalerweise viele verschiedene Chips desselben Herstellers mit demselben Code verwenden können.
Anders als bei AVR starten die ARM-Chips mit deaktivierten Peripheriegeräten. Sie müssen sie zuerst aktivieren. Eine gute Peripheriebibliothek enthält Beispiele zur richtigen Verwendung der Peripheriegeräte, und Sie können dem Datenblatt des Geräts weitere Informationen entnehmen. Denken Sie also daran, die Uhren und Peripheriegeräte zu aktivieren, bevor Sie sie verwenden. Ja, sogar die I/O-Ports gelten als Peripheriegeräte.
Codieren Sie, während Sie lernen. Versuchen Sie nicht, alles auf einmal zu groken, da es wirklich ziemlich komplex ist. Ich würde damit beginnen, den Uhrenbaum (Busse APB, AHB usw.) zu lernen und wie Uhren und Uhrenteiler interagieren. Dann würde ich nachsehen, wo die IDE die Linker-Skripte und den Startcode für Ihr Gerät speichert. Das Linker-Skript ist so ziemlich wie Sie den Speicher organisieren (wo ist RAM, Flash, ISR-Vektortabelle usw.). Das Startskript richtet Ihr Programm ein (etwas wie das Kopieren globaler Variableninitialisierer vom Flash in den RAM). Einige IDEs haben Startskripte in ASM und andere in C. Manchmal können Sie nach einem anderen in der von Ihnen bevorzugten Sprache googeln.
Bringen Sie den Debugger so schnell wie möglich zum Laufen. Es ist ziemlich üblich, am Anfang einen Fehler zu machen, indem einige Dinge (normalerweise Hardware-Initialisierung) in einer anderen Reihenfolge ausgeführt werden, als Sie sollten. Dies löst manchmal eine ISR-Ausnahme aus, die Sie in eine while(1);
Endlosschleife (Standardimplementierung für diese ISR) bringt, die Ihr Programm anhält und selbst mit einem Debugger schwer zu verfolgen ist. Stellen Sie sich ohne Debugger vor.
Wenn Sie von einem Debugger sprechen, versuchen Sie auch, den UART zum Laufen zu bringen, und verwenden Sie dann einen seriellen USB-Adapter, um ihn zu lesen. printf()
Debuggen ist immer nützlich :-)
Ich habe nicht viel an 8051, AVR oder PIC gearbeitet. Aber vor kurzem habe ich angefangen, mir die ARM Cortex MX-Prozessorreihe anzusehen. Daher kann ich Ihnen nicht viel über den Umstieg von 8051, AVR oder PIC sagen, aber meistens aus der Sicht eines Anfängers.
Der ARM®Cortex™-M4-Prozessor basiert auf der Harvard-Architektur und verfügt daher über separate Daten- und Befehlsbusse. Unten ist ein High-Level-Bild.
Diese Woche werden Vertreter von NXP unsere Einrichtung besuchen. Ich werde mich bei ihnen nach NXP ARM-Cortex Mx-Ressourcen erkundigen und sie hier veröffentlichen. Freescale hat Kinetis Low Power 32-Bit-Mikrocontroller (MCUs) auf Basis von ARM® Cortex®-M-Kernen , ich habe gehört, dass sie auch ähnliche Leitfäden zum Erlernen von ARM-Prozessoren haben. Leider habe ich sie nicht recherchiert.
Verweise:
DigitalNinja
AaronD
AaronD
Ronan Paixao