Fondamentaux du C embarqué : Questions clés sur la programmation des microcontrôleurs
Les programmeurs de microcontrôleurs doivent maîtriser les spécificités du C dans des contraintes de ressources très serrées. Voici une liste sélectionnée de questions pratiques portant sur la compilation, la mémoire et l'optimisation. Ces sujets reviennent souvent aux entretiens dans les entreprises de systèmes embarqués.
Mots-clés essentiels et leur utilisation
Static limite la visibilité d'une variable au fichier ou à la fonction courante, en préservant les valeurs entre les appels pour les statiques locales. Volatile empêche le compilateur d'optimiser les variables modifiées de l'extérieur (registres, mémoire, interruptions).
Exemple de code problématique :
int square(volatile int *ptr) {
return *ptr * *ptr;
}
Ici, volatile ne s'applique qu'au premier déréférencement ; le second pourrait être optimisé. Correction : (ptr) (*ptr).
Const volatile convient aux données externes en lecture seule comme les registres de statut. Register suggère de stocker une variable dans un registre CPU (déprécié en C11). Restrict indique au compilateur que les pointeurs ne se chevauchent pas, favorisant la vectorisation.
Gestion de la mémoire et structures
Les variables globales const vont en mémoire Flash. Pour un tableau constant de 32 Ko rempli de 0xFF, utilisez une section de script de lien avec fill(0xFF).
Opérations sur bits : tester un bit avec n & (1 << k), effacer avec n & ~(1 << k). Comparaison de flottants : fabs(a - b) < EPSILON.
Les tailles de structures dépendent de l'alignement. Pour
struct Foo {
int iiii;
char c;
};
la taille est de 8 octets (4 octets de padding). Empaquetage GCC : __attribute__((packed)).
Exemples de déclaration de chaînes :
char str5[]={'s','t','r','i','n','g'}; // taille:7 len:6
char *str1="string"; // taille:4 len:6 (pointeur)
char str3[]="string"; // taille:7 len:6
char str4[10]="string"; // taille:10 len:6
Systèmes de build et chaînes d'outils
Les systèmes de build automatisent la compilation, le lien et les dépendances. Make gère les changements de fichiers, tandis que Ninja est plus rapide grâce au parallélisme et à sa simplicité.
VPATH dans GNU Make recherche les sources dans les répertoires indiqués.
Pipeline de code : .c → .i (préprocesseur) → .s (assembleur) → .o → .elf → .bin/.hex.
Désactiver un avertissement GCC : #pragma GCC diagnostic ignored "-Wrestrict".
Réduire le firmware : -ffunction-sections -fdata-sections, ld --gc-sections, et optimisation -Os.
Artefacts de build
.o— fichiers objets.elf— exécutable avec symboles.hex/.bin— pour le flashage (hex inclut les adresses)
Binutils : objdump (désassembleur), objcopy (convertisseur), readelf (analyseur ELF).
Vérifier le build : make V=1 ou -n (simulation).
Structures de données et algorithmes
Tampon circulaire — tableau annulaire avec indices tête/queue, contrairement au FIFO qui décale les éléments.
Suppression dans liste chaînée : prev->next = node->next (nécessite prev) ou node->next->prev = node->prev (doubly-linked).
Polymorphisme en C : pointeurs de fonctions dans structs (style vtable).
Tâche sans if/switch :
void print_num(int n) {
const char *nums[] = {"","Un","Deux"};
printf("%s", nums[n]);
}
do-while(0) pour macros — itération unique avec plusieurs instructions.
Interruptions et RTOS
Interruption — mécanisme matériel sautant vers un ISR sur un événement. La table de vecteurs contient les adresses des ISR. Cortex-M4 : 12–16 cycles pour l'entrée (sauvegarde contexte).
Flux : matériel sauvegarde PC/LR, charge vecteur, saute à ISR, retourne via POP.
Fonction réentrante — sûre pour appels concurrents (pas de statiques/locaux sans synchro).
Dans RTOS :
- Thread — unité d'exécution avec sa propre pile
- Mutex — exclusion mutuelle
- Sémaphore — compteur d'événements
- Spinlock — verrou busy-wait
Problème de code : deadlock par ordre de verrou (TâcheA : ma→mb, TâcheB : mb→mc→ma).
Inversion de priorité : tâche haute priorité attend une basse. Solution : héritage de priorité.
Opérations atomiques
Exemple : __atomic_exchange_n(&x, &y, __ATOMIC_SEQ_CST).
Bit-banding — accès direct aux bits sans masques (Cortex-M3/4).
Chaîne d'outils et débogage
ABI — interface binaire (convention d'appel).
Segments : .text (code Flash), .data (RAM initialisée), .bss (RAM zéros), .rodata (constantes Flash).
Développer préprocesseur : gcc -E file.c > expanded.i.
Docs clés : fiche technique, manuel de référence, manuel de programmation, schémas.
Points clés à retenir
- Volatile est essentiel pour les registres périphériques
- Les structs empaquetées économisent de la RAM mais ralentissent l'accès
- RTOS nécessite du code thread-safe et protection des sections critiques
- Optimisation build : sections + gc-sections
- Les interruptions utilisent la pile du thread courant
Pour padding 0xFF dans structs : script de lien avec fill ou __attribute__((packed)) + remplissage manuel.
— Editorial Team
Aucun commentaire pour le moment.