SE4Binome2026-10

De projets-se.plil.fr
Révision datée du 10 septembre 2026 à 15:34 par Malami (discussion | contributions)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche

Binome :Drajou Achraf - Alami Mohamed iliass

Pico-ordinateur

Lien du gitea :https://gitea.plil.fr/malami/IOT4_Pico_alami_drajou.git

Chronologie du travail effectué

Séance du 08/09/2026 : Mise en place du git et du wiki. Commencement de la schématique.

Séance du 10/09/2026 : Test de l'ordonnanceur sur le shield Arduino WPSH209 (2 LED à des fréquences différentes) et implémentation manuelle de la sauvegarde/restauration des registres lors d'une interruption.

Schématique

(Carte mère en cours de conception sous KiCad : étage d'alimentation et affectation des broches de l'ATmega32u4 réalisés, sous-systèmes ATmega16u et AT90USB1287 à venir.)

Ordonnanceur

Test Ordonnanceur (Timer LED)

Tout d'abord, nous avons testé notre Ordonnanceur, en faisant clignoter deux LED à deux fréquences différentes.

Nous avons récupéré le code du cours de SE5 : 5.10 ATmega328p : Minuteurs, que nous avons adapté à la schématique du shield Arduino.

#include <avr/io.h>
#include <avr/interrupt.h>

#define CTC1            WGM12
#define PERIODE         20        // interruption toutes les 20 ms

void init_minuteur(int diviseur, long periode)
{
    TCCR1A = 0;
    TCCR1B = (1 << CTC1);          // Mode CTC

    switch(diviseur){
        case    8: TCCR1B |= (1 << CS11); break;
        case   64: TCCR1B |= (1 << CS11) | (1 << CS10); break;
        case  256: TCCR1B |= (1 << CS12); break;
        case 1024: TCCR1B |= (1 << CS12) | (1 << CS10); break;
    }

    OCR1A = F_CPU / 1000 * periode / diviseur;  // Calcul du pas
    TCNT1 = 0;
    TIMSK1 = (1 << OCIE1A);
}

/* Interruption déclenchée toutes les 20 ms */
ISR(TIMER1_COMPA_vect)
{
    static uint16_t compteur_led1 = 0;
    static uint16_t compteur_led2 = 0;

    compteur_led1++;
    compteur_led2++;

    /*
     * 25 x 20 ms = 500 ms
     * LED1 change donc d'état toutes les 500 ms
     */
    if(compteur_led1 >= 25){
        PORTB ^= (1 << PB5);
        compteur_led1 = 0;
    }

    /*
     * 15 x 20 ms = 300 ms
     * LED2 change donc d'état toutes les 300 ms
     */
    if(compteur_led2 >= 15){
        PORTB ^= (1 << PB4);
        compteur_led2 = 0;
    }
}

int main(void)
{
    /*
     * PB5 = Arduino 13 = LED D1 du shield
     * PB4 = Arduino 12 = LED D2 du shield
     */
    DDRB |= (1 << PB5) | (1 << PB4);

    init_minuteur(256, PERIODE);   // Prédiviseur 256, interruption toutes les 20ms
    sei();                         // Autorisation globale des interruptions

    while(1){
        // Rien à faire ici pour ce premier test
    }

    return 0;
}

Les LED du shield clignotent bien comme souhaité, à des rythmes différents et indépendants.

Sauvegarde et restauration des registres

Nous avons d'abord essayé de vulgariser cette partie, afin de comprendre ce que nous faisions.

Le microcontrôleur ne pouvant pas faire plusieurs tâches en même temps, il doit alterner très rapidement entre les tâches. Lorsqu'une interruption survient pendant l'exécution du programme principal, il faut enregistrer l'état et la progression de ce programme (le contenu de ses registres) avant de laisser l'interruption s'exécuter. Une fois l'interruption terminée, le microcontrôleur doit restaurer cet état, afin de reprendre la progression du programme principal exactement là où elle s'était arrêtée.

Nous avons implémenté cette sauvegarde/restauration manuellement, à l'aide de deux macros assembleur appelées au début et à la fin de notre routine d'interruption.

#define SAVE_CONTEXT() \
    asm volatile ( \
        "push r0  \n\t" \
        "in   r0, __SREG__ \n\t" \
        "cli \n\t" \
        "push r0  \n\t" \
        "push r1  \n\t" \
        "clr  r1  \n\t" \
        "push r2  \n\t" "push r3  \n\t" "push r4  \n\t" "push r5  \n\t" \
        "push r6  \n\t" "push r7  \n\t" "push r8  \n\t" "push r9  \n\t" \
        "push r10 \n\t" "push r11 \n\t" "push r12 \n\t" "push r13 \n\t" \
        "push r14 \n\t" "push r15 \n\t" "push r16 \n\t" "push r17 \n\t" \
        "push r18 \n\t" "push r19 \n\t" "push r20 \n\t" "push r21 \n\t" \
        "push r22 \n\t" "push r23 \n\t" "push r24 \n\t" "push r25 \n\t" \
        "push r26 \n\t" "push r27 \n\t" "push r28 \n\t" "push r29 \n\t" \
        "push r30 \n\t" "push r31 \n\t" \
    );

#define RESTORE_CONTEXT() \
    asm volatile ( \
        "pop r31 \n\t" "pop r30 \n\t" "pop r29 \n\t" "pop r28 \n\t" \
        "pop r27 \n\t" "pop r26 \n\t" "pop r25 \n\t" "pop r24 \n\t" \
        "pop r23 \n\t" "pop r22 \n\t" "pop r21 \n\t" "pop r20 \n\t" \
        "pop r19 \n\t" "pop r18 \n\t" "pop r17 \n\t" "pop r16 \n\t" \
        "pop r15 \n\t" "pop r14 \n\t" "pop r13 \n\t" "pop r12 \n\t" \
        "pop r11 \n\t" "pop r10 \n\t" "pop r9  \n\t" "pop r8  \n\t" \
        "pop r7  \n\t" "pop r6  \n\t" "pop r5  \n\t" "pop r4  \n\t" \
        "pop r3  \n\t" "pop r2  \n\t" \
        "pop r1  \n\t" \
        "pop r0  \n\t" \
        "out __SREG__, r0 \n\t" \
        "pop r0  \n\t" \
    );

SAVE_CONTEXT() empile (push) l'ensemble des 32 registres du processeur ainsi que le registre d'état SREG, avant que notre routine d'interruption ne commence à les utiliser. RESTORE_CONTEXT() fait l'opération inverse, en dépilant (pop) ces registres dans l'ordre exactement inverse, juste avant le retour à la tâche interrompue.