« SE4Binome2025-2 » : différence entre les versions

De projets-se.plil.fr
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
Ligne 33 : Ligne 33 :


<br clear="all" />
<br clear="all" />
= Firmware — RTOS =
= Firmware — RTOS =


== 1. Vue d’ensemble du système ==
== 1. Vue d'ensemble du système ==
Le firmware implémente un petit RTOS coopératif optimisé en mémoire pour ATmega (ex. ATmega328P).
Le firmware implémente un '''système d'exploitation temps réel (RTOS) coopératif''' optimisé pour les microcontrôleurs AVR (ATmega328P/AT90USB1286).
Principales caractéristiques :
 
* Ordonnanceur round-robin simple.
'''Architecture coopérative vs préemptive :'''
* Table des tâches statique (pas d’allocation dynamique).
* '''Coopératif''' : Les tâches cèdent volontairement le contrôle (via <code>task_yield()</code>, <code>task_sleep()</code>)
* Gestion des sections critiques imbriquées.
* '''Avantage''' : Plus simple, moins de surcharge mémoire, pas besoin de sauvegarde de contexte
* Timer système à 100 Hz pour la gestion des ticks et du sommeil.
* '''Inconvénient''' : Une tâche malveillante peut bloquer le système
 
'''Optimisations mémoire :'''
* Allocation statique uniquement (pas de malloc/free)
* Taille fixe des structures pour prédictibilité
* Piles partagées entre noyau et applications


== 2. Arborescence (extrait) ==
== 2. Arborescence (extrait) ==
<pre>
<pre>
firmware/
firmware/
  ├─ kernel/
  ├─ kernel/                 # Cœur du système d'exploitation
  │  ├─ kernel.h
  │  ├─ kernel.h           # API publique du noyau
  │  ├─ kernel.c
  │  ├─ kernel.c           # Gestion système (ticks, délais)
  │  ├─ scheduler.h
  │  ├─ scheduler.h         # Déclarations de l'ordonnanceur 
  │  ├─ scheduler.c
  │  ├─ scheduler.c         # Implémentation round-robin
  │  ├─ task.h
  │  ├─ task.h             # Structures de contrôle des tâches
  │  └─ task.c
  │  └─ task.c             # Création et gestion des tâches
  ├─ config.h
  ├─ config.h               # Configuration matérielle (mémoire, timers)
  └─ main.c
  └─ main.c                 # Tâches applicatives (horloge binaire)
</pre>
</pre>
'''Séparation des responsabilités :'''
* <code>kernel/</code> : Fonctionnalités système bas niveau
* <code>config.h</code> : Adaptation au matériel spécifique
* <code>main.c</code> : Logique métier de l'application


== 3. Paramètres de configuration ==
== 3. Paramètres de configuration ==
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
#define MAX_TASKS      4      // nombre max de tâches
#define MAX_TASKS      4      // Compromis fonctionnalité/mémoire
#define STACK_SIZE      96    // octets par pile de tâche
#define STACK_SIZE      96    // Suffisant pour appels de fonctions + variables locales
#define TICK_FREQUENCY  100    // Hz — fréquence du tick système
#define TICK_FREQUENCY  100    // 100Hz = résolution 10ms (équilibre précision/charge CPU)
#define TASK_NAME_LEN  8
#define TASK_NAME_LEN  8     // Noms courts pour économiser la RAM
</syntaxhighlight>
</syntaxhighlight>
'''Justification des valeurs :'''
* '''MAX_TASKS=4''' : Permet tâche système + 3 tâches applicatives dans 2KB RAM
* '''STACK_SIZE=96''' : Empilement typique des appels AVR + marge de sécurité
* '''TICK_FREQUENCY=100''' : Temps de réponse <10ms sans surcharger le CPU


== 4. Structure de contrôle de tâche (TCB) ==
== 4. Structure de contrôle de tâche (TCB) ==
Ligne 84 : Ligne 99 :
} task_t;
} task_t;
</syntaxhighlight>
</syntaxhighlight>
'''Cycle de vie d'une tâche :'''
<pre>
CRÉATION → [READY] ↔ [RUNNING] → [SLEEPING] → [READY]
                    ↓
                [COMPLETED] (si task_exit() appelée)
</pre>


== 5. Gestion des sections critiques (imbriquées) ==
== 5. Gestion des sections critiques (imbriquées) ==
Ligne 102 : Ligne 124 :
}
}
</syntaxhighlight>
</syntaxhighlight>
'''Protection des ressources partagées :'''
* Empêche les accès concurrents aux structures partagées (task_table, variables globales)
* '''Imbrication''' : Appels imbriqués autorisés
* '''Performance''' : Sections critiques très courtes (<1ms)


== 6. Création de tâche ==
== 6. Création de tâche ==
Ligne 108 : Ligne 135 :
                   uint8_t priority, uint8_t *stack_buffer)
                   uint8_t priority, uint8_t *stack_buffer)
{
{
    enter_critical();
   
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
         if (task_table[i].function == NULL) {
         if (task_table[i].function == NULL) {
Ligne 116 : Ligne 145 :
             task_table[i].priority = priority;
             task_table[i].priority = priority;
             strncpy(task_table[i].name, name, TASK_NAME_LEN);
             strncpy(task_table[i].name, name, TASK_NAME_LEN);
           
            leave_critical();
             return i;
             return i;
         }
         }
     }
     }
   
    leave_critical();
     return -1;
     return -1;
}
}
</syntaxhighlight>
</syntaxhighlight>
'''Contraintes :'''
* Appelée avant <code>scheduler_start()</code>
* Piles statiques uniquement
* Noms tronqués à TASK_NAME_LEN


== 7. Ordonnanceur (round-robin) ==
== 7. Ordonnanceur (round-robin) ==
Ligne 144 : Ligne 182 :
void scheduler_run(void) {
void scheduler_run(void) {
     enter_critical();
     enter_critical();
   
     if (is_task_valid(current_task_id)) {
     if (is_task_valid(current_task_id)) {
         task_table[current_task_id].state = TASK_RUNNING;
         task_table[current_task_id].state = TASK_RUNNING;
         leave_critical();
         leave_critical();
       
         task_table[current_task_id].function(task_table[current_task_id].arg);
         task_table[current_task_id].function(task_table[current_task_id].arg);
       
         enter_critical();
         enter_critical();
         if (task_table[current_task_id].state == TASK_RUNNING)
         if (task_table[current_task_id].state == TASK_RUNNING)
             task_table[current_task_id].state = TASK_READY;
             task_table[current_task_id].state = TASK_READY;
     }
     }
   
     current_task_id = get_next_task();
     current_task_id = get_next_task();
     leave_critical();
     leave_critical();
}
}
</syntaxhighlight>
</syntaxhighlight>
'''Algorithme round-robin :'''
* Parcours circulaire équitable
* Recherche O(n)
* Aucune sauvegarde de contexte (coopératif)


== 8. Mécanismes de sommeil et tick système ==
== 8. Mécanismes de sommeil et tick système ==
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
// Configuration du timer hardware pour 100Hz
void timer1_init_100hz(void) {
void timer1_init_100hz(void) {
     TCCR1A = 0;
     TCCR1A = 0;
Ligne 169 : Ligne 217 :
     extern volatile uint32_t system_ticks;
     extern volatile uint32_t system_ticks;
     system_ticks++;
     system_ticks++;
   
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
         if (task_table[i].state == TASK_SLEEPING && task_table[i].sleep_ticks > 0) {
         if (task_table[i].state == TASK_SLEEPING && task_table[i].sleep_ticks > 0) {
Ligne 180 : Ligne 229 :
void task_sleep(uint16_t ticks) {
void task_sleep(uint16_t ticks) {
     if (ticks == 0) ticks = 1;
     if (ticks == 0) ticks = 1;
   
     enter_critical();
     enter_critical();
     task_table[current_task_id].sleep_ticks = ticks;
     task_table[current_task_id].sleep_ticks = ticks;
     task_table[current_task_id].state = TASK_SLEEPING;
     task_table[current_task_id].state = TASK_SLEEPING;
     leave_critical();
     leave_critical();
   
     scheduler_run();
     scheduler_run();
}
}
</syntaxhighlight>
</syntaxhighlight>
'''Gestion du temps :'''
* Timer hardware 100Hz → tick 10ms
* Pas de busy-waiting
* Réveil automatique via ISR


== 9. Initialisation du système ==
== 9. Initialisation du système ==
# Mise à zéro des structures de données.
<syntaxhighlight lang="c">
# Initialisation du timer à 100 Hz.
void kernel_init(void) {
# Création de la tâche d’« idle ».
    memset(task_table, 0, sizeof(task_table));
# Activation des interruptions globales.
    current_task_id = 0;
# Lancement de la boucle principale qui exécute `scheduler_run()`.
    task_count = 0;
    system_ticks = 0;
   
    timer1_init_100hz();
    uart_init();
    io_init();
   
    task_create("idle", idle_task, NULL, PRIORITY_IDLE, idle_stack);
    sei();
}
</syntaxhighlight>
 
'''Séquence de boot :'''
# Réinitialisation
# Configuration hardware
# Création tâche idle
# Activation interruptions
 
== 10. Gestion d'erreurs ==
'''Stratégies de robustesse :'''
* Validation des pointeurs
* Sleep minimal
* Tâche idle toujours active


== 10. Gestion d’erreurs ==
<syntaxhighlight lang="c">
* Vérification de la validité des pointeurs de fonctions.
if (function == NULL || stack_buffer == NULL) {
* Conversion des durées nulles (`sleep(0)`) en `sleep(1)`.
    return -1;
* Gestion des appels imbriqués de sections critiques.
}
* Fallback automatique sur la tâche idle si aucune tâche prête.
</syntaxhighlight>


== 11. Estimations mémoire ==
== 11. Estimations mémoire ==
* Piles : 4 × 96 = 384 octets
<pre>
* TCB : 4 × 28 = 112 octets (approx.)
Piles tâches      : 4 × 96 octets  = 384 octets
* Variables globales : ~20 octets
Structures TCB    : 4 × 28 octets  = 112 octets 
* **Total estimé :** ~516 octets
Variables globales: ~20 octets
TOTAL estimé      : 516 / 2048 (25%)
</pre>


== 12. Intégration du build system ==
== 12. Intégration du build system ==
Ligne 211 : Ligne 291 :
MCU = atmega328p
MCU = atmega328p
F_CPU = 16000000UL
F_CPU = 16000000UL
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os -Wall -std=c99
 
CFLAGS = -mmcu=$(MCU) \
        -DF_CPU=$(F_CPU) \
        -Os -Wall -std=c99
 
all: firmware.hex
flash: firmware.hex
clean:
</syntaxhighlight>
 
== 13. Comportements observables ==
<syntaxhighlight lang="c">
kernel_init();
task_create("task1", ...);
task_create("task2", ...);
kernel_start();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="c">
void ma_tache(void* arg) {
    while (1) {
        faire_travail();
        task_sleep(100);
    }
}
</syntaxhighlight>
== 14. Extensions possibles ==
* Ordonnancement préemptif
* Sémaphores / mutex
* Files de messages
* Gestion d’énergie
'''Ce RTOS démontre :'''
* Les principes fondamentaux des systèmes d’exploitation embarqués
* L’optimisation pour contraintes sévères (2KB RAM)
* La coopération entre tâches sans protection matérielle
* La gestion du temps réel à 10ms

Version du 11 novembre 2025 à 14:08

Objectif

L'objectif du projet est de concevoir un pico-ordinateur complet, intégrant :

  • Une carte mère basée sue le microcontrôleur AT90USB1286

Une partie logicielle permettant l'éxecution de de commandes telles que ls, cp ou mv

Shield Arduino

Une première étape du projet a consisté à développer un shield pour Aduino uno, servant de plateforme de test et de développement pour les cartes filles SPI.

Fonctionalités:

  • Connexion de 5 périphériques SPI via des cartes filles.
  • Gestion des signaux Reset et Interruption.
  • Ajout d'une mémoire externe carte micro-SD via un connecteur Molex 10431.
  • Adaptation des niveaux logiques (5V a 3,3V) grâce à la puce 74LV125.

Ce shield joue le rôle de plateforme de développement temporaire, en attendant la carte mère du pico-ordinateur.

Schématique et routage

Schema shield arduino.jpg

Objectif

Schema shield arduino


Routage shield arduino


Carte mère

Schématique

Schéma carte mère du pico ordinateur



Firmware — RTOS

1. Vue d'ensemble du système

Le firmware implémente un système d'exploitation temps réel (RTOS) coopératif optimisé pour les microcontrôleurs AVR (ATmega328P/AT90USB1286).

Architecture coopérative vs préemptive :

  • Coopératif : Les tâches cèdent volontairement le contrôle (via task_yield(), task_sleep())
  • Avantage : Plus simple, moins de surcharge mémoire, pas besoin de sauvegarde de contexte
  • Inconvénient : Une tâche malveillante peut bloquer le système

Optimisations mémoire :

  • Allocation statique uniquement (pas de malloc/free)
  • Taille fixe des structures pour prédictibilité
  • Piles partagées entre noyau et applications

2. Arborescence (extrait)

firmware/
 ├─ kernel/                 # Cœur du système d'exploitation
 │  ├─ kernel.h            # API publique du noyau
 │  ├─ kernel.c            # Gestion système (ticks, délais)
 │  ├─ scheduler.h         # Déclarations de l'ordonnanceur  
 │  ├─ scheduler.c         # Implémentation round-robin
 │  ├─ task.h              # Structures de contrôle des tâches
 │  └─ task.c              # Création et gestion des tâches
 ├─ config.h               # Configuration matérielle (mémoire, timers)
 └─ main.c                 # Tâches applicatives (horloge binaire)

Séparation des responsabilités :

  • kernel/ : Fonctionnalités système bas niveau
  • config.h : Adaptation au matériel spécifique
  • main.c : Logique métier de l'application

3. Paramètres de configuration

#define MAX_TASKS       4      // Compromis fonctionnalité/mémoire
#define STACK_SIZE      96     // Suffisant pour appels de fonctions + variables locales
#define TICK_FREQUENCY  100    // 100Hz = résolution 10ms (équilibre précision/charge CPU)
#define TASK_NAME_LEN   8      // Noms courts pour économiser la RAM

Justification des valeurs :

  • MAX_TASKS=4 : Permet tâche système + 3 tâches applicatives dans 2KB RAM
  • STACK_SIZE=96 : Empilement typique des appels AVR + marge de sécurité
  • TICK_FREQUENCY=100 : Temps de réponse <10ms sans surcharger le CPU

4. Structure de contrôle de tâche (TCB)

typedef enum {
    TASK_INVALID = 0,
    TASK_READY,
    TASK_RUNNING,
    TASK_SLEEPING
} task_state_t;

typedef struct {
    void (*function)(void *);
    void *arg;
    uint8_t *stack_base;
    task_state_t state;
    uint16_t sleep_ticks;
    uint8_t priority;
    char name[TASK_NAME_LEN];
} task_t;

Cycle de vie d'une tâche :

CRÉATION → [READY] ↔ [RUNNING] → [SLEEPING] → [READY]
                    ↓
                 [COMPLETED] (si task_exit() appelée)

5. Gestion des sections critiques (imbriquées)

#include <avr/interrupt.h>
static volatile uint8_t critical_nesting = 0;

static inline void enter_critical(void) {
    cli();
    critical_nesting++;
}

static inline void leave_critical(void) {
    if (critical_nesting > 0) {
        critical_nesting--;
        if (critical_nesting == 0) sei();
    }
}

Protection des ressources partagées :

  • Empêche les accès concurrents aux structures partagées (task_table, variables globales)
  • Imbrication : Appels imbriqués autorisés
  • Performance : Sections critiques très courtes (<1ms)

6. Création de tâche

int8_t task_create(const char *name, void (*function)(void *), void *arg,
                   uint8_t priority, uint8_t *stack_buffer)
{
    enter_critical();
    
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (task_table[i].function == NULL) {
            task_table[i].function = function;
            task_table[i].arg = arg;
            task_table[i].stack_base = stack_buffer;
            task_table[i].state = TASK_READY;
            task_table[i].priority = priority;
            strncpy(task_table[i].name, name, TASK_NAME_LEN);
            
            leave_critical();
            return i;
        }
    }
    
    leave_critical();
    return -1;
}

Contraintes :

  • Appelée avant scheduler_start()
  • Piles statiques uniquement
  • Noms tronqués à TASK_NAME_LEN

7. Ordonnanceur (round-robin)

static uint8_t current_task_id = 0;

static inline uint8_t is_task_valid(uint8_t id) {
    return (id < MAX_TASKS) &&
           (task_table[id].function != NULL) &&
           (task_table[id].state == TASK_READY);
}

static uint8_t get_next_task(void) {
    uint8_t next = (current_task_id + 1) % MAX_TASKS;
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (is_task_valid(next)) return next;
        next = (next + 1) % MAX_TASKS;
    }
    return current_task_id;
}

void scheduler_run(void) {
    enter_critical();
    
    if (is_task_valid(current_task_id)) {
        task_table[current_task_id].state = TASK_RUNNING;
        leave_critical();
        
        task_table[current_task_id].function(task_table[current_task_id].arg);
        
        enter_critical();
        if (task_table[current_task_id].state == TASK_RUNNING)
            task_table[current_task_id].state = TASK_READY;
    }
    
    current_task_id = get_next_task();
    leave_critical();
}

Algorithme round-robin :

  • Parcours circulaire équitable
  • Recherche O(n)
  • Aucune sauvegarde de contexte (coopératif)

8. Mécanismes de sommeil et tick système

// Configuration du timer hardware pour 100Hz
void timer1_init_100hz(void) {
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
    OCR1A = 2499;
    TIMSK1 |= (1 << OCIE1A);
}

ISR(TIMER1_COMPA_vect) {
    extern volatile uint32_t system_ticks;
    system_ticks++;
    
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (task_table[i].state == TASK_SLEEPING && task_table[i].sleep_ticks > 0) {
            task_table[i].sleep_ticks--;
            if (task_table[i].sleep_ticks == 0)
                task_table[i].state = TASK_READY;
        }
    }
}

void task_sleep(uint16_t ticks) {
    if (ticks == 0) ticks = 1;
    
    enter_critical();
    task_table[current_task_id].sleep_ticks = ticks;
    task_table[current_task_id].state = TASK_SLEEPING;
    leave_critical();
    
    scheduler_run();
}

Gestion du temps :

  • Timer hardware 100Hz → tick 10ms
  • Pas de busy-waiting
  • Réveil automatique via ISR

9. Initialisation du système

void kernel_init(void) {
    memset(task_table, 0, sizeof(task_table));
    current_task_id = 0;
    task_count = 0;
    system_ticks = 0;
    
    timer1_init_100hz();
    uart_init();
    io_init();
    
    task_create("idle", idle_task, NULL, PRIORITY_IDLE, idle_stack);
    sei();
}

Séquence de boot :

  1. Réinitialisation
  2. Configuration hardware
  3. Création tâche idle
  4. Activation interruptions

10. Gestion d'erreurs

Stratégies de robustesse :

  • Validation des pointeurs
  • Sleep minimal
  • Tâche idle toujours active
if (function == NULL || stack_buffer == NULL) {
    return -1;
}

11. Estimations mémoire

Piles tâches      : 4 × 96 octets  = 384 octets
Structures TCB    : 4 × 28 octets  = 112 octets  
Variables globales: ~20 octets
TOTAL estimé      : 516 / 2048 (25%)

12. Intégration du build system

MCU = atmega328p
F_CPU = 16000000UL

CFLAGS = -mmcu=$(MCU) \
         -DF_CPU=$(F_CPU) \
         -Os -Wall -std=c99

all: firmware.hex
flash: firmware.hex
clean:

13. Comportements observables

kernel_init();
task_create("task1", ...);
task_create("task2", ...);
kernel_start();
void ma_tache(void* arg) {
    while (1) {
        faire_travail();
        task_sleep(100);
    }
}

14. Extensions possibles

  • Ordonnancement préemptif
  • Sémaphores / mutex
  • Files de messages
  • Gestion d’énergie

Ce RTOS démontre :

  • Les principes fondamentaux des systèmes d’exploitation embarqués
  • L’optimisation pour contraintes sévères (2KB RAM)
  • La coopération entre tâches sans protection matérielle
  • La gestion du temps réel à 10ms