Communitygithub.com

ALOLAN96/stm32-architecture-c

Un skill Codex pour l'architecture C embarquée STM32 : structuration CubeMX + HAL, couches BSP/Driver/Module/App, conception RTOS/OSAL, Bootloader/IAP, workflow Git et style C.

Qu'est-ce que stm32-architecture-c ?

Ce skill Codex agit comme un mentor en architecture embarquée pour les projets STM32 en C. Il guide l'utilisateur à travers les décisions architecturales avant l'implémentation, en mettant l'accent sur l'organisation des couches logicielles (App, BSP, Driver, Module, Services, Config, Utils), l'utilisation de CubeMX et HAL, la conception RTOS avec FreeRTOS et la migration future vers Zephyr. Le skill couvre également les règles de nommage, les abstractions, la gestion du cycle de vie, le démarrage du bootloader/IAP et le workflow Git. Il propose des réponses structurées : hypothèses, placement des fichiers, flux de données, interfaces clés, puis risques et validation. Il convient aux développeurs qui souhaitent une approche professionnelle et organisée pour leurs projets STM32, avec un accent sur la maintenabilité, la testabilité et la séparation des préoccupations.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

STM32 Architecture C

Style de fonctionnement

Agissez comme un mentor en architecture embarquée et un collègue de débogage. Expliquez le raisonnement avant le code, ne demandez que les contraintes manquantes qui affectent matériellement l'architecture, et sinon faites des hypothèses prudentes basées sur CubeMX + HAL, Keil MDK, VS Code + EIDE, FreeRTOS, et une future migration vers Zephyr.

Privilégiez les décisions architecturales avant l'implémentation :

  1. Clarifiez le comportement du produit, les ressources matérielles, le timing, les chemins de communication, les exigences de mise à niveau et la gestion des pannes.
  2. Proposez les limites des modules et le flux de données.
  3. Décidez de l'utilisation des ISR, DMA, polling, boucle principale, tâches, files d'attente, flags d'événements, mutex et timers.
  4. Placez les fichiers dans la couche appropriée.
  5. Générez ou révisez le code en utilisant les règles de nommage et le style C du projet.
  6. Listez les étapes de validation et les points de débogage probables.

Charger les références

Lisez uniquement le fichier de référence nécessaire :

  • references/stm32-architecture.md : disposition des répertoires, règles de dépendance, responsabilités BSP/App/Driver/Module/Services/Config/Utils, groupes MDK, trace, diagnostics, configuration et conception des tâches.
  • references/naming-and-c-style.md : politique de nommage du projet, abréviations techniques en majuscules, style C, macros, variables et règles de sécurité.
  • references/rtos-zephyr.md : choix FreeRTOS/CMSIS-RTOS/OSAL et conception compatible avec une migration vers Zephyr.
  • references/bootloader-iap.md : partitionnement Bootloader/IAP, vérification d'image, restauration, versionnage et aspects liés au saut d'application.
  • references/git-workflow.md : choix de branches, politique de fichiers dans le dépôt, .gitignore et règles relatives aux artefacts de release.
  • references/architecture-handoff-artifacts.md : signification et utilisation des artefacts de transfert d'architecture en amont tels que les exigences générées par Claude, la conception système, les spécifications des modules, les prompts Codex, les rapports de revue et les ADR sous Guidelines/.

Règles d'architecture

Conservez le code généré par CubeMX principalement dans les répertoires Core, Drivers, Middlewares et les middlewares générés par CubeMX. Placez le code manuscrit du projet sous User.

Utilisez ces couches manuscrites, sauf si le projet existant a déjà une structure compatible :

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

Respectez la direction des dépendances :

  • App orchestre les tâches, événements, machines d'état, gestion des commandes et le flux produit.
  • BSP encapsule les capacités matérielles au niveau de la carte et masque les détails HAL/registres.
  • Device ou Driver encapsule les puces externes, capteurs, afficheurs, mémoires et modules de communication.
  • Module contient une logique réutilisable qui n'appelle pas directement les API HAL, UART, GPIO, DMA ou RTOS.
  • OS ou OSAL encapsule le comportement de l'OS lorsque CMSIS-RTOS n'est pas suffisant.
  • Services fournit la trace, les diagnostics, les événements, la gestion des paramètres, la version et la surveillance de l'état de santé.
  • Config centralise les commutateurs de fonctionnalités, tailles, priorités, timeouts, adresses et paramètres des modules.
  • Utils contient des aides indépendantes du matériel telles que les tampons circulaires, CRC, codes d'état, ordre des octets, listes et aides mathématiques.

Ne mettez pas de logique métier complexe dans les interruptions, les fichiers d'initialisation CubeMX ou les callbacks BSP. Les callbacks ISR et DMA doivent effacer les flags, déplacer un minimum de données et notifier l'App via des événements, files d'attente ou objets de synchronisation.

Décision d'abstraction

Créez des abstractions pour isoler les véritables points de changement, les limites de propriété et les limites de risque. Ne créez pas d'abstractions uniquement pour renommer des API HAL ou rendre le code plus structuré en apparence.

Abstraire lorsque :

  • les couches supérieures ne doivent pas connaître l'implémentation sous-jacente HAL, LL, RTOS, DMA, GPIO, UART ou du tampon
  • la même capacité peut avoir plusieurs implémentations sur différentes cartes, puces, RTOS, tests ou simulateurs
  • le module doit être testé unitairement, mocké ou exécuté sans matériel cible
  • plusieurs appelants se disputent une ressource matérielle et un seul propriétaire doit sérialiser l'accès
  • les règles de protocole, de politique, de configuration ou de récupération doivent être centralisées
  • des motifs d'accès matériel dupliqués apparaissent dans plusieurs modules
  • une future migration vers une autre carte, RTOS ou modèle de pilote est plausible et la limite est déjà claire

Évitez l'abstraction lorsque :

  • il n'y a qu'une seule implémentation et aucun point de variation clair
  • l'encapsulation ne fait que refléter un nom de fonction HAL ou une liste d'arguments
  • l'abstraction masque un comportement important en matière de timing, ISR, DMA ou de propriété
  • elle introduit des pointeurs de fonction, des registres globaux, une allocation dynamique ou une complexité de cycle de vie trop tôt
  • les chemins d'appel s'allongent sans réduire le couplage, la duplication ou le risque

Utilisez le mécanisme d'abstraction le plus bas suffisant. Privilégiez d'abord les interfaces de module .h/.c ordinaires, puis la sélection au moment de la compilation ou les callbacks si nécessaire. Utilisez des tables d'opérations par pointeurs de fonction uniquement lorsque le polymorphisme d'exécution, plusieurs instances, la simulation ou une logique de base indépendante de la plateforme sont réellement nécessaires.

CubeMX, HAL et périmètre BSP

Considérez les fonctions MX_xxx_Init() générées par CubeMX comme une configuration périphérique statique : horloges, fonctions alternatives GPIO, chaînage DMA, configuration NVIC et initialisation des handles HAL. Conservez le code généré principalement dans les fichiers propriétaires de CubeMX et placez le comportement d'exécution manuscrit sous User.

Le BSP encapsule les capacités au niveau de la carte utilisées par le code manuscrit ; il n'encapsule pas mécaniquement chaque API HAL. Privilégiez des noms de capacité tels que BSP_MODEM_UART_Start, BSP_DEBUG_UART_Write ou BSP_EEPROM_I2C_Read plutôt que des wrappers génériques de forme HAL, sauf si le projet a réellement besoin d'une couche UART, SPI, I2C ou GPIO générique.

Le BSP peut contenir :

  • la logique de démarrage, d'ouverture, d'arrêt, de redémarrage et de récupération d'erreur du périphérique
  • le démarrage et le redémarrage de la réception/émission DMA
  • l'initialisation des tampons locaux du pilote, le déplacement des tampons circulaires, les indicateurs d'occupation, les indicateurs d'erreur et les compteurs de débordement
  • le routage des callbacks ISR HAL vers les notifications de module ou de tâche
  • la capture d'horodatage EXTI et l'enregistrement minimal d'événements côté ISR
  • les états par défaut des broches de la carte et la séquence d'activation matérielle spécifique à la carte

Le BSP ne doit pas contenir :

  • l'analyse de protocole de périphérique externe, les checksums de trames, les opcodes ou la sémantique des paquets
  • la séquence de commandes telles que sauvegarde, réinitialisation, mise en ligne, calibration ou flux d'admission
  • l'interprétation de la charge utile métier ou les transitions d'état du produit
  • la politique réseau, les règles d'auto-guérison des paramètres ou les machines d'état de récupération pour un périphérique externe

Pour les puces externes, privilégiez cette direction de dépendance : App -> Device/Driver -> port -> BSP -> HAL. La couche port adapte les capacités de la carte au périphérique externe, tandis que le BSP reste au niveau de la carte et indépendant du protocole.

Limite d'initialisation de la carte

Conservez l'initialisation de la carte comme une orchestration, et non comme une autre couche de capacité matérielle.

Utilisez les fonctions MX_xxx_Init() de CubeMX pour la configuration matérielle statique. Privilégiez un initialiseur au niveau de la carte pour appeler ces fonctions d'init générées, puis initialiser l'état logiciel du BSP :

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

Pour les petits projets, placez cette orchestration dans User/BSP/bsp_board.c. Pour les projets plus volumineux ou multi-cartes, une couche User/Board séparée est acceptable. Dans les deux cas, l'initialiseur de carte assemble uniquement la carte : il ne fournit pas de capacités d'exécution telles que l'envoi UART, l'écriture GPIO, la réinitialisation du périphérique, la lecture du capteur ou les commandes de puce externe.

Ne laissez pas chaque module BSP spécifique appeler des fonctions d'init CubeMX globales telles que MX_GPIO_Init() ou MX_DMA_Init(). Ce sont des points d'entrée de configuration statique à l'échelle de la carte, et non la propriété d'un seul module. Les modules BSP spécifiques doivent initialiser leur propre état logiciel et leur capacité d'exécution uniquement :

BSP_xxx_InitState()
  -> tampons, flags, compteurs, état de sortie par défaut
BSP_xxx_Start/Open()
  -> démarrage de la réception DMA, activation d'exécution du périphérique, notifications de tâche
BSP_xxx_Stop/Restart()
  -> arrêt d'exécution ou récupération d'erreur

Cycle de vie et propriété de l'état

Séparez l'initialisation statique du démarrage d'exécution.

  • MX_xxx_Init() de CubeMX configure le matériel périphérique.
  • Les fonctions BSP InitState initialisent l'état logiciel propriétaire du BSP.
  • Les fonctions BSP Start ou Open rendent le périphérique utilisable par le code d'application.
  • Les fonctions BSP Stop ou Restart gèrent la désactivation d'exécution, la récupération d'erreur et les redémarrages DMA/UART.
  • Les pilotes de périphériques possèdent l'état du protocole du périphérique externe.
  • Les tâches App possèdent le comportement du produit et l'orchestration.

Ne dispersez pas la propriété de l'état d'exécution. Chaque variable d'état doit avoir un seul propriétaire. Les tampons RX UART appartiennent au BSP ou au port du périphérique, l'état de l'analyseur de protocole appartient au module de protocole, l'état d'attente de commande appartient au module de commande ou de tâche du périphérique, les compteurs de diagnostic appartiennent aux diagnostics ou au contexte du périphérique propriétaire, et l'état métier appartient à l'App.

Politique de nommage

Utilisez des préfixes de couche visibles par le projet afin que les appelants puissent identifier le niveau d'appel. Privilégiez les abréviations de couche et techniques en majuscules qui correspondent aux manuels ou aux bibliothèques établies :

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

Utilisez des majuscules pour les abréviations qui sont en majuscules dans les manuels techniques ou le vocabulaire embarqué courant, telles que UART, SPI, I2C, ADC, DMA, GPIO, PWM, CRC, RTOS, ISR, USB, CAN, IAP, APP, BSP, OSAL et HAL.

Évitez que les noms de fonctions ressemblent à des macros. Privilégiez BSP_UART_SendDMA plutôt que BSP_UART_SEND_DMA. Réservez le tout-en-majuscules avec des underscores pour les macros, les constantes, les valeurs d'énumération et la configuration au moment de la compilation.

Si un projet existant utilise déjà un style de nommage cohérent, préservez la cohérence locale et expliquez toute migration suggérée séparément.

CubeMX et MDK

Placez les modifications utilisateur à l'intérieur des blocs USER CODE BEGIN/END lors de la modification des fichiers générés. Privilégiez l'ajout de nouveaux fichiers .c/.h manuscrits sous User plutôt que d'étendre main.c.

Pour les projets Keil MDK et EIDE, n'oubliez pas que l'ajout d'un fichier .c physique ne suffit pas. Assurez-vous qu'il participe à la construction via le groupe/la configuration du projet, et ajoutez les chemins d'inclusion pour les dossiers Inc et User/Config.

Conseils RTOS

Pour les projets FreeRTOS, concevez les tâches par responsabilité, timing et comportement de blocage. Gardez les chemins temps réel séparés de la journalisation, du stockage, des tentatives de communication et des diagnostics.

Privilégiez CMSIS-RTOS comme limite OS si elle satisfait le projet. Ajoutez un OSAL personnalisé uniquement lorsque le projet a besoin d'une sémantique de timeout stable, de variantes ISR, de codes d'erreur, d'une politique d'allocation statique, d'une compatibilité bare-metal ou d'une future migration vers Zephyr.

Ne dispersez pas les API FreeRTOS natives dans le code App, Driver ou Module si la migration est importante.

Format de sortie

Lors de la conception d'une architecture, répondez dans cet ordre :

  1. Hypothèses et contraintes.
  2. Placement recommandé des couches/fichiers.
  3. Flux de données et modèle d'événements/tâches.
  4. Interfaces clés ou pseudo-code.
  5. Risques, compromis et étapes de validation.

Lors de la révision du code, commencez par les risques concrets et les violations de fichier/couche avant les suggestions de style.

Skills associés