PIC32 vs dsPIC vs ARM vs AVR, spielt die Architektur eine Rolle, wenn wir sowieso in C-Sprache programmieren? [abgeschlossen]

Wir verwenden derzeit einen 32-Bit-PIC32-Mikrocontroller. Es funktioniert gut für unsere Bedürfnisse, aber wir untersuchen auch andere Mikrocontroller, die uns besser passen können + wir haben andere Projekte, für die wir MCU auswählen. Zu diesem Zweck haben wir einen ARM-basierten SAM-DA -Mikrocontroller ausgewählt, der derselbe 32-Bit ist, aber ARM-basiert ist (in der Branche beliebter als PIC32).

Jetzt verwenden wir für PIC32 MPLAB, aber für ARM Cortex-M0 verwenden wir Atmel Studio. Wir werden die C-Sprache auf beiden Plattformen verwenden. Was mich interessiert, ist, dass wir zwei 32-Bit-Mikrocontroller (von derselben Firma) verwenden, aber unterschiedliche Architekturen haben. Dies erfordert, dass wir zwei verschiedene Geräte lernen und unsere „Lernkurve“ + Lieferzeit erhöhen. Aber andererseits denke ich auch, da wir in beiden Fällen die C-Sprache verwenden werden, sollte die Lernkurve für ARM nicht so hoch sein und es lohnt sich, auch diesen Prozessor zu erkunden.

Meine Hauptfrage ist, wie groß der Unterschied der Architektur ist, wenn wir in der C-Sprache programmieren, da sie eine Abstraktion der Interna des Mikrocontrollers bietet. Und was sind die Hauptunterschiede zwischen MPLAP und Atmel Studio in Anbetracht der Programmierung in C-Sprache?

Wenn die Dinge mit dem PIC32 funktionieren, wozu dann wechseln? Selbst wenn der Code vollständig portiert wird (das wird es nicht), gibt es immer noch die neue Toolkette und IDE, an die man sich gewöhnen muss. Was ist der Punkt? Aus religiösen Gründen zu wechseln oder "ARM-basiert" (oder irgendetwas anderes) zu sein, ist albern. Sie müssen einen guten Grund haben, aber Sie haben uns keinen gezeigt.
Ich habe nicht nach einem Wechsel gefragt. Ich habe darüber gesprochen, für andere Projekte eine andere Architektur zu wählen, da wir an mehreren Projekten arbeiten und unser bestehendes Design verbessert werden kann. Der Hauptpunkt war die Lernkurve und die Herausforderungen bei der gleichzeitigen Arbeit mit zwei verschiedenen Architekturen.
Eine Sache, die ich festgestellt habe, ist, dass Atmel Studio ein besseres Timing bietet als MPLAB YouTube-Video

Antworten (5)

Das ist ein recht eigensinniges Thema. Ich kann für mich sprechen (AVR, ARM, MSP430).

Unterschied 1 (am signifikantesten) liegt in der Peripherie. Jede MCU hat ähnliche UART, SPI, Timer usw. - nur Registernamen und Bits sind unterschiedlich. Meistens war es das Hauptproblem, mit dem ich mich befassen musste, wenn ich Code zwischen Chips bewegte. Lösung: Schreiben Sie Ihre Treiber mit einer gemeinsamen API, damit Ihre Anwendung portabel sein kann.

Unterschied 2 ist die Speicherarchitektur. Wenn Sie Konstanten im Flash auf einem AVR platzieren möchten, müssen Sie spezielle Attribute und spezielle Funktionen verwenden, um sie zu lesen. In der ARM-Welt dereferenzieren Sie einfach einen Zeiger, da es einen einzigen Adressraum gibt (ich weiß nicht, wie kleine PICs damit umgehen, würde aber annehmen, dass sie näher an AVR liegen).

Unterschied 3 ist die Deklaration und Behandlung von Interrupts. avr-gcchat das ISR()Makro. ARM hat nur einen Funktionsnamen (wie someUART_Handler() - wenn Sie CMSIS-Header und Startcode verwenden). ARM-Interrupt-Vektoren können überall (einschließlich RAM) platziert und zur Laufzeit geändert werden (sehr praktisch, wenn Sie beispielsweise zwei verschiedene UART-Protokolle haben, die umgeschaltet werden können). AVR hat nur die Möglichkeit, Vektoren entweder im "Haupt-Flash" oder im "Bootloader-Bereich" zu verwenden (wenn Sie also Interrupts anders handhaben möchten, müssen Sie eine ifAnweisung verwenden).

Unterschied 4 - Schlafmodi und Leistungssteuerung. Wenn Sie den geringsten Stromverbrauch benötigen, müssen Sie alle Funktionen der MCU nutzen. Dies kann je nach MCU sehr unterschiedlich sein - einige haben gröbere Energiesparmodi, andere können einzelne Peripheriegeräte aktivieren / deaktivieren. Einige MCUs haben einstellbare Regler, sodass Sie sie mit niedrigerer Spannung bei langsamerer Geschwindigkeit usw. betreiben können. Ich sehe keinen einfachen Weg, um die gleiche Effizienz auf einer MCU (sagen wir) mit 3 globalen Leistungsmodi und einer anderen mit 7 Leistungsmodi und zu erreichen individuelle periphere Taktsteuerung.

Das Allerwichtigste, wenn es um Portabilität geht, ist die klare Aufteilung Ihres Codes in hardwareabhängige (Treiber) und hardwareunabhängige (Anwendungs-)Teile. Letzteres können Sie auf einem normalen PC mit einem Mock-Treiber (z. B. Konsole anstelle eines UART) entwickeln und testen. Das hat mich viele Male gerettet, da 90 % des Anwendungscodes fertig waren, bevor die Prototyp-Hardware aus dem Reflow-Ofen kam :)

Das Gute an ARM ist meiner Meinung nach die "Monokultur" - Verfügbarkeit vieler Compiler (gcc, Keil, IAR ... um nur einige zu nennen), vieler kostenloser und offiziell unterstützter IDEs (zumindest für NXP, STM32, Silicon Labs, Nordic) , viele Debug-Tools (SEGGER - insbesondere Ozone, ULINK, OpenOCD ...) und viele Chip-Anbieter (ich fange gar nicht erst an, sie zu nennen). Der PIC32 ist hauptsächlich auf Microchip beschränkt (aber es spielt nur eine Rolle, wenn Sie deren Tools nicht mögen.

Wenn es um C-Code geht. Es ist zu 99% gleich, eine ifAussage ist gleich, eine Schleife funktioniert genauso. Sie sollten sich jedoch um die native Wortgröße kümmern. Zum Beispiel ist eine forSchleife auf einem AVR am schnellsten, wenn Sie sie uint8_tfür den Zähler verwenden, während auf ARM uint32_tder schnellste Typ ist (oder int32_t). ARM müsste jedes Mal auf 8-Bit-Überlauf prüfen, wenn Sie einen kleineren Typ verwenden.

Bei der Auswahl einer MCU und/oder eines Anbieters im Allgemeinen geht es hauptsächlich um Politik und Logistik (es sei denn, Sie haben sehr klare technische Einschränkungen, z. B.: hohe Temperatur – verwenden Sie MSP430 oder Vorago). Selbst wenn die Anwendung auf allem laufen kann und nur 5 % des Codes (Treiber) über die Produktlebensdauer entwickelt und unterstützt werden müssen, sind dies immer noch zusätzliche Kosten für das Unternehmen. Alle Orte, an denen ich gearbeitet habe, hatten einen bevorzugten Anbieter und eine MCU-Linie (wie „wählen Sie einen beliebigen Kinetis, den Sie wollen, es sei denn, es gibt einen sehr guten Grund, sich für etwas anderes zu entscheiden“). Es hilft auch, wenn Sie andere Leute um Hilfe bitten können, also würde ich als Manager vermeiden, eine 5-Personen-Entwicklungsabteilung zu haben, in der jeder einen völlig anderen Chip verwendet.

„AVR ist am schnellsten, wenn Sie uint8_t für den Zähler verwenden, während auf ARM uint32_t der schnellste Typ (oder int32_t) ist. ARM müsste jedes Mal nach einem 8-Bit-Überlauf suchen, wenn Sie einen kleineren Typ verwenden würden.“ Sie können uint_fast8_t verwenden, wenn Sie nur mindestens 8 Bit benötigen.
@Michael - sicher können Sie die _fast-Typen verwenden, aber Sie können sich nicht auf das Überlaufverhalten verlassen. In der stdint.h meines gcc habe ich "typedef unsigned int uint_fast8_t", was im Grunde ein uint32_t ist :)
Der Versuch, eine API zu schreiben, die effizient, universell und vollständig ist, ist schwierig, da verschiedene Plattformen unterschiedliche Fähigkeiten haben. Die CPU spielt wahrscheinlich weniger eine Rolle als die Peripheriegeräte und die damit getroffenen Designentscheidungen. Beispielsweise haben einige Geräte es ermöglicht, dass verschiedene Peripheriegeräte jederzeit in höchstens wenigen Mikrosekunden neu konfiguriert werden können, während andere möglicherweise mehrere Schritte erfordern, die sich über Hunderte von Mikrosekunden oder sogar Millisekunden erstrecken. Eine API-Funktion, die für das frühere Muster gedacht ist, kann in einer Interrupt-Service-Routine verwendet werden, die mit 10.000 Hz läuft, aber ...
... könnte eine solche Verwendung auf Plattformen nicht unterstützen, die eine Verteilung von Operationen über Hunderte von Mikrosekunden erfordern würden. Ich weiß nicht, warum Hardware-Designer nicht sehr bemüht zu sein scheinen, die API-Semantik "schnelle Operation zu jeder Zeit" zu unterstützen, aber viele verwenden ein Modell, das einzelne Operationen synchronisiert, anstatt anzugeben, ob beispielsweise eine Anforderung erteilt wurde Wenn Sie ein Gerät einschalten und der Code erkennt, dass es nicht eingeschaltet sein muss, muss der Code warten, bis sich das Gerät einschaltet, bevor er die Anforderung zum Ausschalten ausgeben kann. Die reibungslose Handhabung in einer API fügt große Komplikationen hinzu.

Ich habe mehrere MCUs von vier verschiedenen Herstellern verwendet. Die Hauptarbeit besteht jedes Mal wieder darin, sich mit der Peripherie vertraut zu machen.

Zum Beispiel ist ein UART selbst nicht zu komplex und ich finde meinen Treiberport leicht. Aber das letzte Mal brauchte ich fast einen Tag, um die Uhren, I/O-Pins unterbrechen, aktivieren usw. in Ordnung zu bringen.

Der GPIO kann sehr komplex sein. Bit-Set, Bit-Clear, Bit-Toggle, Sonderfunktionen aktivieren/deaktivieren, Tri-State. Als nächstes erhalten Sie Interrupts: beliebiger Rand, Anstieg, Abfall, Level-Low, Level-High, Selbstlöschung oder nicht.

Dann gibt es I2C, SPI, PWM, Timer und zwei Dutzend weitere Arten von Peripheriegeräten mit jeweils eigenen Taktaktivierungen, und jedes Mal sind die Register mit neuen Bits unterschiedlich. Bei all denen dauert es viele Stunden, das Datenblatt zu lesen, wie man unter welchen Umständen welches Bit setzt.

Der letzte Hersteller hatte viele Codebeispiele, die ich unbrauchbar fand. Alles wurde abstrahiert. Aber als ich es nachverfolgt habe, ging der Code durch sechs! Ebenen von Funktionsaufrufen zum Setzen eines GPIO-Bits. Schön, wenn Sie einen 3-GHz-Prozessor haben, aber keine MCU mit 48 MHz. Mein Code war am Ende eine einzelne Zeile:

GPIO->set_output = bit.

Ich habe versucht, allgemeinere Treiber zu verwenden, aber ich habe es aufgegeben. Auf einer MCU hat man immer mit Platz und Taktzyklen zu kämpfen. Ich habe festgestellt, dass die Abstraktionsschicht die erste ist, die das Fenster verlässt, wenn Sie eine bestimmte Wellenform in einer Interrupt-Routine erzeugen, die bei 10 kHz aufgerufen wird.

Jetzt habe ich also alles funktioniert und ich habe vor, NICHT wieder zu wechseln, es sei denn, es gibt einen sehr, sehr guten Grund.

All dies muss über die Anzahl der verkauften Produkte und Einsparungen abgeschrieben werden. Eine Million verkaufen: 0,10 sparen, um zu einem anderen Typ zu wechseln, bedeutet, dass Sie 100.000 für Software-Arbeitsstunden aufwenden können. Wenn Sie 1000 verkaufen, müssen Sie nur 100 ausgeben.

Aus diesem Grund bleibe ich persönlich bei Assembler. Schöne Binärdatei, keine Abstraktion.
Der Präprozessor von C kann ziemlich gut damit umgehen, besonders wenn er mit __builtin_constant-Intrinsics kombiniert wird. Wenn man Konstanten für jedes E/A-Bit der Form (Portnummer*32 + Bitnummer) definiert, ist es möglich, ein Makro zu schreiben, OUTPUT_HI(n)das einen Code liefert, der einer Konstante wie 0x6A entspricht, aber eine einfache Unterroutine aufruft, wenn GPIOD->bssr |= 0x400;es ist nicht konstant. Abgesehen davon sind die meisten APIs von Anbietern, die ich gesehen habe, zwischen mittelmäßig und schrecklich. nn

Dies ist eher eine Meinung/Kommentar als eine Antwort.

Sie wollen und sollten nicht in C programmieren. C++ ist bei richtiger Anwendung weit überlegen. (OK, ich muss zugeben, wenn es falsch verwendet wird, ist es viel schlechter als C.) Das beschränkt Sie auf Chips, die einen (modernen) C++-Compiler haben, was ungefähr alles ist, was von GCC unterstützt wird, einschließlich AVR (mit einige Einschränkungen, Filo erwähnt die Probleme eines uneinheitlichen Adressraums), schließt aber fast alle PICs aus (PIC32 könnte unterstützt werden, aber ich habe noch keinen anständigen Port gesehen).

Wenn Sie Algorithmen in C/C++ programmieren, ist der Unterschied zwischen den von Ihnen erwähnten Optionen gering (außer dass ein 8- oder 16-Bit-Chip stark im Nachteil ist, wenn Sie viel 16-, 32- oder höhere Bit-Arithmetik durchführen). Wenn Sie die letzte Unze Leistung benötigen, müssen Sie wahrscheinlich Assembler verwenden (entweder Ihren eigenen oder Code, der vom Anbieter oder einem Drittanbieter bereitgestellt wird). In diesem Fall sollten Sie den von Ihnen ausgewählten Chip vielleicht noch einmal überdenken.

Wenn Sie die Hardware codieren, können Sie entweder eine Abstraktionsschicht verwenden (oft vom Hersteller bereitgestellt) oder Ihre eigene schreiben (basierend auf dem Datenblatt und/oder Beispielcode). In IME vorhandene C-Abstraktionen (mbed, cmsis, ...) sind oft funktional (fast) korrekt, versagen jedoch schrecklich in der Leistung (überprüfen Sie, ob Oldfarts über 6 Ebenen der Indirektion für eine Pin-Set-Operation schimpfen), Benutzerfreundlichkeit und Portabilität. Sie möchten Ihnen alle Funktionen des jeweiligen Chips zur Verfügung stellen, die Sie in fast allen Fällen nicht benötigen und die Sie eher nicht interessieren, und Ihren Code an diesen bestimmten Anbieter (und wahrscheinlich diesen bestimmten Chip) binden.

Hier kann C++ viel besser: Wenn es richtig gemacht wird, kann ein Pin-Set 6 oder mehr Abstraktionsschichten durchlaufen (weil dies eine bessere (portable!) Schnittstelle und kürzeren Code ermöglicht), aber dennoch eine Schnittstelle bereitstellen, die zielunabhängig ist für die einfachen Fälle und führen immer noch zu demselben Maschinencode, den Sie in Assembler schreiben würden .

Ein Ausschnitt des von mir verwendeten Codierungsstils, der Sie entweder begeistern oder sich entsetzt abwenden kann:

// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };

template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {

   static void direction_set_direct( pin_direction d ){
      ( ( d == pin_direction::input )
         ? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER )  = ( 0x1U << pin );
   }

   static void set_direct( bool v ){
      ( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR )  = ( 0x1U << pin );    
   }
};

// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;

// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led  = invert< pin_out< _led > >;

In Wirklichkeit gibt es noch mehr Abstraktionsschichten. Die endgültige Verwendung der LED, sagen wir, um sie einzuschalten, zeigt jedoch nicht die Komplexität oder die Details des Ziels (für ein Arduino Uno oder eine blaue ST32-Pille wäre der Code identisch).

target::led::init();
target::led::set( 1 );

Der Compiler lässt sich von all diesen Schichten nicht einschüchtern, und da keine virtuellen Funktionen beteiligt sind, durchschaut der Optimierer alles (einige Details werden weggelassen, wie das Aktivieren der Peripherieuhr):

 mov.w  r2, #134217728  ; 0x8000000
 ldr    r3, [pc, #24]   
 str    r2, [r3, #16]
 str    r2, [r3, #48]   

So hätte ich es in Assembler geschrieben - WENN ich erkannt hätte, dass die PIO-Register mit Offsets von einer gemeinsamen Basis verwendet werden können. In diesem Fall würde ich das wahrscheinlich tun, aber der Compiler kann solche Dinge viel besser optimieren als ich.

Soweit ich eine Antwort habe, lautet sie: Schreiben Sie eine Abstraktionsschicht für Ihre Hardware, aber tun Sie dies in modernem C++ (Konzepte, Vorlagen), damit Ihre Leistung nicht beeinträchtigt wird. Damit können Sie problemlos zu einem anderen Chip wechseln. Sie können sogar mit der Entwicklung auf einem beliebigen Chip beginnen, den Sie herumliegen haben, mit dem Sie vertraut sind, über gute Debugging-Tools verfügen usw., und die endgültige Auswahl auf später verschieben (wenn Sie mehr Informationen über den erforderlichen Speicher, die CPU-Geschwindigkeit usw. haben).

Meiner Meinung nach ist es eine der Täuschungen der eingebetteten Entwicklung, zuerst den Chip auszuwählen (diese Frage wird in diesem Forum häufig gestellt: Für welchen Chip soll ich mich entscheiden .... Die beste Antwort lautet im Allgemeinen: Es spielt keine Rolle.)

(Bearbeiten - Antwort auf "Also leistungsmäßig wären C oder C++ auf dem gleichen Niveau?")

Bei gleichen Konstrukten sind C und C++ gleich. C++ hat viel mehr Konstrukte für die Abstraktion (nur ein paar: Klassen, Templates, constexpr), die wie jedes Werkzeug zum Guten oder zum Schlechten verwendet werden können. Um die Diskussionen interessanter zu gestalten: Nicht alle sind sich einig, was gut oder schlecht ist...

In Bezug auf die Leistung wären C oder C++ also auf dem gleichen Niveau? Ich würde denken, dass C++ mehr Überladung haben wird. Auf jeden Fall haben Sie mich in die richtige Richtung gelenkt, C++ ist der richtige Weg, nicht C.
C++-Vorlagen erzwingen einen Polymorphismus zur Kompilierzeit, der in Bezug auf die Leistung null (oder sogar negative) Kosten verursachen kann, da der Code für jeden spezifischen Anwendungsfall kompiliert wird. Dies eignet sich jedoch tendenziell am besten für die Zielgeschwindigkeit (O3 für GCC). Laufzeitpolymorphismus kann wie virtuelle Funktionen viel stärkere Nachteile erleiden, obwohl er möglicherweise einfacher zu warten und in einigen Fällen gut genug ist.
Sie behaupten, dass C++ besser ist, aber dann gehen Sie und verwenden Umwandlungen im C-Stil. Zum Schämen.
@JAB Ich habe nie viel für die Besetzungen im neuen Stil empfunden, aber ich werde sie ausprobieren. Aber meine derzeitige Priorität liegt auf anderen Teilen dieser Bibliothek. Das eigentliche Problem ist natürlich, dass ich die Zeiger nicht als Vorlagenparameter übergeben konnte.
@Hans mein cto-Stil (Compile Time Objects) hat einen ziemlich engen Anwendungsfall (nahe der Hardware, bekannte Situation zur Kompilierzeit), er ist eher ein C-Killer als ein Ersatz für traditionelle Verwendungen von virtuell-basiertem OO. Ein nützlicher Beifang ist, dass das Fehlen eines Umwegs es ermöglicht, die Stapelgröße zu berechnen.

Wenn ich das richtig verstehe, möchten Sie wissen, welche architekturspezifischen Funktionen der Plattform in Ihrer C-Sprachumgebung "auftauchen", wodurch es schwieriger wird, wartbaren, portablen Code auf beiden Plattformen zu schreiben.

C ist bereits ziemlich flexibel, da es ein "portabler Assembler" ist. Für alle von Ihnen ausgewählten Plattformen sind GCC-/kommerzielle Compiler verfügbar, die die Sprachstandards C89 und C99 unterstützen, was bedeutet, dass Sie ähnlichen Code auf allen Plattformen ausführen können.

Es gibt ein paar Überlegungen:

  • Einige Architekturen sind von Neumann (ARM, MIPS), andere sind Harvard. Die Haupteinschränkungen treten auf, wenn Ihr C-Programm Daten aus dem ROM lesen muss, zB um Zeichenketten zu drucken, Daten als "const" oder ähnliches zu definieren.

Einige Plattformen/Compiler können diese "Einschränkung" besser verbergen als andere. Auf AVR müssen Sie zB bestimmte Makros verwenden, um ROM-Daten zu lesen. Auf PIC24/dsPIC sind auch spezielle tblrd-Anweisungen verfügbar. Einige Teile verfügen jedoch zusätzlich über die Funktion "Program Space Visibility" (PSVPAG) , die es ermöglicht, eine Seite des FLASH in den RAM abzubilden, wodurch eine sofortige Datenadressierung ohne tblrd verfügbar wird. Der Compiler kann dies sehr effektiv tun.

ARM und MIPS sind von Neumann, haben also Speicherbereiche für ROM, RAM und Peripherie auf einen Bus gepackt. Sie werden keinen Unterschied zwischen dem Lesen von Daten aus RAM oder "ROM" bemerken.

  • Wenn Sie unter C tauchen und sich generierte Anweisungen für bestimmte Operationen ansehen, werden Sie einige große Unterschiede bei I/O feststellen. ARM und MIPS sind RISC -Load-Store-Registerarchitekturen . Das bedeutet, dass der Datenzugriff auf dem Speicherbus über MOV-Anweisungen erfolgen muss. Dies bedeutet auch, dass jede Änderung eines Peripheriewerts zu einer Read-Modify-Write-Operation (RMW) führt. Es gibt einige ARM-Teile, die Bit-Banding unterstützen, das Set/CLR-Bit-Register im E/A-Peripheriebereich zuordnet. Allerdings müssen Sie diesen Zugang selbst codieren.

Andererseits ermöglicht ein PIC24 ALU-Operationen, Daten direkt über indirekte Adressierung zu lesen und zu schreiben (sogar mit Pointer-Modifikationen..). Dies weist einige Merkmale einer CISC-ähnlichen Architektur auf, sodass eine Anweisung mehr Arbeit leisten kann. Dieses Design kann zu komplexeren CPU-Kernen, niedrigeren Taktraten, höherem Stromverbrauch usw. führen. Zum Glück für Sie ist das Teil bereits entworfen. ;-)

Diese Unterschiede können bedeuten, dass ein PIC24 in Bezug auf E / A-Operationen "druckvoller" sein kann als ein ähnlich getakteter ARM- oder MIPS-Chip. Möglicherweise erhalten Sie jedoch ein ARM/MIPS-Teil mit viel höherem Taktgeber für die gleichen Preis-/Paket-/Designbeschränkungen. Ich denke, aus praktischen Gründen denke ich, dass viel "Lernen der Plattform" darin besteht, sich damit auseinanderzusetzen, was die Architektur kann und was nicht, wie schnell einige Operationen sein werden usw.

  • Peripheriegeräte, Taktverwaltung usw. unterscheiden sich je nach Teilefamilie. Genau genommen wird sich dies auch innerhalb des ARM-Ökosystems zwischen den Anbietern ändern, mit Ausnahme einiger Cortex-m-gebundener Peripheriegeräte wie NVIC und SysTick.

Diese Unterschiede können etwas durch Gerätetreiber gekapselt werden, aber letztendlich hat die eingebettete Firmware einen hohen Grad an Kopplung mit der Hardware, sodass kundenspezifische Arbeit manchmal nicht vermieden werden kann.

Wenn Sie die Ökosysteme von Microchip/früher Atmel verlassen, werden Sie möglicherweise feststellen, dass ARM-Teile mehr Einrichtung erfordern, um sie zum Laufen zu bringen. Ich meine in Bezug auf; Uhren für Peripheriegeräte aktivieren, dann Peripheriegeräte konfigurieren und "aktivieren", NVIC separat einrichten usw. Dies ist nur ein Teil der Lernkurve. Sobald Sie sich daran erinnern, all diese Dinge in der richtigen Reihenfolge zu tun, wird sich das Schreiben von Gerätetreibern für all diese Mikrocontroller irgendwann ziemlich ähnlich anfühlen.

  • Versuchen Sie auch, Bibliotheken wie stdint.h, stdbool.h usw. zu verwenden, falls Sie dies noch nicht getan haben. Diese Integer-Typen machen die Breiten explizit, wodurch das Codeverhalten zwischen den Plattformen am vorhersehbarsten wird. Dies kann die Verwendung von 32-Bit-Ganzzahlen auf einem 8-Bit-AVR bedeuten; aber wenn Ihr Code es braucht, soll es so sein.

Ja und nein. Aus Programmierersicht verbergen Sie idealerweise die Details des Befehlssatzes. Aber das ist zum Teil schon nicht relevant, die Peripherie, um die es beim Schreiben des Programms geht, ist nicht Teil des Befehlssatzes. Gleichzeitig können Sie nicht einfach 4096-Byte-Flash-Teile über diese Befehlssätze hinweg vergleichen, insbesondere wenn Sie C verwenden, die Menge des Flash-/Speicherverbrauchs wird stark vom Befehlssatz und Compiler bestimmt, einige sollten niemals einen Compiler sehen (hust PIC Husten) aufgrund dessen, wie viel Verschwendung dieser Ressourcen durch das Kompilieren verbraucht wird. Andere Flash-Verbrauch ist ein kleiner Overhead. Leistung ist auch ein Problem bei der Verwendung von Hochsprache und Leistung in MCU-Anwendungen, sodass es einen Unterschied machen kann, ob Sie 3 US-Dollar pro Board für die MCU oder 1 US-Dollar ausgeben.

Wenn es darum geht, die Programmierung zu vereinfachen (zu den Gesamtkosten des Produkts), sollten Sie in der Lage sein, ein Entwicklerpaket für die MCU herunterzuladen, sodass die Befehlssatzarchitektur etwas ist, das Sie nie sehen. Wenn dies also Ihr Hauptanliegen ist, dann ist nicht besorgniserregend. Es kostet Sie immer noch Geld, was die Produktkosten betrifft, diese Bibliotheken zu verwenden, aber die Markteinführungszeit könnte kürzer sein, ich finde, dass die Verwendung der Bibliotheken mehr Zeit/Arbeit in Anspruch nimmt, als direkt mit den Peripheriegeräten zu sprechen.

Unterm Strich sind die Befehlssätze Ihre geringste Sorge, gehen Sie zu echten Problemen über.