Schreiben eines Programms für verschiedene Mikrocontroller

Ich habe 2 völlig unterschiedliche Mikrocontroller (einer ist 16-Bit-MSP und ein anderer 32-Bit-Nordic). Sie sollen die gleiche Aufgabe erledigen und ich dachte, dass es vielleicht möglich wäre, ein Programm in C-Sprache zu schreiben und dann verschiedene Compiler zu verwenden, während sie programmiert werden. Ich habe einige gemeinsame xxx.h-Dateien mit unterschiedlichen Quellimplementierungen für jedes Gerät erhalten, sodass ein Teil des Codes genau gleich aussehen würde. Der Unterschied besteht darin, dass interne Register geschrieben werden und dieser Teil des Codes für jedes Gerät unterschiedlich sein muss, also habe ich über die Einführung eines Makros nachgedacht, das zeigt, für welchen Mikrocontroller ich Code kompilieren möchte. Dann könnte ich #ifdef verwendenDirektive innerhalb von Quelldateien, um eine bedingte Kompilierung durchzuführen. Ich habe keine Erfahrung mit solcher Programmierung, also brauche ich ein paar Tipps, ist es eine gute Idee oder ist es besser, ein Programm für jeden Mikrocontroller zu schreiben?

Antworten (2)

Der Trick besteht darin, eine Schnittstelle zu definieren, die Ihnen Zugriff auf die E / A-Pins (oder andere Peripheriegeräte und Funktionen, die Sie benötigen, und möglicherweise Verzögerungsfunktionen) ermöglicht. Deklarieren Sie diese Schnittstelle in einer .h-Datei. Schreiben Sie nun Ihre Anwendung in eine oder mehrere .c-Dateien, die diese .h-Datei verwenden. Schreiben Sie zwei .c-Dateien, eine für jede Plattform, die die Schnittstelle für diese bestimmte Plattform implementieren. Wenn Sie für eine Plattform erstellen, kompilieren und verknüpfen Sie sie mit der richtigen Implementierungs-.c-Datei.

Eine mögliche .h-Datei:

bool pin_read( unsigned char pin );
void pin_write( unsigned char pin, bool value );
void configure_pin_as_input(  unsigned char pin );
void configure_pin_as_output(  unsigned char pin );
void delay_us( unsigned int t );

Dieser Ansatz erfordert keine Vorprozessor-Tricksereien, erlegt jedoch jedem Zugriff auf eine plattformspezifische Funktion einen Overhead für Funktionsaufrufe auf. Wenn dies nicht akzeptabel ist, können Sie beide Schnittstellen als Makros in der .h-Datei implementieren (unter Verwendung von #ifdefs, um zwischen ihnen auszuwählen).

Ein weiterer Trick besteht darin, beim .h/.c-Ansatz zu bleiben, aber den gesamten Anwendungscode mit der enthaltenen implementierung.c zu kompilieren und sich auf den Optimierer zu verlassen, um den Funktionsaufruf-Overhead zu eliminieren. Dies erfordert zwei verschiedene „Haupt“-Dateien, eine für das 16-Bit-Ziel

// main.c for MSP430
#include "interface.h"
#include "msp430-interface.c"
#include "application.c"

und eine für das andere Ziel

// main.c for Cortex-M0
#include "interface.h"
#include "CortexM0-interface.c"
#include "application.c"

Persönlich bevorzuge ich C++, um solche Probleme anzugehen, und ich verwende im Wesentlichen den letzten Ansatz, der für C++-Template-basierte Programmierung ganz normal ist (Templates müssen in der .h-Datei sein). (Siehe diesen Artikel und dieses Video für meinen Ansatz)

Im Allgemeinen ist dies eine gute Antwort, aber bitte niemals #include.c-Dateien. Es sollte niemals einen Grund dafür geben, wenn Ihre IDE und Ihr Linker einigermaßen gesund sind. Darüber hinaus fügt dies eine enge Kopplung zwischen Main und der Hardware hinzu, was schlecht ist. Sie sollten nur msp430-interface.c im Projekt gegen den Cortex M0 austauschen müssen, und alles sollte gut verknüpft sein, vorausgesetzt, dass diese beiden C-Dateien die #includegenerische Schnittstellendatei sind und dieselben Funktionsnamen verwenden. Main sollte nur #includedas gleiche generische sein. Stellen Sie sicher, dass Sie Header Guards in jeder einzelnen h-Datei verwenden, die jemals geschrieben wurde.
Ich stimme zu, dass das Einfügen von Code (außer C++-Vorlagen) die am wenigsten wünschenswerte Option ist, und in einem solchen Fall sollten diese Dateien nicht .c genannt werden, um ihre Verwendung explizit anzugeben. In diesem Fall ist die main.c keine klassische Hauptdatei, diese Rolle wurde von der enthaltenen Anwendung übernommen, und die main.c hat die Rolle einer Konfigurationsdatei. Nicht der gebräuchlichste Ansatz, in manchen Situationen immer noch OK. Die Kopplung zwischen dem 'Hauptcode' (in einer der enthaltenen Dateien) und der Hardware ist die gleiche wie beim normalen Interface-in-.h-Dateien-Ansatz.

Nun, ich stelle mir vor, dass das Programm, das Sie ausführen, in beiden Mikrocontrollern die gleiche Funktionalität hat.

Zuerst würden Sie die generischen Teile des Codes schreiben, die nicht von der Architektur abhängen. Wenn Sie Daten von einem bestimmten Gerät aus dem Mikrocontroller schreiben oder abrufen oder sogar auf bestimmte Register des Prozessors zugreifen, würden Sie eine Funktion aufrufen.

In der .c-Datei, die diese Funktion implementiert, würden Sie dann #ifdef mit einer Variablen ausführen, die die Architektur definiert, um zwischen der Funktion für den Mikrocontroller X und der für Y auszuwählen.

Möglicherweise müssen Sie die Kommunikationsschnittstelle zwischen dem generischen Code und der spezifischen Funktion standardisieren, um die Besonderheiten beider MCUs unterstützen zu können.

Denken Sie daran, dass es für ein Programm effizient wäre, das viele gemeinsame Teile hat und nur ein wenig spezifisch für die Architektur ist.

Ein weiterer Vorteil betrifft die Implementierung neuer MCUs. Wenn Sie Ihrem Projekt eine dritte Architektur hinzufügen müssen, schreiben Sie einfach ein paar neue Funktionen, anstatt Ihren gesamten Code neu zu schreiben.