Eingebettete Firmware: Best Practices für C-Array-Initialisierung mit externen Dateidaten und Signalformen
Erfahren Sie, wie Sie C-Arrays mit externen Textdateien initialisieren.
Dieser Leitfaden erklärt, wie Sie Arrays in einem C-Programm mit Daten füllen, die in separaten Textdateien gespeichert sind. Die Werte sind nicht im Quellcode eingebettet, sondern werden zur Kompilierzeit geladen. Wir behandeln eindimensionale und mehrdimensionale Arrays, zeigen, wie Sie steuern können, ob das Array im RAM oder im nichtflüchtigen Speicher liegt, und veranschaulichen, wie Sie zwischen mehreren Datendateien für die Initialisierung wählen können.
Die Beispiele zielen auf einen 32-Bit-ARM-Mikrocontroller ab und werden mit GCC kompiliert. Alle Codeausschnitte verwenden Standard-C und funktionieren mit diesem Compiler.
Grundlagen der Array-Initialisierung
Wenn Sie ein Array in C deklarieren, können Sie eine Liste von Werten – bekannt als Initialisierer – in geschweiften Klammern angeben. Zum Beispiel:

Wenn Sie die Größe in Klammern weglassen, leitet der Compiler sie aus der Anzahl der Initialisierer ab. Wenn das Array größer deklariert wird als die Initialisiererliste, werden die verbleibenden Elemente standardmäßig auf null gesetzt. Mehr Initialisierer als die deklarierte Größe lösen einen Kompilierfehler aus.
Leerzeichen
Initialisierer werden durch Kommas getrennt, und Sie können frei Leerzeichen – Leerzeichen, Tabs, Zeilenumbrüche, Wagenrückläufe, vertikale Tabs oder Formfeeds – zwischen ihnen hinzufügen. In C können Anweisungen mehrere Zeilen umfassen; dies ist besonders nützlich für große Arrays, die mehrere Seiten Quellcode füllen können.

Initialisierung eines Arrays aus einer Datei
Der C-Präprozessor kann beliebige Textdateien mit der #include-Direktive einbinden. Diese Funktion ist nicht auf Header- oder Quelldateien beschränkt; jede Plain-Text-Datei ist akzeptabel. Eine einfache Datei mit Zahlen, Kommas und Leerzeichen kann zur Initialisierung eines Arrays verwendet werden:

Die eingebundene Datei muss reiner Text sein – keine Rich-Text-Formatierung, Header oder andere Artefakte. Ein schnelles Beispiel, erstellt mit Windows Editor, sieht so aus:

Beim Kompilieren erscheint das Array im Speicher wie im Debugger-Screenshot gezeigt. In diesem Fall liegt das Array im RAM, angezeigt durch die hohen Adressen in der "Location"-Spalte.

Speichern eines Arrays im nichtflüchtigen Speicher und Auswählen einer Datendatei
Im obigen Beispiel ist das Array eine globale Variable ohne explizite Platzierung. Der Compiler geht davon aus, dass sie zur Laufzeit geändert werden kann, und platziert die Variable im RAM. Die Initialwerte liegen im nichtflüchtigen Speicher (typischerweise Flash) und eine Boot-up-Routine kopiert sie in den RAM. Wenn das Array wirklich konstant ist, weist die Deklaration mit const den Compiler an, es nur im Flash zu behalten, was wertvollen RAM spart. Der folgende Screenshot zeigt den Effekt: der "Location"-Wert des Arrays zeigt auf niedrige Flash-Adressen.

Sie können #define und #if Direktiven verwenden, um zwischen RAM- und Flash-Speicher zu wechseln oder verschiedene Datendateien für die Initialisierung auszuwählen. Der folgende Code ermöglicht die Wahl des Speicherorts zur Kompilierzeit:

Das #if-Konstrukt ist ein klassisches Beispiel für bedingte Einbindung. Es bestimmt, ob der const-Qualifizierer basierend auf einem Kompilierzeit-Flag angewendet wird.
Ähnlich können Sie bedingt auswählen, welche Datei die Initialdaten liefert:

Testen mit einem großen Array
Zur Validierung der Technik habe ich ein 10.000-Sample-Rauschwellenform in einer CSV-Datei verwendet. Das untenstehende Diagramm zeigt die Wellenform, während die Deklaration demonstriert, wie die Daten in das Programm eingebunden werden:


Die ursprüngliche CSV-Datei hatte keine Kommas nach jedem Wert. Mit einem regulären Ausdruck Suchen/Ersetzen (Suche nach \R und Ersetzen mit ,\R) wurden Kommas zu allen 10.000 Einträgen in einem einzigen Vorgang hinzugefügt.
Das resultierende Array ist im Debugger sichtbar, ordentlich in Gruppen von 100 Elementen angeordnet.

Mehrdimensionale Arrays
Bei der Arbeit mit zwei- oder dreidimensionalen Daten denken Sie daran, dass C Arrays in Zeilen-Hauptreihenfolge speichert. Ein uint16_t test[2][3] Array liegt im Speicher so:
[0,0] [0,1] [0,2] [1,0] [1,1] [1,2]
Der Zugriff auf aufeinanderfolgende Elemente entlang des rechtesten Index ist schneller, da die Daten zusammenhängend sind. Wenn Sie zwei Vektoren von 1.000 Samples benötigen, deklarieren Sie test[2][1000] für maximale Cache-Freundlichkeit.
Der Initialisierer für ein zweidimensionales Array wird normalerweise mit verschachtelten Klammern gruppiert:

Sie können jedoch die äußeren Klammern weglassen; der Compiler füllt das Array dann links-nach-rechts. Er gibt eine Warnung aus: missing braces around initializer. Solange die Gesamtzahl der Werte der Array-Größe entspricht, kompiliert der Code.

Um die Daten auf mehrere Dateien zu verteilen, binden Sie einfach jede Datei nacheinander ein und umschließen die gesamte Liste mit einem Paar Klammern:

Initialisierung von Arrays in Unions

Bei der Initialisierung füllt der Compiler das erste Mitglied zuerst. Wenn die Reihenfolge umgekehrt ist, können Sie eine Warnung sehen, weil die Initialisiererliste nicht vollständig geklammert ist. Doppelte Klammern um das #include stellen die korrekte Gruppierung sicher:
Die Datendatei für die Union sieht so aus:

Im Speicher beginnen sowohl vector als auch matrix an derselben Position, wie im Debugger gezeigt:

Kennen Sie eine andere Methode, ein mehrdimensionales Array aus einer einzelnen numerischen Datei zu initialisieren? Teilen Sie Ihre Ideen in den Kommentaren.
Bonus-Tipp: Strings
Die Initialisierung eines Strings aus einer externen Datei erfordert, dass die Anführungszeichen in der Datei selbst stehen. Ein direktes #include innerhalb des String-Literals bricht den Compiler. Stattdessen platzieren Sie die Anführungszeichen in der Datei und binden sie so ein:

Der resultierende String liegt im RAM, wie im Screenshot illustriert.

Diese Beispiele sind theoretisch und sollten mit jedem Standard-C-Compiler funktionieren. Wenn Sie compiler-spezifische Eigenheiten feststellen, lassen Sie es uns in den Kommentaren wissen.
Quelle bereitgestellt.
Eingebettet
- Die Cloud-Revolution: Wie Cloud Computing IT, Sicherheit und Karrieren transformiert
- STMicroelectronics stellt STM32Cube.AI vor: Edge-Geräte mit eingebetteten Neural Networks befähigen
- DATA MODUL bringt ultra-helle 21,5" & 31,5" Open-Frame Monitore mit EasyTouch für Outdoor & Indoor
- Microchip führt 24-bit & 16-bit Delta-Sigma ADCs mit 153,6 kSPS Datenraten
- Contrinex 2020: Cloud-Ready Sensoren & Bluetooth Light Curtains für Safety
- C++ Arrays verstehen: Deklaration, Initialisierung und Pointer‑Techniken
- C++ Dynamische Array‑Allokation: Praktischer Leitfaden mit Code‑Beispielen
- Dateien in Java mit BufferedReader lesen – Praktischer Leitfaden mit Beispielen
- IoT mit IBM & Tech Data zum Leben erwecken – Teil 2
- IoT in die Realität umsetzen: Tech Data & IBM Insights – Teil 1