Examen Systèmes à base de microcontrôleur

Exercice 1 Procédure d'amorçage (boot) Dans un microcontrôleur STM32, l'espace mémoire utilisé au démarrage est sélectionné par l'état logique des broches matérielles BOOT0 et BOOT1 lors de la réinitialisation (reset). Ces broches permettent de choisir parmi trois espaces d'amorçage : La mémoire Flash principale (exécution du programme utilisateur).

D'après le document Examen Systèmes à base de microcontrôleur

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Examen Systèmes à base de microcontrôleur

Document source

Examen Systèmes à base de microcontrôleur

Microcontroller Systems, Programming · PDF · 7 pages · 2012

Afficher l'aperçu du document

Consulter le document original →

Exercice 1

Procédure d'amorçage (boot)

Dans un microcontrôleur STM32, l'espace mémoire utilisé au démarrage est sélectionné par l'état logique des broches matérielles BOOT0 et BOOT1 lors de la réinitialisation (reset). Ces broches permettent de choisir parmi trois espaces d'amorçage :

  1. La mémoire Flash principale (exécution du programme utilisateur).
  2. La mémoire système (System memory) qui contient le boot loader (chargeur d'amorçage).
  3. La mémoire SRAM intégrée (utile pour le débogage).

Le boot loader est un petit programme pré-enregistré par le fabricant (STMicroelectronics) en usine. Son rôle principal est de permettre la reprogrammation de la mémoire Flash via des interfaces de communication standard (comme l'USART, l'USB ou le bus CAN) sans nécessiter de matériel de programmation dédié.

Protocoles de communication : Synchrones vs Asynchrones

La principale différence réside dans le partage du signal d'horloge (clk).

  • Protocole synchrone : Les équipements maître et esclave partagent une ligne d'horloge commune. Les données sont échantillonnées au rythme de cette horloge, ce qui permet des débits très élevés.
  • Protocole asynchrone : Il n'y a pas de ligne d'horloge partagée. Les deux équipements doivent s'accorder au préalable sur une vitesse de transmission (baud rate). La synchronisation se fait grâce à des bits de début et de fin de trame.

Exemples :

  • UART : Asynchrone.
  • I2C : Synchrone.
  • SPI : Synchrone.

Mode d'adressage et configuration des périphériques

Le STM32 utilise un espace d'adressage unifié (Memory-Mapped I/O). Cela signifie que les différentes mémoires (Flash, SRAM) et les registres de contrôle des périphériques matériels sont tous mappés sur la même carte d'adressage de 4 Go (de 0x00000000 à 0xFFFFFFFF).

Pour gérer la configuration d'un périphérique (par exemple activer une broche ou régler un timer), le microcontrôleur accède en lecture ou en écriture à des adresses mémoires spécifiques, exactement comme s'il s'agissait d'une variable en mémoire vive.

Nature du microcontrôleur STM32 (Cible)

Le STM32 est une cible mixte. Il combine une partie logicielle (le cœur processeur ARM Cortex-M qui exécute les instructions de votre code C/Assembleur) et une partie matérielle (les divers périphériques embarqués tels que les timers, ADC, USART, qui exécutent des traitements matériels de façon parallèle et autonome sans charger le processeur).

Rôle du FPU

FPU signifie Floating Point Unit (Unité de calcul en virgule flottante). C'est un coprocesseur ou un accélérateur matériel intégré dans certains cœurs (comme le Cortex-M4F). Son rôle est d'exécuter les opérations mathématiques sur les nombres décimaux (virgule flottante) directement au niveau matériel, ce qui est considérablement plus rapide que de les émuler par des routines logicielles.

CMSIS

Le CMSIS (Cortex Microcontroller Software Interface Standard) est une bibliothèque d'abstraction matérielle (Hardware Abstraction Layer) standardisée par ARM. Elle fournit une interface logicielle commune pour accéder aux registres du cœur et aux périphériques des processeurs Cortex-M, garantissant ainsi la portabilité du code entre les différents fabricants de puces.

Critères de choix d'un microcontrôleur

Pour sélectionner un microcontrôleur adapté à un projet, plusieurs critères doivent être évalués :

  • Performances et ressources : Puissance du cœur, fréquence d'horloge, taille de la mémoire (Flash, RAM), et périphériques matériels disponibles (ADC, Timers, Bus de communication).
  • Architecture : Famille du processeur (ex: 8 bits vs 32 bits, ARM Cortex-M).
  • Prix : Coût unitaire pour la production en série.
  • Packaging (Boîtier) : Dimensions physiques, nombre de broches, et type de montage (CMS, traversant) pour l'intégration sur la carte électronique.

Exercice 2

Note préliminaire concernant le code de cet exercice : Afin de garantir que le code soit exécutable et compile correctement (règle de fonctionnement), une variable globale volatile uint32_t timeout; a été ajoutée. De plus, les erreurs de syntaxe issues de l'extraction du sujet (comme le point intempestif dans GPIO_DeInit.(GPIOB)) ont été corrigées.

Question 1 - Initialisation de la broche du bus 1-Wire

Le sujet précise que la broche PB9 doit être configurée comme une sortie Push-Pull (GPIO_Mode_Out_PP). Bien qu'un bus 1-Wire classique utilise généralement une configuration à drain ouvert (Open Drain) avec une résistance de tirage vers le haut, nous respectons ici la contrainte de l'énoncé. La méthode adoptée tout au long de cet exercice consiste à reconfigurer dynamiquement la broche en sortie Push-Pull pour imposer un état, ou en entrée pour relâcher le bus et lire les données.

#include "stm32f10x.h"

/* Déclaration de la structure d'initialisation pour être réutilisée */
GPIO_InitTypeDef GPIO_InitStructure;

void Init_ow(void) {
    /* Activation de l'horloge pour le port GPIOB */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    /* Réinitialisation des registres du GPIOB (Correction de l'erreur de syntaxe du sujet) */
    GPIO_DeInit(GPIOB);
    
    /* Initialisation de la broche PB9 en sortie Push-Pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

Question 2 - Systick et temporisation

Le chronogramme nécessite une précision de l'ordre de la microseconde (μs). Nous configurons donc le timer système (SysTick) pour générer une interruption toutes les 1 μs.

/* Variable globale de décompte pour la temporisation bloquante */
volatile uint32_t timeout = 0;

void init_systick(void) {
    /* Configuration du SysTick pour une interruption chaque 1 μs.
       SystemCoreClock contient la fréquence du processeur en Hz. 
       Diviser par 1 000 000 donne le nombre de cycles pour 1 μs. */
    SysTick_Config(SystemCoreClock / 1000000);
}

/* Fonction de callback d'interruption du SysTick (SysTick_Handler en standard) */
void systick_handler(void) {
    if (timeout > 0) {
        timeout--;
    }
}

/* Fonction de temporisation bloquante en microsecondes */
void delay(uint32_t n) {
    timeout = n;
    while (timeout > 0); /* Attente active jusqu'à ce que l'interruption décrémente à 0 */
}

Question 3 - Implémentation des opérations 1-Wire

Ces fonctions implémentent le chronogramme fourni dans l'énoncé. Elles modifient dynamiquement le rôle de la broche PB9 (entrée/sortie) pour simuler le comportement du bus bidirectionnel.

void drive_OW_low(void) {
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /* Met le bus à l'état bas */
    GPIO_ResetBits(GPIOB, GPIO_Pin_9);
}

void drive_OW_high(void) {
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /* Met le bus à l'état haut */
    GPIO_SetBits(GPIOB, GPIO_Pin_9);
}

uint8_t read_OW(void) {
    /* Configure la broche en entrée avec résistance de tirage (Input Pull-Up) */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_9);
}

void OW_write_bit(uint8_t out_bit) {
    drive_OW_low();
    delay(6);
    if(out_bit) {
        drive_OW_high();
    }
    delay(54);
    drive_OW_high();
    delay(10);
}

uint8_t OW_read_bit(void) {
    uint8_t returned;
    drive_OW_low();
    delay(6);
    drive_OW_high(); /* Relâche le bus pour permettre à l'esclave de répondre */
    delay(9);
    returned = read_OW();
    delay(45);
    drive_OW_high();
    delay(10);
    
    return returned;
}

void OW_write_byte(uint8_t out_byte) {
    uint8_t i;
    for (i = 0; i < 8; i++) {
        /* Envoie les bits du poids faible vers le poids fort (LSB first) */
        if (out_byte & (0x01 << i)) {
            OW_write_bit(1);
        } else {
            OW_write_bit(0);
        }
    }
}

uint8_t OW_read_byte(void) {
    uint8_t returned = 0;
    uint8_t i;
    for (i = 0; i < 8; i++) {
        if (OW_read_bit()) {
            returned = returned | (0x01 << i);
        }
    }
    return returned;
}

uint8_t OW_reset_pulse(void) {
    uint8_t returned;
    drive_OW_low();
    delay(480);
    drive_OW_high(); /* Relâche le bus */
    delay(70);
    returned = read_OW(); /* Lecture de la réponse de présence */
    delay(410);
    drive_OW_high();
    return returned; /* Renvoie 0 si un esclave est présent, 1 sinon */
}

Question 4 - Programme principal de lecture d'une clé Dallas

Remarque de correction logique : Le document source d'origine présente un test logique défectueux dans la correction (indiquant if(OW_reset_pulse()) { /// vérifier la présence d'une clé). Or, selon le Tableau 1 de l'énoncé, la fonction retourne 0 lorsqu'un esclave est présent. Ainsi, la condition if(OW_reset_pulse()) ne serait vraie que si aucun esclave n'était détecté. Ce défaut a été corrigé ci-dessous en utilisant if(OW_reset_pulse() == 0) pour garantir un fonctionnement exact. La signature de main a également été corrigée en int main(void) pour respecter les standards du langage C.

/* Prototypes externes supposés définis ailleurs selon l'énoncé */
void usart_init(void);
void usart_sendData(uint8_t* data);

int main(void) {
    uint8_t i;
    uint8_t key[8]; /* Tableau pour stocker l'identifiant sur 64 bits (8 octets) */
    
    init_systick(); /* Activation indispensable de la base de temps non mentionnée dans la correction brute */
    Init_ow();
    usart_init();
    
    /* Initialisation de la LED sur la broche PA7 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    while(1) {
        /* Attente de 500 ms (500 000 μs) avant chaque interrogation */
        delay(500000);
        
        /* Correction : OW_reset_pulse() renvoie 0 si un esclave est présent (voir Tableau 1) */
        if(OW_reset_pulse() == 0) { 
            /* Envoi de la commande de lecture de la ROM de la clé (0x33) */
            OW_write_byte(0x33);
            
            /* Lecture de l'identifiant de la clé sur 64 bits */
            for (i = 0; i < 8; i++) {
                key[i] = OW_read_byte();
            }
            
            /* Transmission du code via l'USART */
            usart_sendData(key);
            
            /* Allume la LED sur PA7 pendant 3 secondes (3 000 000 μs) */
            GPIO_SetBits(GPIOA, GPIO_Pin_7);
            delay(3000000);
            
            /* Éteint la LED */
            GPIO_ResetBits(GPIOA, GPIO_Pin_7);
        }
    }
    return 0;
}

Méthode

Face à une épreuve de développement sur microcontrôleur incluant la manipulation d'un bus de communication bit-bang (logiciel) tel que le 1-Wire :

  1. Analysez minutieusement les chronogrammes (timing) : La totalité de la communication asynchrone sans horloge dédiée (comme le 1-Wire) repose sur des fenêtres temporelles extrêmement strictes. Vérifiez bien les unités (μs) et la configuration de votre timer (ici le SysTick).
  2. Supervisez les modes des broches GPIO : Un bus bidirectionnel sur un seul fil nécessite de basculer la broche en sortie pour écrire sur le bus (ou l'écraser à 0), puis en entrée pour écouter. Dans cet exercice, il était demandé d'alterner entre Out_PP et IPU. Comprendre ce basculement est la clé pour coder correctement les opérations de base (read_bit, write_bit).
  3. Respectez l'architecture par couches : On bâtit des primitives temporelles (delay), on les utilise pour bâtir des primitives bit par bit (read_bit, write_bit), puis octet par octet (read_byte, write_byte), et enfin on construit la logique applicative dans le programme principal.
  4. Validation logique des conditions : Vérifiez toujours la signification des valeurs de retour dans le tableau de l'énoncé. Comme vu dans la question 4, un retour d'état bas (0) peut signifier un "succès" (esclave présent), ce qui est contre-intuitif et une source classique d'erreur en examen.

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions