« SE4Binome2023-7 » : différence entre les versions
Ligne 130 : | Ligne 130 : | ||
comme sur cet exemple: | comme sur cet exemple: | ||
[[Fichier:New.mp4|gauche|vignette]] | |||
Version du 12 janvier 2024 à 14:08
GIT
https://archives.plil.fr/lwijsman/PICO_lwijsman_apalifer
Ordonnanceur
Matériel
Le premier objectif était de réaliser la partie matériel de notre projet.
Un premier TP pratique nous a permis de réaliser différents composants qui allaient nous servir par la suite pour le bon fonctionnement de notre pico-ordinateur.
Nous avons ainsi pu réaliser les composants suivant :
- Réalisation des cables de liaison HE10 carte-mère/carte-fille avec des cables plats ruban 8 broches et des connecteurs HE10 femelles.
- Réalisation du shield : soudure du Lecteur SD, des LEDs et résistances, et des ports HE10 males
Programmation de l'ordonnanceur
Première approche :
Pour l'ordonnanceur, nous avons commencé par réaliser la fonction d'interruption qui se déclenche toutes les 20ms. Ensuite, nous avons créé 2 processus distincts afin de tester le bon fonctionnement de notre ordonnanceur.
void scheduler() {
// Choisi la tâche suivante à exécuter en tournant en boucle
currentTask = (currentTask + 1) % NUM_TASKS;
}
Le processus 2 allume et éteind une LED toutes les 500ms, le processus 0 réalise la même opération sur une LED différente toutes les 1000ms, ces fonctions utilisent la fonction _delay_ms pour maintenir les LEDs allumés pendant un certain temps.
// Définition des tâches
void task0() {
// Code de la tâche 0
Output_setHigh(&PORTB, PB5);
while (1)
{
_delay_ms(1000);
Output_flip(&PORTB, PB5);
}
}
void task2() {
//Code de la tâche 2
Output_setHigh(&PORTD, PD7);
while (1)
{
_delay_ms(500);
Output_flip(&PORTD, PD7);
}
}
En implantant le programme sur la carte + shield, on constate que le programme fonctionne correctement, c'est-à-dire que chacune des deux LEDs clignotent à son rythme et les deux processus de gestion des LEDs fonctionnent simultanément.
Approche avancée :
Nous avons ensuite cherché à implémenter une fonction faisant office de pause, mais qui contrairement au delay, n'agissait pas comme une interruption à tout le programme. La fonction wait_ms permet de mettre une tâche en attente pendant un certain nombre de millisecondes.
void wait_ms(int ms){
cli();
taskList[currentTask].timer = ms;
taskList[currentTask].state = SLEEPING;
TCNT1 = 0;
sei();
TIMER1_COMPA_vect();
}
Le timer de la tâche prend la valeur en milliseconde spécifié dans l'argument de la fonction et la fonction passe en état de sommeil. Dans le programme, la fonction TCNT1 permet de mesurer le temps écoulé depuis l'appel à la fonction wait_ms, elle est donc ici fixée à 0. On fait ensuite directement appel à la routine d'interruption pour déclencher le mécanisme de gestion du temps.
La routine d'interruption du Timer1 est implémentée de telle sorte que le contexte de la tâche en cours est sauvegardé, l'ordonnanceur est appelé, puis le contexte de la prochaine tâche à exécuter est restauré.
ISR(TIMER1_COMPA_vect, ISR_NAKED)
{
// Sauvegarde du contexte de la tâche interrompue
portSAVE_CONTEXT();
// Sauvegarde la valeur actuelle du pointeur de pile (SP)
taskList[currentTask].stackPointer = SP;
// Appel à l'ordonnanceur
scheduler();
// Récupération du contexte de la tâche ré-activée
SP = taskList[currentTask].stackPointer;
// restaure les valeurs des registres depuis la pile.
portRESTORE_CONTEXT();
// return from interupt
asm volatile ( "reti" );
}
La fonction scheduler, commentée ci-dessous, permet de gérer l'ordonnancement des tâches en fonction de leur temps d'attente défini.
void scheduler() {
for(int i = 0; i < NUM_TASKS; i++){// Pour chaque tâche
if(taskList[i].state == SLEEPING){ // Si elle est en état SLEEPING
uint16_t time_elapsed = 20;
if(TCNT1 != 0){ // Le timer a commencé à compter
time_elapsed = TCNT1 * 200 / OCR1A / 10; // Temps en milliseconde écoulé depuis la dernière interruption
TCNT1 = 0; // Réinitialisation du timer pour la prochaine interruption
}
taskList[i].timer = taskList[i].timer - time_elapsed;
if(taskList[i].timer == 0) // Fin du timer
{
taskList[i].state = RUNNING; // La tâche se réveille
}
}
}
// Permet de sélectionner la tâche suivante à exécuter en tournant en boucle
do{
currentTask = (currentTask + 1) % NUM_TASKS;
}while(taskList[currentTask].state == SLEEPING);
}
Le programme complet et détaillé de notre ordonnanceur est disponible sur git.
Connection SPI
Nous avons ajouté des fonctionalitées de comunication SPI.
cela nous permet notament de comuniquer de l'ordonanceur vars l'ecran affin d'y aficher quelque chose:
comme sur cet exemple:
Carte FPGA / VHDL
Carte électronique numerique
Carte fille écran LCD
Référence écran : sparkfun ADM1602k-NSW-FBS
Schematic :
La première étape de notre projet consistait à la réalisation du schematic sous KiCad de notre carte écran. Afin de réaliser le schéma du routage et pour que l'écran soit correctement connecté nous nous sommes référés à la documentation de l'écran afin de relier chacune des broches du connecteur aux labels correspondants. Vous trouverez ci-contre le schematic et les composants de la carte.
Plus spécifiquement, cette carte possède :
- un microcontroleur atmega328p
- un connecteur HE10 permettant de la relier à la carte mère
- un AVR ISP permettant la programmation de la carte
- des LEDs
- un connecteur 1x16 broches permettant la connexion avec l'écran
Une fois le schematic réalisé et après vérification, nous avons pu commencer à effectuer le routage de notre carte.
Après le point d'avancement avec les professeurs, nous nous sommes aperçus que nous n'avions pas correctement effectué le pinout de l'Atmega328p et que les ports MOSI, MISO et CLK n'étaient pas aux bons endroits. Nous avons donc su adapter notre carte afin qu'elle puisse être programmée.
Nous avons donc corrigé le schematic afin qu'il puisse illustré correctement le fonctionnement de la carte.
Routage :
Pour le routage, nous avons également utilisé le logiciel KiCad. Vous trouverez ci-contre l'image correspondante à ce dernier.
Nous avons aussi du adapter notre routage pour qu'il corresponde avec notre carte une fois nos erreurs corrigées.
Soudures et programmation :
Après réception de la carte PCB, nous avons réalisé la soudure des composants sur la carte. Nous avons ainsi pu tenter de programmer la carte avec l'arduino en tant qu'ISP, mais nous avons une erreur.
Egalement, après un test en programmant directement la carte avec un programme simple en C et un makefile adapté, nous en sommes arrivés à la conclusion que notre carte était défaillante.
Comme nous n'arrivions pas à programmer la carte, nous avons premièrement eu quelques doutes concernant la soudure du quartz, nous avons donc choisi de le remplacer mais cela n'a pas résolu le problème.
Comme nous l'avons évoqué précedemment, les connexions de l'AVR ISP était finalement en cause, nous avions inversé SCK, MISO et MOSI. Nous avons donc réussi à corriger le problème directement en cablant sur la carte. Nous avons donc implémenté un programme pour tester le clignotement d'une LED. La carte fonctionne.
Nous avons ensuite soudé le reste des composants (potentiomètre et connecteurs).
L'un des pins du connecteur femelle (16 pins) sur la carte n'était pas correctement relié à la masse et nous empêché d'afficher sur l'écran. Erreur certainement causé au moment de la création du plan de masse, mais finalement corrigée en soudant un fil sur le dessous de la carte de la même manière que précedemment.