« SE4Binome2023-9 » : 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 273 : Ligne 273 :
=== Décodeur SPI ===
=== Décodeur SPI ===


L'atmega328p  
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]]
[[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">
<syntaxhighlight lang="vhdl" line="1">
Ligne 301 : Ligne 305 :
     process(SCK,CS)
     process(SCK,CS)
     begin
     begin
     if CS = '1' then
     if CS = '1' then -- Lorsque CS est à 1 => reset cpt et du mot binaire
     mot <= (others => '0');
     mot <= (others => '0');
     cpt <=0;
     cpt <=0;
     else
     else
     if SCK'event and SCK='1' then
     if rising_edge(SCK) then -- front montant
       mot(7-cpt) <= MOSI;
       mot(7-cpt) <= MOSI; -- Récupération MOSI en commençant d'abord par le MSB
        
        
       if(cpt = 7) then
       if(cpt = 7) then
Ligne 317 : Ligne 321 :
     end if;
     end if;
     end if;
     end if;
    result <=result_temp;
      result <=result_temp;
     end process;
     end process;
end Behavioral;
end Behavioral;

Version du 18 janvier 2024 à 15:22


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;
    }
}

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;
}

Attente SPI/UART

Nous avons décidé de changer notre 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;
      }

  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;


  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);
}

Ordonnacement complet




Système de fichiers


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;
Testbench

Contrôleur VGA

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
       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;

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





















Rendus

GIT

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