« SE4Binome2025-2 » : différence entre les versions
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 | == 1. Vue d'ensemble du système == | ||
Le firmware implémente un | 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 <code>task_yield()</code>, <code>task_sleep()</code>) | ||
* | * '''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) == | == 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 // | #define MAX_TASKS 4 // Compromis fonctionnalité/mémoire | ||
#define STACK_SIZE 96 // | #define STACK_SIZE 96 // Suffisant pour appels de fonctions + variables locales | ||
#define TICK_FREQUENCY 100 // | #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 == | ||
# | <syntaxhighlight lang="c"> | ||
# | void kernel_init(void) { | ||
# Création | memset(task_table, 0, sizeof(task_table)); | ||
# Activation | 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(); | |||
} | |||
</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 | |||
== | <syntaxhighlight lang="c"> | ||
if (function == NULL || stack_buffer == NULL) { | |||
return -1; | |||
} | |||
</syntaxhighlight> | |||
== 11. Estimations mémoire == | == 11. Estimations mémoire == | ||
<pre> | |||
Piles tâches : 4 × 96 octets = 384 octets | |||
Structures TCB : 4 × 28 octets = 112 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
Objectif
Carte mère
Schématique
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 niveauconfig.h: Adaptation au matériel spécifiquemain.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 :
- 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
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