Zugriff auf stm32-Register über Strukturen in GCC

Ich versuche, mein eigenes USB-Gerät mit einem STM32F103 Blue Pill Board in C mit dem GCC-Arm-None-Eabi-Compiler zu rollen, aber ich bin auf ein seltsames Verhalten gestoßen, das ich zu verstehen versuche.

Ich habe eine Struktur erstellt, die die Endpunktpuffer-Deskriptortabellen modelliert, die dem USB-Peripheriegerät zugeordnet sind:

typedef struct _EP_BUF_DSCR {
    uint32_t ADDR_TX;
    uint32_t COUNT_TX;
    uint32_t ADDR_RX;
    uint32_t COUNT_RX;
} EP_BUF_DSCR;

Und ich habe einen Zeiger auf eine dieser Strukturen erstellt und so eingestellt, dass er den Anfang des USB-Paketspeicherbereichs der MCU adressiert:

EP_BUF_DSCR *EP0_DSCR = (EP_BUF_DSCR *) 0x40006000;

Jetzt kann ich also die Felder ADDR_TX und ADDR_RX setzen, indem ich einfach die entsprechenden Felder dereferenziere und ihnen Werte zuweise. Wenn ich jedoch versuche, dies mit dem Feld COUNT_RX zu tun, scheint es keine Wirkung zu haben:

// debugger memory view shows register as set to 0 after running this:
EP0_DSCR->COUNT_RX = 0x8400;  

Aber ich kann den Wert in diesem Register ändern, indem ich einen Zeiger direkt darauf erstelle und ihn dereferenziere:

// debugger memory view shows register set to correct value after running:
*((uint32_t *)(0x40006000 + 12)) = 0x8400;  

Könnte jemand einen Einblick geben, warum dies geschieht? Habe ich etwas Dummes getan oder verlasse ich mich unabsichtlich auf undokumentiertes Compilerverhalten?

Antworten (2)

Typischerweise müssen speicherabgebildete Hardware-Spezialfunktionsregister als flüchtig deklariert werden, damit die Hardware weiß, dass etwas ihren Zustand über den lokalen Ausführungsthread hinaus benötigen oder ändern kann - andernfalls ist es ihr freigestellt, Werte in Registern zwischenzuspeichern oder es zu vernachlässigen, scheinbar unbenutzte überhaupt zu berechnen .

Auf dem STM32 können bestimmte SFRs auch auf eine bestimmte Zugriffsbreite beschränkt sein.

Wenn das, was Sie versuchen zu tun, legitim ist, gibt es wahrscheinlich bereits eine geeignete Definition in den Treiber- oder HAL-Dateien des Herstellers (oder alternativen). Und selbst wenn dies nicht der Fall ist, können Sie viel lernen, indem Sie diese lesen, um zu sehen, wie etwas Einfaches wie ein GPIO-Block deklariert wird.

Natürlich ist es auch möglich, dass mit der bestimmten Operation, die Sie ausführen möchten, etwas ganz bestimmtes nicht stimmt - zum Beispiel, dass sie nicht beschreibbar ist oder nur sinnvoll beschreibbar ist, wenn sich die Hardware in einem bestimmten Zustand befindet, in dem der Debugger Glück hat zu erreichen, während Ihr Programm dies normalerweise nicht tut.

Ah! Es als flüchtig zu deklarieren, scheint ausgereicht zu haben. Danke schön!
@ user3513491 Warum CMSIS nicht verwenden - Sie haben alle bereits deklariert. Das Rad muss nicht neu erfunden werden. Keine CMSIS in nicht HAL, so dass Sie Ihre bloße Registerprogrammierung durchführen können.

Es gibt ein paar Dinge, die hier passieren könnten:

  1. Wie von Chris Stratton angemerkt, müssen Sie EP0_DSCR(und alle anderen Zeiger auf Hardwareregister) als volatile.

  2. Haben Sie APB1 und das USB-Peripheriegerät eingeschaltet und richtig getaktet? Die Register reagieren möglicherweise nicht richtig, wenn die Peripheriegeräte nicht laufen. Insbesondere muss das USB-Peripheriegerät mit 48 MHz getaktet werden, und APB1 muss mit ≥8 MHz getaktet werden.

  3. Hast du das CAN-Peripheriegerät aktiviert? Sowohl CAN als auch USB teilen sich einen Speicherpuffer, sodass Sie nicht beide gleichzeitig verwenden können.