« SE4Binome2023-5 » : différence entre les versions
Ligne 372 : | Ligne 372 : | ||
En conclusion, le programme de la carte mère envoi la demande d'identification (0x00) ainsi que la demande de touche. Et le programme du clavier envoi le caractère '!', et par la suite la touche du tampon. | En conclusion, le programme de la carte mère envoi la demande d'identification (0x00) ainsi que la demande de touche. Et le programme du clavier envoi le caractère '!', et par la suite la touche du tampon. | ||
[[Fichier:Id touche tampon.mov|centré|cadre]] | |||
Version du 17 janvier 2024 à 18:46
Voici le lien de notre Git : https://archives.plil.fr/mnorman1/PICO.git
Puis le zip de notre routage (vérifié par M. Redon) : Fichier:Gerber B5.zip
Ordonnanceur
Soudure
Lors de la première séance de Pico, nous avons soudé quelques cartes telles que le Shield, la carte de leds et la carte 7 segments :
Câble pont Shield Arduino
En parallèle, nous avons réalisé les câbles qui se situent entre la carte Shield et la carte Arduino qui nous servirons pour la communication du bus SPI :
Programmation Ordonnancement
Clignotement de 2 Leds
Nous avons écris un programme d'ordonnancement qui permet de faire clignoter 2 Leds (Led1 et Led2) avec une durée différente :
Remarque : Les temps ne sont pas réels, l'objectif est donc d'endormir les tâches.
Voici cette fois le clignotement des 2 Leds avec l'endormissement des tâches :
Lecture sur port série en mémoire partagée
Par la suite, nous voulons ordonnancer une tâche qui permet de stocker dans une mémoire partagée la valeur reçue sur le port série.
Voici le code que nous avons utilisé :
void serie_recevoir(void) {
while(1) {
loop_until_bit_is_set(UCSR0A, RXC0);
received_data = UDR0;
sleepy(1);
}
}
Ici, la variable globale (mémoire partagée) est une variable globale (received_data) en unsigned char.
Pour lire sur le port série, on utilisera la commande minicom -D /dev/ttyUSB0 -b 9600
Lecture/écriture sur port série
Afin de vérifier le code que nous avons écris dans la partie d'avant, nous écrirons sur le port série ce que nous avons lu. En d'autres termes, nous écrirons sur le port série ce que nous avons écris sur le clavier.
void serie_envoyer(void) {
while(1) {
loop_until_bit_is_set(UCSR0A,UDRE0);
UDR0=received_data;
received_data = '\0';
Sans remettre à '\0' comme ici, lorsque l'on appuie sur une touche, celle-ci s'affiche jusqu'à l'appuie d'une autre touche
sleepy(1);
}
}
Carte FPGA/VHDL
Dans cette partie, nous devons implémenter un contrôleur de bus série SPI.
Tout d'abord, voici comment fonctionne un bus SPI :
Dans un bus SPI, il y a 1 maître et 1 ou plusieurs esclaves. Le transmission des données se font sur 2 fils : MISO et MOSI :
- MOSI : Données du maître vers l'esclave,
- MISO : Données de l'esclave vers le maître.
Puisqu'il y a plusieurs esclaves, le SS (Ship Select) permet d'autoriser 1 esclave précis en l'activant à 0.
Les échanges se font sur front et sont synchronisés par le maître.
Lorsqu'un octet du maître passe dans le registre de l'esclave, celui de l'esclave passe dans le registre du maître simultanément comme représenté sur l'image ci-dessous. C'est le principe du registre à décalage.
Le contrôleur SPI doit jouer le rôle du maître et va contrôler les pins du bus SPI pour échanger des données.
Voici le code que nous avons écris en VHDL :
Dans un process :
case ETAT is
when REPOS => ETAT <= TRANSMISSION;
when TRANSMISSION => if compteur = 8 then
ETAT <= REPOS;
compteur <= 0;
else compteur <= compteur + 1;
end if;
for i in 0 to 6 loop
mosi_in(i) <= mosi_in(i+1);
data_out_s(i) <= data_out_s(i+1);
end loop;
mosi_in(7) <= data_in_s(0);
data_out_s(7) <= mosi_in(0);
end case;
Hors du process :
CS1 <= '1' when select_carte = "001" and ETAT <= REPOS else '0';
CS2 <= '1' when select_carte = "010" and ETAT <= REPOS else '0';
CS3 <= '1' when select_carte = "100" and ETAT <= REPOS else '0';
Nous avons utilisé une machine d'état avec 2 états : un état REPOS qui permet de passer à l'état TRANSMISSION et de mettre à 1 ou à 0 les Chip Select en fonction de la selection d'état que recevra la carte FPGA de la carte mère. Et la fonction transmission qui effectue le registre à décalage.
Ce code, que vous trouverez dans le git, compile, en revanche nous n'avons pas pu faire de tests dessus malheureusement.
Carte électronique numérique
Type de carte choisie
Nous avons choisi de travailler sur la carte fille clavier "matrice de touches"
L'objectif de ce projet pour la carte clavier est de lire les boutons qui sont enfoncés afin de les envoyer à la carte mère via le bus SPI.
Partie électronique
Voici le schéma électronique fini de notre carte :
Nous avons opté pour :
- une matrice de touches de 4 par 8, ce qui nous fait 32 touches soient les 26 lettres de l'alphabet + 6 autre touches qui sont : la flèche de gauche, la flèche de droite, l'espace, un retour chariot, le suppression et enfin un shift qui nous permettra de switcher avec les 10 chiffres (de 0 à 9),
- 2 Leds de test + 1 Led d'alimentation
- un ATmega328p
- un connecteur HE10 8 pins pour un bus SPI + une ligne d'interruption pour communiquer avec la carte mère
- un connecteur 6 pins(2x3) pour un bus SPI afin de programmer la carte.
Fonctionnement matrice de touches :
Le but ici est de pouvoir lire la touche sur laquelle nous avons appuyé pour l'envoyer à la carte mère. Ici, l'idée est d'utiliser le fonctionnement de la matrice de touche qui est le suivant :
Premièrement, il faut se décider duquel entre les lignes et les colonnes seront en entrée et l'autre en sortie. Ici se sont les lignes qui sont en sortie et les colonnes en entrée.
L'idée est simple : alterner entre les lignes pour écrire un 0L sur 1 ligne et sur les autres un 1L. On parcourt ensuite toutes les colonnes (de la ligne à 0L). Si on lit un 0L sur une colonne, alors il suffit d'associer la ligne qui est à 0L avec celle de la colonne et on connait la touche qui a été appuyée.
Les résistances de pull up qui se trouvent au niveau des lignes serviront à avoir un 1L par défaut.
Voici le routage de la carte : Tous les condensateurs se trouvent à côté d'un VCC, le crystal est au plus proche du microcontrôleur et nous avons pris soin d'avoir des longueurs de pistes égales sur XTAL1 et XTAL2 du Crystal.
Pour faire ce routage, nous avons dû créer le footprint des boutons, alors la prise des mesures sur le boutons était nécessaire. Aussi, nous avons décidé d'utiliser un ATmega328p avec des pins traversants pour une question de facilité au niveau de la soudure mais également pour éviter tout court-circuit et donc une perte de temps.
Après avoir reçu la carte imprimée, nous l'avons directement soudée avec tous les composants. On retrouve finalement l'allure d'un réel clavier avec toutes les touches (ici 32) :
Amélioration possible : permettre la programmation en USB. Avec cette carte telle qu'elle est faite, pour insérer un programme dans le microcontrôleur il faut passer par la carte Arduino et pour ça, il faut insérer le programme "ArduinoISP" qui se trouver dans les exemples de l'IDE Arduino, sans oublier tous les branchements à refaire. Ce n'est pas vraiment pratique et c'est une perte de temps qu'il est possible d'enlever en mettant un port USB directement sur la carte clavier.
Partie programmation
La programmation de notre carte se fera via un AVR ISP
Pour cela, on utilise une carte arduino uno dans laquelle on téléverse le programme Arduino ISP, disponible dans les exemples de l'IDE arduino, pour permettre a l'arduino uno d'envoyer notre code vers la carte clavier via le connecteur HE10.
Pour flasher notre carte clavier, 2 solutions :
- utiliser l'IDE arduino, en utilisant le paramètre "upload using programmer" qui permet de televerser du code dans une atmega en passant par le bus SPI. Mais cela néccesite d'écrire le code sur l'IDE arduino. Cette solution permettra simplement de faire des tests rapides en utilisant, par exemple, l'exemple "Blink".
- Créer un Makefile qui permet de téléverser notre code C vers la carte fille en passant par l'arduino uno :
Programme de tests sur la carte
Nous sommes ensuite passés au différents tests de la carte pour tester chaque fonctionnalités.
Premier test : la carte est-elle programmable ?
On utilise un programme très simple qui allume une des LEDs de la carte, le "Blink" disponible dans les exemples de l'IDE arduino.
Cependant, en essayant de programmer la carte, aucune LEDs ne clignotait. Comme le code ne venait pas de nous et qe c'est un code simple, le problème ne pouvait venir que du côté du hardware. Nous avons donc tester notre carte au multimètre et il y avait effectivement un court-circuit au niveau du crystal qui empêchait forcement son fonctionnement. L'erreur vient de notre schématique puisque nous avons tourné à 90° le composant, ce qui créé effectivement un court-circuit. Pour remédier à notre problème, nous avons dé-souder le crystal et l'avons décalé sur la carte.
Une fois le crystal re-soudé dans le bon sens, nous avons pu flasher la carte clavier avec le programme "Blink"
Conclusion : La LED clignote, ce qui nous permet de valider le faite que la carte est programmable via notre AVR ISP.
Deuxième test : la lecture d'un bouton
Pour commencer les tests, nous cherchons à allumer une LED quand on appuie sur un bouton en particulier. Pour cela, nous avons mis à 0L la ligne sur PC3 et lu la colonne sur PD0 :
Ce test nous a permit de vérifier 2 choses :
- que notre clavier matricielle est capable de reconnaître une touche en particulier
- que nous arrivions bien à utiliser les Leds de notre carte. Ainsi nous avons nos "calculs" avec les décalages et les opérations logiques utiles pour mettre à 0L ou lire sur le port une entrée.
Par la suite nous avons gardé l'idée d'allumer une Led lorsqu'un bouton est enfoncé.
Problème rencontré : Au début nous n'arrivions par à lire le bouton bien que notre code soit simple. Nous avons donc vérifié au multimètre si nos boutons étaient bien soudés. Au multimètre, les 4 broches de chaque bouton étaient reliés (donc en court-circuit), ce n'est absolument pas normal. Effectivement, lorsque nous avions créé les footprints des boutons nous n'avons pas mis les broches au bon endroit. Dans les boutons il y a déjà des broches reliées et dans nos footprint nous n'avons pas mis les bons chiffres des broches correctement : ce qui nous a créé un court-circuit. Pour contrer ce problème, nous avons tourné à 90° chaque bouton. C n'était pas simple car il a fallut redresser les pins pour qu'ils rentrent bien dans les trous de la carte. Finalement les boutons fonctionnent correctement et les boutons restent assez droits.
Troisième test : lecture de tous les boutons + shift
Pour commencer, on definit notre matrice de touches grâce à un tableau à 2 dimensions, ça nous permettra de récupérer directement les bonnes touches.
Nous voulions mettre notre clavier en AZERTY, malheureusement notre matrice ne nous le permet pas, alors les touches seront dans l'ordre alphabétique. Dans le code, les lettres ne sont pas dans l'ordre simplement parce que lors du routage nous n'avons pas laissé les boutons comme dans la schématique par simplicité de routage :
traduction des valeurs hexadécimales :
- 0x0E : le shift
- 0x08 : suppression
- 0x1C : flèche gauche
- 0x1D : flèche droite
Ce sont simplement les codes en ASCII.
Cette fonction nous permet d'effectuer la lecture d'une touche avec la méthode du clavier matriciel que nous avons expliqué plus haut.
Ainsi, pour chaque ligne à 0L on lira sur chaque colonne et on vérifie si elle est égale à 0L. Si oui, on retourne directement la valeur de la touche en retournant notre matrice de touches avec les indices de la ligne et de la colonne en cours.
Il y a une condition supplémentaire : si la touche appuyée est le shift (0x0E). Dans ce cas, on bascule on recommence la même méthode, mais cette fois on retournera la valeur appuyée mais grâce à la deuxième matrice cette fois puisqu'elle contient les chiffres de 0 à 9.
Ici, l'utilisateur devra appuyer sur le bouton pour les chiffres en même temps que le bouton du shift.
Finalement, notre clavier ce présent comme ceci :
Quatrième test : le bus SPI
L'objectif de cette partie est d'envoyer les touches lues par le clavier vers la carte mère grâce au bus SPI. On a donc écrit un programme pour tester le bus SPI. L'idée était simplement d'envoyer une une variable en fonction de ce que l'on recevait.
Pour utiliser le bus SPI, il est nécessaire d'utiliser une interruption (une ISR) qui permettra, pour rester dans le cahier des charges, d'envoyer des valeurs en fonctions de la valeur que l'on reçoit.
ISR(SPI_STC_vect) {
char data = SPDR;
_delay_ms(40);
if(data == 0x00) {
SPDR = '!';
} else if (data == 0x05) {
SPDR = bouton;
bouton : touche qui a été appuyée
}
Explication de la communication en bus SPI :
Dans cette partie, l'idée est de communiquer avec la carte mère. Pour cela, voici un schéma explicatif de notre mèthode de communication :
Remarque : la taille du tampon sera, le plus souvent, égale à 1 puisque le microcontrôleur est plus rapide qu'un humain.
Explication du code :
Puisque c'est un test sur le spi, nous avons seulement envoyé des valeurs aléatoires afin de vérifier si le bus spi marche, mais en gardant la configuration que l'on doit avoir avec le schéma ci-dessus.
Afin de pouvoir vérifier nos codes correctement, nous avons utiliser un Arduino uno qui "remplace" la carte mère et qui envoi les valeurs 0x00, 0x01 ou 0x05 de la même manière, avec le bus SPI et les bonnes configurations d'initialisation du bus en tant que "maître".
En revanche, après beaucoup d changement de codes, la communication ne fonctionnait pas. Nous avons alors regardé à l’oscilloscope l'allure de nos signaux MISO et MOSI :
- Le MOSI était cohérant avec les données envoyées
- Le MISO en revanche a des valeurs de tensions écrasées, ce qui fait que la valeur récupérée par le maitre est toujours nulle.
Conclusion : Après de nombreux essais sur différents shield, le bus SPI de certaines cartes ne fonctionnent pas, et d'autres fonctionnent, et ce même avec un code identiques. C'est assez troublant puisque même avec la dernière carte modifiée et soudée par M. Redon, le bus SPI ne fonctionne pas avec notre code. La seule carte avec laquelle nos programmes fonctionnent, c'est avec celle du groupe 9. Les shields des binômes 4,5 et 6 ont ce problème.
Cinquième test : affichage sur le port série
Afin de vérifier plus précisément et de continuer nos tests, le but étant de programmer la gestion de l'affichage des données reçues par le bus SPI sur le port série.
Pour ce faire, nous utilisons la même fonction serie_envoyer()
que dans l'ordonnanceur.
while(1) {
spi_activer();
_delay_ms(20);
data_recue = spi_envoi(0x05);
serie_envoyer(data_recue);
spi_desactiver();
_delay_ms(100);
}
Le but étant, ici, d'envoyer directement la demande de touche et de l'afficher directement sur le port série grâce au minicom
.
Voici la vidéo montrant le fonctionnement de notre clavier :
VIDEO
Programme final
Finalement, l'idée est de stocker les touches appuyées dans un tableau, pour pouvoir les envoyer à la carte mère lorsqu'elle le demande. Avant la demande de touche, la carte mère envoi la donnée 0x00 pour que l'on réponde que la carte est un clavier avec le caractère '!'. Nous avons décidé d'utilisé cette réponse seulement pour que l'on puisse l'afficher sur le port série (0x05 étant un caractère non imprimable).
Voici les fonctions qui permettent d'ajouter et de supprimer les touches du tampon :
void remplir_tampon() {
tampon[cpt_tampon] = bouton;
cpt_tampon++;
}
void vider_tampon() {
for (int i=0; i < cpt_tampon; i++) {
if (i == cpt_tampon - 1 ) {
tampon[i] = '\0';
} else {
tampon[i] = tampon[i+1];
}
}
cpt_tampon--;
}
L'idée est de vider le tampon à chaque fois que l'on envoi le tableau à la case 0 :
if(data == 0x05) {
SPDR = tampon[0];
vider_tampon();
if(cpt_tampon == 0) {
PORTC &= ~(1 << PC4);
}
En conclusion, le programme de la carte mère envoi la demande d'identification (0x00) ainsi que la demande de touche. Et le programme du clavier envoi le caractère '!', et par la suite la touche du tampon.
VIDEO
Remarque : ce code n'est pas parfait puisqu'il écrit 1 fois de trop le caractère. Ex : si on appuie sur 'A', on lira sur le port série 'A', en revanche si on appuie juste après sur 'B', on lira 'A' puis 'B'. Nous n'avons malheureusement pas pu décoder cette erreur.
Points importants à savoir
il faut débrancher et rebrancher le câble du bus SPI pour que la communication fonctionne. Aussi, si le programme du maître ne s'injecte pas dans l'Arduino, il faut insérer d'abord un programme d'exemple comme ArduinoISP.
Nous avons mis à part des programmes fonctionnels dans les répertoires nommés "Codes_OK". Tout est expliqué dans des README qui se trouvent (presque) dans chaque répertoire. Un README qui se trouve dans le répertoire clavier/Code explique ce que les combinaisons de codes du maître et de l'esclave font. Des vidéos se trouvent également dans le répertoire Videos, qui montrent la fonctionnalité de chaque codes. Il y a d'ailleurs plus (+) de vidéos sur le git.