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

De projets-se.plil.fr
Aller à la navigation Aller à la recherche
 
(76 versions intermédiaires par 3 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
== Ordonnanceur / Système d'exploitation ==
== <big>Ordonnanceur / Système d'exploitation</big> ==


=== <u>Réalisation du Shield</u> ===
=== <u><big>Réalisation du Shield</big></u> ===
'''Séance 13/09 :''' Parmi les activités disponibles, nous avons soudé le régulateur de tension, le lecteur SD, les résistances liées aux LEDs. Nous avons réalisé 2 câbles pour les cartes filles et un adaptateur.
<small>'''Séance 13/09 :''' Parmi les activités disponibles, nous avons soudé le régulateur de tension, le lecteur SD, les résistances liées aux LEDs. Nous avons réalisé 2 câbles pour les cartes filles et un adaptateur.</small>
[[Fichier:Photo de la carte Shield après soudure.jpg|vignette|carte Shield après première soudure|gauche|333x333px]]
[[Fichier:Photo de la carte Shield après soudure.jpg|vignette|<big>carte Shield après première soudure</big>|gauche|333x333px]]
[[Fichier:2 câbles .jpg|vignette|câbles|néant|200x200px]]
[[Fichier:2 câbles .jpg|vignette|<big>câbles</big>|néant|200x200px]]
[[Fichier:Adaptateur.jpg|vignette|néant|200x200px]]
[[Fichier:Adaptateur.jpg|vignette|néant|200x200px]]
<br>
<big><br></big>
'''Séance 19/09 :''' Durant cette séance nous avons soudé les autres résistances et les connecteurs. Et nous avons testé le bon fonctionnement de la carte Shield.  
<small>'''Séance 19/09 :''' Durant cette séance nous avons soudé les autres résistances et les connecteurs. Et nous avons testé le bon fonctionnement de la carte Shield.</small>


Voici la carte Shield soudé :  
<small>Voici la carte Shield soudé :</small>
<br>
<big><br></big>
[[Fichier:Photo de carte soudé.jpg|vignette|Photo de la carte Shield soudé|néant]]                       
[[Fichier:Photo de carte soudé.jpg|vignette|<big>Photo de la carte Shield soudé</big>|néant]]                       


Nous avons soudé cette carte :  
<small>Nous avons soudé cette carte :</small>


[[Fichier:20230919.jpg|vignette|néant|200x200px]]Pour le test nous avons utilisé le code qui est disponible sur le git. Voici ci-dessous une vidéo montrant le clignotement des LEDs.
[[Fichier:20230919.jpg|vignette|néant|200x200px]]<small>Pour le test nous avons utilisé le code qui est disponible sur le git. Voici ci-dessous une vidéo montrant le clignotement des LEDs.</small>
[[Fichier:Vidéo.mp4|centré|vignette]]
[[Fichier:Vidéo.mp4|centré|vignette]]
=== <u><big>Clignotement des LEDs avec ordonnanceur</big></u> ===
Au cours de la fabrication de notre carte, nous avons commencé la réalisation de l'ordonnanceur. 
Pour réaliser l'ordonnanceur, nous avons besoin d'une fonction de gestion d'interruption ISR qui sera déclenché par l'interruption.
<syntaxhighlight lang="c">
ISR(TIMER1_COMPA_vect, ISR_NAKED) {
    SAVE_REGISTERS();
 
    taches[current].pointer_pile = SP;
    // Appel à l'ordonnanceur
    scheduler();
 
    SP = taches[current].pointer_pile;
    RESTORE_REGISTERS();
    asm volatile ( "reti" );
}
</syntaxhighlight>
Le principe c'est de créer deux tâches pour faire clignoter deux LEDs du bouclier avec des périodes différentes. La première tache allume et éteint une LED toutes les 200ms, la deuxième tache réalise la même opération sur une LED différente toutes les 1000ms.
<syntaxhighlight lang="c">
// Tâche 1
void task_LED_1() {
    DDRD |= (1 << LED4_PIN);
    PORTD &= ~(1 << LED4_PIN);
    while (1) {
        PORTD ^= (1 << LED4_PIN);
        _delay_ms(200); // Fréquence 200 ms
    }
}
 
// Tâche 2
void task_LED_2() {
    DDRC |= (1 << LED1_PIN);
    PORTC &= ~(1 << LED1_PIN);
    while (1) {
        PORTC ^= (1 << LED1_PIN);
        _delay_ms(1000); // Fréquence 1000 ms
    }
}
</syntaxhighlight>
Et voici la fonction permettant d'initialiser le contexte de chaque tâche :
<syntaxhighlight lang="c">
void init_task(int id)
{
    uint16_t save_sp = SP;
    SP= taches[id].pointer_pile;
    uint16_t adresse=(uint16_t)taches[id].task_func;
    asm volatile("push %0" : : "r" (adresse & 0x00ff) );
    asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
    SAVE_REGISTERS();
    taches[id].pointer_pile = SP;
    SP = save_sp;
}
</syntaxhighlight>
[[Fichier:VID .mp4|centré|vignette]]
=== <u><big>Afficheur 7 segments</big></u> ===
Pour l'afficheur 7 segments, nous l'avons dans un premier temps fait sans ordonnancement pour connaitre sa fonctionnalité. Pour le tester on a juste affiché "1 2 3 4" sur l'afficheur comme indiqué sur l'image ci-dessous :
<syntaxhighlight lang="c">
void afficheur(void)
{
    spi_activer();
    spi_exch(0x76);
    spi_desactiver();
    char seg[4] = "1234";
    while(1){
        spi_activer();
        for(uint8_t i = 0; i < 4 ; i++)
        {
            spi_exch(seg[i]);
        }
        _delay_ms(1000);
        spi_desactiver();
    }
}
</syntaxhighlight>
[[Fichier:Afficheur.jpg|néant|vignette]]
Pour l'ordonnancement, nous l'avons testé sur l'afficheur 7 segments avec lecture/écriture sur le port série :
<syntaxhighlight lang="c">
// fonction pour afficheur 7 segments
void afficheur(void)
{
    pause_smp(SEMAPHORE_SPI);
    spi_activer(SS_3);
    spi_exch(0x76);
    spi_desactiver(SS_3);
    while(1){
        for(int i = 0; i < 4 ; i++)
        {
            spi_activer(SS_3);
            spi_exch(caractere);
        }
        spi_desactiver(SS_3);
        _wait_ms(5);
        liber_smp(SEMAPHORE_SPI);
    }
}
</syntaxhighlight>
[[Fichier:AfficheurOrdonnancer.mp4|néant|vignette|Ordonnancement,écriture port série]]
Les fonctions <code>spi_activer</code> et <code>spi_desactiver</code> sont utilisées pour activer et désactiver les différents esclaves sur le bus SPI en manipulant les broches SS correspondantes. Cela permet de sélectionner le périphérique avec lequel le microcontrôleur va communiquer via le bus SPI.
<syntaxhighlight lang="c">
void spi_activer(int chip_s) {
    if (chip_s == SS_2 || chip_s == SS_3) {
        PORTD &= ~(1 << chip_s);
    } else if (chip_s == SS_5) {
        PORTB &= ~(1 << chip_s);
    }
}
void spi_desactiver(int chip_s) {
    if (chip_s == SS_2 || chip_s == SS_3) {
        PORTD |= (1 << chip_s);
    } else if (chip_s == SS_5) {
        PORTB |= (1 << chip_s);
    }
}
</syntaxhighlight>
=== <u><big>La matrice Led</big></u> ===
Nous avons essayé l'affichage avec la matrice Led par le protocole SPI. On a inclut les représentations matricielles pour les chiffres de 0 à 9 et les lettres de l'alphabet de A à Z.
Nous avons écrit une fonction pour l'envoi de données à la matrice LED via le protocole SPI. Elle active le périphérique SPI, envoie la ligne et la valeur via SPI, puis désactive le périphérique.
<syntaxhighlight lang="c">
void matrice_Byte(uint8_t row, uint8_t value) {
    // Activer le périphérique SPI correspondant à la matrice
    spi_activer(SS_2);
    // Envoyer les données à la matrice via SPI
    spi_exch(row);
    spi_exch(value);
    // Désactiver le périphérique SPI
    spi_desactiver(SS_2);
}
</syntaxhighlight>
Pour l'affichage de caractère et le test :
<syntaxhighlight lang="c">
//fonction pour la matrice
void display_on_matrix(void) {
    char l = caractere;
    int index = 0;
    if (l >= '0' && l <= '9')
        index = l - '0';
    else if (l >= 'a' && l <= 'z')
        index = 10 + (l - 'a');
    else if (l >= 'A' && l <= 'Z')
        index = 10 + (l - 'A');
    while(1){
        pause_smp(SEMAPHORE_SPI);
        spi_activer(SS_2);
        spi_exch(0x76);
        _wait_ms(1);
        spi_desactiver(SS_2);
        if (index >= 0 && index <= 9) {
            for (uint8_t i = 0; i < 8; i++) {
                matrice_Byte(i + 1, numbers[index][i]);
            }
        } else if (index >= 10 && index <= 35) {
            for (int i = 0; i < 8; i++) {
                matrice_Byte(i + 1, letters[index - 10][i]);
            }
        }
        spi_desactiver(SS_2);
        _wait_ms(500);
        liber_smp(SEMAPHORE_SPI);
    }
}
</syntaxhighlight>Malheureusement, malgré nos efforts, la fonction <code>display_on_matrix</code> n'a pas réussi à produire l'affichage voulu sur la matrice LED. Malgré la mise en œuvre du protocole SPI et des déterminations d'index pour les caractères, l'effet clignotant souhaité n'a pas été observé.Faute de temps, nous avons décidé de recentrer nos efforts sur la carte LCD.


== Carte FPGA / VHDL ==
== Carte FPGA / VHDL ==
===Controleur VGA===
Un contrôleur VGA sert à générer des signaux de synchronisation horizontale (HS) et verticale (VS), ainsi que des signaux de couleur (r, g, b) pour un écran VGA. L’horloge d'entrée est à 100 MHz et génère une horloge de sortie à 65 MHz généré par le wizard (clk_wiz_0).
Le code ci-dessous permet de générer des signaux VGA pour afficher des images colorées sur un écran VGA et un Basys3
* Génération des signaux HS et VS pour indiquer les périodes de synchronisation horizontale et verticale
<syntaxhighlight lang="vhdl" line>
process1 : process(clock_65Mhz)
begin
       if clock_65Mhz'event and clock_65Mhz = '1' then
            if compteur_vert < 774 then
                if compteur_hori < 1048 then
                    HS <= '1';
                    compteur_hori <= compteur_hori + 1;
                 elsif compteur_hori >= 1048 and compteur_hori <1183 then
                    HS <= '0';
                    compteur_hori <= compteur_hori + 1;
                 elsif compteur_hori >= 1183 and compteur_hori < 1343 then
                    HS <= '1';
                    compteur_hori <= compteur_hori + 1;
                 else
                 HS <= '1';
                 compteur_hori <= 0;
                 compteur_vert <= compteur_vert + 1;
                  end if;
             end if;
             if compteur_hori < 1048 then
                if compteur_vert < 774 then
                    VS <= '1';
                    compteur_vert <= compteur_vert + 1;
                elsif compteur_vert <= 774 and compteur_vert < 780 then
                    VS <= '0';
                    compteur_vert <= compteur_vert + 1;
                elsif compteur_vert <=780 and compteur_vert < 805 then
                    VS <='1';
                    compteur_vert <= compteur_vert + 1;
                else
                    VS<= '1';
                    compteur_vert <= 0;
                    compteur_hori <= compteur_hori + 1;
               end if;
            end if;
</syntaxhighlight>
* Génération des Signaux de Couleur (r, g, b) en utilisant les boutons b1, b2 et b3
<syntaxhighlight lang="vhdl" line>
if b1 = '1' then
    --couleur rouge
                if compteur_hori > 300 and compteur_hori < 600 then
                    if compteur_vert > 300 and compteur_vert < 600 then
                        r <= "1111";
                        g <= "0000";
                        b <= "0000";
                    end if;
                end if;
             end if;
--couleur verte
             if b2 = '1' then
                if compteur_hori > 360 and compteur_hori < 1080 then
                    if compteur_vert > 201 and compteur_vert < 604 then
                        r <= "0000";
                        g <= "1111";
                        b <= "0000";
                    end if;
                end if;
             end if;
--couleur bleue
             if b3 = '1' then
                if compteur_hori > 360 and compteur_hori < 1080 then
                    if compteur_vert > 201 and compteur_vert < 604 then
                        r <= "0000";
                        g <= "0000";
                        b <= "1111";
                    end if;
                end if;
             end if;
           end if;
</syntaxhighlight>
===Controleur bus SPI===
Les dispositifs SPI communiquent en utilisant une architecture maître-esclave avec un seul maître.Un ou plusieurs dispositifs esclaves SPI sont pris en charge par la sélection d'esclaves individuels <code>'''SS'''</code>
Avant de démarrer une communication avec un esclave particulier, le maître active la ligne <code>'''SS'''</code> associée à cet esclave en la faisant passer de l'état haut à l'état bas.
Une fois que l'esclave est sélectionné, le maître peut commencer à envoyer des données sur la ligne '''MOSI''' et recevoir des données sur la ligne '''MISO'''.
[[Fichier:SPI un maitre-esclave.png|gauche|vignette|546x546px|Un maitre un esclave]]
[[Fichier:SPI plusieurs esclaves.png|vignette|néant]]
Nous devons illustrer une implémentation d'un émetteur SPI synchrone. Dans notre contexte, on gère la transmission séquentielle d'octets sur la ligne '''MOSI''', tout en signalant la fin de chaque transmission par le signal <code>'''complete'''</code>. Lorsqu'il est déclenché par un front montant d'horloge, le code gère différents états, tels que l'état <code>'''IDLE'''</code> où le système est en attente, et l'état <code>'''TRANSFER'''</code> où les données sont envoyées bit par bit.
Voici le code pour tester chaque état
* Etat IDLE
Dans l'état IDLE, le système est en attente d'une nouvelle transmission SPI, et la transition vers l'état de transfert est conditionnée par la détection d'un signal de démarrage. Lorsque ce signal est activé, l'état passe à TRANSFER, initialisant la communication en activant la sélection d'esclave et préparant la transmission des données.
<syntaxhighlight lang="vhdl" line>
when IDLE =>
    if start = '1' then
        -- Début d'une nouvelle transmission
        state <= TRANSFER;
        ss <= '0';  -- Activer la sélection d'esclave
        bit_counter <= 7;  -- Initialiser le compteur de bits
        mosi <= data_buffer(7);
    end if;
</syntaxhighlight>
* Etat TRANSFER
L'état TRANSFER gère la transmission séquentielle des bits sur la ligne MOSI.
<syntaxhighlight lang="vhdl" line>
when TRANSFER =>
    if bit_counter > 0 then
    -- Transfert des bits
        bit_counter <= bit_counter - 1;
        mosi <= data_buffer(bit_counter);
    else
        -- Fin de la transmission
        state <= DONE;
    end if;
</syntaxhighlight>
* Etat DONE
Dans l'état DONE, après la fin de la transmission SPI, le système désactive la sélection d'esclave et peut lire les données reçues sur la ligne MISO
<syntaxhighlight lang="vhdl" line>
when DONE =>
-- Désactiver la sélection d'esclave et générer une impulsion d'horloge
      ss <= '1';
      --sclk <= not sclk;
      complete <= '1';
      -- Lecture des données reçues sur la ligne MISO
      data_in <= data_in (6 downto 0) & miso;
</syntaxhighlight>


== Carte électronique numérique ==
== Carte électronique numérique ==
Ligne 50 : Ligne 376 :
'''Séance 24/10''' :  
'''Séance 24/10''' :  


Le routage terminé, le prof a vérifier le routage et nous avons envoyé la carte afinde l'imprimer.
Le routage terminé, le prof a vérifié le routage final et nous avons envoyé la carte afin de l'imprimer.


Durant cette même séance nous avons commencer la programmation de l'ordonnancement.
Durant cette même séance nous avons commencer la programmation de l'ordonnancement.
[[Fichier:Carte fille.png|néant|vignette|routage fini|500x500px]]Voici une vue 3D de ce qui va se trouver au dessus de notre carte. Notre écran se trouvera donc en dessous.[[Fichier:3Dviewer.png|néant|vignette|500x500px|vue 3D]]
[[Fichier:Carte fille.png|néant|vignette|routage fini|500x500px]]Voici une vue 3D de ce qui va se trouver au dessus de notre carte. Notre écran se trouvera donc en dessous.[[Fichier:3Dviewer.png|néant|vignette|500x500px|vue 3D]]


=== <u>Présentation de la carte fille LCD</u>  ===
'''Séance 14/11''' :
Nous avons reçu la carte, nous avons commencé la soudure des composants nécessaires pour un premier test de la carte.
* Le microprossesseur
* une led
* L'horloge
[[Fichier:Crte électronique.jpg|vignette|300x300px|gauche]]
[[Fichier:Crte soudée.jpg|vignette|centré]]Pour vérifier le bon fonctionnement de la carte, nous avons mis une led afin de savoir la fonctionnalité de la carte.
Pour cela, on a testé avec le code exemple présent Arduino IDE. 
<syntaxhighlight lang="c">
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(7, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
  digitalWrite(7, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(1000);                      // wait for a second
  digitalWrite(7, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                      // wait for a second
}
</syntaxhighlight>
[[Fichier:Video 2023-11-28 at 21.38.57.mp4|vignette|Test de la led|néant]]
Nous avons soudé le reste des composants ainsi que l'écran LCD.
* Le potentiomètre
* le bouton reset
* les connecteurs
* l'écran LCD
* Les capacités
Voici tous la présentation de tous les composants de notre carte fille :[[Fichier:Imagecarte.jpg|vignette|400x400px|carte soudée|néant]]
=== <u>Test de l'ecran avec Aduino IDE</u> ===
[[Fichier:Testlcd.jpg|vignette|467x467px|test lcd]]
Nous avons testé le lcd avec le code qui est présent sur Arduino IDE.
Voici le code utilisé:
<syntaxhighlight lang="c">
// include the library code:
#include <LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = A0, en = A2, d4 = 4, d5 = 5, d6 = 9, d7 = 10;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
  pinMode(A1,OUTPUT);
  digitalWrite(A1,LOW);
  delay(1000);
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("hello, world!");
  pinMode(7,OUTPUT);
  digitalWrite(7,HIGH);
}
void loop() {
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(millis() / 1000);
}
</syntaxhighlight>
=== <u>Programmation de la carte fille LCD</u> ===
Les broches nécessaire à la programmation de l'écran LCD :
* RS (Register Select)
* EN (Enable)
* R/W (Read/Write)
* DB4 à DB7
==== Ecriture sur la carte LCD ====
La fonction <code>'''lcdWrite4bits'''</code> est conçue pour écrire quatre bits de données sur l’écran LCD en utilisant une interface 4 bits.
<syntaxhighlight lang="c">
void lcdWrite4bits(uint8_t data, uint8_t rs){
    if(rs)
        LCD_RS_ON();
    else
        LCD_RS_OFF();
    LCD_RW_OFF();
    LCD_EN_ON();
    _delay_us(1);
    if(data & 1)
        LCD_DB4_ON();
    else
        LCD_DB4_OFF();
    if(data & 2)
        LCD_DB5_ON();
    else
        LCD_DB5_OFF();
    if(data & 4)
        LCD_DB6_ON();
    else
        LCD_DB6_OFF();
    if(data & 8)
        LCD_DB7_ON();
    else
        LCD_DB7_OFF();
    LCD_EN_OFF();
    _delay_us(1);
}
</syntaxhighlight>
- <code>'''rs'''</code> indique si les données à écrire sont des données : 
* d'instruction '''« 0 »'''
* des données à afficher '''« 1 »'''
- Les blocs de <code>if</code> testent chaque bit dans <code>data</code>. Ils activent ou désactivent les lignes de données (DB4 à DB7).
==== Gestion des caractères de contrôle et initialisation LCD ====
Pour initialiser l'écran LCD, nous avons écris la fonction suivante
<syntaxhighlight lang="c">
void lcdInit(){
    LCD_RS_INIT();
    LCD_EN_INIT();
    LCD_RW_INIT();
    LCD_DB4_INIT();
    LCD_DB5_INIT();
    LCD_DB6_INIT();
    LCD_DB7_INIT();
    _delay_ms(1);
    lcdWrite8bits(0x28,0); //4bits, 2 lignes
    lcdWrite8bits(0x08,0); //LCD off
    lcdWrite8bits(0x01,0); //Clear LCD
    _delay_ms(2);
    lcdWrite8bits(0x06,0); //incrementation
    lcdWrite8bits(0x0c,0);
}
</syntaxhighlight>
* <code>'''lcdWrite8bits(0x0c, 0)'''</code> : Allume l'écran LCD sans afficher le curseur.
La fonction '''lcdWrite8bits''' est conçue pour écrire une valeur de 8 bits de data sur l'écran LCD.
Pour la gestion du retour à la ligne et le retour chariot il faut vérifier si le caractère data est égal à un retour à la ligne <code>'''\n'''</code> ou un retour chariot <code>'''\r'''</code>.
<syntaxhighlight lang="c">
if ((data == '\n') || (data == '\r')) {
        pos.current_x = 0;
        pos.current_y = (pos.current_y + 1) % NB_LIGNE;
        lcdMoveCursor(pos.current_y, 0);
        return;
    }
</syntaxhighlight>
La mise à jour du curseur dépend de la valeur de '''data''' et si elle vaut l'un des caractère de contrôle, le curseur se mettra à jour sur la ligne suivante grâce à la fonction <code>'''lcdMoveCursor(pos.current_y, 0)'''</code>
[[Fichier:Retour_chariot_et_à_la_ligne.jpg|alt=Gestion /n et /r|centré|246x246px]]
Pour le retour à la ligne automatique, faut vérifier la position de x, pour cela on a ajouté la condition suivante :
<syntaxhighlight lang="c">
if (pos.current_x == NB_COL) {
        pos.current_x = 0;
        pos.current_y = (pos.current_y + 1) % NB_LIGNE;
        lcdMoveCursor(pos.current_y, 0);}
</syntaxhighlight>
Pour déplacer le curseur sur l'écran LCD :
<syntaxhighlight lang="c">
void lcdMoveCursor(uint8_t row, uint8_t col) {
    if (row == 0) {
        // Pour la première ligne
        lcdWrite8bits(0x80 + col, 0); // Adresse de début pour la première ligne + colonne
    } else if (row == 1) {
        // Pour la deuxième ligne
        lcdWrite8bits(0xC0 + col, 0); // Adresse de début pour la deuxième ligne + colonne
    }
}
</syntaxhighlight>
Pour effacer le contenu affiché sur un écran LCD. on a ajouté la fonction lcdClear qui envoie une commande à l'écran LCD pour effacer son contenu, puis introduit un délai de 2 millisecondes pour permettre à l'écran de s'effacer correctement avant de poursuivre l'exécution du programme.
<syntaxhighlight lang="c">
void lcdClear() {
    lcdWrite8bits(0x01, 0);
    _delay_ms(2);
}
</syntaxhighlight>
==== Affichage des caractères ====
Pour afficher chaque caractère on a écris la fonction <code>'''lcdPutc'''</code>
<syntaxhighlight lang="c">
void lcdPutc(char c){
    busy = 1;
    lcdWrite8bits(c,1);
    busy = 0;
}
</syntaxhighlight>
<code>'''busy'''</code> à '''1''' est utilisé pour indiquer l'état de l'écran LCD s'il est en train de traiter une opération. 
Voici la vidéo montrant le bon fonctionnement de notre carte :
[[Fichier:Affichage.mp4|néant|vignette|Vidéo]]
le code source complet est dans le git.
=== <u>LCD avec SPI</u> ===
Lors de la programmation de la carte fille, nous avons constaté qu'il y'a une erreur sur la carte: le pin CS de l'Atmega est liée au PD6 alors qu'il doit être connecté au PB2. Donc on a du interchanger CS et B7 sinon la communication SPI ne sera pas fonctionnelle.
Nous avons donc procéder avec monsieur Redon la modification des pins.
[[Fichier:Piste à découper.jpg|néant|vignette]]
Nous avons couper la piste du pin 14 et le relier avec le "via" connecté à CS avec un fil. Puis relier le pin 10 avec B7 de l'écran LCD. 
[[Fichier:Carte resoudée.jpg|néant|vignette|Carte avec inter-changement des pins B7 et CS]]Malheureusement on a pris beaucoup de temps pour constater l'erreur, donc on a perdu du temps en pensant que l'erreur était dans le code.
==== Programmation du microcontrôleur de la carte LCD ====
Pour traiter les commandes reçues via l'interface SPI, on a ajouté la fonction commandes() :
<syntaxhighlight lang="c">
void commandes(void)
{
    char cmd = SPDR; //spi_wait_cmd();
    char data = spi_wait_cmd();
    char tmp;
    if(cmd == 0x00)
    {
        lcdPutc('o'); //test
        lcdPutc('k');
        if(data == INFO)
        {
            tmp = data;
            if(tmp != 0xff)
            {
                lcdPutc(data);
            }
            else if(tmp==0x00)
            {
                SPDR = 0xA2; //Pour Affichage,  Envoi de la donnée 0xA2 via SPI si la donnée reçue est égale à 0x00
            }
        }
    }
    if(cmd == 0xff)
    {
        if(data == 0xff)
        {
            SPDR = 0x55; // Envoi de la donnée 0x55 via SPI si la commande est 0xff et la donnée reçue est 0xff
        }
    }
}
</syntaxhighlight>
Voici le main pour tester la communication entre le maitre (arduino) et notre écran LCD :
<syntaxhighlight lang="c">
int main() {
    spi_init();
    lcdInit();
    commandes();
    sei();
    while (1) {
    }
    return 0;
}
</syntaxhighlight>
Pour gérer la réception des données via le port SPI et pour configurer l'écran comme Esclave :
<syntaxhighlight lang="c">
void spi_init(void) {
    // Configuration des broches
    SPI_DDR = (1 << SPI_MISO);      // MISO en sortie
    SS_DDR &= ~((1 << SPI_SCK) | (1 << SPI_SS));
    SS_DDR &= ~(1 << SPI_MOSI);
    // Configuration du registre de contrôle SPI
    SPCR = (1 << SPE);
}
uint8_t spi_wait_cmd(void) // Communication sur le bus SPI
{
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
}
</syntaxhighlight>
==== Programmation du maitre ====
Pour la configuration de la carte maitre, on a repris le code spi qu'on a utilisé pour l'ordonnancement avec quelque modification pour utiliser un seul connecteur HE10:
<syntaxhighlight lang="c">
void spi_init(void)
{
    // Configurer la broche de sortie pour MOSI
    SPI_DDR |= (1 << SPI_MOSI);
    // Configurer la broche de sortie pour SCK
    SPI_DDR |= (1 << SPI_SCK);
    // Configurer la broche d'entrée pour MISO
    SPI_DDR &= ~(1 << SPI_MISO);
      DDRD |= (1<<SS_3);
      PORTD |= (1<<SS_3);
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}
uint8_t spi_exch(uint8_t output) // Communication sur le bus SPI
{
    SPDR = output;
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
}
</syntaxhighlight>
Voici le main qu'on a fait pour tester la communication SPI entre le maitre et esclave:
<syntaxhighlight lang="c">
int main() {
    spi_init();
    spi_desactiver(SS_3);
DDRD |= (1<<4);
    while(1){
        spi_activer(SS_3);
        spi_exch(0);
        spi_exch(1);//clavier
        spi_exch(0xff);
        spi_desactiver(SS_3);
    }
    return 0;
}
</syntaxhighlight>
Voici le résultat du test qui permet de tester la communication sur le port SPI:
[[Fichier:Test spi.jpg|néant|vignette]]'''la primitive système :'''
Cette fonction permet une transmission SPI constituée de l'octet de commande 0x01, et de communiquer les chaînes de caractères à envoyer.
<syntaxhighlight lang="c">
void envoyer_commande_ecran(uint8_t ligne_selection, const char* caracteres) {
    // Activer la ligne de sélection SPI (SS) correspondante à l'écran
    spi_activer(ligne_selection);
    // Envoyer l'octet de commande
    spi_exch(0x01);
    // Envoyer les octets correspondant aux caractères
    size_t longueur_chaine = strlen(caracteres);
    for (size_t i = 0; i < longueur_chaine; ++i) {
        spi_exch(caracteres[i]);
    }
    // Désactiver la ligne de sélection SPI (SS)
    spi_desactiver(ligne_selection);
}
</syntaxhighlight>
== Conclusion ==
Durant le projet, comme nous l’avons déjà cité plus haut, nous avons ressoudé la carte LCD à cause d’un problème d’inter-changement de pins. Et comme c’était sensible, la soudure s’est enlevée pendant les vacances et nous n’avons pas pu travailler correctement avec la carte car nous avons perdu beaucoup de temps pour pouvoir la ressouder.
Pour la conception de la carte électronique, de la conception du schématique et le routage, le temps était bien géré jusqu'à la soudure des composants.
Pour la programmation :
* Pour la partie ordonnanceur nous avons réussi à faire ordonnancer la tâche d'écriture sur l'afficheur 7 segments et la lecture avec le port série. En revanche nous n’avons pas eu le temps de programmer la matrice led correctement malgré notre tentative parce que nous avons préféré nous concentrer sur la programmation de notre carte fille LCD Cette partie était une partie compliquée à programmer mais nous en avons beaucoup appris.
* Pour la carte fille LCD : c’est une partie que nous avons bien découverte et nous avons bien aimé la programmation pour afficher des caractères sur l’écran. Cette partie nous a aidé à renforcer nos connaissances sur la programmation et le fonctionnement d’une carte électronique ainsi que la connaissance en communication SPI. Nous avons réussi à avoir un affichage sur l’écran par simple communication avec arduinoIDE ainsi qu’une communication SPI avec l’Arduino(faisant référence à la carte mère) cependant l’envoi de données n’a pas pu être fonctionnelle.


== GIT ==
== GIT ==
<p style="clear: both;" />Voici le lien du git : https://archives.plil.fr/handrian/projet_pico.git
<p style="clear: both;" />Voici le lien du git : https://archives.plil.fr/handrian/projet_pico.git

Version actuelle datée du 5 février 2024 à 11:09

Ordonnanceur / Système d'exploitation

Réalisation du Shield

Séance 13/09 : Parmi les activités disponibles, nous avons soudé le régulateur de tension, le lecteur SD, les résistances liées aux LEDs. Nous avons réalisé 2 câbles pour les cartes filles et un adaptateur.

carte Shield après première soudure
câbles
Adaptateur.jpg


Séance 19/09 : Durant cette séance nous avons soudé les autres résistances et les connecteurs. Et nous avons testé le bon fonctionnement de la carte Shield.

Voici la carte Shield soudé :

Photo de la carte Shield soudé

Nous avons soudé cette carte :

20230919.jpg

Pour le test nous avons utilisé le code qui est disponible sur le git. Voici ci-dessous une vidéo montrant le clignotement des LEDs.

Clignotement des LEDs avec ordonnanceur

Au cours de la fabrication de notre carte, nous avons commencé la réalisation de l'ordonnanceur.

Pour réaliser l'ordonnanceur, nous avons besoin d'une fonction de gestion d'interruption ISR qui sera déclenché par l'interruption.

ISR(TIMER1_COMPA_vect, ISR_NAKED) {
     SAVE_REGISTERS();
  
     taches[current].pointer_pile = SP;

     // Appel à l'ordonnanceur
     scheduler();
  
    SP = taches[current].pointer_pile;

     RESTORE_REGISTERS();
     asm volatile ( "reti" );
 }

Le principe c'est de créer deux tâches pour faire clignoter deux LEDs du bouclier avec des périodes différentes. La première tache allume et éteint une LED toutes les 200ms, la deuxième tache réalise la même opération sur une LED différente toutes les 1000ms.

 // Tâche 1
void task_LED_1() {
    DDRD |= (1 << LED4_PIN);
    PORTD &= ~(1 << LED4_PIN);

    while (1) {
        PORTD ^= (1 << LED4_PIN);
        _delay_ms(200); // Fréquence 200 ms
    }
}
  
 // Tâche 2
void task_LED_2() {
    DDRC |= (1 << LED1_PIN);
    PORTC &= ~(1 << LED1_PIN);

    while (1) {
        PORTC ^= (1 << LED1_PIN);
        _delay_ms(1000); // Fréquence 1000 ms
    }
}

Et voici la fonction permettant d'initialiser le contexte de chaque tâche :

void init_task(int id)
{
    uint16_t save_sp = SP;
    SP= taches[id].pointer_pile;

    uint16_t adresse=(uint16_t)taches[id].task_func;
    asm volatile("push %0" : : "r" (adresse & 0x00ff) );
    asm volatile("push %0" : : "r" ((adresse & 0xff00)>>8) );
    SAVE_REGISTERS();

    taches[id].pointer_pile = SP;
    SP = save_sp;
}

Afficheur 7 segments

Pour l'afficheur 7 segments, nous l'avons dans un premier temps fait sans ordonnancement pour connaitre sa fonctionnalité. Pour le tester on a juste affiché "1 2 3 4" sur l'afficheur comme indiqué sur l'image ci-dessous :

void afficheur(void)
{
     spi_activer();
     spi_exch(0x76);
     spi_desactiver();
    char seg[4] = "1234";
     while(1){
         spi_activer();
         for(uint8_t i = 0; i < 4 ; i++)
        {
            spi_exch(seg[i]);
        }

        _delay_ms(1000);
        spi_desactiver();
    }
}
Afficheur.jpg

Pour l'ordonnancement, nous l'avons testé sur l'afficheur 7 segments avec lecture/écriture sur le port série :

// fonction pour afficheur 7 segments
void afficheur(void)
{
    pause_smp(SEMAPHORE_SPI);
    spi_activer(SS_3);
    spi_exch(0x76);
    spi_desactiver(SS_3);

    while(1){
        for(int i = 0; i < 4 ; i++)
        {
            spi_activer(SS_3);
            spi_exch(caractere);
        }

         spi_desactiver(SS_3);
         _wait_ms(5);
        liber_smp(SEMAPHORE_SPI);
    }
}


Les fonctions spi_activer et spi_desactiver sont utilisées pour activer et désactiver les différents esclaves sur le bus SPI en manipulant les broches SS correspondantes. Cela permet de sélectionner le périphérique avec lequel le microcontrôleur va communiquer via le bus SPI.

void spi_activer(int chip_s) {
    if (chip_s == SS_2 || chip_s == SS_3) {
        PORTD &= ~(1 << chip_s);
    } else if (chip_s == SS_5) {
        PORTB &= ~(1 << chip_s);
    }
}

void spi_desactiver(int chip_s) {
    if (chip_s == SS_2 || chip_s == SS_3) {
        PORTD |= (1 << chip_s);
    } else if (chip_s == SS_5) {
        PORTB |= (1 << chip_s);
    }
}

La matrice Led

Nous avons essayé l'affichage avec la matrice Led par le protocole SPI. On a inclut les représentations matricielles pour les chiffres de 0 à 9 et les lettres de l'alphabet de A à Z. Nous avons écrit une fonction pour l'envoi de données à la matrice LED via le protocole SPI. Elle active le périphérique SPI, envoie la ligne et la valeur via SPI, puis désactive le périphérique.

void matrice_Byte(uint8_t row, uint8_t value) {
    // Activer le périphérique SPI correspondant à la matrice
    spi_activer(SS_2);

    // Envoyer les données à la matrice via SPI
    spi_exch(row);
    spi_exch(value);

    // Désactiver le périphérique SPI
    spi_desactiver(SS_2);
}

Pour l'affichage de caractère et le test :

//fonction pour la matrice
void display_on_matrix(void) {
    char l = caractere;
    int index = 0;

    if (l >= '0' && l <= '9')
        index = l - '0';
    else if (l >= 'a' && l <= 'z')
        index = 10 + (l - 'a');
    else if (l >= 'A' && l <= 'Z')
        index = 10 + (l - 'A');

    while(1){
        pause_smp(SEMAPHORE_SPI);
        spi_activer(SS_2);
        spi_exch(0x76);
        _wait_ms(1);
        spi_desactiver(SS_2);
        if (index >= 0 && index <= 9) {
            for (uint8_t i = 0; i < 8; i++) {
                matrice_Byte(i + 1, numbers[index][i]);
            }
        } else if (index >= 10 && index <= 35) {
            for (int i = 0; i < 8; i++) {
                matrice_Byte(i + 1, letters[index - 10][i]);
            }
        }

        spi_desactiver(SS_2);
        _wait_ms(500);

        liber_smp(SEMAPHORE_SPI);
    }
}

Malheureusement, malgré nos efforts, la fonction display_on_matrix n'a pas réussi à produire l'affichage voulu sur la matrice LED. Malgré la mise en œuvre du protocole SPI et des déterminations d'index pour les caractères, l'effet clignotant souhaité n'a pas été observé.Faute de temps, nous avons décidé de recentrer nos efforts sur la carte LCD.

Carte FPGA / VHDL

Controleur VGA

Un contrôleur VGA sert à générer des signaux de synchronisation horizontale (HS) et verticale (VS), ainsi que des signaux de couleur (r, g, b) pour un écran VGA. L’horloge d'entrée est à 100 MHz et génère une horloge de sortie à 65 MHz généré par le wizard (clk_wiz_0).

Le code ci-dessous permet de générer des signaux VGA pour afficher des images colorées sur un écran VGA et un Basys3

  • Génération des signaux HS et VS pour indiquer les périodes de synchronisation horizontale et verticale
process1 : process(clock_65Mhz)
begin
       if clock_65Mhz'event and clock_65Mhz = '1' then
            if compteur_vert < 774 then
                if compteur_hori < 1048 then
                    HS <= '1';
                    compteur_hori <= compteur_hori + 1;
                 elsif compteur_hori >= 1048 and compteur_hori <1183 then
                    HS <= '0';
                    compteur_hori <= compteur_hori + 1;
                 elsif compteur_hori >= 1183 and compteur_hori < 1343 then
                    HS <= '1';
                    compteur_hori <= compteur_hori + 1;
                 else
                 HS <= '1';
                 compteur_hori <= 0;
                 compteur_vert <= compteur_vert + 1;
                  end if;
             end if;
             if compteur_hori < 1048 then
                if compteur_vert < 774 then
                    VS <= '1';
                    compteur_vert <= compteur_vert + 1;
                elsif compteur_vert <= 774 and compteur_vert < 780 then
                    VS <= '0';
                    compteur_vert <= compteur_vert + 1;
                elsif compteur_vert <=780 and compteur_vert < 805 then
                    VS <='1';
                    compteur_vert <= compteur_vert + 1;
                else
                    VS<= '1';
                    compteur_vert <= 0;
                    compteur_hori <= compteur_hori + 1;

               end if;
            end if;
  • Génération des Signaux de Couleur (r, g, b) en utilisant les boutons b1, b2 et b3
if b1 = '1' then
		    --couleur rouge
                if compteur_hori > 300 and compteur_hori < 600 then
                    if compteur_vert > 300 and compteur_vert < 600 then
                        r <= "1111";
                        g <= "0000";
                        b <= "0000";
                    end if;
                end if;
             end if;

		 --couleur verte
             if b2 = '1' then
                if compteur_hori > 360 and compteur_hori < 1080 then
                    if compteur_vert > 201 and compteur_vert < 604 then
                        r <= "0000";
                        g <= "1111";
                        b <= "0000";
                    end if;
                end if;
             end if;

		 --couleur bleue
             if b3 = '1' then
                if compteur_hori > 360 and compteur_hori < 1080 then
                    if compteur_vert > 201 and compteur_vert < 604 then
                        r <= "0000";
                        g <= "0000";
                        b <= "1111";
                    end if;
                end if;
             end if;
           end if;

Controleur bus SPI

Les dispositifs SPI communiquent en utilisant une architecture maître-esclave avec un seul maître.Un ou plusieurs dispositifs esclaves SPI sont pris en charge par la sélection d'esclaves individuels SS Avant de démarrer une communication avec un esclave particulier, le maître active la ligne SS associée à cet esclave en la faisant passer de l'état haut à l'état bas. Une fois que l'esclave est sélectionné, le maître peut commencer à envoyer des données sur la ligne MOSI et recevoir des données sur la ligne MISO.

Un maitre un esclave


SPI plusieurs esclaves.png

Nous devons illustrer une implémentation d'un émetteur SPI synchrone. Dans notre contexte, on gère la transmission séquentielle d'octets sur la ligne MOSI, tout en signalant la fin de chaque transmission par le signal complete. Lorsqu'il est déclenché par un front montant d'horloge, le code gère différents états, tels que l'état IDLE où le système est en attente, et l'état TRANSFER où les données sont envoyées bit par bit.


Voici le code pour tester chaque état

  • Etat IDLE

Dans l'état IDLE, le système est en attente d'une nouvelle transmission SPI, et la transition vers l'état de transfert est conditionnée par la détection d'un signal de démarrage. Lorsque ce signal est activé, l'état passe à TRANSFER, initialisant la communication en activant la sélection d'esclave et préparant la transmission des données.

when IDLE =>
    if start = '1' then
        -- Début d'une nouvelle transmission
        state <= TRANSFER;
        ss <= '0';   -- Activer la sélection d'esclave
        bit_counter <= 7;   -- Initialiser le compteur de bits
        mosi <= data_buffer(7);
    end if;
  • Etat TRANSFER

L'état TRANSFER gère la transmission séquentielle des bits sur la ligne MOSI.

when TRANSFER =>
     if bit_counter > 0 then
     -- Transfert des bits
         bit_counter <= bit_counter - 1;
         mosi <= data_buffer(bit_counter);
     else
         -- Fin de la transmission
         state <= DONE;
     end if;
  • Etat DONE

Dans l'état DONE, après la fin de la transmission SPI, le système désactive la sélection d'esclave et peut lire les données reçues sur la ligne MISO

when DONE =>
-- Désactiver la sélection d'esclave et générer une impulsion d'horloge
      ss <= '1';
      --sclk <= not sclk;
      complete <= '1';

      -- Lecture des données reçues sur la ligne MISO
      data_in <= data_in (6 downto 0) & miso;

Carte électronique numérique

Type de carte choisie

La carte fille écran LCD.

Elle se compose :

  • d'un ATMega328p
  • d'un écran LCD à base de contrôleur HD44780
  • d'un connecteur HE10
  • d'un connecteur AVR ISP pour programmer le microcontrôleur
  • d'un potentiomètre pour régler la luminosité des cristaux liquides
  • d'un bouton switch pour le reset

Réalisation de la carte

Séance 22/09 : Durant cette séance nous avons commencé la schématique de notre carte.

Voici le schéma électronique de notre carte fille LCD :

Schematic de la carte fille LCD

Séance 03/10 : nous avons commencé le routage.

Séance 17/10 : routage terminé

Après le routage nous avons obtenu :

Routage1.png


Séance 24/10 :

Le routage terminé, le prof a vérifié le routage final et nous avons envoyé la carte afin de l'imprimer.

Durant cette même séance nous avons commencer la programmation de l'ordonnancement.

routage fini

Voici une vue 3D de ce qui va se trouver au dessus de notre carte. Notre écran se trouvera donc en dessous.

vue 3D

Présentation de la carte fille LCD

Séance 14/11 :

Nous avons reçu la carte, nous avons commencé la soudure des composants nécessaires pour un premier test de la carte.

  • Le microprossesseur
  • une led
  • L'horloge
Crte électronique.jpg
Crte soudée.jpg

Pour vérifier le bon fonctionnement de la carte, nous avons mis une led afin de savoir la fonctionnalité de la carte.

Pour cela, on a testé avec le code exemple présent Arduino IDE.

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(7, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(7, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(7, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

Nous avons soudé le reste des composants ainsi que l'écran LCD.

  • Le potentiomètre
  • le bouton reset
  • les connecteurs
  • l'écran LCD
  • Les capacités

Voici tous la présentation de tous les composants de notre carte fille :

carte soudée

Test de l'ecran avec Aduino IDE

test lcd

Nous avons testé le lcd avec le code qui est présent sur Arduino IDE. Voici le code utilisé:

// include the library code:
#include <LiquidCrystal.h>

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = A0, en = A2, d4 = 4, d5 = 5, d6 = 9, d7 = 10;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  pinMode(A1,OUTPUT);
  digitalWrite(A1,LOW);
  delay(1000);
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("hello, world!");
  pinMode(7,OUTPUT);
  digitalWrite(7,HIGH);
}

void loop() {
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(millis() / 1000);
}


Programmation de la carte fille LCD

Les broches nécessaire à la programmation de l'écran LCD :

  • RS (Register Select)
  • EN (Enable)
  • R/W (Read/Write)
  • DB4 à DB7

Ecriture sur la carte LCD

La fonction lcdWrite4bits est conçue pour écrire quatre bits de données sur l’écran LCD en utilisant une interface 4 bits.

void lcdWrite4bits(uint8_t data, uint8_t rs){

    if(rs)
        LCD_RS_ON();
    else
        LCD_RS_OFF();

    LCD_RW_OFF();

    LCD_EN_ON();
     _delay_us(1);

    if(data & 1)
        LCD_DB4_ON();
    else
        LCD_DB4_OFF();

    if(data & 2)
        LCD_DB5_ON();
    else
        LCD_DB5_OFF();

    if(data & 4)
        LCD_DB6_ON();
    else
        LCD_DB6_OFF();

    if(data & 8)
         LCD_DB7_ON();
    else
        LCD_DB7_OFF();


    LCD_EN_OFF();
    _delay_us(1);
}

- rs indique si les données à écrire sont des données :

  • d'instruction « 0 »
  • des données à afficher « 1 »

- Les blocs de if testent chaque bit dans data. Ils activent ou désactivent les lignes de données (DB4 à DB7).

Gestion des caractères de contrôle et initialisation LCD

Pour initialiser l'écran LCD, nous avons écris la fonction suivante

void lcdInit(){

    LCD_RS_INIT();
    LCD_EN_INIT();
    LCD_RW_INIT();
    LCD_DB4_INIT();
    LCD_DB5_INIT();
    LCD_DB6_INIT();
    LCD_DB7_INIT();

    _delay_ms(1);

    lcdWrite8bits(0x28,0); //4bits, 2 lignes
    lcdWrite8bits(0x08,0); //LCD off
    lcdWrite8bits(0x01,0); //Clear LCD

    _delay_ms(2);
    lcdWrite8bits(0x06,0); //incrementation
    lcdWrite8bits(0x0c,0);
}
  • lcdWrite8bits(0x0c, 0) : Allume l'écran LCD sans afficher le curseur.

La fonction lcdWrite8bits est conçue pour écrire une valeur de 8 bits de data sur l'écran LCD. Pour la gestion du retour à la ligne et le retour chariot il faut vérifier si le caractère data est égal à un retour à la ligne \n ou un retour chariot \r.

 if ((data == '\n') || (data == '\r')) {
        pos.current_x = 0;
        pos.current_y = (pos.current_y + 1) % NB_LIGNE;
        lcdMoveCursor(pos.current_y, 0);
        return;
    }

La mise à jour du curseur dépend de la valeur de data et si elle vaut l'un des caractère de contrôle, le curseur se mettra à jour sur la ligne suivante grâce à la fonction lcdMoveCursor(pos.current_y, 0)

Gestion /n et /r

Pour le retour à la ligne automatique, faut vérifier la position de x, pour cela on a ajouté la condition suivante :

 if (pos.current_x == NB_COL) {
        pos.current_x = 0;
        pos.current_y = (pos.current_y + 1) % NB_LIGNE;
        lcdMoveCursor(pos.current_y, 0);}

Pour déplacer le curseur sur l'écran LCD :

void lcdMoveCursor(uint8_t row, uint8_t col) {
    if (row == 0) {
        // Pour la première ligne
        lcdWrite8bits(0x80 + col, 0); // Adresse de début pour la première ligne + colonne
    } else if (row == 1) {
        // Pour la deuxième ligne
        lcdWrite8bits(0xC0 + col, 0); // Adresse de début pour la deuxième ligne + colonne
    }
}

Pour effacer le contenu affiché sur un écran LCD. on a ajouté la fonction lcdClear qui envoie une commande à l'écran LCD pour effacer son contenu, puis introduit un délai de 2 millisecondes pour permettre à l'écran de s'effacer correctement avant de poursuivre l'exécution du programme.

void lcdClear() {
    lcdWrite8bits(0x01, 0);
    _delay_ms(2);
}

Affichage des caractères

Pour afficher chaque caractère on a écris la fonction lcdPutc

void lcdPutc(char c){
    busy = 1;
    lcdWrite8bits(c,1);
    busy = 0;
}

busy à 1 est utilisé pour indiquer l'état de l'écran LCD s'il est en train de traiter une opération.

Voici la vidéo montrant le bon fonctionnement de notre carte :

le code source complet est dans le git.

LCD avec SPI

Lors de la programmation de la carte fille, nous avons constaté qu'il y'a une erreur sur la carte: le pin CS de l'Atmega est liée au PD6 alors qu'il doit être connecté au PB2. Donc on a du interchanger CS et B7 sinon la communication SPI ne sera pas fonctionnelle.

Nous avons donc procéder avec monsieur Redon la modification des pins.

Piste à découper.jpg

Nous avons couper la piste du pin 14 et le relier avec le "via" connecté à CS avec un fil. Puis relier le pin 10 avec B7 de l'écran LCD.

Carte avec inter-changement des pins B7 et CS

Malheureusement on a pris beaucoup de temps pour constater l'erreur, donc on a perdu du temps en pensant que l'erreur était dans le code.

Programmation du microcontrôleur de la carte LCD

Pour traiter les commandes reçues via l'interface SPI, on a ajouté la fonction commandes() :

void commandes(void)
{
    char cmd = SPDR; //spi_wait_cmd();
    char data = spi_wait_cmd();
    char tmp;

    if(cmd == 0x00)
    {
        lcdPutc('o'); //test
        lcdPutc('k');

        if(data == INFO)
        {
            tmp = data;
            if(tmp != 0xff)
            {
                lcdPutc(data);
            }
            else if(tmp==0x00)
            {
                SPDR = 0xA2; //Pour Affichage,  Envoi de la donnée 0xA2 via SPI si la donnée reçue est égale à 0x00
            }
        }
    }
    if(cmd == 0xff)
    {
        if(data == 0xff)
        {
            SPDR = 0x55; // Envoi de la donnée 0x55 via SPI si la commande est 0xff et la donnée reçue est 0xff
        }
    }
}

Voici le main pour tester la communication entre le maitre (arduino) et notre écran LCD :

int main() {
    spi_init();
    lcdInit();
    commandes();
    sei();
    while (1) {
    }
    return 0;
}

Pour gérer la réception des données via le port SPI et pour configurer l'écran comme Esclave :

void spi_init(void) {
    // Configuration des broches
    SPI_DDR = (1 << SPI_MISO);       // MISO en sortie
    SS_DDR &= ~((1 << SPI_SCK) | (1 << SPI_SS));
    SS_DDR &= ~(1 << SPI_MOSI);

    // Configuration du registre de contrôle SPI
    SPCR = (1 << SPE);
}
uint8_t spi_wait_cmd(void) // Communication sur le bus SPI
{
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
}

Programmation du maitre

Pour la configuration de la carte maitre, on a repris le code spi qu'on a utilisé pour l'ordonnancement avec quelque modification pour utiliser un seul connecteur HE10:

void spi_init(void)
{
    // Configurer la broche de sortie pour MOSI
    SPI_DDR |= (1 << SPI_MOSI);

    // Configurer la broche de sortie pour SCK
    SPI_DDR |= (1 << SPI_SCK);

    // Configurer la broche d'entrée pour MISO
    SPI_DDR &= ~(1 << SPI_MISO);

      DDRD |= (1<<SS_3);
      PORTD |= (1<<SS_3);

    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}
uint8_t spi_exch(uint8_t output) // Communication sur le bus SPI
{
    SPDR = output;
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
}

Voici le main qu'on a fait pour tester la communication SPI entre le maitre et esclave:

int main() {
    spi_init();
    spi_desactiver(SS_3);

DDRD |= (1<<4);
    while(1){
        spi_activer(SS_3);

        spi_exch(0);
        spi_exch(1);//clavier

        spi_exch(0xff);
        spi_desactiver(SS_3);

    }
    return 0;
}

Voici le résultat du test qui permet de tester la communication sur le port SPI:

Test spi.jpg

la primitive système :

Cette fonction permet une transmission SPI constituée de l'octet de commande 0x01, et de communiquer les chaînes de caractères à envoyer.

void envoyer_commande_ecran(uint8_t ligne_selection, const char* caracteres) {
    // Activer la ligne de sélection SPI (SS) correspondante à l'écran
    spi_activer(ligne_selection);

    // Envoyer l'octet de commande
    spi_exch(0x01);
    // Envoyer les octets correspondant aux caractères
    size_t longueur_chaine = strlen(caracteres);
    for (size_t i = 0; i < longueur_chaine; ++i) {
        spi_exch(caracteres[i]);
    }

    // Désactiver la ligne de sélection SPI (SS)
    spi_desactiver(ligne_selection);
}

Conclusion

Durant le projet, comme nous l’avons déjà cité plus haut, nous avons ressoudé la carte LCD à cause d’un problème d’inter-changement de pins. Et comme c’était sensible, la soudure s’est enlevée pendant les vacances et nous n’avons pas pu travailler correctement avec la carte car nous avons perdu beaucoup de temps pour pouvoir la ressouder.

Pour la conception de la carte électronique, de la conception du schématique et le routage, le temps était bien géré jusqu'à la soudure des composants.

Pour la programmation :

  • Pour la partie ordonnanceur nous avons réussi à faire ordonnancer la tâche d'écriture sur l'afficheur 7 segments et la lecture avec le port série. En revanche nous n’avons pas eu le temps de programmer la matrice led correctement malgré notre tentative parce que nous avons préféré nous concentrer sur la programmation de notre carte fille LCD Cette partie était une partie compliquée à programmer mais nous en avons beaucoup appris.
  • Pour la carte fille LCD : c’est une partie que nous avons bien découverte et nous avons bien aimé la programmation pour afficher des caractères sur l’écran. Cette partie nous a aidé à renforcer nos connaissances sur la programmation et le fonctionnement d’une carte électronique ainsi que la connaissance en communication SPI. Nous avons réussi à avoir un affichage sur l’écran par simple communication avec arduinoIDE ainsi qu’une communication SPI avec l’Arduino(faisant référence à la carte mère) cependant l’envoi de données n’a pas pu être fonctionnelle.

GIT

Voici le lien du git : https://archives.plil.fr/handrian/projet_pico.git