Gestion afficheurs, 2013/2014, TD1 : Différence entre versions

De Wiki de Projets IMA
(1. Gérer la liaison série)
(A. Le produit fini)
Ligne 266 : Ligne 266 :
  
 
Il fallait toutefois configurer la foxBoard avec minicom pour pouvoir ensuite accéder au ssh et ainsi finaliser ntore projet
 
Il fallait toutefois configurer la foxBoard avec minicom pour pouvoir ensuite accéder au ssh et ainsi finaliser ntore projet
 +
 +
[[File:Liaisonserie.JPG|thumb|liaison série avec FoxBoard]]
  
 
===='''B. Les autorisations pour l'exécution'''====
 
===='''B. Les autorisations pour l'exécution'''====

Version du 14 juin 2014 à 10:00

Vidéo : http://projets-imasc.plil.net/mediawiki/images/d/df/Gestion_afficheurs_compress%C3%A9.mp4

Projet SC :

Gestion d'afficheurs 16 segments


Partie programmation

Cette partie sera décomposée en X parties correspondant aux X difficultés rencontrées lors de ce projet.


1. Gérer la liaison série

Pour transmettre notre programme au banc d'essai, il faut tout d'abord initialiser ce banc d'essai et dire à notre contrôleur (ici l'ordinateur) où il se trouve.

Ceci ne constituais pas une grande difficulté étant donné que de nombreux travaux avaient déjà étaient fait sur la liaison série et que le code permettant de l'initialiser était sur le wiki :

  stty -F /dev/ttyACM0 9600 cs8 \
 -hupcl -icrnl -ixon -opost -onlcr -isig -icanon \
 -iexten -echo -echoe -echok -echoctl -echoke ignbrk

source : http://rex.plil.fr/Enseignement/Systeme/Projet.IMA3/systeme018.html

liaison série avec FoxBoard

Il fallait rajouter une sécurité à cette initialisation pour éviter de perdre des données :

  sleep 9999d 3</dev/ttyACM0 &

source : http://rex.plil.fr/Enseignement/Systeme/Projet.IMA3/systeme018.html

Ces données seront réutilisées lors du passage à la FoxBoard. Notre Port série est maintenant configuré, on peut envoyer notre code !

2. Mise en place de l'application sur ordinateur

A.Un afficheur : un caractère !

Nous avons vite compris que l'afficheur 16 segments était codé sur 16 bits.

Nous avons dans un premier temps affiché un code 16 bits (le 0x0001 par exemple) sur les 8 segments pour comprendre comment on pouvait gérer les 8 afficheurs.

Après plusieurs essais, nous avons opté pour la création d'un tableau qui contiendrai le message à afficher.

Dans le premier code nous avions un compteur d'afficheur qui recevait les cases du tableau. A chaque incrémentation compteur correspondait une incrémentation tableau, ce qui nous permettait d'afficher des caractères différents sur les afficheurs.


B. L'alphabet latin, un bonheur !

Maintenant que nous savons afficher, il faut maîtriser le contenu de l'affichage.

Nous avons donc mis chaque bit à 1 séparément (ex : 0x0001 puis 0x0002 puis 0x0004 ..) et reportés sur papier quel segment s'allumait pour ce bit.

Nous avons ensuite nommé chaque segment pour pouvoir créer nos caractères !

Cette technique nous permet de modéliser un afficheur :


afficheur 16 segments

C. Lire un fichier, c'est la base !

Notre application doit permettre de modifier le message affiché. Pour ce faire, notre page html modifiera un fichier et notre code C lira ce fichier pour créer le message. On présente maintenant le code de l'action "creer_message":

  void cree_message(FILE* fp)
  {
    int i=0,j=0;
    char car;
    while(j<8) { x[j]=0x0000; j++;}                 //Cette boule sert à initialiser les 8 afficheurs au caractère NULL (0x0000)
    while (!feof(fp))
      {
        fscanf(fp,"%c",&car);
        while((car!=carac[i])){i++;}               //Cette boucle stocke dans x le message du fichier en paramètre 
        x[j]=correspondance[i];                    //lit le premier caractere, on le cherche dans le tableau carc[]
        j++;                                       //on stocke dans x le code 16bits correspondant puis on incrémente la case j du message x
        i=0;                                       //on met i à 0 pour la recherche du caractère suivant
      }
    x[j--]=0x0000;                                 //Avec cette methode le dernier caractère est doublé car le fscanf n'est pas fait avant la boucle
    while (j<MAX_SIZE)                             //Ainsi on remet le dernier caractère du message x à NULL puis on met 8 caractère NULL qui serviront 
      {                                            //au défilement
        x[j]=0x0000;
        j++;
      }
  }



D. Faut que ça bouge !

Dans un afficheur publicitaire, les messages défilent de droite à gauche.

Pour reproduire cet effet, nous procédons en 3 étapes!

Étape 1 : Créer le message et afficher les 8 premiers caractères → Cette étape est décrite dans la partie précédente Étape 2 : Afficher la suite du message pas à pas → Pour ce faire, on utilise deux boucles, dont une qui commence quand les 8 premiers caractères du tableau ont été affichés → On parcours le tableau au complet en incrémentant à chaque période le code que l'afficheur reçoit de 1 dans le tableau

Étape 3 : Une boucle infinie → On utilise un «  while 1 » sur tout le main pour pouvoir revenir au début de notre tableau contenant le message et ainsi afficher le message en continu !

Cette action est l'action principale de notre application, c'est-à-dire que nous avons choisi de la mettre dans le main étant donné qu'elle boucle sur 1

Voici donc la partie du main qui gère le défilement:


    unsigned char low[MAX_SIZE];                            // correspondance est codé sur 16 bits, mais on ne peut envoyer que 8bits à la fois
    unsigned char high[MAX_SIZE];                           // on découpe donc le tableau message en 2 tableaux low et high
    int sd=serialOpen(SERIAL_DEVICE,SERIAL_BOTH);           // ouverture du port série
    int i=0,z=0,a=0,tot=0;
    while(1)
    {
       FILE*fp=fopen(argv[1],"r");
       if(fp==NULL){ perror("fopen(message)"); exit(-1); }  // vérification du bon déroulement de l'ouverture du fichier passé en argument
       cree_message(fp);                                    // on crée le message x
       for(i=0;i<MAX_SIZE;i++)                              // i va servir à lire dans le message x 
        {
          if (i>8) a++;                                     // cette ligne permet d'initialiser rapidement sans devoir décaler les 8 premiers caractères
          tot=i+a;                                          //NULL, puis on stocke l'endroit où on va lire dans tot (i + le nombre de fois ou on a été   
          for(z=0;z<8;z++)                                  // au dessus de i
               {
                 low[z] = x[tot] & 0x00ff;                  // La correspondance est en 16 bits, or l'envoi sur z se fait en 8bits, ces fonctions nous 
                 high[z]= x[tot]>>8;                        // permettent de stocker correctement sur z, z représentant le numéro de l'afficheur 
                 if(write(sd,&high[z],sizeof(char))!=1){ perror("main.write"); exit(-1); }  //Écriture sur l'afficheur
                 if(write(sd,&low[z],sizeof(char))!=1){ perror("main.write");  exit(-1); }
                 usleep(delai);
                 tot++;
               }
          if (tot>MAX_SIZE) i=MAX_SIZE;
        }                                                  // Cette ligne nous permet de sortir un peu plus rapidement de la
         tot=0;                                            //boucle lorsque le message a été affiché 
         i=0;                                              // on réinitialise les variables pour le message suivant, on ferme fp
         a=0;
         z=0;
         fclose(fp);
    }
message défilant sur les afficheurs
message défilant sur les afficheurs


On sait désormais lire notre message et le faire défiler, mais nous sommes loin d'avoir répondu au cahier des charges !

E. Le cahier des charges

Les deux grands axes du cahier des charges concernant la programmation sont les suivants :


1) Par le biais d'une page php (que nous verrons plus tard), nous modifierons un fichier.txt contenant le message à afficher. Ce message peut être modifié pendant l’exécution et dois défiler en continu.

2) Par le biais d'une page php, la vitesse de défilement peut être modifiée.


1) Le fichier texte !

La modification du fichier texte se fait dans une fonction écrire.php, voici son code :

    <?php
    $message=$_REQUEST['message'];                //on récupère le message de la page html
    file_put_contents("note.txt",$message);       //on écrit ce message dans note.txt
    echo "message=$message";
    ?>

La question que l'on se pose maintenant est "Comment faire appel à cette fonction?"

Grâce aux commande ajax, on peut récupérer une message sur la page html et appeler le fichier ercire.php

Voici les lignes du code html qui vont permettre de récupérer le message dans une case texte prévue à cet effet:

    <input type="text" id="message" value="message">
    <input type="submit" value="Envoyer" onclick="javascript:envoyer();">


Voici le code ajax qui va permettre d'appeler ecrire.php avec comme paramètre le message:

    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
    function envoyer(){
    var m=$('#message').val();
    $.ajax({url: 'ecrire.php', type: 'post', data: {message: m}});
    </script>
2) La vitesse de défilement

La vitesse de défilement doit pouvoir se gérer grâce à deux boutons sur la page html.

Un bouton fast qui va appeler la fonction rapide.php et un bouton slow qui va appeler la fonction lent.php

En cliquant sur ces boutons, l'utilisateur va envoyer un signe (SIGUSR1 pour Fast et SIGUSR2 pour slow)

Grâce aux fonctionx lent.php et rapide.php ainsi qu'au pid, le code c (corresp.c) va récupérer le signal envoyé ou non par l'utilisateur

Voici le code des boutons Fast et Slow ainsi que le code ajax qui renvoie aux fonction lent.php et rapide.php :

    <input type="submit" value="Fast" onclick="javascript:rapide();">
    <input type="submit" value="Slow" onclick="javascript:lent();">
    function rapide(){
    $.ajax({url: 'rapide.php', type:'post'});
    }
    function lent(){
    $.ajax({url: 'lent.php', type:'post'});
    }

Passons maintenant à la fonction rapide.php :

    define('SIGUSR1',10);                          // Ici les valeur des signaux SIGUSR1 et SIGUSR2
    define('SIGUSR2',12);
    $pid=trim(file_get_contents("pid.txt"));       // On récupère le pid (envoyé par le code c dans pid.txt) pour pouvoir communiquer avec le .c
    if(is_numeric($pid)){
    posix_kill($pid,SIGUSR1);                      // On envoie SIGUSR1 au code c
    $erreur=posix_get_last_error();                // Vérification que l'envoi s'est bien fait
    if($erreur!=0) echo 'Erreur : '.posix_strerror($erreur)."
";

Pour la fonction lent.php, il suffit de remplacer SIGUSR1 par SIGUSR2 !

Comment le code exploite ces signaux?

Dans le code qui gère le défilement, nous avons vu qu'a chaque fois que les 8 afficheurs étaient remplis, une pause usleep(delai) était effectuée

Ce "delai" est en fait une variable globale. Lorsque le code va recevoir un signal SIGUSR1 ou SIGUSR2, il va appeler respectivement les fonction "rapide" et "lent" et ainsi modifier la valeur de "delai"

Les fonction rapide et lent sont les suivant :

   void rapide(int sig){             // le paramètre est un entier qui représente le signal reçu, c'est le 10 et 12 que nous avions défini dans les .php
   delai=delai/2;}
   void lent(int sig){
   delai=delai*2;}

Ces fonction sont simples, la complexité réside dans la réception et l'appel à ces fonctions.

Ceci se fait dans le main grâce au code suivant:

   FILE*fd=fopen("pid.txt","w");
   if(fd==NULL){perror("open(pid)"); exit(-1);}           //on écrit le pid dans le fichier pid.txt, pour pouvoir communiquer avec le php
   fprintf(fd,"%d",getpid());
   fclose(fd);
   memset(&action,'\0',sizeof(action));                  //on ecrase l'adresse de action pour ne pas mélanger les fonctions rapide et lent
   action.sa_handler=rapide;                             // on stocke la fonction rapide dans action
   if(sigaction(SIGUSR1,&action,NULL)<0){perror("sigaction()"); exit(-1);}  // on appelle action si on reçoit SIGUSR1
   memset(&action,'\0',sizeof(action));
   action.sa_handler=lent;                                                  // pareil pour SIGUSR2 et lent
   if(sigaction(SIGUSR2,&action,NULL)<0){perror("sigaction()"); exit(-1);}


Tout notre code est prêt et fonctionne, il reste maintenant à tout embarquer sur la foxBoard !


3. Fox....Board to be alive !

A. Le produit fini

Le produit fini de notre projet est un système embarqué (la foxBoard) qui contiendra notre application et qui, dès que la foXboard sera branchée aux afficheurs, exécutera l'application.

Il fallait toutefois configurer la foxBoard avec minicom pour pouvoir ensuite accéder au ssh et ainsi finaliser ntore projet

liaison série avec FoxBoard

B. Les autorisations pour l'exécution

Sur la foxBoard, comme sur l'ordinateur, le code html ne peut pas modifier notre fichier "note.txt" si celui-ci n'est pas autorisé www-data Par un raisonnement similaire, le code html ne peut pas récupérer la valeur dans "pid.txt" si ce fichier n'est pas autorisé sur www-data


Une fois que ces autorisations sont faites, que les fichiers sont placés au bon endroit (var/www/html) et que l'application est compilée, il faut créer un script permettant de l'exécuter au démarrage


C. Le script rc.local

La foxBoard contient (comme tout système unix) un fichier rc.local se lançant au démarrage. Par défaut celui ci est vide mais si vous voulez lancer une application au démarrage, il faut entrer dans ce fichier les lignes de commandes que vous entreriez dans le terminal pour lancer l'application.


Voici notre scipt qui finalise donc le projet:

  stty -F /dev/ttyACM0 9600 cs8 \                        //initialisation du port série (vu au premier paragraphe)
 -hupcl -icrnl -ixon -opost -onlcr -isig -icanon \
 -iexten -echo -echoe -echok -echoctl -echoke ignbrk
 sleep 9999d 3</dev/ttyACM0 &                            //Cette tache est une tache de fond, elle met un certain temps à s'exécuter                     
 sleep 5                                                 //on ajoute donc une temporisation avant de passer à l'exécution de l'application
 ( cd /var/www/html ; su www-data -c "./test note.txt" ) & //Exécution
 exit 0

4. Les problèmes nous font avancer  !

On a trouvé judicieux de recenser les problèmes rencontrés lors de ce projet, nous remercions également Mr Xavier REDON pour l'aide et les explications apportées durant ce projet.

1 --> trouver à quel bit correspond quel segment (difficulté faible, mais nous avons tout de même du réfléchir pour trouver une solution rapide)

2 --> La gestion des signaux était quelque chose de nouveau et a engendré la difficulté 3.

3 --> Comprendre et maîtriser les autorisations.

4 --> Bloquage lors de la réception du signal. En effet notre code fonctionnait mais nous avions oubliés les sécurités, on créait donc une boucle infinie. Suite à une intervention de M. REDON, les vérifications ont été codés et le programme fonctionnait parfaitement.

5 --> le minicom pour configurer la foxBoard.


Sur ces difficultés, nous retiendrons particulièrement les sécurités sur les vérifications de la bon ouverture des fichiers

Le projet est terminé, le cahier des charges comblé, nous vous laissons avec une vidéo du travail final!

Partie électronique :

Première séance :

Découverte du projet et du cahier des charges.

Prise en main du logiciel altium et suivi partiel du tutoriel.


Deuxième séance :

Fin du tutoriel proposé. Compréhension du schéma.

Définition du cahier des charges de notre projet et proposition de schéma électronique.

Découverte d'un possible problème sur nos afficheurs à anode commune (contrôle par la masse)


Troisième séance :

Mise en place du schéma électronique établi à la séance précédente sur altium.

Correction des quelques problèmes rencontrés puis simulation (réussie) du schéma.

Problème des afficheurs confirmé (courant et tension admissibles), étude avec les deux professeurs des solutions possibles.

Câblage d'une solution retenue (driver de leds + 2 demi afficheurs) -> la solution résolve partiellement le problème (on peut fournir une tension et un courant correct).

Commande de 4 transistors pouvant résoudre le dernier problème électronique (contrôle des afficheurs).


Quatrième séance :

Câblage de l’autre moitié du premier afficheur. Le reste ne sera pas câblé car trop long et inutile pour résoudre le problème des transistors.

Les transistors n’ont pas permis de contrôler séparément chacun des afficheurs, car leur tension de seuil de déclenchement est trop élevée par rapport à la tension que l’on récupère déjà à côté pour les drivers de leds et afficheurs.

On a cependant une petite simulation de 3 caractères (IMA) pour montrer que notre montage fonctionne (on peut afficher les caractères désirés, mais le défilement ne s'effectue pas. Il faudrait trouver quatre transistors capables de se déclencher avec les 3,3V max que la nanoboard peut fournir, sachant que les leds s'allument avec une tension minimale de 3,8V).


Explications détaillées :

Après avoir pris en main la NanoBoard grâce au tutoriel proposé (http://rex.plil.fr/Enseignement/Systeme/Projet.IMA3/tutoriel_nanoboard.pdf), nous avons établi un cahier des charges pour notre projet.
Voici l’idée globale retenue pour le circuit :

Schéma théorique


Explications :


On récupère les informations du port série, ainsi que son signal d’horloge. On mémorise ces informations grâce aux bascules, elles même gérées par un démultiplexeur relié à l’horloge du port série. On cadence ainsi les informations afin que le multiplexeur les reçoivent une par une. Le décodeur va ensuite choisir tour à tour un afficheur grâce au signal d’horloge, et le multiplexeur envoie ainsi une information sur tous les afficheurs, mais seulement un sera allumé à ce moment grâce au décodeur.
Il nous est possible d’envoyer qu’un seul bus de 16 bits sur les afficheurs reliés en dérivation, car la fréquence de sélection du décodeur est bien plus rapide que la fréquence rétinienne de l’homme. On a donc l’impression que le message est fixe avec un caractère affiché sur chaque afficheur, alors qu’en réalité il a toujours qu’un seul afficheur d’allumé.

Après plusieurs essais, voici notre circuit définitif sur Altium :

Circuit sur Altium


Nous avons dissocié les deux compteurs afin de pouvoir contrôler la fréquence d’affichage des caractères.

Simulation :


Simulation

Dans le rectangle du haut nous pouvons envoyer le code binaire d’un caractère à afficher. Le rectangle du dessous permet d’envoyer ce code en mettant l’horloge à 1. Au milieu nous gérons la fréquence d’affichage (ici 2Hz afin d’avoir le temps de prendre une photo). Puis dans l’avant dernier rectangle on peut voir que notre code binaire se déplace correctement. Et dans le dernier un voit quel afficheur est sélectionné par le décodeur.

Tests sur l'oscilloscope


On voit ici de D7 à D4 les 4 bascules, et de D0 à D3 les 4 codes binaires envoyés.

Câblage :


Après cette simulation concluante, nous avons commencé le câblage. Celui-ci s’est avéré fastidieux dès le départ, car le simple fait d’avoir 4 afficheurs pour lesquels il fallait 16 fils chacun nous promettait de nous y faire consacrer une bonne partie de la séance.
En regardant la datasheet de la NanoBoard sur internet nous avons constaté que les sorties de la carte pouvaient fournir au maximum 3,3V chacune (dont une à 5V), et notre encadrant nous a dit qu’il valait mieux ne pas tirer plus de 20mA par sortie.
Le problème alors rencontré est que nos afficheurs ont une led par petits segments, et deux par grands segments. Après quelques tests avec l’encadrant, nous avons constaté qu’il fallait minimum 3,8V pour un grand segment si on voulait l’allumer avec 20mA. La NanoBoard ne permettait donc pas d’allumer tous les segments (ce sont des afficheurs à anode commune).

Datasheet des afficheurs
Câblage









Un rapide calcul nous a permis de déterminer la valeur des résistances à mettre avant chaque afficheur : une résistance de 50Ω (47Ω) pour un petit segment, une de 300Ω pour un grand (270Ω).
La solution retenue après discussion avec les deux encadrants a été d’utiliser deux amplificateurs (drivers ULN2803) pour avoir une bonne tension sur nos afficheurs. Le problème créé avec cette solution est que nous ne pouvions plus contrôler séparément nos afficheurs. Nous avons donc eu l’idée d’utiliser un transistor par afficheur, qui recevrait l’ordre de commande des sorties de la carte correspondants à celles du décodeur.
Après plusieurs tests, nous avons compris que ceux utilisés (des TMOS) ne pourraient fonctionner car leur tension de seuil VGS est plus élevée que les 3,3V que peut fournir notre carte.

Julian 7.jpg



Conclusion :


Ce projet nous a permis de mettre en pratique nos connaissances informatiques et électroniques acquises au cours de l'année.
Nous aurions aimé pouvoir relier directement la partie informatique à la partie électronique afin de pouvoir tester la totalité du projet.