Aller au contenu

TP02 - Opérateurs de comparaison, opérateurs sur bits, structures conditionnelles, affichage en hexadécimal

📊 Partie 1 – Structures conditionnelles et opérateurs de comparaison⚓︎

Structures contionnelles

En langage C, les structures conditionnelles permettent de tester des situations et d'exécuter du code en conséquence.

Code

if (condition) {
    // bloc exécuté si la condition est vraie
} else if (autre_condition) {
    // bloc exécuté si l'autre condition est vraie
} else {
    // bloc par défaut
}

Exemple :⚓︎

Code

// Vérifie si le nombre est positif
if (nombre > 0) {
    printf("Le nombre est positif.\n");
}
// Sinon, vérifie si le nombre est négatif
else if (nombre < 0) {
    printf("Le nombre est négatif.\n");
}
// Si aucune des conditions ci-dessus n'est vraie, le nombre est nul
else {
    printf("Le nombre est nul.\n");
}

Opérateurs de comparaison

Symbole Signification
== égal à
!= différent de
< strictement inférieur
<= inférieur ou égal
> strictement supérieur
>= supérieur ou égal

Exercice 1

🎯 Objectif pédagogique : utiliser if - else et les opérateurs de comparaison

  1. Ouvrir le dossier langage_C dans VS Code
  2. Créer un dossier tp02 dans le dossier tps
  3. Créer le fichier tp02_partie_1-2.c ⚠️ ne pas oublier l'extension .c
  4. Ecrire un programme pour saisir les variables tempEau1 et tempEau2 (float) et afficher laquelle des deux est la plus chaude.
  5. Faire de même avec tempZone1 et tempZone2.

✔ Partie 2 – Tests de fonctionnement⚓︎

Exercice 2

Info

Pour cet exercice, copier-coller le code final du tp01 (ou copier-coller la correction donnée plus bas)

🎯 Objectif pédagogique : utiliser if - else, les opérateurs de comparaison et réaliser un affichage clair

✅ Tests à réaliser⚓︎

  • Les résultats des tests seront stocker dans les variables : tempEauStable, tempZonesStable, pressionNormale, debitOk, sondeDefaut, alerteSysteme. Choisir le type adapté pour minimiser la taille en mémoire.
  • On leur assignera la valeur 1 si le test est valide, 0 dans le cas contraire.

  • tempEauStable : La température de l'eau est considéré comme stable (Etat logique 1) si :

    • l'écart de température des eaux de refroidissement est inférieur à 0.4 °C
    • ET que la température de l'eau 1 est inférieure à 15 °C
    • ET que la température de la zone 2 est inférieure à 15 °C
  • tempZonesStable : La température des zones est considéré comme stable (Etat logique 1) si :

    • l'écart de température des zones est inférieur à 1 °C
    • ET que la température de la zone 1 est supérieure ou égale à 20 °C et inférieur à 25 °C
    • ET que la température de la zone 2 est supérieure ou égale à 20 °C et inférieur à 25 °C
  • pressionNormale : La pression est considérée comme normale (Etat logique 1) si elle est supérieure à 100 hPa.

  • debitOk : Le débit est correct (Etat logique 1) s’il est compris entre 200 et 300 litres par minute (inclus).

  • sondeDefaut : La sonde est considérée comme en défaut de vérification périodique (Etat logique 1) si son identifiant est pair (divisible par 2).

  • alerteSysteme : Une alerte est déclenchée si au moins une des conditions suivantes est vraie :

    • Température eaux instable
    • Température zones instable
    • Pression anormale
    • Débit hors norme
    • Sonde défectueuse
  • 🖨️ Affichage attendu

Affichez un rapport clair à l'écran :

-----------------------------------------------------
        🔍 DIAGNOSTIC DU SYSTÈME SENTINEL
-----------------------------------------------------
🔢 Numéro prochain diagnostic   : 235

🆔 Sonde ID                     : 17
🆔 Numéro sonde pair            : 0

📁 Chemin log                   : 'C:\logs\rapport.txt'

🌡️  Temp. eau 1                  : 13.86 °C
🌡️  Temp. eau 2                  : 14.12 °C
📊 Ecart temp. eau              :  0.26 °C

🌡️  Temp. zone 1                 : 23.7 °C
🌡️  Temp. zone 2                 : 23.4 °C
📊 Ecart temp. zone             :  0.3 °C

🧪 Pression circuit             : 11200 Pa
🚿 Débit                        : 280 L/min
🚿 Niveau de débit              : 93.33 %

[Température eaux stable]        : 1
[Température zones stable]       : 1
[Pression normale]               : 1
[Débit correct]                  : 1
[Défaut Sonde]                   : 0
-----------------------------------------------------
🛑 Alerte système                : 0
Corrigé du tp01
#include <stdio.h>
#include <math.h> // Pour la fonction fabs

// icônes pour affichage (à insérer comme un caractère)
// 🌡️📊🧪🚿🆔📁🔍🛑

int main() {
    // Variables de saisie
    float tempEau1, tempEau2, tempZone1, tempZone2;
    unsigned int pression, debit;
    char cheminLog[50]; // Chemin du fichier de log, max 49 caractères + 1 pour le '\0'
    unsigned short idSonde, numDiag;

    // Saisie
    printf("🌡️ Entrer la température de l'eau du circuit 1 (°C) : ");
    scanf("%f", &tempEau1);

    printf("🌡️ Entrer la température de l'eau du circuit 2 (°C) : ");
    scanf("%f", &tempEau2);

    printf("🌡️ Entrer la température de la zone 1 (°C) : ");
    scanf("%f", &tempZone1);

    printf("🌡️ Entrer la température de la zone 2 (°C) : ");
    scanf("%f", &tempZone2);

    printf("🧪 Entrer la pression du circuit (hPa) : ");
    scanf("%d", &pression);

    printf("🚿 Entrer le débit mesuré (L/min) : ");
    scanf("%d", &debit);

    printf("🔢 Entrer le numéro de diagnostic : ");
    scanf("%hd", &numDiag);

    printf("🆔 Entrer l'identifiant de la sonde : ");
    scanf("%hd", &idSonde);

    printf("📁 Chemin du fichier de log (sans espace, max 99 caractères) : ");
    scanf(" %99s", cheminLog);  // Espace avant %s pour ignorer les retours à la ligne

    // Calcul de la température moyenne des eaux de refroidissement
    float tempMoyenneEaux = (tempEau1 + tempEau2) / 2.0;

    // Calcul de la température moyenne des zones
    float tempMoyenneZones = (tempZone1 + tempZone2) / 2.0;   

    // Calcul de l'écart de température des eaux
    float ecartTempEaux = fabsf(tempEau1 - tempEau2);

    // Calcul de l'écart de température des zones
    float ecartTempZones = fabsf(tempZone1 - tempZone2);

    // Conversion de la pression en Pa
    unsigned int pressionConvertie = pression * 100; 

    // Calcul du niveau de débit en %
    unsigned int debitNiveau = (debit * 100) / 300; 

    // Incrémentation du numéro de diagnostic
    numDiag++;

    // parité de l'identifiant de la sonde
    // == compare l'égalité des 2 valeurs de chaque de l'opérateur
    // si idSonde % 2 = 0 alors idSonde % 2 == 0 sera égale à 1 donc idSonde est un nombre pair
    // si idSonde % 2 = 1 alors idSonde % 2 == 0 sera égale à 0 donc idSonde est un nombre impair
    // Ici, on utilise un unsigned char pour stocker la parité de l'identifiant de la sonde
    unsigned char pair = (idSonde % 2 == 0); 

// Affichage formaté
    printf("\n-----------------------------------------------------\n");
    printf("          🔍 DIAGNOSTIC DU SYSTÈME SENTINEL\n");
    printf("-----------------------------------------------------\n");

    printf("\n🔢 Numéro prochain diagnostic   : %u\n", numDiag);

    printf("\n🆔 Sonde ID                     : %u\n", idSonde);
    printf("🆔 Parité sonde                 : %hhu\n", pair);

    printf("\n📁 Chemin log                   : %s\n", cheminLog);

    printf("\n🌡️ Temp. eau circuit 1           : %.2f °C\n", tempEau1);
    printf("🌡️ Temp. eau circuit 2           : %.2f °C\n", tempEau2);
    printf("🌡️ Temp. moyenne des eaux        : %.2f °C\n", tempMoyenneEaux);
    printf("📊 Écart temp. eaux             : %.2f °C\n", ecartTempEaux);

    printf("\n🌡️ Temp. zone 1                  : %.2f °C\n", tempZone1);
    printf("🌡️ Temp. zone 2                  : %.2f °C\n", tempZone2);
    printf("🌡️ Temp. moyenne des zones       : %.2f °C\n", tempMoyenneZones);
    printf("📊 Écart temp. zones            : %.2f °C\n", ecartTempZones);

    printf("\n🧪 Pression circuit             : %d Pa\n", pressionConvertie);
    printf("🚿 Débit                        : %d L/min\n", debit);
    printf("🚿 Niveau de débit              : %d %%\n", debitNiveau);

return 0;
}

🔁 Partie 3 – Opérateurs sur bits⚓︎

Opérateurs sur bits

Opérateur Nom Action
& ET bit à bit Garde les bits communs
| OU bit à bit Active les bits présents dans au moins un opérande
^ OU exclusif Active les bits différents
~ NON bit à bit Inverse tous les bits
<< Décalage gauche Multiplie par \(2^n\)
>> Décalage droit Divise par \(2^n\)

Warning

Attention à ne pas confondre les opérateurs de manipulation des bits « et » (&) et « ou inclusif » (|) avec leurs homologues « et » (&&) et « ou » (||) logiques. Il s’agit d’opérateurs totalement différents au même titre que les opérateurs d’affectation (=) et d’égalité (==). De même, l’opérateur de manipulation des bits « et » (&) n’a pas de rapport avec l’opérateur d’adressage (&), ce dernier n’utilisant qu’un opérande.

Les opérateurs « et », « ou inclusif » et « ou exclusif »⚓︎

Chacun de ces trois opérateurs attend deux opérandes entiers et produit un nouvel entier en appliquant une table de vérité à chaque paire de bits formée à partir des bits des deux nombres de départs. Plus précisément :

  • L’opérateur « et » (&) donnera 1 si les deux bits de la paire sont à 1 ;
  • L’opérateur « ou inclusif » (|) donnera 1 si au moins un des deux bits de la paire est à 1 ;
  • L’opérateur « ou exclusif » (^) donnera 1 si un seul des deux bits de la paire est à 1.

Ceci est résumé dans le tableau ci-dessous, donnant le résultat des trois opérateurs pour chaque paire de bits possible.

Bit 1 Bit 2 Opérateur « et » Opérateur « ou inclusif » Opérateur « ou exclusif »
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0

🔹 & – ET bit à bit Exemple :

Code

unsigned char octet_test = 0b00001101; // bits 0, 2 et 3 actifs
if ((octet_test & 0b00000001) != 0) {
    printf("bit 0 actif\n");
}

Intérêt : - Sert à tester si un bit est actif sans toucher aux autres

🔹 | – OU bit à bit Exemple :

Code

unsigned char octet_test = 0b00000001; // bit 0 actif
statut = statut | 0b00000100;         // on active aussi le bit 2

Intérêt : - Permet d’ajouter un état sans effacer les autres

🔹 ^ – OU exclusif (XOR) Exemple :

Code

unsigned char a = 0b00000101;
unsigned char b = 0b00000110;
unsigned char resultat = a ^ b;  // donne 0b00000011

Intérêt : - Sert à basculer des bits (inversion sélective) - Peut être utilisé pour comparer deux octets et détecter les différences

🔹 ~ – NON bit à bit (inversion) Exemple :

Code

unsigned char a = 0b00001111;
unsigned char b = ~a;  // b = 0b11110000

Intérêt : - Inversion totale de tous les bits

L’exemple ci-dessous utilise ces quatre opérateurs. Les bits des deux opérandes sont pris un à un pour former des paires et chacune d’elle se voit appliquer la table de vérité correspondante afin de produire un nouveau nombre entier.

Code

#include <stdio.h>

int main()
{
    int a = 0x63; // 0x63 = 99 = 0110 0011 
    int b = 0x2A; // 0x2A = 42 = 0010 1010 

    // 0110 0011 & 0010 1010 = 0010 0010 = 0x22 = 34
    printf("%02X\n", a & b);
    // 0110 0011 | 0010 1010 = 0110 1011 = 0x6B = 107
    printf("%02X\n", a | b);
    // 0110 0011 ^ 0010 1010 = 0100 1001 = 0x49 = 73
    printf("%02X\n", a ^ b);
    // ~(0110 0011) = 1001 1100 = 0x9C = 156
    printf("%02X\n", ~a);
    return 0;
}

Sortie

22
6B
49 9C

Afficher une valeur en hexadécimal

  • %X ou %x : lettres capitales ou minuscules
  • Utiliser 0x en préfixe pour clarifier
unsigned int id = 10;
printf("%2X\n", id); // Affiche 0xFF
  • le format %x Une donnée entière affichée en héxadécimal (ff, par exemple).
  • le format %X Une donnée entière affichée en héxadécimal et en majuscules (FF, par exemple).
  • Le « 02 » devant X demande à printf() de toujours afficher deux chiffres hexadécimaux. Si la valeur à afficher est inférieure à 10 (en hexadécimal), un zéro sera placé devant.

(crédits : zestedesavoir.com)

🧩 Création d’un module de lecture d’état

📘 Contexte global⚓︎

Dans le cadre du système Sentinel, vous avez précédemment réalisé un outil de diagnostic initial (tp01 et tp02) capable de lire et afficher des valeurs physiques comme :

  • température de l’eau de refroidissement
  • température des salles serveurs
  • pression des circuits
  • débit d’eau
  • numéro de la sonde, etc.

🛠️ Mission : concevoir un module complémentaire, plus bas niveau⚓︎

Ce nouveau programme agira comme un outil de traitement système :

  • Il récupère les données du diagnostic principal (transmises en mémoire ou par fichier)
  • Il exploite un octet d’état (statut) dans lequel sont stockés des signaux binaires internes, comme l’état de certains équipements (ventilateurs, sondes…)
  • Il permet au centre de supervision de lire, activer ou désactiver certains bits de contrôle, sans modifier les autres

🔢 L’octet de statut⚓︎

L’octet de type unsigned char nommé statut regroupe 8 indicateurs binaires.
Chaque bit représente un état ou un ordre.

Bit n° Valeur binaire État ou commande
0 0b00000001 Ventilateur actif
1 0b00000010 Sonde en veille
2 0b00000100 Refroidissement forcé
3 0b00001000 Mode maintenance
4 0b00010000 Alerte température critique
5 0b00100000 Niveau d’eau bas
6 0b01000000 Surveillance désactivée
7 0b10000000 Urgence : arrêt immédiat requis
  • Cet octet peut contenir plusieurs états actifs à la fois (par exemple, bits 0, 2 et 4 peuvent être à 1 en même temps).

🔧 Objectif de l’activité⚓︎

Vous allez :

  1. Lire un état binaire (ex. : est-ce que le ventilateur est allumé ?)
  2. Activer un état : déclencher un refroidissement forcé
  3. Désactiver un état : sortir une sonde de la veille
  4. Fusionner plusieurs états pour créer un statut global

🎓 Pourquoi faire cela ?⚓︎

Ce type de manipulation est fréquent en embarqué et supervision :
un seul octet peut stocker 8 informations booléennes, ce qui est :

  • rapide à traiter
  • peu coûteux en mémoire
  • facile à transmettre en communication réseau

Exercice 3

🎯 Objectif pédagogique : lire un état booléen stocké dans un bit précis (bit 0)

Contexte : Le bit 0 correspond à l’état du ventilateur.
Si ce bit est actif (=1), alors le ventilateur est allumé.

📝 Commencer par créer un fichier tp02_module_statut.c

🔣 traduire en langage C et testé l'algorithme suivant : Il faut déterminer quel opérateur sur bits utiliser.

Algo

Définir une variable statut contenant l'octet de statut : 00001011 (en binaire)
Définir un masque `masqueVentilActif` avec le bit 0 activé : 00000001

SI (bit 0 de statut est actif) ALORS // c'est ici qu'il faut utiliser un opérateur sur bits
    Afficher "Ventilateur actif"
SINON
    Afficher "Ventilateur inactif"
FIN SI

Exercice 4

🎯 Objectif : activer un bit spécifique sans modifier les autres

Contexte : Le bit 2 est associé à un mode spécial : refroidissement forcé
Le système doit pouvoir activer ce bit à la demande, sans perturber les autres états.

🔣 traduire en langage C et testé l'algorithme suivant :

Algo

// inutile de redéfinir l'octet statut, il a été défini dans le code de l'exercice 3
Définir un masque `masqueForceFroid` approprié
Mettre à jour la variable statut (réafectation) avec le résultat de l'opération sur bit choisie
Afficher le nouveau contenu de statut en hexadécimal (voir le code exemple des opératurs sur bits 🔝)

Tip

Pour vérifier le résultat, il faut convertir la valeur hexadécimale en binaire.

Exercice 5

🎯 Objectif : désactiver un bit sans toucher aux autres

Contexte : Le bit 1 correspond à l'état "sonde en veille". Lors d’un réveil système, ce bit doit être désactivé (=0).

🔣 traduire en langage C et testé l'algorithme suivant :

Algo

// inutile de redéfinir l'octet statut, il a été mise à jour dans le code de l'exercice 4
Définir un masque `masqueReveilSonde` approprié
Mettre à jour la variable statut avec le résultat de l'opération sur bit choisie
Afficher le nouveau contenu de statut en hexadécimal (voir le code exemple des opérateurs sur bits 🔝)

Tip

Si vous n'avez pas utilisé l'opérateur ~, trouvez une autre solution dans laquelle il est utilisé.
Aide : Il faut redéfinir le masque. Attention à la valeur de statut si vous l'avez modifié dans votre première solution.