Erstellen einer programmierbaren Nachschlagetabelle auf STM32

7

Ich versuche, eine Nachschlagetabelle zu definieren, die auf einem STM32F103 keine Konstante ist. Grundsätzlich möchte ich eine Flash-Seite haben, die sich im normalen Betrieb wie eine konstante Nachschlagetabelle verhält, aber hin und wieder (denken Sie an Tage auseinander) möchte ich in der Lage sein, diese Tabelle zu löschen und eine neue auf die Flash-Seite zu schreiben . Ich glaube, ich verstehe, wie man die HAL verwendet, um Funktionen auszuführen, die ich in meinem Programm benötige, aber es scheint, als müsste ich diesen Speicherblock in der Linker-Datei deklarieren, und ich habe große Probleme, ein Beispiel dafür zu finden deckt das ab. Die Standard-mem.ld von CubeMX sieht folgendermaßen aus:

MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
}

Es scheint, als ob ich danach einen Block mit Ausgabeabschnitten benötige, in dem ich ein Schlüsselwort für> FLASH festschreibe, aber ich weiß nicht, welches Schlüsselwort das wäre oder wie ich die Tabelle im Code deklarieren würde, um ihr die richtige Adresse zu geben.

Ich habe den emulierten EEPROM-Anwendungshinweis gesehen, aber es scheint viel zusätzlicher Aufwand für den Speicher zu sein, der nicht genügend Lösch- / Schreibzyklen aufweist, um über die Lebensdauer des Speichers besorgt zu sein. Hilfe!

Brett K Smith
quelle
Es gibt keinen Grund, den Linker überhaupt zu beteiligen. Irgendwann müssen Sie angeben, wo sich die Tabelle befindet und welchen Block der Flash löschen soll. Man würde annehmen, dass Sie nur diese Tabelle aktualisieren möchten, nicht die gesamte Binärdatei, also möchten Sie wirklich, dass die Tabelle sowieso eine separate Binärdatei ist. kann diese Adresse sicher im Linker-Skript fest codieren, könnte aber genauso einfach oder nicht einfacher sein, irgendwo im Code ein #define zu erstellen.
old_timer

Antworten:

7

Hinweis: Das Verknüpfen ist nicht Teil der C-Sprachstandards, daher implementiert jeder Compiler Linker-Dateien anders. Sie scheinen GCC zu verwenden, daher werde ich einen Code freigeben, der damit funktioniert.

linker.ld:

MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 31K
/*(1)*/ FIXED_DATA(rw)  : ORIGIN = 0x8007C00, LENGTH = 1K /* your LUT here*/
}
............
.FIXED_DATA (NOLOAD):/*(2)*/
  {
    *(STATIC_DATA);/*(3)*/
  } >FIXED_DATA /*(4)*/

In C:

__attribute__((section("STATIC_DATA"))) 
             const static volatile statistic_static_data_t staticData;

Was dies tut:

(1) Erstellen Sie einen Bereich mit dem Namen FIXED_DATA mit der angegebenen Position und Größe. Beachten Sie, dass mein Code von einem anderen Gerät stammt. Überprüfen Sie in Ihrem Datenblatt, wie groß Ihre Blöcke sind (sie sind in einem Gerät möglicherweise nicht gleich groß!). Stellen Sie sicher, dass Sie FLASH entsprechend verkleinern, da sonst eine Fehlermeldung angezeigt wird, dass sie nicht in den Speicher passen.

(2) Erstellen Sie einen Abschnitt mit dem Namen FIXED_DATA. Es muss eigentlich nicht FIXED_DATA heißen, aber es hilft, den Überblick zu behalten. Das Attribut NOLOAD weist den Linker an, den Bereich nicht zu füllen (siehe unten).

(3) Fügen Sie alle mit STATIC DATA gekennzeichneten Variablen in diesen Speicherbereich ein

(4) Fügen Sie alles in diesem Abschnitt in den Bereich FIXED_DATA ein, den wir oben erstellt haben

In der C-Datei müssen Sie nur die Variablen markieren, die Sie in den Bereich einfügen möchten. Ich empfehle, sie const zu nennen, da Sie im Allgemeinen nicht direkt in Flash schreiben möchten. Volatile hilft bei Compiler-Optimierungen, bei denen davon ausgegangen wird, dass sich konstante Daten nie ändern.

Warum all diese Schwierigkeiten durchmachen, anstatt die viel einfacheren Lösungen in anderen Antworten zu verwenden? Weil es aufrüstbar ist . Wenn Sie in Zukunft ein FW-Update durchführen möchten, möchten Sie möglicherweise die gespeicherten Daten im Speicher behalten. Die NOLOAD-Anweisung in der Linker-Datei bewirkt genau das: Der Linker füllt die Daten nicht mit 0, wie dies normalerweise der Fall wäre, wenn dort eine globale Variable lebt.

Sie können mehr über die arkanen Wege des Linkers erfahren, wenn Sie nach "ld syntax" suchen.

Makotanist
quelle
1
Dies ist meiner Meinung nach der richtige Weg. Gute Antwort.
Bitsmack
Das ist es, wonach ich gesucht habe. Vielen Dank. Eine Frage, weil ich in letzter Zeit eine Reihe von Linker-Dateien angestarrt habe. Sollte es eine Art geben. = ALIGN (4); Anweisung, nachdem das STATIC_DATA-Attribut deklariert wurde oder vom Datentyp impliziert wird.
Brett K Smith
@BrettKSmith Ich glaube nicht, dass Sie Daten falsch ausrichten können. Der kleinste Datentyp ist 8 Bit, und der Compiler fügt Auffüllungen in Strukturen und Bitfelder ein, um eine ordnungsgemäße Ausrichtung sicherzustellen. Wenn Sie sich an einer größeren Größe ausrichten müssen (z. B. 16 Byte), empfehlen wir Ihnen, dies auch in der Linker-Datei zu tun. So erhalten Sie eine garantierte Ausrichtung, wenn Sie dem Abschnitt eine neue Variable hinzufügen.
Makotanist
Guter Punkt über flüchtig. Im Fall von STM32 ist es bedauerlich, dass sich die kleinen Seiten (wenn sie nicht einheitlich sind) am Anfang des Flashs befinden, sodass es nützlich sein kann, den Interrupt-Vektor von .text zu trennen. Auf diese Weise ist es auch etwas unabhängiger von der Flash-Größe (wenn Sie beispielsweise nach der Prototyping-Phase eine kleinere MCU wählen)
Jan Dorniak
4

Das ist nicht so kompliziert zu machen.

Grundsätzlich benötigen Sie eine konstante globale Variable, die in Flash platziert wird und die Position mithilfe eines Pragmas definiert.

In C ++ könnte der Header ungefähr so ​​aussehen:

class FlashLookUpTable
{
    public:
    struct LookUpTable_t
    {
        uint32_t table[100];
    };

    public:
    static LookUpTable_t const * GetLookUpTablePointer();

    private:
    static const uint32_t FLASH_PAGE_SIZE = 1024U; // or whatever the flash smallest deletable size is
    // This variable contains the number of bytes needed to store the structure in complete flash pages
    static const uint32_t ARRAY_SIZE = (sizeof(LookUpTable_t)/FLASH_PAGE_SIZE) + FLASH_PAGE_SIZE;

    union FlashPageSizedStructure
    {
        LookUpTable_t t;
        uint8_t flashpage[ARRAY_SIZE];
    }

    static const FlashPageSizedStructure tableInFlash;

};

Und so sieht die Implementierung aus:

// the exact pragma depends on the compiler used, this one works for IAR
// the location should be at the start of a page boundary, especially when using this union approach
#pragma location=0x800FC00U
const FlashLookUpTable::FlashPageSizedStructure FlashLookUpTable::tableInFlash = 
{
    // initialize values here
}

FlashLookUpTable::LookUpTable_t const * FlashLookUpTable::GetLookUpTablePointer(void) const 
{
    return &tableInFlash.t;
}

Um diese Seite in Flash zu schreiben, benötigen Sie einen Puffer (entweder im RAM oder in Flash) mit der gleichen Größe wie eine Flash-Seite, da Sie die Seite löschen müssen, bevor Sie sie erneut schreiben, sodass eine Änderung eines einzelnen Werts an Ort und Stelle erfolgt nicht möglich.

Je nachdem, wie genau Sie es verwenden, müssen Sie die Struktur möglicherweise als deklarieren volatile. Dies geschieht insbesondere, wenn Sie direkt auf die Tabelle zugreifen (nicht mit einem Zeiger wie in diesem Fall).

Einige Compiler optimieren den Code so, dass sie die Konstante aus der Tabelle direkt in den Code übernehmen. Das Ergebnis ist, dass wenn Sie den Wert in Ihrer Tabelle ändern, der Wert im Code nicht berücksichtigt wird.

Der IAR-Compiler hatte einige Probleme (es ist in der aktuellen Version behoben) bei der Handhabung von a, static volatile constalso wechselte ich zur Verwendung eines Zeigers.


Wenn Sie die Werte ändern möchten, benötigen Sie eine Art Flash-Algorithmus.

Das Schreiben von Flash besteht immer aus:

  1. Seiteninhalt sichern
  2. Update mit geänderten Werten aktualisieren
  3. Seite löschen
  4. Seite schreiben
  5. Zur Sicherheit: Vergleichen Sie die geschriebene Seite mit dem Backup

Hinweis für Fortgeschrittene: In einigen Fällen können Sie ausnutzen, dass Sie Nullen an Positionen schreiben können, an denen sich eine Eins befand, sodass Sie 0x7F in 0x3F ändern können, aber nicht umgekehrt. In diesem Fall müssen Sie keine Seite löschen. Einige Controller unterstützen dies möglicherweise nicht.

Arsenal
quelle
Wenn es als const deklariert ist, kann ich die Tabelle dann aus dem Programm heraus überschreiben, oder würde die const-Eigenschaft dies nicht verhindern?
Brett K Smith
1
@Arsenal Wie stellen Sie sicher, dass der Linker keine nicht verwandten Daten auf derselben Flash-Seite ablegt, die beim Löschen des Flashs zerstört würden?
Berendi - Protest
1
@BrettKSmith Sie müssen einen Flash-Algorithmus verwenden, um in die Werte zu schreiben. Die const verhindert, dass Sie versehentlich Werte zuweisen, die zu einer Fehlerausnahme führen.
Arsenal
@berendi Während es an dem Linker liegt, wo Sachen platziert werden sollen, neigt es dazu, den Blitz von Anfang bis Ende zu füllen. Also platziere ich diese Variablen am Ende des Flashs. Wenn Sie sich wirklich Sorgen machen, können Sie eine Vereinigung mit einem Array von Bytes mit der Größe der Flash-Seite erstellen. Der Linker hat dann keinen Platz, um etwas anderes zu platzieren.
Arsenal
@berendi Ich habe den Code mit einer Union aktualisiert, um zu zeigen, was ich mit meinem Kommentar meine. Es ist wahrscheinlich ein besserer Ansatz, danke für die Eingabe.
Arsenal
4

Ja, es wäre am saubersten, einen Abschnitt für Ihre Tabelle zu deklarieren.

Am einfachsten ist es jedoch, den FLASH-Bereich um die Seitengröße zu reduzieren und dann:

int *table = (int *) (0x0800000 +0x20000 - PAGE_SIZE)

Hier ist "0x20000" die Größe des Flash-Speichers. In / my / code verwende ich eine Definition von MY_FLASH_SIZE, definiert als: #define MY_FLASH_SIZE ((* (kurz *) 0x1ffff7cc) << 10), die für die F0-Serie funktioniert, vielleicht auch für Ihre. Ich bin von 128.000 Geräten in der Entwicklung (nur ein paar Nickel teurer als die 64.000) zu 64.000 Geräten in der Produktion gewechselt, ohne zu bemerken, dass dies die Position des Flash-Bereichs verändert hat ... :-)

rew
quelle
Die Linker-Datei ändert sich also in: FLASH (rx): ORIGIN = 0x08000000, LENGTH = 127K und diese Deklaration des Int-Zeigers würde in den Text meines Codes aufgenommen?
Brett K Smith
1

Wenn Sie Flash übrig haben, können Sie eine globale const-Variable deklarieren, die doppelt so groß wie eine Flash-Seite ist, und ihre Startadresse auf ein Vielfaches der Flash-Seitengröße runden. Ein bisschen verschwenderisch, aber keine Notwendigkeit, Linkerscript-Magie zu betreiben.

Wouter van Ooijen
quelle
1
Warum sollte dies im Flash und nicht im RAM gespeichert werden? Ich verstehe nicht
Brett K Smith
Entschuldigung, ich habe vergessen: eine const- Variable. In modernem C ++ ist constexpr .
Wouter van Ooijen