Chapitre I – Notions de base de la programmation C embarqué
📘 Chapitre I

Notions de base de la
programmation C embarqué

Microcontrôleurs · Compilateurs · Variables · Structures de contrôle · Pointeurs · Tableaux

🎯 STM32 / Cortex-M4
💻 Langage C
🔧 Système Embarqué
📐 ARM Architecture
I

Introduction — Systèmes embarqués

💡
Définition Un système embarqué est un système électronique et informatique autonome, spécialisé dans une tâche bien précise. Il combine matériel (hardware) et logiciel (software) pour accomplir une fonction déterminée, souvent en temps réel.
Plateformes courantes
🔲

Microcontrôleurs

STM32, PIC, Atmel — circuits intégrés tout-en-un (CPU + mémoire + périphériques)

🍓

Raspberry Pi

Mini-ordinateur Linux, idéal pour prototypage et applications IoT

📱

Smartphones

Samsung, iPhone, Huawei — systèmes embarqués complexes multi-cœurs

🔧

FPGA

Spartan 3E, Artix 7 — circuits reconfigurables pour traitements parallèles

Capteurs Boutons, T° Capteurs… Microcontrôleur CPU + RAM + Flash Périphériques Programme C embarqué Mémoire Flash / SRAM Actionneurs LEDs, Moteurs… Affichage LCD, UART…
Figure 1 — Architecture générale d'un système embarqué
Pourquoi le langage C pour les microcontrôleurs ?

🐢 Langages haut niveau

Java, Python, C# — plus simples mais gourmands en ressources. Nécessitent un OS ou runtime.


❌ Pas adapté aux MCU

⚡ Langage C (bas niveau)

Proche de la machine, compilation efficace, accès direct aux registres matériels, faible empreinte mémoire.


✅ Idéal pour MCU
🎯
C embarqué vs C standard Le C embarqué est le langage C en tenant compte des contraintes matérielles : Taille mémoire (Ko vs Go) · Fréquence système (MHz vs GHz) · Architecture cible spécifique (ARM Cortex-M4, etc.)

II

Notions de base de la programmation C

1 · Structure d'un programme

En C, tout le code est organisé en fonctions. La fonction spéciale main() est le point d'entrée de tout programme.

1 2 3 4 5 6 7 8 #include <stdio.h> /* Bibliothèque E/S */ void main() { int a; a = 127; printf ( "a = %d" , a); } 📦 Inclusion de bibliothèque Ajoute des fonctions prédéfinies 💬 Commentaire (ignoré par le compilateur) 🚀 Fonction principale — point d'entrée 🟢 Début du bloc d'instructions 📌 Déclaration d'une variable entière ✏️ Affectation de valeur (a reçoit 127) 📺 Affichage du résultat à l'écran 🔴 Fin du bloc d'instructions
Figure 2 — Anatomie d'un programme C
📝
Remarques importantes • Les commentaires s'écrivent entre /* … */ ou après // — ils sont ignorés par le compilateur.
• Le C est sensible à la casse : main ≠ Main.
• Les mots réservés (for, while, int…) s'écrivent toujours en minuscules.
2 · Compilation

Le code source C est du texte lisible par un humain. Le compilateur le traduit en code machine exécutable par le microcontrôleur.

Code Source .c (texte) Préprocesseur Compilation Analyse syntaxique Compilateur Fichiers Objets .o (binaire partiel) Linker Exécutable .hex / .bin / .elf → Flash MCU
Figure 3 — Pipeline de compilation C embarqué
3 · Variables et types de données
🗃️
Qu'est-ce qu'une variable ? Une variable est un emplacement mémoire identifié par un nom. Chaque variable possède : un nom, un type (qui détermine la taille en mémoire), et une valeur.

Règle : une variable doit être déclarée avant utilisation et associée à un type.
Types Entiers int Entier standard short Entier court (16 bits) long Entier long (32 bits) char Caractère (8 bits, numérique) Types Réels float Réel simple précision double Réel double précision long double Réel très haute précision
Figure 4 — Types de données de base en C
Tailles des types — ARM Cortex M4
TypeTaille (octets)MinimumMaximum
int4−2 147 483 6482 147 483 647
unsigned int404 294 967 295
short2−32 76832 767
unsigned short2065 535
long4−2 147 483 6482 147 483 647
unsigned long404 294 967 295
char1−128127
unsigned char10255
float4−3.4×10³⁸3.4×10³⁸
double8−1.7×10³⁰⁸1.7×10³⁰⁸
Déclaration et affectation
C#include <stdio.h>

int main() {
    int a;          // Déclaration
    a = 127;        // Affectation

    float b = 3.14; // Déclaration + initialisation
    char  c = 'A'; // Caractère (stocké comme entier)

    printf("a = %d, b = %f, c = %c\n", a, b, c);
    return 0;
}
fenêtre 1
Cliquez sur Exécuter pour voir le résultat.
4 · Opérateurs
a) Opérateurs arithmétiques
OpérateurNomExempleRésultat
+Additiona + bSomme de a et b
-Soustractiona - bDifférence
*Multiplicationa * bProduit
/Division7 / 2 → 3 (entiers) / 7 / 2.0 → 3.5Quotient
%Modulo7 % 2 → 1Reste de la division
b) Opérateurs logiques bit-à-bit
OpérateurNomNotationUtilisation MCU
&ANDX & YMasquer des bits (lire un bit)
|ORX | YMettre un bit à 1
~NOT~XInverser tous les bits
^XORX ^ YBasculer un bit
>>Décalage droitX>>YDivision par 2^Y
<<Décalage gaucheX<<YMultiplication par 2^Y
fenêtre — bit-à-bit
Cliquez sur Exécuter pour voir le résultat.
📌 x = 12 (1100) et y = 10 (1010) en binaire — modifie-les pour vérifier tes propres calculs.
⚠️
Pas de type booléen en C Toute valeur différente de 0 est considérée comme VRAI, et 0 est considéré comme FAUX.
Exemple : (4 < 10) vaut 1 (vrai) · !(5 > 1) vaut 0 (faux)
c) Opérateurs de comparaison
OpérateurSignificationExemple
==Égal àa == b
!=Différent dea != b
<Strictement inférieura < b
<=Inférieur ou égala <= b
>Strictement supérieura > b
>=Supérieur ou égala >= b
d) Opérateurs d'affectation et d'incrémentation
Cint x = 5;

// Affectation combinée
x += 3;   // équivalent à  x = x + 3  → x vaut 8
x -= 2;   // équivalent à  x = x - 2  → x vaut 6
x *= 2;   // équivalent à  x = x * 2  → x vaut 12

// Incrémentation / décrémentation
x++;     // post-incrément : utilise x, PUIS x = x + 1
++x;     // pré-incrément  : x = x + 1, PUIS utilise x
x--;     // décrémentation de 1
fenêtre 2
Cliquez sur Exécuter pour voir le résultat.
5 · Entrées / Sorties

📺 printf() — Affichage

printf("format", valeurs);

Affiche du texte et des valeurs formatées sur la console.

⌨️ scanf() — Saisie

scanf("format", &variable);

Lit une valeur entrée par l'utilisateur (le & donne l'adresse).

SpécificateurTypeExemple printf
%dintprintf("%d", age);
%ldlongprintf("%ld", x);
%uunsigned intprintf("%u", n);
%ffloat / doubleprintf("%.2f", pi);
%ccharprintf("%c", lettre);
%schar[] (chaîne)printf("%s", nom);
Exemple#include <stdio.h>
int main() {
    int   nombreDeVies = 5, niveau = 1;
    float score = 98.5;

    printf("Vous avez %d vies, niveau %d, score %.1f\n",
           nombreDeVies, niveau, score);

    int age = 0;
    printf("Entrez votre âge : ");
    scanf("%d", &age);
    printf("Âge saisi : %d\n", age);
    return 0;
}
fenêtre 3
Cliquez sur Exécuter pour voir le résultat.
📌 scanf ouvrira une petite boîte de dialogue du navigateur pour te demander la valeur.
6 · Structures conditionnelles
a) L'instruction if … else
Début Condition vraie ? OUI (≠ 0) Bloc «if» NON (= 0) Bloc «else» Suite
Figure 5 — Organigramme de la structure if … else
Exemplefloat moy;
printf("Donnez votre moyenne : ");
scanf("%f", &moy);

if (moy >= 10) {
    printf("Mabrouk ! Vous avez réussi.\n");
} else {
    printf("Échec : il faut travailler encore plus !\n");
}
fenêtre 4
Cliquez sur Exécuter pour voir le résultat.
📌 Essaie avec une note ≥ 10 puis < 10 pour voir les deux branches.
b) if imbriqués — comparer A, B et C
if imbriquésif (A > B) {
    printf("%d est plus grand que %d\n", A, B);
} else {
    if (A < B)
        printf("%d est plus petit que %d\n", A, B);
    else
        printf("%d est égal à %d\n", A, B);
}
fenêtre 5
Cliquez sur Exécuter pour voir le résultat.
📌 Change les valeurs de A et B et relance pour tester les trois cas.
c) L'instruction switch
🔀
switch vs if-else switch est préféré quand on teste une seule variable entière contre plusieurs valeurs constantes. Les types autorisés : int, short, long, char.
switchswitch (choix) {
    case 1:
        printf("Option 1 choisie\n");
        break;   // OBLIGATOIRE pour ne pas tomber dans le case suivant
    case 2:
        printf("Option 2 choisie\n");
        break;
    default:   // exécuté si aucun case ne correspond
        printf("Erreur de choix\n");
}
fenêtre 6
Cliquez sur Exécuter pour voir le résultat.
📌 Essaie 1, 2, puis une autre valeur pour voir le default.
7 · Structures répétitives
Boucle for Nombre d'itérations connu à l'avance for (init; cond; maj) Boucle while Condition testée AVANT l'itération while (condition) Boucle do-while Exécutée AU MOINS une fois do { } while (cond);
Figure 6 — Les trois types de boucles en C
for// Affiche "Bonjour" 10 fois
int a, k = 10;
for (a = 1; a <= k; a++) {
    printf("Bonjour\n");
}

// Compte à rebours
for (int i = 10; i >= 1; i--) {
    printf("%d\n", i);
}
printf("Décollage !\n");
fenêtre 7
Cliquez sur Exécuter pour voir le résultat.
whileint i = 0;
while (i < 5) {
    printf("i = %d\n", i);
    i++;   // IMPORTANT : sinon boucle infinie !
}
fenêtre 8
Cliquez sur Exécuter pour voir le résultat.
📌 Essaie de supprimer la ligne i++; — le programme s'arrêtera tout seul après un certain nombre de tours (protection anti-boucle infinie), au lieu de planter ton navigateur.
do-whileint choix;
do {
    printf("Entrez 1 ou 2 : ");
    scanf("%d", &choix);
} while (choix != 1 && choix != 2);
// La saisie est garantie au moins une fois
fenêtre 9
Cliquez sur Exécuter pour voir le résultat.
📌 Entre d'abord une valeur invalide (ex. 9) : la boucle redemande. Entre ensuite 1 ou 2 pour sortir.
8 · Fonctions

Les fonctions permettent de structurer, réutiliser et tester le code séparément.

float evaluerExp (float a, float b) { float val = 0; val = 2 * (a + b); return val; } Type retour Nom fonction Paramètres formels
Figure 7 — Anatomie d'une fonction C
Fonctions// Définition de la fonction
float evaluerExp(float a, float b) {
    float val = 0;
    val = 2 * (a + b);
    return val;   // retourne un seul résultat
}

int main() {
    float resultat = evaluerExp(3.0, 5.0); // appel avec paramètres effectifs
    printf("Résultat : %f\n", resultat);     // affiche 16.0
    return 0;
}
fenêtre 10
Cliquez sur Exécuter pour voir le résultat.

III

Les pointeurs

🔍
Définition Un pointeur est une variable qui contient l'adresse mémoire d'une autre variable. Il permet d'accéder et de modifier des données directement en mémoire.
Passage par valeur vs passage par adresse

❌ Par valeur (copie)

La fonction reçoit une copie de la variable. Les modifications n'affectent pas l'original.

✅ Par adresse (pointeur)

La fonction reçoit l'adresse de la variable. Les modifications se répercutent sur l'original.

Opérateurs essentiels
OpérateurNomRôleExemple
&Adresse deDonne l'adresse d'une variablep = &n;
*DéréférencementAccède au contenu pointé*p = 40;
Adresse 0x200 20 variable n Adresse 0x300 0x200 pointeur pNombre pointe vers #include <stdio.h> int n = 20; int *pNombre; pNombre = &n; *pNombre = 40; // n vaut 40 !
Figure 8 — Fonctionnement d'un pointeur en mémoire
Pointeursint n = 20;
int *pNombre = NULL;  // toujours initialiser à NULL

pNombre = &n;           // pNombre contient l'adresse de n
printf("%d\n", *pNombre); // affiche 20 (contenu de n)

*pNombre = 40;          // modifie n via le pointeur
printf("%d\n", n);      // affiche 40 !
fenêtre 11
Cliquez sur Exécuter pour voir le résultat.
Fonctions retournant plusieurs valeurs

En C, return ne retourne qu'une seule valeur. Pour retourner plusieurs résultats, on passe les adresses des variables en paramètre.

min & maxvoid min_max(int a, int b, int *pmin, int *pmax) {
    if (a < b) { *pmin = a; *pmax = b; }
    else       { *pmin = b; *pmax = a; }
}

int main() {
    int a = 8, b = 3, min, max;
    min_max(a, b, &min, &max);
    printf("min = %d, max = %d\n", min, max); // min=3, max=8
    return 0;
}
fenêtre 12
Cliquez sur Exécuter pour voir le résultat.

IV

Tableaux et structures

1 · Les tableaux
📊
Tableau = ensemble ordonné de valeurs du même type On les repère par des indices qui commencent à 0. Un tableau de 5 éléments a les indices 0, 1, 2, 3, 4.
int T[5]; 2 3 5 7 11 T[0] T[1] T[2] ← T[i] (i=2) T[3] T[4] Règles C : • Indices de 0 à N-1 • T[i] = (i+1)ème élément • Déclaration : type nom[N];
Figure 9 — Représentation d'un tableau int T[5]
Tableauxint Premiers[4];
Premiers[0] = 2;
Premiers[1] = 3;
Premiers[2] = 5;
Premiers[3] = 7;

printf("Le 3e nombre premier : %d\n", Premiers[2]); // 5
printf("Somme 2e+3e : %d\n", Premiers[1] + Premiers[2]); // 8

// Parcours avec une boucle for
for (int i = 0; i < 4; i++) {
    printf("Premiers[%d] = %d\n", i, Premiers[i]);
}
fenêtre 13
Cliquez sur Exécuter pour voir le résultat.
2 · Les structures
🏗️
Structure = regroupement de variables de types différents sous un même nom Contrairement aux tableaux, les champs d'une structure peuvent être de types différents.
struct// Définition de la structure
struct etudiant {
    char  nom[30];
    int   age;
    float moyenne;
};

// Avec typedef — plus pratique
typedef struct etudiant Etudiant;

int main() {
    Etudiant E1;              // Déclaration

    E1.age     = 20;          // Accès aux champs avec '.'
    E1.moyenne = 14.5;
    printf("Donner le nom : ");
    scanf("%s", E1.nom);

    printf("Étudiant : %s, âge %d, moy %.1f\n",
           E1.nom, E1.age, E1.moyenne);
    return 0;
}
AccèsSignification
E1.ageChamp age de l'étudiant E1
E1.moyenneChamp moyenne de E1
E1.nom[0]Premier caractère du nom de E1
E1 = E2Copie globale de toute la structure

TD

Travaux Dirigés — Exercices

📌
Tous les exercices se compilent avec : gcc -o prog mon_programme.c && ./prog
Partie 1 — Variables et E/S
1

Affichage de variables

Déclarez un entier age et un double taille. Initialisez-les et affichez-les avec des messages descriptifs.

2

Somme de deux nombres

Demandez deux entiers à l'utilisateur et affichez leur somme.

3

Calcul de moyenne

Saisir trois décimaux et afficher leur moyenne.

4

Mon premier programme

Afficher le message "Bonjour, monde !" sur la console.

à toi de jouer — Partie 1
Cliquez sur Exécuter pour voir le résultat.
Partie 2 — Structures conditionnelles
5

Nombre pair ou impair

Saisir un entier et déterminer s'il est pair ou impair. Indice : utiliser %.

6

Le plus grand de deux nombres

Saisir deux nombres et afficher le plus grand.

7

Type de triangle

Entrer les trois côtés d'un triangle et déterminer s'il est équilatéral, isocèle ou scalène.

8

Année bissextile

Vérifier si une année est bissextile (divisible par 4, sauf si divisible par 100 mais pas par 400).

à toi de jouer — Partie 2
Cliquez sur Exécuter pour voir le résultat.
Partie 3 — Boucles
9

Compte à rebours

Afficher un compte à rebours de 10 à 1 avec while, puis "Décollage !".

10

Somme des N premiers entiers

Saisir N et calculer la somme 1 + 2 + … + N avec for.

11

Table de multiplication

Afficher la table de multiplication d'un nombre donné (de 1 à 10).

12

Factorielle

Calculer n! pour un entier positif donné.

à toi de jouer — Partie 3
Cliquez sur Exécuter pour voir le résultat.
Partie 4 — Tableaux
13

Initialisation et affichage

Déclarer un tableau de 5 entiers, l'initialiser avec {1,2,3,4,5} et afficher tous les éléments.

14

Somme des éléments

Calculer et afficher la somme de tous les éléments d'un tableau.

15

Plus grand élément

Trouver et afficher la valeur maximale d'un tableau d'entiers.

16

Inversion d'un tableau

Inverser l'ordre des éléments d'un tableau et afficher le résultat.

à toi de jouer — Partie 4
Cliquez sur Exécuter pour voir le résultat.
📘 Chapitre I — Notions de base de la programmation C embarqué
Microcontrôleurs · ARM Cortex-M4 · STM32 · Langage C