« SE4Binome2023-9 » : différence entre les versions

De projets-se.plil.fr
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
 
(40 versions intermédiaires par le même utilisateur non affichées)
Ligne 4 : Ligne 4 :
== Ordonnanceur / SE ==
== Ordonnanceur / SE ==


=== Matériel pour l'ordonnanceur ===
=== Ordonnanceur ===
 
===== Matériel pour l'ordonnanceur =====
[[Fichier:Carte du bouclier avant soudure.jpg|vignette|gauche|Carte du bouclier avant soudure|304x304px]]
[[Fichier:Carte du bouclier avant soudure.jpg|vignette|gauche|Carte du bouclier avant soudure|304x304px]]
[[Fichier:Carte après soudure.jpg|vignette|droite|Carte du bouclier après première soudure|285x285px]][[Fichier:He10 cable.jpg|vignette|centre|2 câbles HE10/HE10|317x317px]]
[[Fichier:Carte après soudure.jpg|vignette|droite|Carte du bouclier après première soudure|285x285px]][[Fichier:He10 cable.jpg|vignette|centre|2 câbles HE10/HE10|317x317px]]
Ligne 33 : Ligne 35 :




=== Test du bouclier ===
===== Test du bouclier =====
Pour tester le bon fonctionnement des LED, nous avons simplement écrit un code avec <code>PORTB</code>, <code>PORTC</code>et<code>PORTD</code>à <code>0xFF</code>(le code est disponible sur le git) :[[Fichier:Video test led.mov|vignette|Test des LED du shield|centré]]
Pour tester le bon fonctionnement des LED, nous avons simplement écrit un code qui allume chaque LED :[[Fichier:Video test led.mov|vignette|Test des LED du shield|centré]]
<p style="clear: both;" />
<p style="clear: both;" />


=== Programmation du timer1 ===
==== Programmation du timer1 ====
Il est demandé de générer une interruption toutes les 20 ms. En nous inspirant du code présent sur cette [https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme038.html page], nous avons pu, par le biais d'interruptions, allumer la LED d'un Arduino Uno.
Il est demandé de générer une interruption toutes les 20 ms. En nous inspirant du code présent sur cette [https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme038.html page], nous avons pu, par le biais d'interruptions, allumer la LED d'un Arduino Uno.


Pour générer une interruption toutes les 20 ms, il est nécessaire de régler le nombre de "ticks" et donc la valeur du registre <code>OCR1A</code>. On aura alors <code>OCR1A = 1250</code> pour une pré-division de la fréquence d'horloge de 256.
Pour générer une interruption toutes les 20 ms, il est nécessaire de régler le nombre de "ticks" et donc la valeur du registre <code>OCR1A</code>. On aura alors <code>OCR1A = 1250</code> pour une pré-division de la fréquence d'horloge de 256.


=== Ordonnanceur basique ===
==== Ordonnanceur basique ====


Après avoir programmé le timer1 pour qu'il déclenche une interruption toutes les 20 ms, nous avons besoin d'écrire dans l'ISR (avec une la sauvegarde et la récupération du contexte :
Après avoir programmé le timer1 pour qu'il déclenche une interruption toutes les 20 ms, nous avons besoin d'écrire dans l'ISR (avec une la sauvegarde et la récupération du contexte :
Ligne 53 : Ligne 55 :
/* Sauvegarde du contexte de la tâche interrompue */
/* Sauvegarde du contexte de la tâche interrompue */
portSAVE_CONTEXT();
portSAVE_CONTEXT();
taches[current_task].ppile = SP;
taches[current_task].stack = SP;


/* Appel à l'ordonnanceur */
/* Appel à l'ordonnanceur */
scheduler();
scheduler();


SP = taches[current_task].ppile;
SP = taches[current_task].stack;
/* Récupération du contexte de la tâche ré-activée */
/* Récupération du contexte de la tâche ré-activée */
portRESTORE_CONTEXT();
portRESTORE_CONTEXT();
Ligne 75 : Ligne 77 :
     for(uint8_t i = 1; i < NB_TASK ; i++){
     for(uint8_t i = 1; i < NB_TASK ; i++){
         uint16_t save = SP;
         uint16_t save = SP;
         SP = taches[i].ppile;
         SP = taches[i].stack;
         uint16_t adresse=(uint16_t)taches[i].fct;
         uint16_t adresse=(uint16_t)taches[i].fct;
         asm volatile("push %0" : : "r" (adresse & 0x00ff) );
         asm volatile("push %0" : : "r" (adresse & 0x00ff) );
         asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
         asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
         portSAVE_CONTEXT();
         portSAVE_CONTEXT();
         taches[i].ppile=SP;
         taches[i].stack=SP;
         SP = save;
         SP = save;
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
On doit bien faire attention à ne pas dépasser <code>0x8FFF</code> comme adresse de SP :
[[Fichier:Map sram.png|centré|vignette]]


Voici une vidéo où l'on fait clignoter deux LED avec un temps différent :  
Voici une vidéo où l'on fait clignoter deux LED avec un temps différent :  
[[Fichier:Video led 2time.mp4|vignette|centré|LED allumées avec temps différents]]
[[Fichier:Video led 2time.mp4|vignette|centré|LED allumées avec temps différents]]


=== Lecture/écriture sur le port série ===
==== Lecture/écriture sur le port série ====


<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
Ligne 113 : Ligne 119 :




 
==== Afficheur 7 segments ====
=== Afficheur 7 segments ===


Pour tester l'afficheur 7 segments, nous avons utilisé un [https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme014.html programme] d'envoi SPI. Nous n'avons toutefois pas encore compris comment afficher un caractère spécifique, nous avons réussi seulement pour l'instant à afficher une série de 0 :
Pour tester l'afficheur 7 segments, nous avons utilisé un [https://rex.plil.fr/Enseignement/Systeme/Systeme.PSE/systeme014.html programme] d'envoi SPI. Nous n'avons toutefois pas encore compris comment afficher un caractère spécifique, nous avons réussi seulement pour l'instant à afficher une série de 0 :
Ligne 145 : Ligne 150 :


Voici l'affichage de "test":
Voici l'affichage de "test":
[[Fichier:7seg 7.mp4|centré|vignette|[[Fichier:Video3.mp4|vignette]]]]
[[Fichier:7seg 7.mp4|centré|vignette|]]
 
 
==== Matrice LED ====
 
Voici notre fonction pour envoyer un carcatère à la matrice :
<syntaxhighlight lang="c">
void aff_matrix(void){
 
    char a;
    int index;
    while(1){
        P(SEM_SPI);
        index = selection(caractere_partage);
        spi_activer(SS_2);
        _wait_ms(1);
        for(int LED=0; LED<8; LED++){
            for(int j=0; j<8;j++){
                a = hex[index][LED] & (1<<j);
                spi_echange(a);
            }
        }
        spi_desactiver(SS_2);
        _wait_ms(500);
        V(SEM_SPI);
    }
}
</syntaxhighlight>
Voici une vidéo du fonctionnement :
[[Fichier:Video3.mp4|centré|vignette]]
 
Nous avons écrit un tableau stockant les configurations de chaque chiffre hexadécimal :
<syntaxhighlight lang="c">
char hex[16][8] = {
    {0x7e,0x42,0x42,0x42,0x42,0x42,0x7e,0x00}, //--> 0
    {0x08,0x18,0x28,0x08,0x08,0x08,0x3e,0x00}, //--> 1
    {0x18,0x24,0x04,0x08,0x10,0x20,0x7c,0x00}, //--> 2
    {0x18,0x24,0x04,0x18,0x04,0x04,0x38,0x00}, //--> 3
    {0x20,0x20,0x24,0x24,0x3e,0x04,0x04,0x00}, //--> 4
    {0x7e,0x40,0x40,0x7e,0x02,0x02,0x7e,0x00}, //--> 5
    {0x7e,0x40,0x40,0x7e,0x42,0x42,0x7e,0x00}, //--> 6
    {0x7f,0x02,0x04,0x08,0x10,0x20,0x40,0x00}, //--> 7
    {0x3c,0x42,0x42,0x3c,0x42,0x42,0x3c,0x00}, //--> 8
    {0x7e,0x42,0x42,0x7e,0x02,0x02,0x7e,0x00}, //--> 9
    {0x18,0x24,0x42,0x42,0x7e,0x42,0x42,0x42}, //--> A
    {0x7c,0x42,0x42,0x42,0x7c,0x42,0x42,0x7e}, //--> B
    {0x7e,0x40,0x40,0x40,0x40,0x40,0x40,0x7e}, //--> C
    {0x78,0x44,0x42,0x42,0x42,0x42,0x42,0x7c}, //--> D
    {0x7e,0x40,0x40,0x7e,0x40,0x40,0x40,0x7e}, //--> E
    {0x7e,0x40,0x40,0x7c,0x40,0x40,0x40,0x40}, //--> F
};
 
 
</syntaxhighlight>
Nous avons aussi fait une fonction pour faire correspondre le caractère partagé au bon index du tableau précédent :
<syntaxhighlight lang="c">
int selection(char select)// pour choisir l index dans le tableau pour la matrice
{
    int result = 0;
    if(select >= '0' && select <= '9')
        result = select - 48;
    else if(select >= 'a' && select <= 'f')
        result = 10 + (select - 'a');
    else if(select >= 'A' && select <= 'F')
        result = 10 + (select - 'A');
   
    return result;
}
</syntaxhighlight>
 
==== Sémaphores ====
 
Nous avons trouvé dans un ancien projet IMA (Malette arduino éducative) une implémentation d'ordonnanceur. Nous avons décidé de complétement changer notre scheduler et de prendre pour base l'ordonnaceur du projet IMA vu que nous avions du mal à ajouter les sémaphores... Nous avons alors modifié la fonction <code>scheduler</code> de façon à gérer l'attente (flags) directement dans l'ordonnanceur et de mesurer le temps écoulé.
<syntaxhighlight lang="c">
void scheduler(void)
{
do{
  int i;
 
  long int elapsed=timer_elapsed();//temps écoulé


=== Matrice LED ===
  TCNT1=0;


=== Implémentation de "sleep" ===
  for(i=0;tasks[i].fct!=NULL;i++)
=== Ordonnacement complet ===
    if(tasks[i].etat==(WAIT_TIME|STATE_SLEEP)){
      tasks[i].time -= elapsed;
      if(tasks[i].time<=0) tasks[i].etat=STATE_RUN;
      }
  //série UART
  for(i=0;tasks[i].fct!=NULL;i++)
    if(tasks[i].etat==(WAIT_SERIAL|STATE_SLEEP))
    if (UCSR0A & (1 << RXC0)) tasks[i].etat=STATE_RUN;
 
  //SPI
  for(i=0;tasks[i].fct!=NULL;i++)
    if(tasks[i].etat==(WAIT_SPI|STATE_SLEEP))
      if(SPSR & 1<<SPIF) tasks[i].etat=STATE_RUN;
 
 
  unsigned char first=1;
  while(1){
    if(task_current==NULL) task_current=tasks;
    else if(task_current->fct!=NULL) task_current++;
    if(task_current->fct==NULL){
      task_current=NULL;
      if(first){ first=0; continue; }
      else break;
      }
    if((task_current->etat & STATE_MASK)!=STATE_SLEEP) break;
    }
  } while(task_current==NULL);
}
</syntaxhighlight>
 
Vient ensuite l'implémentation des sémaphores :
 
<syntaxhighlight lang="c">
static int *get_pointer(int s){
int *p;
switch(s){
  case SEM_SERIAL: p=&serial; break;
  case SEM_SPI: p=&spi; break;
  default: p=NULL; break;
  }
return p;
}
 
// "on bloque"
void P(int s)
{
int *p=get_pointer(s);
if(p==NULL) return;
cli();
if(*p==1){
  task_current->etat=(WAIT_SEMAPHORE|STATE_SLEEP);
  task_current->time=s;
  TIMER1_COMPA_vect();
  }
sei();
}
 
// "on libère"
void V(int s)
{
int *p=get_pointer(s);
if(p==NULL) return;
cli();
*p=0;
task_t *t=(task_t *)tasks;
while(t->fct!=NULL){
  if(t->etat==(WAIT_SEMAPHORE|STATE_SLEEP) && t->time==s){ //réordonnancement
    t->etat=STATE_RUN;
    *p=1;
    break;
    }
  t++;
  }
sei();
}
</syntaxhighlight>
 
==== Ordonnacement complet ====
[[Fichier:Video ord.mp4|vignette|centré|Ordonnancement]]
[[Fichier:Video ord.mp4|vignette|centré|Ordonnancement]]
<br>
<br>
<br>
<br>
=== Système de fichiers ===
Nous avons essayé d’implémenter (à la dernière minute) un système de fichiers sur le bouclier arduino suite à des problèmes dans notre carte mère.
Les fonctions que nous avons utilisées sont inspirées du projet EC1 de l'année denière.
==== Cahier des charges ====
* le système de fichiers doit résider dans la mémoire de 8 Mo, les accès à la mémoire se font par blocs de 256 octets ; - l'accès au système de fichiers se fait par des primitives système
* le micro-système de fichiers ne comporte qu'un répertoire : le répertoire principal, ce dernier peut comporter au maximum 64 fichiers, un fichier est caractérisé par un nom de 16 caractères au maximum, sa taille et - ses blocs de données, un fichier est décrit par un maximum de 16 blocs de données;
* le superbloc du système de fichiers comprend, en plus de la description du répertoire racine, une carte bit à bit des blocs de données libres, cette carte nécessite 16 blocs ;
A noter que la description du système ne peut pas se faire avec "une structure de données C" sinon la place en mémoire utilisée serait beaucoup trop grande.
==== Commandes ====
Nous avons utilisé les fonctions <code>ReadData</code> et <code>WriteBlock</code> déja fournis de base dans le code de test de la carte SD.
On réserve une plage de blocs pour le superbloc et la bitmap en commencant l'écriture à 1040 : 64*16 (fichiers) + 16 (bitmap) = 1040.
===== LS =====
<syntaxhighlight lang="c">
void list(void) {
unsigned char buff[MAX_FILENAME_LENGTH];
int fileCount = 0;
// Parcourir les blocs multiples de 16 à partir de 0 jusqu'à MAX_FILES_IN_DIRECTORY
for (int blockNum = 0; blockNum < MAX_FILES_IN_DIRECTORY; blockNum += 16) {
readData(&sd,blockNum, 0, MAX_FILENAME_LENGTH,buff);
// Vérifier si le nom de fichier est vide
if (buff[0] != 0) {
// Afficher le nom du fichier
send_Ustring(buff);
send_string("\r\n");
fileCount++;
}
}
if (fileCount == 0) {
send_string("Aucun fichier trouvé.\r\n");
}
}
</syntaxhighlight>
===== CREATE =====
<syntaxhighlight lang="c">
void create(char *filename){
size_t sizeFilename = strlen(filename);
unsigned char buff[MAX_FILENAME_LENGTH];
uint8_t IsPlace = 0;
if (sizeFilename > MAX_FILENAME_LENGTH) {
send_string("Impossible de créer le fichier, nom trop long\n");
return;
}
// Parcours des blocs réservés pour la description des fichiers (superbloc)
for (uint32_t blockNum = 0; blockNum < MAX_FILES_IN_DIRECTORY; blockNum += 16) {
readData(&sd,blockNum, 0, MAX_FILENAME_LENGTH,buff);
// Vérifier si le bloc est vide (pas de nom de fichier)
if (buff[0] == 0) {
// Écrire le nom du fichier dans l'emplacement vide du superbloc
if (sizeFilename < MAX_FILENAME_LENGTH) {
sizeFilename += 1; // Ajouter '\0' s'il y a de la place
}
writeBlock(&sd,blockNum,(const unsigned char *)filename,0);
placeFound = blockNum;
IsPlace = 1;
break; // Fichier créé, sortir de la boucle
}
}
if (IsPlace != 0) {
send_string("Le fichier a été créé avec succès.\r\n");
} else {
send_string("Plus de place dans le système de fichier pour créer ce fichier.\r\n");
}
}
</syntaxhighlight>
[[Fichier:Video command.mp4|vignette|centré|Commandes echo,ls,version,create]]
Notre fonction <code>LS</code> "ne s'actualise pas" du premier cooup lorsque nous créons un fichier. Nous avons aussi écrit d'autres fonctions comme <code>RM</code> par exemple, mais elles ne fonctionnent pas comme désiré.
==== Identification cartes filles ====
Nous avons créé une structure de données nous permettant de pouvoir identifier les cartes filles pour savoir sur quels CS "envoyer la fonction" (correspond à la fonction <code>DEVICES</code>) :
<syntaxhighlight lang="c">
void Identification(void)
{
    for(int i = 0; i< NB_SS;i++)
    {
        uint8_t type = 0;
        spi_activer(chips[i].SS);
        spi_echange(0x00);
        type = spi_echange(0x00);
        if ((type >> 4) == 0x0A) {
            switch(type & 0x0F)
            {
                case 0x01:
                    chips[i].device = KEYBOARD;
                    break;
                case 0x02:
                    chips[i].device = SCREEN;
                    break;
                case 0x04:
                    chips[i].device = NETWORK;
                    break;
                case 0x08:
                    chips[i].device = SOUND;
                    break;
            }
        }
        spi_desactiver(chips[i].SS);
    }
}
</syntaxhighlight>


== Carte FPGA / VHDL ==
== Carte FPGA / VHDL ==
=== Décodeur SPI ===
Sur l'atmega328p, lorsque <code>CS</code> est au niveau bas, le slave est sélectionné et la transmission a lieu. Le master envoie alors une clock <code>SCK</code>, grâce à laquelle le slave pourra récupérer les données envoyées sur <code>MOSI</code> :
[[Fichier:Graph.png|vignette|centré|Fonctionnement SPI ATMEGA328P avec CPHA = 0]]
Pour le décodeur, on supposera une configuration "classique" avec <code>CPHA = 0</code> (récupération des données sur front montant),  et <code> DORD = 0</code> (l'envoi commence par le MSB).
De plus, nous utilisons que <code>MOSI</code> vu que nous n'envoyons rien au master.
L'enjeu est de récupérer la trame de données qui est en série et de la rendre "en parallèle" pour une utilisation autre.
<syntaxhighlight lang="vhdl" line="1">
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity SPIDecoder is
    Port ( SCK : in STD_LOGIC;
          MOSI : in STD_LOGIC;
          CS : in std_logic;
          result : out STD_LOGIC_VECTOR(7 downto 0));
end SPIDecoder;
architecture Behavioral of SPIDecoder is
 
signal cpt : integer range 0 to 8 := 0;
signal mot : STD_LOGIC_VECTOR(7 downto 0);
signal tmp : std_logic;
signal result_temp : STD_LOGIC_VECTOR(7 downto 0);
begin
    process(SCK,CS)
    begin
    if CS = '1' then -- Lorsque CS est à 1 => reset cpt et du mot binaire
    mot <= (others => '0');
    cpt <=0;
    else
    if rising_edge(SCK) then -- front montant
      mot(7-cpt) <= MOSI; -- Récupération MOSI en commençant d'abord par le MSB
     
      if(cpt = 7) then
        cpt <= 0;
        result_temp <= mot;
        else
        cpt <= cpt+1;
    end if;
   
    end if;
    end if;
      result <=result_temp;
    end process;
end Behavioral;
</syntaxhighlight>
Nous avons envoyé sur <code>MOSI</code> <code>0xF0</code> en série:
[[Fichier:Testbench.png|vignette|centré|Testbench]]
=== Contrôleur VGA ===
On suppose que l'écran a comme résolution 1024x768 et comme fréquence 65Hz.
Pour que la communication avec l'écran marche, ces conditions doivent être respectées :
[[Fichier:Vga disp.png|vignette|centré|Trame pour VGA 65 Hz]]
En VHDL, l'idée sera de parcourir grâce à un compteur les pixels horizontaux/verticaux tout en respectant les périodes données ci-dessus.
<syntaxhighlight lang="vhdl">
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity vga_display is
    Port ( HS : out STD_LOGIC;
          VS : out STD_LOGIC;
          clk100Mhz : in STD_LOGIC;
          R : out STD_LOGIC_VECTOR (3 downto 0);
          G : out STD_LOGIC_VECTOR (3 downto 0);
          B : out STD_LOGIC_VECTOR (3 downto 0));
end vga_display;
architecture Behavioral of vga_display is
signal compteur_v : integer range 0 to 805; -- 0 a 768 a 1 -- entre 771 et 777 a 0
signal compteur_h : integer range 0 to 1343; -- 0 a 1023 il est à 1 -- entre 1047 et 1183 à 0
signal HSS : std_logic;
signal VSS : std_logic;
signal compteur : integer range 0 to 1000000 := 0;
signal clk_65Hz : std_logic ;
component clk_wiz_0
port
(-- Clock in ports
  -- Clock out ports
  clk_out1          : out    std_logic;
  clk_in1          : in    std_logic
);
end component;
begin
    your_instance_name : clk_wiz_0 -- Création d'une horloge à 65 Hz
      port map (
      -- Clock out ports 
      clk_out1 => clk_65Hz,
      -- Clock in ports
      clk_in1 => clk100MHz
    );
   
    process(clk_65Hz)
    begin
   
    if clk_65Hz'event and clk_65Hz='1'then
                if compteur_v < 771 then
                    if compteur_h < 1048 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                    elsif compteur_h >= 1048 and compteur_h < 1183 then
                        HSS <= '0';
                        compteur_h <= compteur_h + 1;
                    elsif compteur_h >= 1183 and compteur_h < 1343 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                    else
                        HSS <= '1';
                        compteur_h <= 0;
                        compteur_v<=compteur_v+1;
                  end if;             
                    VSS <= '1'; 
                elsif compteur_v >= 771 and compteur_v < 776  then
                    if compteur_h < 1048 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                    elsif compteur_h >= 1048 and compteur_h < 1183 then
                        HSS <= '0';
                        compteur_h <= compteur_h + 1;
                    elsif compteur_h >= 1183 and compteur_h < 1343 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                    else
                        HSS <= '1';
                        compteur_h <= 0;
                        compteur_v<=compteur_v+1;
                  end if;   
                    VSS <= '0';
                  elsif compteur_v >= 776 and compteur_v < 805 then
                    if compteur_h < 1048 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                    elsif compteur_h >= 1048 and compteur_h < 1183 then
                        HSS <= '0';
                        compteur_h <= compteur_h + 1;
                    elsif compteur_h >= 1183 and compteur_h < 1343 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                    else
                        HSS <= '1';
                        compteur_h <= 0;
                        compteur_v<=compteur_v+1;
                  end if;             
                  else
                        compteur_v<=0;
                        VSS <= '1';
                  end if;
                 
                  if HSS = '1' and  VSS = '1' then
                    R <= "1111";
                    G <= "0000";
                    B <= "0000";
                    end if;
   
    end if;
    end process;
   
    HS <= HSS;
    VS <= VSS;
   
end Behavioral;
</syntaxhighlight>
Voici ce qu'on obtient lors du testbench :
'''Hsync :'''
[[Fichier:Tpw hs.png|vignette|centré]]
[[Fichier:Ts hs.png|vignette|centré]]
'''Vsync :'''
[[Fichier:Tpw vs.png|vignette|centré]]
[[Fichier:Ts vs.png|centré|vignette]]
Il faudrait ensuite implémenter le composant "décodeurSPI" pour pouvoir commander l'écran depuis un atmega par exemple.


== Carte électronique numérique ==
== Carte électronique numérique ==
Ligne 177 : Ligne 702 :
=== Schématique/Routage ===
=== Schématique/Routage ===


Voici la schématique de la carte mère ainsi que du programmateur :[[Fichier:CM page-0001.jpg|vignette|Schématique de la carte mère|307x307px|gauche]][[Fichier:Progjpg.jpg|vignette|Schématique du programmateur|300x300px|néant]]
Voici la schématique de la carte mère ainsi que du programmateur :[[Fichier:CM page-0001.jpg|vignette|Schématique de la carte mère|307x307px|gauche]]
 
<br>
<br>
Voici le routage de la carte:                                                                                                                                                                                                                                                                     
Voici le routage de la carte:                                                                                                                                                                                                                                                                     


Ligne 184 : Ligne 710 :
Voici la vue 3D de la carte:                                                                                                                                                                                                                                                                       
Voici la vue 3D de la carte:                                                                                                                                                                                                                                                                       
</div>[[Fichier:Screenshot 2023-11-06 06-29-16.png|vignette|Visualisation 3D|néant]]
</div>[[Fichier:Screenshot 2023-11-06 06-29-16.png|vignette|Visualisation 3D|néant]]
=== PCB soudé ===
[[Fichier:Carte11.jpg|vignette|gauche|Carte soudée de dos]]
[[Fichier:Carte22.jpg|vignette|droite|Carte soudée de face]]
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
=== Problèmes rencontrés ===
Lors de l'élaboration de notre KiCad pour la carte, nous avons décidé d'utiliser 5V pour l'alimentation de la carte. Sauf que cela nous a porté préjudice... En prenant 5V, nous n'avions pas accès directement à l'UART, ce qui rend l'écriture du code très difficile vu qu'on a aucune idée du fonctionnement. Il est impératif d'avoir un aperçu du résultat pour coder le système de fichiers. C'est pourquoi nous avons tenté tant bien que mal à essayer de "sortir" les broches TX/RX mais sans succès. Nous avons aussi tenté d'utiliser le SPI au lieu de l'UART pour communiquer avec la carte mais cela s'est avéré un peu trop fastidieux.
Nous avons alors très tardivement codé sur la carte SD du bouclier.


= Rendus =
= Rendus =

Version actuelle datée du 18 janvier 2024 à 16:48


Ordonnanceur / SE

Ordonnanceur

Matériel pour l'ordonnanceur
Carte du bouclier avant soudure
Carte du bouclier après première soudure
2 câbles HE10/HE10
Adaptateur
Bouclier soudé













Test du bouclier

Pour tester le bon fonctionnement des LED, nous avons simplement écrit un code qui allume chaque LED :

Programmation du timer1

Il est demandé de générer une interruption toutes les 20 ms. En nous inspirant du code présent sur cette page, nous avons pu, par le biais d'interruptions, allumer la LED d'un Arduino Uno.

Pour générer une interruption toutes les 20 ms, il est nécessaire de régler le nombre de "ticks" et donc la valeur du registre OCR1A. On aura alors OCR1A = 1250 pour une pré-division de la fréquence d'horloge de 256.

Ordonnanceur basique

Après avoir programmé le timer1 pour qu'il déclenche une interruption toutes les 20 ms, nous avons besoin d'écrire dans l'ISR (avec une la sauvegarde et la récupération du contexte :

/*code...*/
ISR(TIMER1_COMPA_vect,ISR_NAKED)
{
/* Sauvegarde du contexte de la tâche interrompue */
portSAVE_CONTEXT();
taches[current_task].stack = SP;

/* Appel à l'ordonnanceur */
scheduler();

SP = taches[current_task].stack;
/* Récupération du contexte de la tâche ré-activée */
portRESTORE_CONTEXT();
asm volatile ( "reti" );
}

/*code...*/

Ainsi qu'une fonction init_task, qui s'occupera d'initialiser toutes les tâches à part la première (pile vide au début, pas nécessaire) :

void init_task()
{
    for(uint8_t i = 1; i < NB_TASK ; i++){
        uint16_t save = SP;
        SP = taches[i].stack;
        uint16_t adresse=(uint16_t)taches[i].fct;
        asm volatile("push %0" : : "r" (adresse & 0x00ff) );
        asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
        portSAVE_CONTEXT();
        taches[i].stack=SP;
        SP = save;
    }
}

On doit bien faire attention à ne pas dépasser 0x8FFF comme adresse de SP :

Map sram.png

Voici une vidéo où l'on fait clignoter deux LED avec un temps différent :

LED allumées avec temps différents

Lecture/écriture sur le port série

void serial_send(unsigned char c)
{
P(SEM_SERIAL);
task_current->etat=(WAIT_SERIAL|STATE_SLEEP);
TIMER1_COMPA_vect();
UDR0 = c;
V(SEM_SERIAL);
}

char get_serial(void){
P(SEM_SERIAL);
task_current->etat=(WAIT_SERIAL|STATE_SLEEP);
TIMER1_COMPA_vect();
V(SEM_SERIAL);
return UDR0;
}


Afficheur 7 segments

Pour tester l'afficheur 7 segments, nous avons utilisé un programme d'envoi SPI. Nous n'avons toutefois pas encore compris comment afficher un caractère spécifique, nous avons réussi seulement pour l'instant à afficher une série de 0 :

Test de l'afficheur 7 segments, envoi de 0


Après avoir consulté les ressources SparkFun, nous avons pu écrire cette fonction, qui prend en compte le caractère partagé :

void aff7seg(void)
{
    P(SEM_SPI);
    spi_activer(SS_3);
    spi_echange(0x76);
    spi_desactiver(SS_3);
    V(SEM_SPI);
        while(1){
        P(SEM_SPI);
        spi_activer(SS_3);
        for(int i = 0; i<4 ; i++)
            spi_echange(caractere_partage);
         
        spi_desactiver(SS_3);
        _wait_ms(50);     
        V(SEM_SPI);
        }
}

Voici l'affichage de "test":


Matrice LED

Voici notre fonction pour envoyer un carcatère à la matrice :

void aff_matrix(void){

    char a;
    int index;
    while(1){
        P(SEM_SPI);
        index = selection(caractere_partage);
        spi_activer(SS_2);
        _wait_ms(1);
        for(int LED=0; LED<8; LED++){
            for(int j=0; j<8;j++){
                a = hex[index][LED] & (1<<j);
                spi_echange(a);
            }
        }
        spi_desactiver(SS_2);
        _wait_ms(500);
        V(SEM_SPI);
    }
}

Voici une vidéo du fonctionnement :

Nous avons écrit un tableau stockant les configurations de chaque chiffre hexadécimal :

char hex[16][8] = {
    {0x7e,0x42,0x42,0x42,0x42,0x42,0x7e,0x00}, //--> 0
    {0x08,0x18,0x28,0x08,0x08,0x08,0x3e,0x00}, //--> 1
    {0x18,0x24,0x04,0x08,0x10,0x20,0x7c,0x00}, //--> 2
    {0x18,0x24,0x04,0x18,0x04,0x04,0x38,0x00}, //--> 3
    {0x20,0x20,0x24,0x24,0x3e,0x04,0x04,0x00}, //--> 4
    {0x7e,0x40,0x40,0x7e,0x02,0x02,0x7e,0x00}, //--> 5
    {0x7e,0x40,0x40,0x7e,0x42,0x42,0x7e,0x00}, //--> 6
    {0x7f,0x02,0x04,0x08,0x10,0x20,0x40,0x00}, //--> 7
    {0x3c,0x42,0x42,0x3c,0x42,0x42,0x3c,0x00}, //--> 8
    {0x7e,0x42,0x42,0x7e,0x02,0x02,0x7e,0x00}, //--> 9
    {0x18,0x24,0x42,0x42,0x7e,0x42,0x42,0x42}, //--> A
    {0x7c,0x42,0x42,0x42,0x7c,0x42,0x42,0x7e}, //--> B
    {0x7e,0x40,0x40,0x40,0x40,0x40,0x40,0x7e}, //--> C
    {0x78,0x44,0x42,0x42,0x42,0x42,0x42,0x7c}, //--> D
    {0x7e,0x40,0x40,0x7e,0x40,0x40,0x40,0x7e}, //--> E
    {0x7e,0x40,0x40,0x7c,0x40,0x40,0x40,0x40}, //--> F
};

Nous avons aussi fait une fonction pour faire correspondre le caractère partagé au bon index du tableau précédent :

int selection(char select)// pour choisir l index dans le tableau pour la matrice
{
    int result = 0;
    if(select >= '0' && select <= '9')
        result = select - 48;
    else if(select >= 'a' && select <= 'f') 
        result = 10 + (select - 'a');
    else if(select >= 'A' && select <= 'F')
        result = 10 + (select - 'A');
    
    return result;
}

Sémaphores

Nous avons trouvé dans un ancien projet IMA (Malette arduino éducative) une implémentation d'ordonnanceur. Nous avons décidé de complétement changer notre scheduler et de prendre pour base l'ordonnaceur du projet IMA vu que nous avions du mal à ajouter les sémaphores... Nous avons alors modifié la fonction scheduler de façon à gérer l'attente (flags) directement dans l'ordonnanceur et de mesurer le temps écoulé.

void scheduler(void)
{
do{
  int i;
  
  long int elapsed=timer_elapsed();//temps écoulé

  TCNT1=0;

  for(i=0;tasks[i].fct!=NULL;i++)
    if(tasks[i].etat==(WAIT_TIME|STATE_SLEEP)){
      tasks[i].time -= elapsed;
      if(tasks[i].time<=0) tasks[i].etat=STATE_RUN;
      }
  //série UART
  for(i=0;tasks[i].fct!=NULL;i++)
    if(tasks[i].etat==(WAIT_SERIAL|STATE_SLEEP))
    if (UCSR0A & (1 << RXC0)) tasks[i].etat=STATE_RUN;

  //SPI
  for(i=0;tasks[i].fct!=NULL;i++)
    if(tasks[i].etat==(WAIT_SPI|STATE_SLEEP))
      if(SPSR & 1<<SPIF) tasks[i].etat=STATE_RUN;


  unsigned char first=1;
  while(1){
    if(task_current==NULL) task_current=tasks;
    else if(task_current->fct!=NULL) task_current++;
    if(task_current->fct==NULL){
      task_current=NULL;
      if(first){ first=0; continue; }
      else break;
      }
    if((task_current->etat & STATE_MASK)!=STATE_SLEEP) break;
    }
  } while(task_current==NULL);
}

Vient ensuite l'implémentation des sémaphores :

static int *get_pointer(int s){
int *p;
switch(s){
  case SEM_SERIAL: p=&serial; break;
  case SEM_SPI: p=&spi; break;
  default: p=NULL; break;
  }
return p;
}

// "on bloque"
void P(int s)
{
int *p=get_pointer(s);
if(p==NULL) return;
cli();
if(*p==1){
  task_current->etat=(WAIT_SEMAPHORE|STATE_SLEEP);
  task_current->time=s;
  TIMER1_COMPA_vect();
  }
sei();
}

// "on libère"
void V(int s)
{
int *p=get_pointer(s);
if(p==NULL) return;
cli(); 
*p=0;
task_t *t=(task_t *)tasks;
while(t->fct!=NULL){
  if(t->etat==(WAIT_SEMAPHORE|STATE_SLEEP) && t->time==s){ //réordonnancement
     t->etat=STATE_RUN;
     *p=1;
     break;
     }
  t++;
  }
sei();
}

Ordonnacement complet




Système de fichiers

Nous avons essayé d’implémenter (à la dernière minute) un système de fichiers sur le bouclier arduino suite à des problèmes dans notre carte mère. Les fonctions que nous avons utilisées sont inspirées du projet EC1 de l'année denière.

Cahier des charges

  • le système de fichiers doit résider dans la mémoire de 8 Mo, les accès à la mémoire se font par blocs de 256 octets ; - l'accès au système de fichiers se fait par des primitives système
  • le micro-système de fichiers ne comporte qu'un répertoire : le répertoire principal, ce dernier peut comporter au maximum 64 fichiers, un fichier est caractérisé par un nom de 16 caractères au maximum, sa taille et - ses blocs de données, un fichier est décrit par un maximum de 16 blocs de données;
  • le superbloc du système de fichiers comprend, en plus de la description du répertoire racine, une carte bit à bit des blocs de données libres, cette carte nécessite 16 blocs ;

A noter que la description du système ne peut pas se faire avec "une structure de données C" sinon la place en mémoire utilisée serait beaucoup trop grande.

Commandes

Nous avons utilisé les fonctions ReadData et WriteBlock déja fournis de base dans le code de test de la carte SD. On réserve une plage de blocs pour le superbloc et la bitmap en commencant l'écriture à 1040 : 64*16 (fichiers) + 16 (bitmap) = 1040.

LS
void list(void) {

unsigned char buff[MAX_FILENAME_LENGTH];

int fileCount = 0;

// Parcourir les blocs multiples de 16 à partir de 0 jusqu'à MAX_FILES_IN_DIRECTORY

for (int blockNum = 0; blockNum < MAX_FILES_IN_DIRECTORY; blockNum += 16) {

readData(&sd,blockNum, 0, MAX_FILENAME_LENGTH,buff);

// Vérifier si le nom de fichier est vide

if (buff[0] != 0) {

// Afficher le nom du fichier

send_Ustring(buff);

send_string("\r\n");

fileCount++;

}

}

if (fileCount == 0) {

send_string("Aucun fichier trouvé.\r\n");

}

}
CREATE
void create(char *filename){

size_t sizeFilename = strlen(filename);

unsigned char buff[MAX_FILENAME_LENGTH];

uint8_t IsPlace = 0;

if (sizeFilename > MAX_FILENAME_LENGTH) {

send_string("Impossible de créer le fichier, nom trop long\n");

return;

}

// Parcours des blocs réservés pour la description des fichiers (superbloc)

for (uint32_t blockNum = 0; blockNum < MAX_FILES_IN_DIRECTORY; blockNum += 16) {

readData(&sd,blockNum, 0, MAX_FILENAME_LENGTH,buff);

// Vérifier si le bloc est vide (pas de nom de fichier)

if (buff[0] == 0) {

// Écrire le nom du fichier dans l'emplacement vide du superbloc

if (sizeFilename < MAX_FILENAME_LENGTH) {

sizeFilename += 1; // Ajouter '\0' s'il y a de la place

}

writeBlock(&sd,blockNum,(const unsigned char *)filename,0);


placeFound = blockNum;

IsPlace = 1;

break; // Fichier créé, sortir de la boucle

}

}

if (IsPlace != 0) {

send_string("Le fichier a été créé avec succès.\r\n");

} else {

send_string("Plus de place dans le système de fichier pour créer ce fichier.\r\n");

}
}

Notre fonction LS "ne s'actualise pas" du premier cooup lorsque nous créons un fichier. Nous avons aussi écrit d'autres fonctions comme RM par exemple, mais elles ne fonctionnent pas comme désiré.


Identification cartes filles

Nous avons créé une structure de données nous permettant de pouvoir identifier les cartes filles pour savoir sur quels CS "envoyer la fonction" (correspond à la fonction DEVICES) :

void Identification(void)
{
    for(int i = 0; i< NB_SS;i++)
    {
        uint8_t type = 0;
        spi_activer(chips[i].SS);

        spi_echange(0x00);

        type = spi_echange(0x00);

        if ((type >> 4) == 0x0A) {

            switch(type & 0x0F)
            {
                case 0x01:
                    chips[i].device = KEYBOARD;
                    break;
                case 0x02:
                    chips[i].device = SCREEN;
                    break;
                case 0x04:
                    chips[i].device = NETWORK;
                    break;
                case 0x08:
                    chips[i].device = SOUND;
                    break;
            }
        }
        spi_desactiver(chips[i].SS);
    }
}

Carte FPGA / VHDL

Décodeur SPI

Sur l'atmega328p, lorsque CS est au niveau bas, le slave est sélectionné et la transmission a lieu. Le master envoie alors une clock SCK, grâce à laquelle le slave pourra récupérer les données envoyées sur MOSI :

Fonctionnement SPI ATMEGA328P avec CPHA = 0

Pour le décodeur, on supposera une configuration "classique" avec CPHA = 0 (récupération des données sur front montant), et DORD = 0 (l'envoi commence par le MSB). De plus, nous utilisons que MOSI vu que nous n'envoyons rien au master.

L'enjeu est de récupérer la trame de données qui est en série et de la rendre "en parallèle" pour une utilisation autre.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity SPIDecoder is
    Port ( SCK : in STD_LOGIC;
           MOSI : in STD_LOGIC;
           CS : in std_logic;
           result : out STD_LOGIC_VECTOR(7 downto 0));
end SPIDecoder;

architecture Behavioral of SPIDecoder is
   
signal cpt : integer range 0 to 8 := 0;
signal mot : STD_LOGIC_VECTOR(7 downto 0);
signal tmp : std_logic;
 signal result_temp : STD_LOGIC_VECTOR(7 downto 0);

begin
    process(SCK,CS)
    begin
    if CS = '1' then -- Lorsque CS est à 1 => reset cpt et du mot binaire
    mot <= (others => '0');
    cpt <=0;
    else
    if rising_edge(SCK) then -- front montant
      mot(7-cpt) <= MOSI; -- Récupération MOSI en commençant d'abord par le MSB
      
      if(cpt = 7) then
        cpt <= 0;
        result_temp <= mot; 
        else
        cpt <= cpt+1;
     end if; 
     
    end if;
    end if;
      result <=result_temp;
    end process;
end Behavioral;

Nous avons envoyé sur MOSI 0xF0 en série:

Testbench

Contrôleur VGA

On suppose que l'écran a comme résolution 1024x768 et comme fréquence 65Hz. Pour que la communication avec l'écran marche, ces conditions doivent être respectées :

Trame pour VGA 65 Hz


En VHDL, l'idée sera de parcourir grâce à un compteur les pixels horizontaux/verticaux tout en respectant les périodes données ci-dessus.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity vga_display is
    Port ( HS : out STD_LOGIC;
           VS : out STD_LOGIC;
           clk100Mhz : in STD_LOGIC;
           R : out STD_LOGIC_VECTOR (3 downto 0);
           G : out STD_LOGIC_VECTOR (3 downto 0);
           B : out STD_LOGIC_VECTOR (3 downto 0));
end vga_display;


architecture Behavioral of vga_display is
signal compteur_v : integer range 0 to 805; -- 0 a 768 a 1 -- entre 771 et 777 a 0
signal compteur_h : integer range 0 to 1343; -- 0 a 1023 il est à 1 -- entre 1047 et 1183 à 0
signal HSS : std_logic;
signal VSS : std_logic;

signal compteur : integer range 0 to 1000000 := 0;
signal clk_65Hz : std_logic ;


component clk_wiz_0
port
 (-- Clock in ports
  -- Clock out ports
  clk_out1          : out    std_logic;
  clk_in1           : in     std_logic
 );
end component;

begin

    your_instance_name : clk_wiz_0 -- Création d'une horloge à 65 Hz
       port map ( 
      -- Clock out ports  
       clk_out1 => clk_65Hz,
       -- Clock in ports
       clk_in1 => clk100MHz
     );
    
    process(clk_65Hz)
    begin
    
     if clk_65Hz'event and clk_65Hz='1'then
                if compteur_v < 771 then
                    if compteur_h < 1048 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                     elsif compteur_h >= 1048 and compteur_h < 1183 then
                        HSS <= '0';
                        compteur_h <= compteur_h + 1;
                     elsif compteur_h >= 1183 and compteur_h < 1343 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                     else
                        HSS <= '1';
                        compteur_h <= 0;
                        compteur_v<=compteur_v+1;
                  end if;              
                    VSS <= '1';   
                elsif compteur_v >= 771 and compteur_v < 776  then
                    if compteur_h < 1048 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                     elsif compteur_h >= 1048 and compteur_h < 1183 then
                        HSS <= '0';
                        compteur_h <= compteur_h + 1;
                     elsif compteur_h >= 1183 and compteur_h < 1343 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                     else
                        HSS <= '1';
                        compteur_h <= 0;
                        compteur_v<=compteur_v+1;
                  end if;     
                    VSS <= '0'; 
                  elsif compteur_v >= 776 and compteur_v < 805 then
                    if compteur_h < 1048 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                     elsif compteur_h >= 1048 and compteur_h < 1183 then
                        HSS <= '0';
                        compteur_h <= compteur_h + 1;
                     elsif compteur_h >= 1183 and compteur_h < 1343 then
                        HSS <= '1';
                        compteur_h <= compteur_h + 1;
                     else
                        HSS <= '1';
                        compteur_h <= 0;
                        compteur_v<=compteur_v+1;
                  end if;               
                   else
                        compteur_v<=0;
                        VSS <= '1'; 
                   end if;
                   
                   if HSS = '1' and  VSS = '1' then
                    R <= "1111";
                    G <= "0000";
                    B <= "0000";
                    end if;
    
    end if;
    end process;
    
    HS <= HSS;
    VS <= VSS;
    

end Behavioral;

Voici ce qu'on obtient lors du testbench :

Hsync :

Tpw hs.png
Ts hs.png

Vsync :

Tpw vs.png
Ts vs.png

Il faudrait ensuite implémenter le composant "décodeurSPI" pour pouvoir commander l'écran depuis un atmega par exemple.

Carte électronique numérique

Type carte choisi

Carte mère

Caractéristiques de la carte mère

ReX : a mettre comme sous-section de la section "Carte électronique numérique"*

ReX : utilisez la syntaxe mediawiki pour les items.

  • Alimentation : USB
  • Tension alimentation : 5V avec régulateur
  • Programmation par AVR ISP

Schématique/Routage

Voici la schématique de la carte mère ainsi que du programmateur :

Schématique de la carte mère



Voici le routage de la carte:

Routage

Voici la vue 3D de la carte:

Visualisation 3D

PCB soudé

Carte soudée de dos
Carte soudée de face





















Problèmes rencontrés

Lors de l'élaboration de notre KiCad pour la carte, nous avons décidé d'utiliser 5V pour l'alimentation de la carte. Sauf que cela nous a porté préjudice... En prenant 5V, nous n'avions pas accès directement à l'UART, ce qui rend l'écriture du code très difficile vu qu'on a aucune idée du fonctionnement. Il est impératif d'avoir un aperçu du résultat pour coder le système de fichiers. C'est pourquoi nous avons tenté tant bien que mal à essayer de "sortir" les broches TX/RX mais sans succès. Nous avons aussi tenté d'utiliser le SPI au lieu de l'UART pour communiquer avec la carte mais cela s'est avéré un peu trop fastidieux.

Nous avons alors très tardivement codé sur la carte SD du bouclier.

Rendus

GIT

https://archives.plil.fr/yelqasta/pico_ybenmbar_yelqasta_se4.git