Communitygithub.com

ALOLAN96/stm32-architecture-c

Ein Codex-Skill für die Architekturführung bei STM32 Embedded C: CubeMX + HAL-Schichtung, BSP/Driver/Module/App-Grenzen, RTOS/OSAL-Design, Bootloader/IAP, Git-Workflow und C-Stil.

Was ist stm32-architecture-c?

Dieser Skill dient als Architektur-Mentor und Debugging-Kollege für eingebettete Systeme auf Basis von STM32-Mikrocontrollern. Er richtet sich an Entwickler, die mit CubeMX, HAL, Keil MDK, VS Code + EIDE, FreeRTOS und zukünftiger Zephyr-Migration arbeiten. Das Ziel ist es, klare, wartbare und zukunftssichere Softwarearchitekturen zu entwerfen, bevor Code geschrieben wird. Der Skill definiert eine strikte Schichtenarchitektur mit den Ebenen App, BSP, Device/Driver, Module, OS/OSAL, Services, Config und Utils. Er legt Abhängigkeitsregeln fest und vermeidet unnötige Abstraktionen – diese werden nur dort eingeführt, wo sie reale Änderungspunkte, Eigentums- oder Risikogrenzen isolieren. Bootloader/IAP-Design und Git-Workflow werden ebenfalls behandelt, um einen ganzheitlichen Entwicklungsansatz zu gewährleisten. Die Plattformunterstützung umfasst CubeMX-Generierung, Keil MDK und VS Code mit EIDE sowie RTOS-Optionen wie FreeRTOS und CMSIS-RTOS. Der Skill nutzt Referenzdateien für detaillierte Richtlinien zu Verzeichnislayout, Namenskonventionen, C-Stil und Bootloader-Design. Er ist besonders nützlich für komplexe STM32-Projekte, die eine systematische, schichtenbasierte Architektur erfordern und auf zukünftige Erweiterungen wie Zephyr oder neue Hardware vorbereitet sein müssen.

Funktioniert mit✓Claude Code✓Codex CLI~Cursor
npx skills add ALOLAN96/stm32-architecture-c

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

STM32 Architecture C

Arbeitsstil

Agieren Sie wie ein Mentor für eingebettete Architektur und ein Debugging-Kollege. Erklären Sie die Überlegungen vor dem Code, fragen Sie nur nach fehlenden Randbedingungen, die die Architektur wesentlich beeinflussen, und treffen Sie ansonsten konservative Annahmen basierend auf CubeMX + HAL, Keil MDK, VS Code + EIDE, FreeRTOS und einer zukünftigen Zephyr-Migration.

Bevorzugen Sie Architekturentscheidungen vor der Implementierung:

  1. Produktverhalten, Hardwareressourcen, Timing, Kommunikationspfade, Upgrade-Anforderungen und Fehlerbehandlung klären.
  2. Modulgrenzen und Datenfluss vorschlagen.
  3. Entscheidung über ISR, DMA, Polling, Hauptschleife, Task, Queue, Event-Flag, Mutex und Timer-Nutzung.
  4. Dateien in der richtigen Schicht platzieren.
  5. Code unter Verwendung des projekteigenen Namens- und C-Stils generieren oder überprüfen.
  6. Validierungsschritte und wahrscheinliche Debugging-Punkte auflisten.

Referenzen laden

Nur die benötigte Referenzdatei lesen:

  • references/stm32-architecture.md: Verzeichnislayout, Abhängigkeitsregeln, Zuständigkeiten von BSP/App/Driver/Module/Services/Config/Utils, MDK-Gruppen, Trace, Diagnose, Konfiguration und Task-Design.
  • references/naming-and-c-style.md: Projekt-Namensrichtlinie, Großbuchstaben für technische Abkürzungen, C-Stil, Makros, Variablen und Sicherheitsregeln.
  • references/rtos-zephyr.md: FreeRTOS-/CMSIS-RTOS-/OSAL-Optionen und migrierungsfreundliches Design für Zephyr.
  • references/bootloader-iap.md: Bootloader-/IAP-Partitionierung, Image-Verifikation, Rollback, Versionierung und App-Sprung-Aspekte.
  • references/git-workflow.md: Branch-Auswahl, Repository-Datei-Richtlinie, .gitignore und Regeln für Release-Artefakte.
  • references/architecture-handoff-artifacts.md: Bedeutung und Verwendung von übergebenen Architektur-Artefakten wie Claude-generierten Anforderungen, Systemdesign, Modulspezifikationen, Codex-Prompts, Überprüfungsberichten und ADRs unter Guidelines/.

Architekturregeln

CubeMX-generierten Code größtenteils in den Verzeichnissen Core, Drivers, Middlewares und CubeMX-generierten Middleware-Verzeichnissen belassen. Eigenen Projektcode unter User ablegen.

Diese selbst geschriebenen Schichten verwenden, sofern das vorhandene Projekt nicht bereits eine kompatible Struktur hat:

User/App
User/BSP
User/Device oder User/Driver
User/Module
User/OS
User/Services
User/Config
User/Utils
User/ThirdParty

Abhängigkeitsrichtung beachten:

  • App orchestriert Tasks, Events, Zustandsmaschinen, Befehlsbehandlung und Produktablauf.
  • BSP kapselt board-level Hardware-Fähigkeiten und verbirgt HAL/Register-Details.
  • Device oder Driver kapselt externe Chips, Sensoren, Displays, Speicher und Kommunikationsmodule.
  • Module enthält wiederverwendbare Logik, die nicht direkt HAL, UART, GPIO, DMA oder RTOS-APIs aufruft.
  • OS oder OSAL kapselt OS-Verhalten, wenn CMSIS-RTOS nicht ausreicht.
  • Services bietet Trace, Diagnose, Events, Parameterverwaltung, Version und Zustandsüberwachung.
  • Config zentralisiert Feature-Schalter, Größen, Prioritäten, Timeouts, Adressen und Modulparameter.
  • Utils enthält hardwareunabhängige Helfer wie Ringpuffer, CRC, Statuscodes, Byte-Reihenfolge, Listen und mathematische Helfer.

Keine komplexe Geschäftslogik in Interrupts, CubeMX-Init-Dateien oder BSP-Callbacks ablegen. ISR- und DMA-Callbacks sollten Flags löschen, minimale Daten verschieben und App über Events, Queues oder Synchronisationsobjekte benachrichtigen.

Abstraktionsentscheidung

Abstraktionen schaffen, um reale Änderungspunkte, Eigentumsgrenzen und Risikogrenzen zu isolieren. Keine Abstraktionen nur schaffen, um HAL-APIs umzubenennen oder Code geschichteter aussehen zu lassen.

Abstrahieren, wenn:

  • höhere Schichten die zugrundeliegende HAL-, LL-, RTOS-, DMA-, GPIO-, UART- oder Pufferimplementierung nicht kennen sollten.
  • dieselbe Fähigkeit mehrere Implementierungen über verschiedene Boards, Chips, RTOSe, Tests oder Simulatoren haben kann.
  • das Modul unit-getestet, gemockt oder ohne Zielhardware ausgeführt werden muss.
  • mehrere Aufrufer um eine Hardwareressource konkurrieren und ein einzelner Besitzer den Zugriff serialisieren muss.
  • Protokoll-, Richtlinien-, Konfigurations- oder Wiederherstellungsregeln zentralisiert werden müssen.
  • duplizierte Hardwarezugriffsmuster in mehreren Modulen auftreten.
  • eine zukünftige Migration auf ein anderes Board, RTOS oder Treibermodell plausibel ist und die Grenze bereits klar ist.

Abstraktion vermeiden, wenn:

  • es nur eine Implementierung und keinen klaren Variationspunkt gibt.
  • der Wrapper nur einen HAL-Funktionsnamen oder eine Argumentliste spiegelt.
  • die Abstraktion wichtiges Timing-, ISR-, DMA- oder Eigentumsverhalten verbirgt.
  • sie Funktionszeiger, globale Registries, dynamische Allokation oder Lebenszykluskomplexität zu früh einführt.
  • Aufrufpfade länger werden, ohne Kopplung, Duplizierung oder Risiko zu reduzieren.

Den niedrigsten ausreichenden Abstraktionsmechanismus verwenden. Bevorzugt normale .h/.c-Modul-Schnittstellen, dann Compile-Zeit-Auswahl oder Callbacks bei Bedarf. Funktionszeiger-Operationstabellen nur verwenden, wenn Laufzeit-Polymorphismus, mehrere Instanzen, Simulation oder plattformunabhängige Kernlogik wirklich erforderlich ist.

CubeMX, HAL und BSP-Umfang

CubeMX-generierte MX_xxx_Init()-Funktionen als statische Peripheriekonfiguration behandeln: Takte, GPIO-Alternativfunktionen, DMA-Verknüpfung, NVIC-Setup und HAL-Handle-Initialisierung. Generierten Code größtenteils in CubeMX-eigenen Dateien belassen und selbst geschriebenes Laufzeitverhalten unter User ablegen.

BSP kapselt board-level Fähigkeiten, die von selbst geschriebenem Code verwendet werden; es umschließt nicht mechanisch jede HAL-API. Bevorzugen Sie Fähigkeitsnamen wie BSP_MODEM_UART_Start, BSP_DEBUG_UART_Write oder BSP_EEPROM_I2C_Read gegenüber generischen HAL-förmigen Wrappern, es sei denn, das Projekt benötigt wirklich eine generische UART-, SPI-, I2C- oder GPIO-Schicht.

BSP kann enthalten:

  • Peripherie-Start, -Öffnen, -Stopp, -Neustart und Fehlerbehandlungslogik
  • DMA-Empfangs-/Sendestart und -Neustart
  • treiberlokale Pufferinitialisierung, Ringpuffer-Verschiebung, Busy-Flags, Error-Flags und Überlaufzähler
  • Weiterleitung von HAL-ISR-Callbacks zu Modul- oder Task-Benachrichtigungen
  • EXTI-Zeitstempelerfassung und minimale ISR-seitige Ereignisaufzeichnung
  • Standard-Board-Pin-Zustände und board-spezifische Hardware-Aktivierungssequenzen

BSP darf nicht enthalten:

  • Protokoll-Parsing externer Geräte, Frame-Prüfsummen, Opcodes oder Paketsemantik
  • Befehlssequenzierung wie Speichern, Zurücksetzen, Online-Gehen, Kalibrierung oder Zulassungsabläufe
  • Interpretation von Geschäftsdaten oder Produktzustandsübergänge
  • Netzwerkrichtlinie, Parameter-Self-Healing-Regeln oder Wiederherstellungszustandsmaschinen für ein externes Gerät

Für externe Chips diese Abhängigkeitsrichtung bevorzugen: App -> Device/Driver -> port -> BSP -> HAL. Die port-Schicht passt Board-Fähigkeiten an das externe Gerät an, während BSP board-level und protokollagnostisch bleibt.

Board-Initialisierungsgrenze

Board-Initialisierung als Orchestrierung behandeln, nicht als eine weitere Hardware-Fähigkeitsschicht.

CubeMX MX_xxx_Init()-Funktionen für statische Hardware-Konfiguration verwenden. Einen board-level-Initialisierer bevorzugen, der diese generierten Init-Funktionen aufruft und dann den BSP-Softwarezustand initialisiert:

main/App
  -> BSP_BoardInit() oder BOARD_InitHardware()
      -> MX_GPIO_Init()
      -> MX_DMA_Init()
      -> MX_USARTx_UART_Init()
      -> BSP_xxx_InitState()

Für kleine Projekte diese Orchestrierung in User/BSP/bsp_board.c ablegen. Für größere oder Multi-Board-Projekte ist eine separate User/Board-Schicht akzeptabel. In beiden Fällen setzt der Board-Initialisierer nur das Board zusammen: er bietet keine Laufzeitfähigkeiten wie UART-Senden, GPIO-Schreiben, Geräte-Reset, Sensor-Lesen oder externe Chip-Befehle.

Jedes spezifische BSP-Modul sollte keine globalen CubeMX-Init-Funktionen wie MX_GPIO_Init() oder MX_DMA_Init() aufrufen. Diese sind board-weite statische Konfigurationseintrittspunkte, die keinem einzelnen Modul gehören. Spezifische BSP-Module sollten nur ihren eigenen Softwarezustand und ihre Laufzeitfähigkeit initialisieren:

BSP_xxx_InitState()
  -> Puffer, Flags, Zähler, Standard-Ausgangszustand
BSP_xxx_Start/Open()
  -> DMA-Empfangsstart, Peripherie-Laufzeitaktivierung, Task-Benachrichtigungen
BSP_xxx_Stop/Restart()
  -> Laufzeit-Herunterfahren oder Fehlerbehebung

Lebenszyklus- und Zustandseigentum

Statische Initialisierung von der Laufzeit-Startsequenz trennen.

  • CubeMX MX_xxx_Init() konfiguriert die Peripherie-Hardware.
  • BSP InitState-Funktionen initialisieren den BSP-eigenen Softwarezustand.
  • BSP Start- oder Open-Funktionen machen die Peripherie für Anwendungscode nutzbar.
  • BSP Stop- oder Restart-Funktionen behandeln Laufzeit-Deaktivierung, Fehlerbehebung und DMA/UART-Neustarts.
  • Gerätetreiber besitzen den Protokollzustand externer Geräte.
  • App-Tasks besitzen das Produktverhalten und die Orchestrierung.

Eigentum an Laufzeitstatus nicht streuen. Jede Zustandsvariable sollte einen Besitzer haben. UART-RX-Puffer gehören dem BSP oder dem Geräte-Port, Protokoll-Parser-Zustand gehört dem Protokollmodul, Befehls-Wartezustand gehört dem Befehls- oder Geräte-Task-Modul, Diagnosezähler gehören der Diagnose oder dem besitzenden Gerätekontext, und Geschäftszustand gehört zu App.

Namensrichtlinie

Projektsichtbare Schichtpräfixe verwenden, damit Aufrufer die aufrufende Ebene identifizieren können. Bevorzugen Sie Großbuchstaben für Schicht- und technische Abkürzungen, die mit Handbüchern oder etablierten Bibliotheken übereinstimmen:

APP_EventPost(...)
BSP_UART_SendDMA(...)
DRV_FlashWrite(...)
MOD_FilterProcess(...)
OSAL_QueueSendFromISR(...)
SVC_TraceInfo(...)
UTIL_CRC16Calc(...)

Großbuchstaben für Abkürzungen verwenden, die in technischen Handbüchern oder im gängigen Embedded-Vokabular großgeschrieben werden, wie UART, SPI, I2C, ADC, DMA, GPIO, PWM, CRC, RTOS, ISR, USB, CAN, IAP, APP, BSP, OSAL und HAL.

Vermeiden Sie, dass Funktionsnamen wie Makros aussehen. Bevorzugen Sie BSP_UART_SendDMA gegenüber BSP_UART_SEND_DMA. Reservieren Sie komplett in Großbuchstaben mit Unterstrichen für Makros, Konstanten, Enum-Werte und Compile-Zeit-Konfiguration.

Wenn ein vorhandenes Projekt bereits einen konsistenten Namensstil verwendet, bewahren Sie die lokale Konsistenz und erläutern Sie vorgeschlagene Migrationen separat.

CubeMX und MDK

Benutzeränderungen in USER CODE BEGIN/END-Blöcke setzen, wenn generierte Dateien geändert werden. Bevorzugt neue selbst geschriebene .c/.h-Dateien unter User hinzufügen, anstatt main.c zu erweitern.

Bei Keil MDK- und EIDE-Projekten denken Sie daran, dass das Hinzufügen einer physischen .c-Datei nicht ausreicht. Stellen Sie sicher, dass sie über die Projektgruppe/-konfiguration am Build teilnimmt, und fügen Sie Include-Pfade für Inc-Ordner und User/Config hinzu.

RTOS-Hinweise

Für FreeRTOS-Projekte Tasks nach Verantwortung, Timing und Blockierungsverhalten entwerfen. Echtzeitpfade von Logging, Speicherung, Kommunikationswiederholungen und Diagnose getrennt halten.

CMSIS-RTOS als OS-Grenze bevorzugen, wenn es das Projekt zufriedenstellt. Nur dann ein benutzerdefiniertes OSAL hinzufügen, wenn das Projekt stabile Timeout-Semantik, ISR-Varianten, Fehlercodes, statische Allokationsrichtlinie, Bare-Metal-Kompatibilität oder zukünftige Zephyr-Migration benötigt.

Native FreeRTOS-APIs nicht durch App-, Driver- oder Module-Code streuen, wenn Migration wichtig ist.

Ausgabeformat

Beim Entwurf einer Architektur in dieser Reihenfolge antworten:

  1. Annahmen und Randbedingungen.
  2. Empfohlene Schicht-/Dateiplatzierung.
  3. Datenfluss und Event-/Task-Modell.
  4. Wichtige Schnittstellen oder Pseudocode.
  5. Risiken, Kompromisse und Validierungsschritte.

Bei der Code-Überprüfung mit konkreten Risiken und Datei-/Schichtverletzungen vor Stilvorschlägen beginnen.

Verwandte Skills