« SE4Binome2024-5 » : différence entre les versions

De projets-se.plil.fr
Aller à la navigation Aller à la recherche
Ligne 222 : Ligne 222 :
([https://gitea.plil.fr/ktouron/se4-djadja-touron_pico/src/branch/main/CF_Clavier/prog/test.c Code du test])
([https://gitea.plil.fr/ktouron/se4-djadja-touron_pico/src/branch/main/CF_Clavier/prog/test.c Code du test])


Afin de tester chaque touche de notre matrice, nous avons décidé de faire le programme le plus simple possible en utilisant les broches GS de nos encodeurs. Il n'y a pas d'histoire de ligne ou de colonnes ici, si une touche est pressée alors le GS de l'encodeur ou sa ligne est reliée passera à l'état haut. Une manière très simple de savoir si une touche est utilisable et si nos trois encodeurs sont opérationnels également. Nous avons trois leds pour trois encodeurs, pour chaque encodeur, s'allumera une led spécifique si il reçoit un 1 parmi des 0.
Afin de tester chaque touche de notre matrice, nous avons décidé de faire le programme le plus simple possible en utilisant les broches GS de nos encodeurs. Il n'y a pas d'histoire de ligne ou de colonnes ici, si une touche est pressée alors le GS de l'encodeur ou sa colonne est reliée passera à l'état haut. Une manière très simple de savoir si une touche est utilisable et si nos trois encodeurs sont opérationnels également. Nous avons trois leds pour trois encodeurs, pour chaque encodeur, s'allumera une led spécifique si il reçoit un 1 parmi des 0.


Afin de mettre en place ce système, il nous faut un programme capable de balayer toutes les lignes de notre matrice via un décodeur.
Afin de mettre en place ce système, il nous faut un programme capable de balayer toutes les lignes de notre matrice via un décodeur.
Ligne 250 : Ligne 250 :
}
}
</syntaxhighlight></div>
</syntaxhighlight></div>
[[Fichier:B5_test_matrice.mp4|vignette|alt=test d'une touche pressée |test d'une touche pressée|right|800px]]
[[Fichier:B5_test_matrice.mp4|vignette|alt=test d'une touche pressée |test d'une touche pressée|right|800px]]
<br style="clear: both;" />
<br style="clear: both;" />
Ici, grâce à l'initialisation d'un tableau de la représentation en binaire des chiffres 0 à 7, il suffit de faire une procédure qui prend en paramètre le numéro de ligne souhaitée puis de vérifier dans le tableau quelle sortie mettre à 0 et 1. Nous avions remarqué que mettre les sorties pour les décodeurs sur le même port côte à côte facilite la programmation.
Ici, grâce à l'initialisation d'un tableau de la représentation en binaire des chiffres 0 à 7, il suffit de faire une procédure qui prend en paramètre le numéro de ligne souhaitée puis de vérifier dans le tableau quelle sortie mettre à 0 et 1. Nous avions remarqué que mettre les sorties pour les décodeurs sur le même port côte à côte facilite la programmation.
===Programme de détection===
([https://gitea.plil.fr/ktouron/se4-djadja-touron_pico/src/branch/main/CF_Clavier/prog/detect.c Code du test])
Dans le projet, nous avons eu des problèmes de lignes et colonnes qui n'était pas conduit entre notre carte et la matrice. Pour les identifier, nous avons mis en place un programme de détection qui lors de la pression d'une touche, va afficher grâce aux leds, de quel encodeur sa colonne régit puis quelle ligne et quelle colonne est activée. Ce programme permet ainsi de diagnostiquer rapidement les problèmes de conduction entre la carte et la matrice en identifiant précisément les correspondances défectueuses.
Pour cela, nous avons imaginer deux fonctions show_ligne et show_colonne, permettant d'afficher sur nos trois leds la ligne et la colonne. Si les leds affichent : double clignotements de la led du milieu  (allumée, éteint, allumé) puis (éteint, allumée, allumée) alors la ligne L6 (voir KiCad) et la 4e colonne du deuxième encodeur sont activées.
<div style="float:left; width: 48%; padding-left: 100px;"><syntaxhighlight lang="c" line="1">
void show_ligne(int i){
    turn_off_leds();
    for (int bit = 0; bit < 3; bit++){
        if (tab_outputs[i][bit] == 1) {
            PORTC |= (1 << (bit+LED1));
        }
        else
            PORTC &= ~(1 << (bit+LED1));
        }
    _delay_ms(SHOW_TIME);
    turn_off_leds();
}}
}
</syntaxhighlight></div>
<div style="float:right; width: 48%; padding-right: 100px;"><syntaxhighlight lang="c" line="1">
void show_colonne(){
    turn_off_leds();
    for (int i = Q0; i < Q2 + 1 ; i++){
        if (((inputs >> i) & 1) == 1) {
            PORTC |= (1 << (i+LED1));
        }
        else
            PORTC &= ~(1 << (i+LED1));
    }
    _delay_ms(SHOW_TIME);
    turn_off_leds();
}
</syntaxhighlight></div>
<br style="clear: both;" />
[[Fichier:B5_test_matrice.mp4|vignette|alt=test d'une touche pressée |test d'une touche pressée|center|800px]]
<br style="clear: both;" />

Version du 26 janvier 2025 à 13:10

Introduction

Ce wiki documente le développement d’un pico-ordinateur modulaire. Ce dernier est constitué d’une carte principale, composée d’une carte Shield permettant la connexion des cartes filles, et d’une carte mère (initialement simulée par une Arduino Uno avant sa conception finale). Plusieurs cartes filles sont prévues, chacune remplissant une fonction spécifique : gestion du clavier, affichage, port série, réseau, mémoire de masse et son.

Notre binôme est spécifiquement chargé du développement de la carte fille dédiée au clavier.

Toutes les ressources nécessaires, incluant les codes sources et les schémas, sont disponibles sur le dépôt Git suivant : Lien du Git

Carte Shield

Recto de la carte avant soudage des composants
Verso de la carte avant soudage des composants
Photo de la carte avec les composants soudés


Test

Allumage de LEDS sur Programmateur AVR

Avant de programmer directement sur le shield, nous avons allumé des LED pour tester la carte. Nous avons directement utilisé le code exemple fourni par l'IDE arduino en adaptant simplement les pins correspondant aux leds. Code du test

Lecture de la carte SD

Avant de programmer directement sur le shield, nous avons tester si la carte SD était bien détectée et lue. On a utilisé le code test présent dans l'IDE arduino en adaptant le port utilisé.Code du test

Carte SD bien détéctée

Connecteurs IDC HE10

Afin de tester nos connecteur HE10, nous avons utilisé un afficher 7 segments (Sparkfun 7 segments display). Pour pouvoir vérifier leurs fonctionnements nous avons branché l'afficheur sur chaque connecteur tour à tour. Le programme implanté est un simple connecteur à afficher sur l'afficheur. Voici une vidéo du compteur fonctionnant sur un des 5 connecteurs HE10. (Code ici (section : Example 2 SPI))

Test connecteur HE10 avec afficheur 7 segments.

Ordonnanceur

Pour les interruptions de notre ordonnanceur nous avons utilisé la procédure d'initialisation du minuteur avec le Timer1 disponible dans les cours de Mr.Redon en renseignant une période de 20ms. Nous l'avons disposé dans le fichier minuteur.c car nous n'allons plus le modifier pour le reste du projet. Dans un autre fichier process.c (avec process.h), nous allons y mettre nos structures pour les process et leurs états puis une union pour le temps qui le caractérise ainsi que toutes les fonctions associés.

Lien du répertoire

Processus

Structure

typedef struct {
    uint16_t adresseDepart;  //adresse de la fonction de départ 
    uint16_t adressePile;       //adresse de la pile d'exécution
    Etat etat;        
} Process;

Notre structure caractérisant un processus est définie par les trois champs ci-contre.

Une adresse de départ correspondant à la fonction du processus associé, l'adresse où se situe le processus dans la pile puis l'état de la tâche. Nous avons décidé de faire une structure à part entière compte tenu des multitudes d'états que nous pourrons ajouter pendant la progression du projet.

Etat d'un processus

typedef union {
  int sleepingtime;       
} Time;

typedef struct {
  int id;
  Time time;
} Etat ;

L'état d'un processus est alors une structure à deux champs (pour l'instant), l'id doit correspondre à un '#DEFINE' pour avoir un sens tandis que nous avons choisi de faire une union pour le temps qui caractérise notre état afin qu'il puisse avoir un nom et type de variable cohérent avec son état.(Ici seulement sleepingtime car seul l'état endormi existe pour le moment)

Tableau des états
Etat ACTIVE SLEEPY ... ...
Id 0 1 ... ...

Procédures principales

ISR

ISR(TIMER1_COMPA_vect, ISR_NAKED)    // Procédure d'interruption
{
  TCNT1=0;//reset timer
  /* Sauvegarde du contexte de la tâche interrompue */
  portSAVE_REGISTERS();
   table_process[indice_tache].adressePile = SP;
  /* Appel à l'ordonnanceur */
  selectProcess();
  /* Récupération du contexte de la tâche ré-activée */
  SP = table_process[indice_tache].adressePile;
  portRESTORE_REGISTERS();
  
  asm volatile ( "reti" );
}

L'ISR est ce qu'il va être executer lors des interruptions toutes les 20 millisecondes. Cela consiste à sauvegarder tous les registres de la tâche qui était en cours avant l'appel de l'ISR pour ensuite passer à la tâche suivante valide (c'est à dire active) sans oublier de charger les registres de la nouvelle tâche.

Nous devons également, à chaque fois avant de changer de tâche, enregistrer le pointeur de pile dans le processus afin de pouvoir revenir là ou elle en est la prochaine fois qu'elle sera appelée. Respectivement, une fois la nouvelle tâche choisie, le pointeur de pile doit prendre l'adresse de là ou en était la tâche avant qu'elle soit interrompue.

Séléction de processus

Lors de la séléction de processus, nous devons parcourir tous les processus jusqu'au moment où nous en trouvons un actif. Ainsi, il deviendra la tâche que nous traiterons lors du prochain intervalle de 20 ms. A ce stade, à chaque fois que la procédure selectProcess est appelée, nous devons réduire le temps de repos de chaque process endormi de 20 ms. D'autres conditions pourrons être ajoutée si ajout d'état a lieu. (voir l'ajout de selectProcess dans l'ISR)

Test

Clignotement de deux LED à fréquences indivisibles entre elles

Afin de vérifier la réussite de notre ordonnanceur à fonctionnement tourniquet, nous avons programmer deux simples tâches changeant l'état d'une LED. Une à une fréquence 200ms et l'autre 300ms (avec un _delay_ms).

void tache1(void){
  while (1){
    PORTC ^= (1 << PC0);
    _delay_ms(300);}}
void tache2(void){
  while (1){
    PORTD ^= (1 << PD7);
    _delay_ms(200);}}
test des frequences imprevisibles


Clignotement grâce à l'état endormi

Nous ajoutons l'état endormi afin que nos tâches ne mettent pas en pause l'ordonnanceur avec un _delay_ms. Pour cela, nous incorporons une fonction makeSleep qui permet de mettre la tâche qui l'appelle dans cet état.


void makeSleep(int t){
  table_process[indice_tache].etat.id = SLEEPY;
  table_process[indice_tache].etat.time.sleepingtime = t;
  TIMER1_COMPA_vect();}
void tache1(void){
  while (1){
    PORTC ^= (1 << PC0);
    _delay_ms(300);}}
void tache3(void){
  while (1){
    PORTC ^= (1 << PC3);
    //_delay_ms(1000);
    makeSleep(1000);}}
void tache2(void){
  while (1){
    PORTD ^= (1 << PD7);
    //_delay_ms(1000);
    makeSleep(700);}}
test des frequences imprevisibles


A noter que la première tâche executée ne doit pas être endormie.


Carte Fille Matrice de touches

Explications générales sur la réalisation de la carte

Clavier DELL
Matrice du clavier DELL
Matrice du clavier DELL

Pour concevoir la carte fille dédiée au clavier, nous avons réutilisé un ancien clavier DELL équipé d’une matrice déjà fonctionnelle. L’idée consiste à connecter les sorties de cette matrice directement à notre carte fille à l’aide d’un connecteur adapté.

Cependant, un défi se pose : la matrice du clavier comprend un total de 26 sorties (sans compter les LED et autres signaux). Cela dépasse largement les capacités en nombre de broches d’un ATmega328P.

Pour remédier à ce problème, nous avons mis en place une solution d’encodage. Nous avons utilisé un décodeur (3 vers 8) pour gérer les lignes et trois encodeurs (8 vers 3) pour gérer les colonnes. Les encodeurs sont montés en cascade pour réduire significativement le nombre de broches nécessaires. Cette solution permet de maximiser les ressources du microcontrôleur tout en gérant efficacement les signaux de la matrice.

Nous avons opté pour le décodeur 74HC138D (qui va envoyer un 0 parmi des 1) et trois encodeurs CD4532BM (qui cherche le premier 1 parmi des 0). On sent quand même les problèmes venir petit à petit.

Détails des composants

ATmega328P

  • Micro-contrôleur

74HC138D

  • Décodeur : envoie un 0 parmi des 1.

CD4532BM

  • Encodeur : Nous avons étudié la datasheet et comment la mise en cascade fonctionne.
Mise en cascade des encodeurs.
Mise en cascade des encodeurs.

On voit qu'un encodeur possède un bus de sortie de 5 bits. GS -> à l'état haut quand le composant reçoit un 1, 0 sinon. E0 -> à l'état haut quand il reçoit seulement des 0, 1 sinon. Puis Q2, Q1 ,Q0 la représentation binaire de l'entrée donnée.

Afin de les mettre en cascade, il faut relié le E0 d'un premier encodeur au Ei de l'autre. Ensuite, nous allons faire un "OU" entre les GS afin de savoir si un des encodeurs reçoit un 1, puis grâce aux GS des deux premiers encodeurs nous sauront quelle encodeur à reçu le 1 (si les deux sont & 0 alors c'est le troisième). De même, faire une porte "OU" sur Q2, Q1, Q0 afin de réduire le nombre d'entrées/sorties nécessaires.

74LS32

  • Porte logique Or pour la mise en cascade des encodeurs.

Connecteur HE10

  • Connecteur pour la liaison PicoShield-CarteFille

Connecteur de la matrice

AVR-ISP-6

  • Programmateur AVR

LED_SMD

  • 3 LEDs

Hardware

Kicad

Schématique de la carte Fille

Pour réaliser la carte fille, nous avions dans un premier temps conçu une carte avec une matrice de touches directement implenter sur celle-ci, mais jugeant que cela était trop simple nous avons été redirigé vers l'utilisation d'une matrice déjà réalisé. Ainsi nous avons avons reutiliser celle d'un vieux clavier dell en créant les empreintes à la bonne taille pour se connecter sur la matrice

Avant du PCB
Arrière du PCB
PCB vue 3D avant
PCB vue 3D arrière


Carte après soudure des composants

Différents Problèmes rencontrés

Avant même la réception de la carte, alors que nous commençons a essayé d'écrire un premier programme, nous nous rendions compte qu'envoyer un 0 parmi des 1 et de rechercher un 1 parmi des 0 ne fonctionnera pas (logique). Nous avons alors pris la décision de changer le décodeur car il était pour nous plus simple de changer un composant que les trois encodeurs. Nous avons opté pour lui de la même famille, le 74HTC238D.

Dès la réception de la carte, nous avons effectué un premier test avec une simple programmation de LED. Nous nous sommes rapidement aperçus que nous avions configuré deux noms d'étiquettes de reset distinctes sur KiCad : une pour l'AVR (RST) et une pour le connecteur HE-10 (RESET). Cependant, seul le reset du connecteur HE-10 était correctement relié au microcontrôleur. Pour résoudre ce problème, nous avons simplement connecté les deux broches de reset à l'aide d'un fil.

Lors de l'examen initial de notre carte, nous avons également constaté une erreur que nous avions commise. En effet, comme nous avions initialement conçu une matrice de touches sans encodeurs ni décodeurs, il n'était donc pas nécessaire d'intégrer des résistances de pull-up ou de pull-down. Nous avons alors juste rajouter ces composants, or il en résulte que lorsqu'une touche sera pressée, un 1 sera bien envoyé mais pas parmi des 0, parmi des états indéterminé. En général, les états indéterminés, on aime pas trop, surtout quand cela empêche ta carte de pouvoir fonctionner comme voulu. Il a donc été nécessaire d'ajouter des résistances de pull-down sur chaque colonne de notre clavier. Afin de simplifier cette modification, nous avons utilisé des résistances CMS que nous avons positionnées comme des dominos (verticalement), ce qui nous a permis de faire passer un fil de masse directement au-dessus des résistances.

METTRE UNE PHOTO DES RESISTANCES EN DOMINOS.

Une fois tous ces problèmes de base résolus, nous avons enfin pu nous attaquer à la programmation du clavier. Pour vérifier que toutes les touches étaient correctement détectées, nous avons réalisé un petit programme de test (Code du test). Ce programme nous a permis d’associer chaque LED à un décodeur différent via la broche GS des décodeurs. Ainsi, nous avons pu identifier quelles touches ne répondaient pas. Premièrement, beaucoup de touches ne répondaient pas, moins d'une dizaine. Nous avons alors fait de multiples tests au multimètre sur notre carte mais aussi sur une autre matrice de touches similaire. Une des hypothèses fut qu'il fallait bien étamer les pistes pour permettre la conduction entre la carte et la matrice. Nous avons alors rajouter de l'étain, puis + de touches commençaient à fonctionner. Il fallait alors un nouveau programme de test pour détecter quelles lignes ou colonnes n'étaient pas bien étamés afin de permettre une conduction (Code de détection). Il suffisait ainsi de rajouter de l'étain sur les colonnes et les lignes qui ne répondaient pas. Enfin, toutes les touches sont détectées lors de d'une pression !

Programmation de la carte

Programme de test

(Code du test)

Afin de tester chaque touche de notre matrice, nous avons décidé de faire le programme le plus simple possible en utilisant les broches GS de nos encodeurs. Il n'y a pas d'histoire de ligne ou de colonnes ici, si une touche est pressée alors le GS de l'encodeur ou sa colonne est reliée passera à l'état haut. Une manière très simple de savoir si une touche est utilisable et si nos trois encodeurs sont opérationnels également. Nous avons trois leds pour trois encodeurs, pour chaque encodeur, s'allumera une led spécifique si il reçoit un 1 parmi des 0.

Afin de mettre en place ce système, il nous faut un programme capable de balayer toutes les lignes de notre matrice via un décodeur.

int tab_outputs[8][3] = {
    {0, 0, 0},
    {1, 0, 0},
    {0, 1, 0},
    {1, 1, 0},
    {0, 0, 1},
    {1, 0, 1},
    {0, 1, 1},
    {1, 1, 1},
};


void maj_output(int lig){
    for (int i = 0 ; i < 3 ; i++){
        if (tab_outputs[lig][i] == 0){
            PORTC &= ~(1 << i);
        }
        else if (tab_outputs[lig][i] == 1){
            PORTC |= (1 << i);
        }
    }
}



Ici, grâce à l'initialisation d'un tableau de la représentation en binaire des chiffres 0 à 7, il suffit de faire une procédure qui prend en paramètre le numéro de ligne souhaitée puis de vérifier dans le tableau quelle sortie mettre à 0 et 1. Nous avions remarqué que mettre les sorties pour les décodeurs sur le même port côte à côte facilite la programmation.

Programme de détection

(Code du test)

Dans le projet, nous avons eu des problèmes de lignes et colonnes qui n'était pas conduit entre notre carte et la matrice. Pour les identifier, nous avons mis en place un programme de détection qui lors de la pression d'une touche, va afficher grâce aux leds, de quel encodeur sa colonne régit puis quelle ligne et quelle colonne est activée. Ce programme permet ainsi de diagnostiquer rapidement les problèmes de conduction entre la carte et la matrice en identifiant précisément les correspondances défectueuses.

Pour cela, nous avons imaginer deux fonctions show_ligne et show_colonne, permettant d'afficher sur nos trois leds la ligne et la colonne. Si les leds affichent : double clignotements de la led du milieu (allumée, éteint, allumé) puis (éteint, allumée, allumée) alors la ligne L6 (voir KiCad) et la 4e colonne du deuxième encodeur sont activées.

void show_ligne(int i){
    turn_off_leds();
    for (int bit = 0; bit < 3; bit++){
        if (tab_outputs[i][bit] == 1) {
            PORTC |= (1 << (bit+LED1));
        }
        else 
            PORTC &= ~(1 << (bit+LED1));
        }
    _delay_ms(SHOW_TIME);
    turn_off_leds();
}}
}
void show_colonne(){
    turn_off_leds();
    for (int i = Q0; i < Q2 + 1 ; i++){
        if (((inputs >> i) & 1) == 1) {
            PORTC |= (1 << (i+LED1));
        }
        else 
            PORTC &= ~(1 << (i+LED1));
    }
    _delay_ms(SHOW_TIME);
    turn_off_leds();
}