Leçons de niveau 15

Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/Utiliser des shields Arduino avec les FPGA

Une page de Wikiversité.
Aller à : navigation, rechercher
Début de la boite de navigation du travail pratique
Utiliser des shields Arduino avec les FPGA
Image logo représentative de la faculté
T.P. no 10
Leçon : Very High Speed Integrated Circuit Hardware Description Language

Ce TP est de niveau 15.

Précédent : TPs ATTiny861 avec Altera
Suivant : Sommaire
Icon falscher Titel.svg
En raison de limitations techniques, la typographie souhaitable du titre, « Travail pratique : Utiliser des shields Arduino avec les FPGA
Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/Utiliser des shields Arduino avec les FPGA
 », n'a pu être restituée correctement ci-dessus.

Comme son titre l'indique, ce chapitre est destiné à étudier les liaisons dangereuses entre les shields destinés aux Arduinos et les FPGA. Ici, le mot shield sera pris dans un sens très large : tout ce qui se connecte à l'Arduino rapidement, y compris avec des fils et des plaques à essais.

La très grande majorité des shields Arduino sont prévus pour être alimenté en V. L'utilisation de ces shields avec des FPGA qui fonctionnent en 3,3 V nécessite donc un certain nombre de précautions. Celui qui craint le plus dans l'histoire est naturellement le FPGA.

Il existe des cartes FPGA avec un connecteur de type Arduino, donc capable de recevoir un shield :

  • la DE0 nano Soc de chez Terasic en est un premier exemple. Elle est équipée d'un cyclone V de chez Altera(Euh... pardon, de chez Intel) . Le connecteur Arduino de cette carte fournit du V à l'endroit normal du V des cartes Arduino. Cela veut dire que si vous mettez un shield sur cette carte, vous risquez d'endommager le FPGA. Il suffit d'avoir 3 boutons en pull-up et vous avez automatiquement du V sur quelques entrées du FPGA !
  • La carte Arty en est un autre exemple de chez Digilent. Nous ne l'avons pas encore exploré (car nous n'en possédons pas) et ne savons donc pas comment est géré le V. Une phrase dans la documentation nous laisse penser qu'on a le même type de problème qu'avec la carte précédente : The Arty is not compatible with shields that output V digital or analog signals. Driving pins on the Arty shield connector above V may cause damage to the FPGA.

Pour cela nous avons décidé de faire nos propres shields en gardant quelques bonnes idées des shields commercialisés. Il est difficile cependant de se mettre un bandeau sur les yeux et de ne pas regarder vers l'orient. Même si construire est une idée raisonnable pour un établissement qui enseigne l'électronique et donc la fabrication de cartes, les bas prix des shields venant de Chine la met à mal pour les hobbyistes et même les enseignants qui ont des budgets de plus en plus serrés. Nous avons en effet trouvé un shield avec quelques boutons et un afficheur LCD de deux lignes de 16 caractères pour un peu moins de et un shield multifonction pour un peu plus de . Tous les deux ont été testés et fonctionnent parfaitement avec des Arduino. Nous allons donc explorer leur utilisation dans ce chapitre en remplaçant l'Arduino par un FPGA.

Sommaire

Étude du shield multi fonction[modifier | modifier le wikicode]

Le shield multifonction est décrit par exemple ici.

Cette carte est prévue pour un fonctionnement en V. Les 4 digits d'affichage sept segments sont pilotés par deux registres à décalage qu'il nous faudra étudier pour bien comprendre ce que l'on cherche à réaliser. Le point important à ce stade est qu'ils ont été choisis en CMOS (74HC595) et que par conséquent le 3,3 V ne leur fait pas peur. L'autre problème éventuel est de savoir si les afficheurs fonctionnent à 3,3 V. La réponse est OUI.

Début d’un principe
Fin du principe

Présentation[modifier | modifier le wikicode]

Prenez le code d'exemple présenté ici. Nous vous conseillons fortement son utilisation avec une carte Arduino pour une prise de contact rapide. On reproduit ce code ici

/* Define shift register pins used for seven segment display */
#define LATCH_DIO 4
#define CLK_DIO 7
#define DATA_DIO 8
 
/* Segment byte maps for numbers 0 to 9 */
const byte SEGMENT_MAP[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0X80,0X90};
/* Byte maps to select digit 1 to 4 */
const byte SEGMENT_SELECT[] = {0xF1,0xF2,0xF4,0xF8};
 
void setup ()
{
/* Set DIO pins to outputs */
pinMode(LATCH_DIO,OUTPUT);
pinMode(CLK_DIO,OUTPUT);
pinMode(DATA_DIO,OUTPUT);
}
 
/* Main program */
void loop()
{
 
/* Update the display with the current counter value */
WriteNumberToSegment(0 , 0);
WriteNumberToSegment(1 , 1);
WriteNumberToSegment(2 , 2);
WriteNumberToSegment(3 , 3);
}
 
/* Write a decimal number between 0 and 9 to one of the 4 digits of the display */
void WriteNumberToSegment(byte Segment, byte Value)
{
digitalWrite(LATCH_DIO,LOW);
shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_MAP[Value]);
shiftOut(DATA_DIO, CLK_DIO, MSBFIRST, SEGMENT_SELECT[Segment] );
digitalWrite(LATCH_DIO,HIGH);
}

Ce programme est sensé afficher 0123 sur les 4 digits. Il montre aussi quelques détails importants sur le câblage :

  • le transfert des données se fait à l'aide de trois fils (seulement) et c'est ce qui en fait tout son intérêt. Ces trois fils sont décrit maintenant
  • le fil de donnée DATA_DIO est câblé en broche 8 Arduino
  • le fil d'horloge de transfert CLK_DIO est câblé en broche 7 Arduino
  • le fil de mémorisation est câblé en broche 4

Ces informations sont vitales pour nous.

Revenons un peu sur cette histoire des trois fils. Un afficheur sept segments 4 digits comporte au moins 12 fils :

  • 7 fils pour chacun des segments
  • 1 fil pour le point
  • 4 fils pour la sélection de chacun des digits.

Réussir à n'utiliser que trois sorties pour l'utiliser est donc une idée intéressante. Elle est réalisable en remplaçant l'écriture parallèle par une écriture série. Ce qui rend la mise en œuvre délicate est le multiplexage des afficheurs : il faut mettre à jour sans arrêt l'affichage pour faire croire au lecteur qu'il est simultané sur les quatre afficheurs alors qu'il n'en n'est rien. En programmation, ceci peut être réalisé à l'aide d'une interruption qui s'occupe des transferts dans les deux 74HC595. En VHDL, le matériel doit s'occuper de tout cela.

Notre montage d'essai[modifier | modifier le wikicode]

Connecter un shield multifonction avec une nexys 3

Nous n'avons pas beaucoup de fils à brancher à cause de l'utilisation des registres à décalage. 5 fils en tout (alimentation comprise). Le connecteur PMod des cartes Digilent est déjà décrit dans un autre chapitre et permet de brancher 6 fils (y compris alimentation). Un simple connecteur PMod est donc suffisant et donc facile à trouver sur les cartes Digilent.

  • le Vcc 3,3 V de PMod est amené au V du shield
  • La masse de PMod est amenée à la masse du shield
  • le suivant, fil de donnée, est amené à la broche 8 du shield
  • le suivant, fil d'horloge, est amené à la broche 7 du shield
  • le suivant, horloge de mémorisation, est amené à la broche 4 du shield.

Tous les numéros apparaissant dans cette description correspondent à la sériegraphie du shield.

Évidemment, une connexion aussi simple ne permet pas d'avoir accès à toutes les fonctionnalités du shield. Pour cet exemple seuls les 4 digits des afficheurs sept segments sont gérés par le FPGA. Si vous voulez utiliser l'information des boutons poussoirs il vous faut savoir qu'ils sont montés avec une résistance de tirage vers le haut (pull-up) et sont connectés aux broches serigraphiées A1, A2 et A3 du shield. Donc trois fils en plus. Les 4 leds sont reliées à 13, 12, 11 et 10 encore 4 fils. Le buzzer est relié à 3 avec donc un seul fil.

Du VHDL pour afficher une donnée sur 4 digits[modifier | modifier le wikicode]

Résoudre ce problème est pédagogiquement très intéressant pour l'étude des registres à décalages et aussi pour la gestion d'un séquencement à l'aide d'un compteur.

Dans la suite nous serons amenés à réaliser deux types de signaux :

  • un tick : signal à un pendant une seule période d'horloge globale du FPGA (nous utiliserons cette dénomination anglo-saxonne dans la suite de ce chapitre)
  • une horloge : signal qui reste à 1 sur plusieurs fronts et à 0 sur plusieurs fronts de l'horloge globale du FPGA

Le tick ne servira qu'à l'intérieur du FPGA pour réaliser des montages synchrones tandis que l'horloge pilotera des circuits externes incapables de fonctionner à la fréquence du FPGA. Tout ceci va très vite devenir plus clair avec les mises en pratiques dans les exercices qui suivent.

Introduction : Exercice 1[modifier | modifier le wikicode]

Diminuer la fréquence de comptage d'un compteur 16 bits

Nous allons commencer par réaliser un compteur 16 bits. Nous allons afficher son comptage sur les Leds disponibles : 8 sur la Nexys 3 (Digilent) et 16 sur la DE2-115 (Terasic). Il est donc impossible d'utiliser l'horloge 100MHz de la carte Nexys 3 et espérer voir quelque chose à l’œil. Il faut donc utiliser un autre compteur pour diminuer cette fréquence. Ceci a été présenté plusieurs fois dans ce livre et ailleurs :

Mais à chaque fois il s'agissait de prendre le bit de poids fort du compteur et de l'utiliser comme horloge de l'étage suivant. Cette façon de faire est assez facile à comprendre mais déconseillée pour une utilisation dans un FPGA. On doit lui préférer la méthode synchrone : toutes les horloges de tous les composants sont reliées à une horloge commune.

Nous allons donc réaliser l'ensemble demandé dans les règles de l'art : les deux compteurs utiliseront la même horloge. On vous demande de réaliser l'ensemble présenté par le schéma ci-dessus, en commençant par trouver l'équation combinatoire demandée. Il s'agit de réaliser un tick sur ENO, c'est à dire un signal qui ne vaut un que pendant une seule période d'horloge.

Le multiplexeur et le transcodeur : Exercice 2[modifier | modifier le wikicode]

Schéma complet à réaliser

On vous donne le schéma complet de ce que l'on cherche à faire dans la figure ci-contre. On vous demande de réaliser le multiplexeur et le transcodeur (à droite du schéma).

Le multiplexeur ne présente aucune difficulté de réalisation.

Le transcodeur non plus. Il vous faut seulement savoir qu'il faut un '0' pour allumer un segment et que le segment g est poids faible.

En fin de l'exercice 2 vous aurez donc le compteur 25 bits, le compteur 16 bits, le multiplexeur commandé par 4 interrupteurs et le transcodeur.

Cet ensemble peut être testé sur les afficheurs de votre carte FPGA. Vous prendrez quatre interrupteurs pour la sélection même si le séquenceur de l'exercice 3 n'en délivrera que deux. Cela vous permettra de savoir comment on sélectionne une seul afficheur à la fois et surtout ce qui se passe lorsque vous en sélectionnez deux.

Le séquenceur : Exercice 3[modifier | modifier le wikicode]

Table de séquencement partielle

Comme d'habitude, l'affichage consiste à réaliser des choses visibles et d'autres non visibles. Par exemple, faire défiler des nombres est intéressant si cela est visible. Mais la commutation des afficheurs, elle, reste invisible. Ceci nécessite la réalisation de plusieurs fréquences :

  • une d'environ 2,98 Hz (= 100MHz / 2**25) nécessite donc un compteur de 25 bits sera utilisée pour faire défiler un compteur de 16 bits qui sera affiché
  • une d'environ 1,56 MHz (= 100MHz / 2**6) servira à envoyer les données au shield (servira d'horloge de données)
  • une d'environ 1525 Hz (= 100MHz / 2**16) servira à commuter les afficheurs

Nous allons essayer de respecter les règles matérielles des FPGA pour les horloges en question. Cela veut dire qu'en fait ce ne seront pas des horloges mais des signaux de validation. Nous n'avons pas assez utilisé cette règle dans ce livre et essayerons de résoudre ce problème plus tard.

Réaliser le séquenceur. La difficulté, bien sûr est de réaliser une partie matérielle qui est en fait du combinatoire. Nous allons vous aider en vous présentant un extrait de la table de vérité (figure ci-contre). Une bonne compréhension de celle-ci est nécessaire pour résoudre ce problème.

Une bonne exploration de la table de séquencement vous montre que :

  • les bits Q17 et Q16 serviront à la sélection du digit en cours d'affichage : il y en a 4 donc deux bits suffisent (cela n'apparaît pas sur la table de séquencement)
  • les bits Q24 à Q18 ne sont pas utiles au séquencement : les équations logiques ne les feront donc pas apparaître
  • il y a deux types de sortie :
    • tick ne sont définis que pour une période d'horloge : Eno et Load. Ils seront utiles au fonctionnement interne, c'est à dire au FPGA mais pas au shield directement.
    • horloges utiles à l'extérieure : Done. On ne peut pas prendre un tick pour cela car cela serait bien trop rapide pour les circuits 74HC595D du shield.
  • le 0->1->0 qui apparaît en dernière colonne a pour signification que l'on doit réaliser une période d'horloge sur une ligne. Le seul moyen de faire cela est d'utiliser le bit de poids immédiatement plus petit, à savoir Q5.

1°) Exprimer Eno et Load en fonction de Q(15 downto 0)

2°) Exprimer Done en fonction de Q(15 downto 6)

3°) On vous donne la réalisation de clk1MHz

clk1MHz <= cmpt(5) when cmpt(15 downto 6) >= "0000000001" 
           and cmpt(15 downto 6) < "0000010000" else
             '0';

N'aviez-vous pas remarquer que cette "horloge" ne fonctionne pas tout le temps, seulement après un Load ?

4°) Complétez ce qui est nécessaire pour réaliser le séquenceur complet

5°) A l'aide d'un code un peu similaire à celui ci-dessous, on vous demande d'essayer encore une fois votre code sur votre carte FPGA (sans shield)

with sel select
    s <= "0001" when "00",
         "0010" when "01",
         "0100" when "10",
         "1000" when others;

Le composant "Load_And_Send" : Exercice 4[modifier | modifier le wikicode]

Schéma complet à réaliser

On redonne ci-contre, la figure que l'on cherche à réaliser. Il ne reste plus qu'à réaliser le composant du bas (à droite) que l'on a appelé load_and_send.

  • Une des entrées de ce composant est "sel" mais sur deux bits. Un code comme celui donné dans l'exercice 3 (5° question) sera nécessaire pour le transformer en sélection de 1 parmi 3.
  • L'autre est naturellement la donnée qui sort du transcodeur.

Ce que l'on doit faire avec ce composant est de former un signal de 16 bits avec les deux données qui n'en font que 11, et ceci lors d'un LOad synchrone. On vous rappelle que ceci peut se faire avec le code VHDL :

if rising_edge(clk) then
   if load = '1' then
        s16bits <= s & "0000" & dataIn & '1';  
   end if;
end if;

où l'opérateur '&' en VHDL est l'opérateur de concaténation. Le '1' complètement à droite est là pour éteindre le point.

  • sa troisième fonctionnalité est de réaliser un décalage vers la droite quand son entrée 'en' est à un.

Quand tout cela sera réalisé, il sera grand temps de réaliser l'ensemble complet.

Le code complet corrigé[modifier | modifier le wikicode]

Voici le code complet correspondant à la résolution de notre problème.

Avec ce code source, seule la gestion du point n'est pas correcte : pas de point pour le digit des unités comme nous le désirons , mais un point sur les trois autres digits !

Comme la photo l'a montré, nous utilisons le connecteur JD d'une carte Nexyx 3. Nous donnons donc le fichier ucf pour cette carte :

## This file is a general .ucf for Nexys3 rev B board
## To use it in a project:
## - remove or comment the lines corresponding to unused pins
## - rename the used signals according to the project

## Clock signal
NET "clk_100MHz"            LOC = "V10" | IOSTANDARD = "LVCMOS33";   #Bank = 2, pin name = IO_L30N_GCLK0_USERCCLK,   Sch name = GCLK
Net "clk_100MHz" TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 100000 kHz;

##JD, LX16 Die only
#NET "JD<0>"  LOC = "G11" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L40P,   Sch name = JD1
#NET "JD<1>"  LOC = "F10" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L40N,   Sch name = JD2
#NET "JD<2>"  LOC = "F11" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L42P,   Sch name = JD3
#NET "JD<3>"  LOC = "E11" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L42N, 
NET "LatchClk"  LOC = "F10" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L40N,  Sch name = JD2
NET "ShiftClk"  LOC = "F11" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L42P,  Sch name = JD3
NET "DataOut"   LOC = "E11" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L42N,  Sch name = JD4
#NET "JD<4>"  LOC = "D12" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L47P,    Sch name = JD7
#NET "JD<5>"  LOC = "C12" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L47N,    Sch name = JD8
#NET "JD<6>"  LOC = "F12" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L51P,    Sch name = JD9
#NET "JD<7>"  LOC = "E12" | IOSTANDARD = "LVCMOS33";   #Bank = 3, Pin name = IO_L51N,    Sch name = JD10

Comptage décimal : Exercice 5[modifier | modifier le wikicode]

Cascader des compteurs décimaux pour 4 digits

Le compatge précédent affiche en hexadécimal. Les informaticiens savent le lire mais pas le commun des mortels. On vous demande donc de remplacer le compteur sur 16 bits par quatre compteur BCD cascadés.

Le schéma de principe est présenté dans la figure ci-contre. L'idée générale est de remplacer le compteur 16 bits de la section précédente par quatre compteurs cascadés : un par digit.

Nous n'avons pas nommé les fils internes dans le figure. C'est la première étape de ce que vous ferez si vous ne voulez pas vous perdre dans le câblage des composants.

Indication : Vous disposez d'un compteur décimal BCD cascadable complet:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CounterBCD is
   port( EN: in std_logic;
 	 Clock: in std_logic;
 	 Reset: in std_logic;
	 ENO : out std_logic;
 	 Output: out std_logic_vector(3 downto 0));
end CounterBCD;
 
architecture Behavioral of CounterBCD is
   signal cmpt: std_logic_vector(3 downto 0);
	signal s_en_cmpt: std_logic_vector(4 downto 0);
begin   process(Clock,Reset)
   begin
      if(rising_edge(Clock)) then
		  if Reset='1' then
         cmpt <= "0000";
 	      elsif EN='1' then
	         if cmpt="1001" then
	            cmpt<="0000";
	         else
	           cmpt <= cmpt + 1;
	         end if;
         end if;
      end if;
   end process;
   Output <= cmpt;
	s_en_cmpt <= en & cmpt;
   with s_en_cmpt select
     ENO <= '1' when "11001",
            '0' when others;
end Behavioral;

Voici la solution correspondante.

Réalisation d'un réveil : Exercice 6[modifier | modifier le wikicode]

Réaliser un compteur des heures minutes

Transformer l'ensemble des quatre compteurs décimaux en un ensemble capable de compter des heures et minutes.

La résolution de ce problème nécessitera d'utiliser

  • le reset pour les compteurs dizaine et unité des heures : détection de 23 et du en sur les unités passe à 00 à l'aide du reset (bouclage synchrone par du combinatoire, synchrone car le reset est synchrone)
  • pour les minutes, le comptage des dizaine nécessitera la réalisation d'un compteur modulo 6. Il s'agit de partir du compteur BCD de la section précédente et d'exécuter une simple modification.

L'ensemble du travail à réaliser vous est présenté dans la figure ci-contre. Cet ensemble remplacera le compteur 16 bits qui attaquait le multiplexeur. Nous gardons donc la fréquence de 3 Hz pour ne pas attendre trop longtemps le passage de 23:59 à 00:00 qui est la difficulté de cet exercice.

Et si l'on mettait un processeur pour piloter tout cela[modifier | modifier le wikicode]

Maintenant que nous avons réalisé un périphérique fonctionnel, il est grand temps de le piloter par un processeur. Ce travail va consister donc à remplacer le compteur 16 bits de l'exercice 4 par deux ports d'un processeur. Nous allons utiliser pour cela l'ATMega16 déjà décrit dans ce livre.

Exercice 7 : Réalisation simple avec deux PORTs[modifier | modifier le wikicode]

Réalisation d'un réveil à l'aide d'un processeur sur notre shield multifonction

L'objectif de cet exercice est d'arriver au même point que l'exercice 6 mais avec un processeur. Il s'agit donc de faire défiler des chiffres en format hh:mm, deux digits pour les heures et deux pour les minutes. Le transcodage matériel est gardé, cela implique que les données seront sur 16 bits. Pour simplifier le dessin de la figure, un certain nombre de détails n'ont pas été présentés :

  • le fichier io.vhd n'est pas dessiné mais une partie de son contenu seulement (le process iowr qui est le seul qui nous intéresse)
  • le fichier complet du processeur qui est le fichier complet contenu dans le FPGA n'est que très partiellement présenté

Malgré cette représentation partielle par le dessin, nous espérons que le travail à réaliser vous paraîtra clair :

  • retirer le compteur 16 bits de l'exercice 6 et de le remplacer par une entrée sur 16 bits
  • réaliser l'entrée 16 bits avec deux PORTs du processeur. On prendra PORTB pour les 8 bits de poids faible, donc les minutes et PORTC pour les 8 bits de poids fort, donc les heures
  • le composant top de l'exercice 6 amputé de son compteur 16 bits, sera ensuite changé en composant de "io.vhd" et câblé correctement. Ses trois sorties seront amenées jusqu'aux sorties du processeur
Début d’un principe
Fin du principe

Réaliser ensuite le programme qui compte comme un réveil. 0n vous conseille cependant d'utiliser l'incrémentation ++, habituelle en C, et de la faire suivre d'une série de tests.

Exercice 8 : Réalisation d'un réveil complet[modifier | modifier le wikicode]

Nous allons utiliser les ressources complètes du shield multifonction pour réaliser un réveil complet. Il s'agit d'utiliser en plus des afficheurs, les trois boutons et le buzzer. Il doit être possible de régler l'heure de réveil et l'armement de ce dernier. L'heure courante sera gardée assez rapide pour les tests.

Le cahier des charges est le suivant :

  • le bouton de gauche est utilisé pour l'armement du réveil et allume une LED.
  • le bouton du milieu est utilisé pour passer en mode réglage (incrémentation) de l'heure de réveil. Un nouvel appui passe en mode décrémentation. Enfin un nouvel appui quitte le mode réglage
  • le bouton de droite incrémente l'heure de réveil. Un bon fonctionnement serait d'augmenter la vitesse de défilement de ces heures en fonction du temps d'appui

La sérigraphie du shield vous donne :

  • bouton gauche en A1, bouton milieu en A2 et bouton droite en A3
  • LED D1 en 13. En fait les 4 LEDs font face à leur connecteurs
  • Le buzzer est en (-3)

Ces informations sont importantes pour câbler votre shield au FPGA.

Voir aussi[modifier | modifier le wikicode]

Adapter des shields 5V aux cartes FPGA 3,3V[modifier | modifier le wikicode]

Nous avons déjà eu l'occasion de le répéter dans ce livre, les cartes Digilent sont conçues de plus en plus pour n'avoir comme connecteur d'extension que des PMod. C'est pratique si l'on achète des extensions PMod. Nous en utilisons quelques unes avec succès (voir chapitre sur la robotique mobile) mais nous les trouvons bien trop chères. Un sonar se retrouve à 40€ alors que le module HC SR04 peut être trouvé aux alentours de 2€. Un PMod double afficheur 7 segments coûte 16 € tandis qu'on a utilisé des quadruples afficheurs pour moins de 4 €. Bien sûr le branchement n'est alors pas immédiat. Nous ne prétendons pas que les qualités sont les mêmes mais pour les utiliser avec des étudiants la basse qualité suffit bien souvent. L'autre problème important est qu'il n'y a pas de 5V sur le PMod !

Digilent va garder ses PMod certainement longtemps... mais commence à faire des cartes avec des connecteurs de type Arduino comme la carte Arty. Mais là c'est le prix de la carte elle-même qui pose problème ! Elle est à 99,00$ aux US, mais se retrouve à 174€ chez nous !

Tous ces discours n'amènent qu'à une question : comment faire pour utiliser les shields 5V avec les cartes FPGA ?

Les réponses sont encore chinoises :

  • vous pouvez trouver un module d'alimentation 5V et 3,3V pour plaque à essais pour un peu plus de 2€. Voici un lien qui montre de quoi nous parlons.
  • si vous avez besoin d'adapter les tensions, des modules convertisseur de tension logique bidirectionnel peuvent être trouvés aux alentours d'1€.

Soyons un peu plus précis. Dans le cas général, au-delà des problèmes de PMod, vous allez vous trouver dans deux situations différentes :

  • l'i2c, abordé dans la section suivante. C'est un problème à part car ce qui caractérise l'i2c c'est qu'il nécessite des résistances de tirage (pull-up). L'i2c 5V aura donc des résistances de tirages reliées à 5V. Si votre maître est en i2c 3,3V vous aurez votre maître qui sera tiré à 5V dès qu'il recevra une réponse du périphérique... et il ne supporte pas forcément. Il vous faut alors les adaptations bidirectionnelles évoquées.
  • tous les autres protocoles comme le SPI, la rs232 et d'autres ne nécessitent pas forcément une adaptation bidirectionnelle. Si un périphérique 5V est commandé par un signal 3,3V cela ne pose aucun problème en général. Si ce périphérique 5V envoi un 1 logique au maître 3,3V, il faut adapter. Cela peut se faire avec une simple paire de résistance 1k, 2k ou même 10k, 20k.

De l'I2C pour accéder à d'autres capteurs[modifier | modifier le wikicode]

Nous avons déjà eu l'occasion d'aborder l'i2c dans ce livre, mais dans un contexte précis : Etude de la manette Nunchuk. Nous avions réalisé un périphérique complètement adapté à la manette. Il a cependant été réalisé avec une grosse machine d'états et il est temps pour nous de réaliser un périphérique à peu près compatible avec les périphériques i2c que l'on trouve dans les AVR. L'intérêt est alors sa facilité de mise en œuvre par programme.

Conception du périphérique i2c[modifier | modifier le wikicode]

La conception d'un périphérique i2c est trop ardue pour en faire un TP de niveau 15. Nous avons décidé de la déplacer dans la partie cours de ce livre, dans le chapitre Améliorer l'ATMega8 avec l'ATMega16 et l'ATMega32. Ce cours a été rédigé sous forme d'exercices et nous invitons les architectes matériels en herbe à les réaliser, voir à les améliorer.

Nous allons garder dans ce chapitre de TP les utilisations pratiques de ce périphérique.

Périphérique Tiny RTC[modifier | modifier le wikicode]

C'est un composant dont le prix varie beaucoup : nous l'avons acheté pour environ de 1,1€ et peut être encore trouvé à ce prix. De toute façon, pas de quoi faire un crédit sur 20 ans.

Ce composant est décrit dans son propre WIKI. Vous y trouverez une librairie et un programme d'exemple suffisant pour se lancer.

Le problème pour ce composant avec un FPGA est encore une fois électrique. Le circuit central, le DS1307 possède deux entrées de tension :

  • une entrée batterie avec une tension entre 2,0V et 3,5V (peut laisser présager une compatibilité avec le FPGA mais ...)
  • une entrée avec une tension entre 4,5V et 5,5V pour l'interface i2c, et là c'est le drame...

Il est donc impossible de brancher le Tiny RTC directement sur un FPGA : une adaptation en tension est nécessaire.

Périphérique MPU6050 sur carte Nexys 3[modifier | modifier le wikicode]

Il s'agit d'un accéléromètre doublé d'un gyroscope. Il peut être trouvé aux environs de 2€ et quand on sait qu'il possède un processeur 32 bits spécialisé pour les mouvements (Digital Motion Processor : DMP), il est facile de se laisser tenter. Signalons aussi qu'un chapitre de TP est consacré à ce composant dans le livre sur les AVR.

Utilisation en 3,3V avec un microcontrôleur[modifier | modifier le wikicode]

Relier le launchPad tm4c123 au MPU6050

Comme nous en avons pris l'habitude dans ce chapitre, nous commençons par essayer les shields avec un processeur, en général 32 bits, qui a l'avantage de fonctionner en 3,3V. Nous ne dérogerons pas à ce rituel ici.

Pour ne pas utiliser toujours un MSP432, nous avons décidé d'utiliser une carte launchpad tm4c123 de chez Texas Instrument. Elle sera essayée avec l'environnement de programmation Energia tellement proche de l'Arduino qu'un Copier-Coller du code est suffisant.

Voici donc notre code de départ. Il se cantonne à relever les données de l'accéléromètre, pas celles du gyroscope et de les envoyer à travers la liaison série.

// MPU-6050 Short Example Sketch
// By Arduino User JohnChi
// August 17, 2014
// Public Domain
#include<Wire.h>
const int MPU=0x68; // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ;

void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}

void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,6,true); // request a total of 6 registers
  AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.println(AcZ);
  delay(333);
}

Qui fonctionne correctement à part quelque fois des petits problèmes avec la liaison série qui n'ont donc rien à voir avec ce que l'on cherche à résoudre. Le MPU6050 fonctionne donc parfaitement lorsqu'il est alimenté en 3,3V ou en 5V.

Utilisation avec un FPGA[modifier | modifier le wikicode]

Relier la carte Nexys 3 au MPU6050

Le périphérique pour la Nunchuk dans un autre chapitre est utilisable tel quel. Par contre le programme doit être légèrement modifié. La raison en est que le prorocole i2c utilisé est différent. Je ne parle pas du protocole i2c physique, qui bien sûr, reste identique pour les deux périphériques... mais de la façon d'interroger le périphérique pour lui demander des données.

L'adresse du périphérique MPU6050 peut être 0x68 ou 0x69 en fonction de la valeur sur l'entrée AD0 du circuit. Nous prendrons AD0 relié à la masse ce qui fixe l'adresse à 0x68.

Le code commence bien sûr par une initialisation de l'i2c physique puis se poursuit l'envoi de 0x6B suivi de 0x00. Ceci termine la partie setup

Ensuite vient la boucle infinie qui réinitialise la lecture par l'envoi de 0x3B puis d'une lecture des six données. Comme d'habitude en i2c la fin de lecture se termine par un non acquittement suivi d'un stop.

Voici donc ce code complet. Nous y avons laissé les sous-programmes pour la liaison série même si nous ne les utilisons pas.

Vous pouvez remarquer le "PORTC=t[0] qui permet de sortir la première valeur lue sur les leds. C'est le poids fort de l'accélération suivant l'axe des x. Un bon fonctionnement doit donc se traduire par des leds qui changent quand l'orientation de l'accéléromètre varie car celui-ci est sensible à l'accélération de pesanteur.

Périphérique MPU6050 sur carte Altera DE2-115[modifier | modifier le wikicode]

A priori le portage n'est qu'électrique : on débranche la nexys 3 et on branche la DE2-115. Mais, comme toujours, il faut se méfier des à priori...

Rappelons pour ceux qui arrivent ici sans avoir lu grand chose des autres parties de ce livre, que nous n'avons pas encore porté notre processeur préféré (ATMega16) sur les cyclones d'Altera mais que nous devons nous contenter du modeste ATTiny861. C'est mieux que rien et cela se programme en C.

Pour commander notre coeur i2c, nous avons décidé d'utiliser les registres du Tiny861 et non ceux des ATMega : ceux des ATMegas ont un nom en TWXX tandis que ceux du Tiny861 ont un nom en USIXX. Pourquoi procéder comme cela ?

  • avantage : on évite les redéfinitions des noms de registres dans la partie entête du programme c
  • inconvénient : on s'éloigne du cœur i2c du vrai Tiny861 de chez Atmel car si les noms ont changés, il en est de même des fonctionalités

L'inconvénient est facile à lever : il suffit d'écrire une librairie pour l'i2c... que voici

Panneau d’avertissement

La librairie fournie ci-dessus ne fonctionne pas dans un ATTiny861 du commerce.

Réalisation matérielle[modifier | modifier le wikicode]

Nous vous proposons sans détailler la ressource complète sur mon site personnel. Comme d'habitude le sous-répertoire soft contient le programme c et sa compilation.

Voir aussi[modifier | modifier le wikicode]

Module d'affichage LED avec affichage de l'horloge pour Arduino[modifier | modifier le wikicode]

Ce module n'est pas à proprement parler un shield. Si vous voulez voir son aspect tapez le titre de cette section dans Google ou autre. Ce qui nous intéresse est son prix (3,84 € au moment de l'écriture de cette section) et que sa conception est complètement différente du shield multi fonctions de la section précédente. Une des conséquences est qu'il nous faudra modifier notre VHDL pour l'utiliser. Sa mise en œuvre nécessite de résoudre deux problèmes :

  • est-il compatible avec le 3,3V des FPGA ?
  • maîtriser le circuit TM1637 de Titan Micro electronics qui équipe ce module d'affichage

Compatibilité électrique[modifier | modifier le wikicode]

La documentation du TM1637 affirme que ce composant peut être alimenté en 5V comme en 3,3V. Il pourra être essayé avec des cartes Digilent mais aussi avec des cartes Terasic. La compatibilité avec les FPGA est donc parfaite.

Programmation du TM1637[modifier | modifier le wikicode]

Du code pour Arduino est facilement trouvable dans GitHub. Pour entrer dans le monde du 3,3V, nous allons utiliser ce code avec Energia et comme cible une carte MSP432. Pour information Energia est l'environnement compatible Arduino pour les processeurs Texas Instrument. Nous utilisons une carte MSP432 parce que le 5V nécessaire au bon fonctionnement du module d'affichage y est présent.

Decompactez la librairie précédente dans le répertoire libraries de l'installation d'Energia. Nous n'avons pas pris le risque de garder le tiret dans le nom de celle-ci et l'avons remplacé par un caractère de soulignement. Ce travail se fait donc exactement de la même manière que pour l'environnement Arduino.

Vous avez alors à disposition un exemple que nous reproduisons :

qui fonctionne parfaitement en 3,3V.

Ceci est parfaitement conforme à la documentation qui autorise une alimentation en 5V comme en 3,3V.

Réalisation matérielle[modifier | modifier le wikicode]

La communication se faisant avec un protocole de type i2c (ce n'est pas de l'i2c car il n'y a pas d'adresse) les données sont bidirectionnelles. Ceci est réalisé traditionnellement par des résistances de pull-up. Celles que l'on peut utiliser avec les contraintes dans les FPGA sont en général considérées comme trop grandes. Nous avons eu l'occasion de les utiliser avec la manette Nunchuk et un FPGA Xilinx sans problème réel de fonctionnement. D'autre part, nous avons fait fonctionner de l'i2c sans contrainte de pull-up dans les FPGA Altera.

En somme, il semble qu'il n'y a pas lieu de déclarer des entrées/sorties bidirectionnelles comme pull-up. Pour ceux qui désirent le faire sur Altera, Voir comment utiliser le "Pin Planer" ou l' "Assignement Editor" pour cela.

Programme d'essai[modifier | modifier le wikicode]

Panneau d’avertissement

Nous avons fait des tests sur plaque à essais sans réussir à allumer l'afficheur avec le code ci-dessous. Ce code est donc donné seulement pour information mais pas comme un code déjà fonctionnel. A priori notre module i2c n'est donc pas compatible avec le TM1637. Il nous faudra donc essayer d'autres solutions.

#define TM1637_I2C_COMM1    0x40
#define TM1637_I2C_COMM2    0xC0
#define TM1637_I2C_COMM3    0x80
const uint8_t digitToSegment[] = {
 // XGFEDCBA
  0b00111111,    // 0
  0b00000110,    // 1
  0b01011011,    // 2
  0b01001111,    // 3
  0b01100110,    // 4
  0b01101101,    // 5
  0b01111101,    // 6
  0b00000111,    // 7
  0b01111111,    // 8
  0b01101111,    // 9
  0b01110111,    // A
  0b01111100,    // b
  0b00111001,    // C
  0b01011110,    // d
  0b01111001,    // E
  0b01110001     // F
  };
int main() {
  uint8_t i, t[4];
  uint16_t cmpt=0;
// init
  TWIInit();
  _delay_ms(10);
// loop
  while(1) {
    t[0] = digitToSegment[cmpt & 0x000F];
    t[1] = digitToSegment[(cmpt & 0x00F0)>>4];
    t[2] = digitToSegment[(cmpt & 0x0F00)>>8];
    t[3] = digitToSegment[(cmpt & 0xF000)>>12];
    // reinitialisation pour ecriture
    TWIStartWriteStop(TM1637_I2C_COMM1);
    TWIStartWrite(TM1637_I2C_COMM2);
    for (i=0;i<3;i++)
      TWIWrite(t[i]);
    TWIWriteStop(t[3]);
    TWIStartWriteStop(TM1637_I2C_COMM3 | 0x0F);
    PORTC=cmpt;
    _delay_ms(500);
    cmpt++;
  }
  return 0;
}

Les sous-programmes manquants sont présentés dans la partie i2c de ce chapitre.

Voir aussi[modifier | modifier le wikicode]

Étude du Shield LCD keypad[modifier | modifier le wikicode]

Ce shield est décrit ICI. Il peur être trouvé pour environ 3,70 € sur Internet (au moment de l'écriture de ces lignes).

  • Sa première particularité est qu'il doit être alimenté en 5V
  • Sa deuxième particularité est que les 5 boutons poussoirs sont reliés à une seule entrée qui devient donc analogique. La lecture de ces boutons nécessite donc un convertisseur analogique numérique. ATTENTION la tension analogique en question dépasse 3,3V !!!

La question est donc de vérifier que malgré une alimentation à 5V, l'écran LCD fonctionne correctement. Pour ce faire, nous avons câblé le shield avec une carte MSP432 qui permet une alimentation du shield en 5V mais qui possède un processeur de type ARM qui fonctionne en 3,3V. L'essai ayant été concluant, nous sommes prêt à tenter notre chance avec un FPGA. Nous allons utiliser la carte DE2-115 qui possède un connecteur d'extension sur lequel le 5V est disponible, ce qui n'est pas le cas pour les cartes de Digilent (les connecteurs PMod n'utilisent que du 3,3V).

Le code utilisé a nécessité l'installation de la librairie LiquidCrystal avec Energia :

#include <LiquidCrystal.h>

LiquidCrystal lcd(6, 7, 2, 3, 4, 5);        // select the pins used on the LCD panel
// dans l'ordre P1.5 , P4.3, P4.1, P3.3, P3.2 P6.0
// reliés à :    9,    8,    7,    -6 ,  -5,  4 (repérés sur Arduino)
unsigned long tepTimer ;    

void setup(){ 
    lcd.begin(16, 2);                       // start the library
}

void loop(){ 
    lcd.setCursor(0, 0);                   // set the LCD cursor   position 
    int val;                               // variable to store the value coming from the analog pin 
    double data;                           // variable to store the temperature value coming from the conversion formula
    val=analogRead(A1);                     // read the analog in value:
    data = (double) val * (5/10.24);       // temperature conversion formula
    
    if(millis() - tepTimer > 500){         // output a temperature value per 500ms 
             tepTimer = millis();

             // print the results to the lcd
             lcd.print("T: ");               
             lcd.print(data);             
             lcd.print("C");              
     } 
}

Les paramètres de lcd sont les noms Energia de la carte MSP432.

Module VHDL seul[modifier | modifier le wikicode]

Comment connecter une carte DE2-115 à un shield LCD

Nous allons essayer, dans cette section, d'utiliser un module VHDL touvé chez Opencores. Il se trouve dans Projets -> Other -> 16x2 LCD controller et a été réalisé par Daniel Drescher.

Nous rappelons que nous avons décidé d'utiliser la carte DE2-115 de Terasic équipée d'un Cyclove IV de chez Altera. Ceux qui connaissent la carte savent qu'elle est déjà équipée d'un affichage LCD. Mais nous allons en rajouter quand même un deuxième pour comprendre les problèmes à résoudre dans ce cas. En fait il n'y a aucun problème, le cœur trouvé sur Opencores.org fonctionne immédiatement. Reste quand même à comprendre un peu son fonctionnement pour aller plus loin.

Voici en résumé les connexions que l'on a réalisé :

LCD en rs d(7) d(6) d(5) d(4)
Arduino Pin 9 8 7 6 5 4
DE2-115 GPIO[13] GPIO[11] GPIO[7] GPIO[5] GPIO[3] GPIO[1]

Les numéros des broches avec la convention Arduino sont données ici pour se repérer pour les broches de Shield. Pour celui qui n'a jamais utilisé d'Arduino, espérons que le schéma suffira.

Le ficher de contraintes correspondant à l'image ci-contre est donné au format CSV :

To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved
CLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL,
lcd_e,Output,PIN_AF15,4,B4_N2,3.3-V LVTTL,
lcd_rs,Output,PIN_AF16,4,B4_N2,3.3-V LVTTL,
lcd_db[7],Output,PIN_AE16,4,B4_N2,3.3-V LVTTL,
lcd_db[6],Output,PIN_Y16,4,B4_N0,3.3-V LVTTL,
lcd_db[5],Output,PIN_Y17,4,B4_N0,3.3-V LVTTL,
lcd_db[4],Output,PIN_AC15,4,B4_N2,3.3-V LVTTL,

Adapter ceci pour une autre carte est relativement simple. Il suffit de changer le fichier de contraintes.

Reste cependant à répondre à une question : peut-on directement mettre ce shield sur la carte DE0-nano SOC qui possède un connecteur pour Shield de type Arduino ? La réponse est : affaire à suivre. Le problème est lié à la façon dont sont câblés les boutons poussoirs : quand aucun bouton poussoir n'est appuyé, vous vous trouvez avec 5V sur la broche A0 de l'Arduino, qui dans le cas de la carte en question est relié à un convertisseur analogique numérique extérieur au FPGA, mais qui semble ne supporter que 4,096V (tension de référence par défaut). Le circuit utilisé pour la conversion analogique numérique est un LTC2308. Le parcourt rapide de sa documentation (du LTC2308) m'a permis de déterminer que son alimentation est en 5V, mais pas si ces 5V peuvent être utilisés en tension de référence. Donc personnellement je n'utiliserai pas ce shield directement sur la DE0-nano SOC avant d'avoir lu une documentation plus complète. Mais vous pouvez utiliser ce shield avec de simples fils, un peu comme sur la figure, en laissant tomber les boutons pour le moment.

Exercice 1[modifier | modifier le wikicode]

On vous demande de changer l'affichage réalisé par le module de chez Opencores.org. Vous pourrez y afficher ce que vous désirez, le but étant de vous montrer comment les choses se passent.

Câbler le shield directement sur un PORT[modifier | modifier le wikicode]

En général l'utilisation d'un afficheur LCD avec un microcontrôleur se fait naturellement de cette manière. Dans un FPGA, il est possible de faire autrement mais il est bon de commencer par le plus utilisé. Le processeur enfoui que l'on utilisera sera un ATTiny861.

Si vous avez réalisé l'exercice 1, vous avez compris que la gestion des deux lignes de 16 caractères nécessite 32 octets, soit 32 PORTs... et ceci est un problème car c'est ce que possède le Tiny... et si on les utilise tous ici, on ne pourra plus faire grand chose d'autre ! Il nous faudra trouver donc une autre solution.

Dans ce qui suit nous allons montrer encore une fois comment on peut libérer des ressources de code en réalisant un périphérique, d'abord très simplifié, puis général. Mais pour commencer, nous allons montrer d'abord qu'un simple affichage peut être réalisé en interfaçant directement l'afficheur LCD sans passer par la partie matérielle de l'exercice 1.

Exercice 2[modifier | modifier le wikicode]

Réaliser une interface directe de l'afficheur à un Tiny861 et montrer qu'un texte général peut être affiché.

Indication : on adaptera le code C ci-dessous au matériel.

//      lcd.c
//      
//      Copyright 2014 Michel Doussot <michel@mustafar>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.


#include <avr/io.h>
#define RS 		0x04
#define E		0x02
#define RW 		0x08
#define DATAS  	0xF0

// unite approximative 2us
void delai(unsigned long int delai) {
	volatile long int i=0;
	for(i=0;i<delai;i+=1);
} 

// portB :
// b7 b6 b5 b4 b3 b2 b1 b0
// D3 D2 D1 D0 RW RS  E CE0
void rs_haut(void) {
   PORTB = PORTB | RS;
}

void rs_bas(void) {
   PORTB = PORTB & ~RS; 
}

void rw_haut(void) {
   PORTB = PORTB | RW;
}

void rw_bas(void) {
   PORTB = PORTB & ~RW; 
}

void e_haut(void) {
   PORTB = PORTB | E;
   delai(8);
}

void e_bas(void) {
    PORTB = PORTB & ~E; 
    delai(8);
}

void e_puls(void) {
   e_haut();
   e_bas();
}

void ecris_4(unsigned char valeur) {
	unsigned char v;
	v = (valeur << 4) & DATAS;
	PORTB = PORTB & ~DATAS ;
	PORTB = PORTB | v ;
    e_puls();
}

void ecris_8(unsigned char valeur) {
	unsigned char v;
	v = valeur & DATAS;
	PORTB = PORTB & ~DATAS ;
	PORTB = PORTB | v ;
    e_puls();
	v = (valeur << 4) & DATAS;
	PORTB = PORTB & ~DATAS ;
	PORTB = PORTB | v ;
    e_puls();    
}


void setup() {

   PORTB = 0;
   delai(6000);
   ecris_4(0x03);
   delai(1600);
   ecris_4(0x03);
   delai(800);
   ecris_4(0x03);
   delai(800);
   ecris_4(0x02);
   delai(40);
   ecris_4(0x02);
   ecris_4(0x08);
   delai(40);
   ecris_4(0x00);
   ecris_4(0x06);
   delai(40);
   ecris_4(0x00);
   ecris_4(0x0C);
   delai(40);
   ecris_4(0x00);
   ecris_4(0x01);
   delai(800);   
}

void writecar(char car) {
	rs_haut();
	ecris_8((unsigned char)car);
}

void writestr(char *chaine) {
	rs_haut();
	while (*chaine) {
		ecris_8((unsigned char)*chaine++);
	}
}

Le point important de ce code par rapport à tout ce qui a été vu jusqu'ici est l'apparition de lignes comme PORTB = PORTB | ... qui montrent que PORTB doit être en entrée et en sortie.

Il est possible de continuer à faire évoluer la librairie d'affichage à l'infini pour une utilisation plus simple. Mais nous allons maintenant vraiment nous intéresser à la réalisation d'un périphérique capable d'afficher. Pour commencer nous allons abandonner le caractère généraliste de l'afficheur pour nous cantonner à un affichage très spécialisé.

Exercice 3[modifier | modifier le wikicode]

Pouvez-vous imaginer et réaliser une interface (même partielle) entre le fichier « lcd16x2_ctrl.vhd » et notre Tiny861 pour au moins gérer un affichage des deux chiffres d'un compteur BCD. Évidemment on laissera tomber le fichier « lcd16x2_ctrl_demo.vhd » qui n'est là que pour montrer comment l'ensemble fonctionne.

Indications :

  • le Tiny861 sur carte Altera possède déjà son chapitre de Travaux Pratiques.
  • le compteur BCD est réalisé en C dans le processeur... Il est sur 8 bits et les 4 bits de poids faibles représentent les unités tandis que les 4 bits de poids forts représentent les dizaines. Ce que l'on veut donc c'est qu'une instruction de type PORTX=0x23 affiche directement 23 quelque part sur votre afficheur LCD.

Exercice 4[modifier | modifier le wikicode]

Nous avons comme objectif d'utiliser deux FIFO : un par ligne à afficher. L'avantage est une utilisation de deux PORTs seulement, un par FIFO. L'inconvénient est qu'il va falloir bâtir un séquenceur capable de vider les FIFO pour réaliser l'affichage des lignes.

Réflexion :

  • l'utilisation de deux PORTs comme indiqué dans l'énoncé peut n'être qu'une assertion de principe ! On peut utiliser un troisième PORT pour communiquer avec le séquenceur puisqu'il en faut un. Par exemple, utiliser le bit B0 de poids faible d'un troisième registre est possible : on le met à 1 pour dire au périphérique : ça y est les deux FIFO sont prêts à être utilisés pour être affichés, à toi de te débrouiller. On admet bien sûr que le processeur et son périphérique se tutoient.
  • on peut aussi n'utiliser que deux PORTs et faire partir le séquenceur systématiquement quand les deux FIFO sont pleins

1°) Avant de mettre au point l'utilisation de deux lignes, nous allons nous contenter de la première ligne. Nous allons aussi utiliser un seul PORT qui écrira dans le FIFO. C'est donc le signal FIFO plein qui fera partir le séquenceur.

Indications :

Ecriture dans un FIFO par l'intermédiaire d'un PORT
  • La partie donnée dans la figure ci-contre concerne l'écriture dans un FIFO. On rappelle qu'un FIFO est un registre dans lequel on écrit des données les unes à la suite des autres. La première donnée écrite sera la première donnée pouvant être lue. Si vous réalisez ce qui est en rouge dans la figure, vous aurez un FIFO complètement fonctionnel en écriture. Vous faites un programme qui écrit deux valeurs dans PORTB et elles partiront toutes les deux dans le FIFO. Si vous n'avez que cette écriture de disponible, vous ne pourrez jamais rien faire des valeurs qui sont dans le FIFO. Remarquez aussi que l'on a gardé la partie de l'exercice 3 qui affiche deux chiffres décimaux en deuxième ligne. Il faut compléter cette figure par un séquenceur destiné à lire les données disponibles dans le FIFO. Avant de vous attaquer à cette autre partie du problème, posez-vous un peu et essayez de bien comprendre ce qui est fait : c'est la partie la plus simple du problème que vous avez sous les yeux.
  • Le séquenceur qui lit les 16 données du FIFO comporte 32 états. Il faut deux états pour lire une donnée car un état permet d'écrire dans line1_buffer tandis que l'autre dit au FIFO qu'une de ses données est lue. Pour ce séquenceur, seul le démarrage est conditionné à "full" qui signifie que le FIFO est rempli. Pour tous les autres états, il n'y a aucune condition de passage à l'état suivant, ..., puis à la fin on revient à l'état initial. Ce séquenceur est donc facile à écrire mais long (toutes les 32 transitions sont à réaliser dans un grand case when).
Description d'un séquenceur pour lire le FIFO
  • Si le séquenceur est réalisé avec un compteur, cela permet de remplacer le grand case when par une simple incrémentation. Nous pouvons choisir un compteur de 5 bits : les 4 bits de poids fort feront le compteur jusqu'à 16 pour vider le FIFO tandis que le bit de poids faible fera le signal de lecture du FIFO. En effet après lecture d'un FIFO, il faut lui dire que sa donnée a été lue pour qu'il prépare la suivante.
  • Le problème du signal FIFO plein est qu'il cesse d'être à 1 dès la première lecture. Vous allez utiliser ce signal et le mémoriser pendant tout le temps nécessaire à vider ce FIFO. C'est probablement là que réside la plus grande difficulté de conception de notre module. Prenez donc le temps de méditer cette figure. Nous avons dessiné des rectangles en pointillés parce qu'ils représentent des process (pour éviter les PORT MAP). Le process de comptage n'a rien de difficile. Le process qui maintien s_en_d à 1 tant que la lecture du FIFO n'est pas terminée n'est pas simple (enfin tout est relatif). Votre réflexion doit se faire en vous posant les questions :
    • quelle condition fait passer s_en_d à 1 ? Réponse le passage de full à 1
    • quelle condition fait passer s_en_d à 0 ? Réponse quand le compteur est arrivé au bout du comptage
    • que se passe-t-il en dehors de ces conditions ? Réponse un simple maintien (mémorisation).
  • Si les indications ci-dessus vous semblent insuffisantes, une autre version de la question 1°) plus conventionnelle est donnée un peu plus bas : question 1°-bis)
  • Le FIFO est donné ci-dessous

Et maintenant voici la solution complète :

Autre description d'un séquenceur pour lire le FIFO

1°-bis) La façon de faire de la question 1°) est intéressante mais assez difficile pour des étudiants non spécialistes. La réalisation d'un séquenceur avec un compteur demande une réflexion importante et assez inhabituelle : les séquenceurs sont plutôt réalisés par des machines d'états. Lorsque nous avons décidé nous-même de donner cet exercice en examen à l'UTT, nous avons choisi de réaliser le séquenceur de manière plus classique comme le montre le schéma ci-contre.

2°) Réaliser l'interface complète des deux lignes.

3°) La façon de faire des deux questions précédentes consiste à prendre un module tout fait et à l'adapter à notre processeur en réalisant le moins de modifications possibles. Il serait plus confortable de réaliser une partie matérielle capable de reprendre les principales actions réalisées par la librairie C et de les implanter dans le matériel. Cela permettrait aux utilisateurs qui ont l'habitude d'utiliser une telle librairie de retrouver un peu leurs marques. Mais ce travail consiste alors, à ne prendre dans le module "lcd16x2_ctrl.vhd" que les parties intéressantes. Ceci nécessite donc beaucoup de temps : Avis aux amateurs.

Indications : Pour mieux comprendre le texte de la question donnons un peu plus de détails.

  • en ce qui concerne l'initialisation, faut-il la rendre accessible par le processeur ou la laisser complètement indépendante ?
  • réaliser le transfert 8 bits dans l'afficheur
  • distinguer l'envoi d'une commande de l'envoi d'une donnée. Ceci peut être fait à l'aide de deux registres/PORTs :
    • l'envoi d'une donnée se fait dans le PORTA (par exemple)
    • l'envoi d'une commande se fait par le PORTB (par exemple)
  • cette façon de faire ne nécessite pas obligatoirement de FIFO mais probablement un bit en lecture pour dire que l'envoi de donnée ou la commande est terminée. Certaines commandes sont relativement longues, comme l'effacement de l'écran.

4°) Encore plus difficile : réaliser un coprocesseur qui est capable d'exécuter certaines routines. Il faut alors prévoir un mécanisme de communication entre le Tiny861 et le coprocesseur pour que le premier puisse demander au deuxième de réaliser une routine précise.

Tourelle pan/tilt et sonar[modifier | modifier le wikicode]

Petit rappel avant de commencer :

  • le mot anglais pan signifie faire un panoramique horizontal
  • le mot anglais tilt signifie incliner

Ce que nous allons traiter dans cette section n'est pas à proprement parler un shield. Mais nous avions prévenu : nous traitons dans ce chapitre de shields au sens large, c'est à dire tout ce qui peut être connecté à un Arduino en quelques secondes.

Ce sujet a été donné comme projet à des étudiants en 2016/2017. Il a consisté à réaliser une carte d'extension de la carte DE2-115 sur laquelle se trouvent une tourelle pan/tilt commandée par deux servomoteurs et un sonar HC-SR04. L'idée est d'explorer un peu le problème pour une éventuelle future utilisation en robotique mobile.

Présentation de la carte d'extension[modifier | modifier le wikicode]

Nous avons imposé aussi une carte MSP432 sur l'ensemble ainsi qu'un connecteur pour la manette Nunchuk déjà évoquée plusieurs fois dans ce livre.

Pour vous faire une idée plus précise, le compte-rendu de réalisation de deux binômes est disponible sur Internet dans le Wiki de l'IUT de troyes :

Commande des servomoteurs[modifier | modifier le wikicode]

La commande de plusieurs servomoteurs n'est pas un problème dans un FPGA. Vous trouverez facilement du code VHDL pour cela. Nous n'avons pas choisi le code le plus compact mais l'essentiel est qu'il fonctionne :

Exercice 1

Réaliser un code VHDL qui utilise le fichier "servo.vhd" du ZIP ci-dessus ainsi que tous ses sous ensembles (servoclock.vhd, servotiming.vhd et servounit.vhd). On n'utilisera aucun processeur mais seulement un compteur 31 bits en lui prenant les 8 bits de poids fort pour réaliser la commande de servo. Autrement dit on fait un balayage horizontal en une quarantaine de secondes.

Lecture du sonar[modifier | modifier le wikicode]

Nous utilisons le célèbre sonar HC-SR04 qui peut être trouvé pour moins de 2€ assez facilement. Nous allons partir du code VHDL de Mike Field. Ce lien semble indiquer qu'il fait fonctionner le HR-SR04 en 3,3V puisqu'il n'y a pas de 5V sur les PMods ! Nous avons essayé en 3,3V absolument sans succès, ce qui est parfaitement conforme à la documentation (alimentation entre 4,5V et 5,5V).

Nous l'avons fait fonctionner cependant en 5V d'alimentation et sans convertisseur de niveaux logiques, puis avec, pour des essais plus longs. En effet, la broche "echo" délivre du 5V ce qui n'est pas conseillé pour un FPGA. Ces essais montrent qu'il est possible de commander "Trig" en 3,3V. La boche "echo" étant une entrée (pour le FPGA) un simple diviseur de tension avec des résistances 20k/10k est suffisant.

Tout ceci nous amène à envisager une utilisation avec les cartes terasic qui ont du 5V sur leurs connecteurs.

Exercice 2 : adaptation du code pour une de2-115[modifier | modifier le wikicode]

1°) Le code de Mike Field devra être changé :

  • utilisation en 50 MHz au lieu des 100 MHz de la Basys 3
  • les afficheurs de la de2-115 ne sont pas multiplexés contrairement à ceux de la Basys 3
  • remplacement des types "unsigned" par des "std_logic_vector" et changer les librairies en conséquence

Ce dernier point n'est pas obligatoire mais nos étudiants de niveau 15 ne connaissent que les "std_logic_vector" et nous n'avons pas l'intention de leur enseigner plus sur le sujet.

Pour faire ces modifications vous pouvez repérer les deux process qui gèrent l'affichage et les commenter et les remplacer par un câblage du composant de l'exercice 2 du Corrigé du TP1. Ce composant nécessite bien sûr lui-même, le corrigé de l'exercice 1.

Avec ces petites modifications le code de Mike Field est parfaitement fonctionnel. Nous ne mettons pas la correction à disposition pour le moment car il faut bien laisser chercher nos étudiants.

Les contraintes pour la carte de TO/Baczkowski sont :

To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved
MCLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL,
Unit7segs[6],Output,PIN_H22,6,B6_N0,2.5 V,
Unit7segs[5],Output,PIN_J22,6,B6_N0,2.5 V,
Unit7segs[4],Output,PIN_L25,6,B6_N1,2.5 V,
Unit7segs[3],Output,PIN_L26,6,B6_N1,2.5 V,
Unit7segs[2],Output,PIN_E17,7,B7_N2,2.5 V,
Unit7segs[1],Output,PIN_F22,7,B7_N0,2.5 V,
Unit7segs[0],Output,PIN_G18,7,B7_N2,2.5 V,
Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V,
Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V,
Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V,
Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V,
Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V,
Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V,
Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V,
sonar_trig,Output,PIN_AE16,4,B4_N2,3.3-V LVTTL,
sonar_echo,Input,PIN_Y16,4,B4_N0,3.3-V LVTTL,

2°) Le code de Mike Field affiche incorrectement après 99. Pouvez-vous le modifier pour avoir 3 digits d'affichage en utilisant des compteurs BCD cascadés.

Utiliser un processeur Tiny861[modifier | modifier le wikicode]

Pour coordonner l'ensemble du travail de balayage de la tourelle pan/tilt et la lecture du sonar, nous avons décidé d'utiliser un processeur. Comme d'habitude, ce processeur sera enfoui dans le FPGA.

Nous avions comme objectif initial de réaliser un affichage sur écran VGA en niveaux de gris de profondeur à partir des données du sonar. Mais, les étudiants comme le tuteur ayant pris du retard nous nous rabattons sur un affichage de chacune des lignes horizontales balayées sur un afficheur lcd (sous forme de barregraphe). Cet afficheur est présent sur la carte DE2-115 que nous utilisons. Si vous n'en possédez pas, nous avons déjà présenté dans ce chapitre comment en utiliser un externe et très bon marché.

Schéma du montage réalisé[modifier | modifier le wikicode]

Si l'on veut connaître comment programmer notre Tiny, il nous faut savoir comment les périphériques sont connectés au processeur. C'est l'objet de la figure ci-contre (à venir si elle n'est pas encore là) où l'on discerne :

  • l'utilisation du PORTB pour la gestion de l'afficheur lcd
    • b7 .. b4 seront les données sur 4 bits
    • b3 sera RW
    • b2 sera RS
    • b1 sera E
  • l'utilisation du PORTA pour sortir sur des LEDs
  • l'utilisation de DDRA pour commander la position horizontale de la tourelle (pan)
  • l'utilisation de DDRB pour commander la position verticale de la tourelle (tilt)
  • l'utilisation de PINB en lecture pour lire la distance en cm
  • l'utilisation de DDRB en lecture pour avoir en poids faible le status du sonar

Ressource[modifier | modifier le wikicode]

Nous vous donnons la ressource de départ sur notre site :

Tout y est réalisé pour être conforme au schéma que l'on vient de présenter.



Code de départ[modifier | modifier le wikicode]

Le code de départ est donné pour que vous n'ayez pas tout à faire. Ce code vous montre comment créer un caractère spécifique pour l'afficheur LCD et aussi comment l'utiliser.

Tout le monde aura reconnu un cœur comme caractère spécial qui semble indiquer notre préférence entre Linux et Windows.

Code final[modifier | modifier le wikicode]

L'objectif de ce code est de gérer :

  • le balayage horizontal
  • le balayage vertical
  • de synchroniser les deux balayages
  • de gérer le sonar
  • de créer les nouveaux caractères du barregraphe
  • d'afficher les résultats sur l'écran LCD

Utilisation d'une caméra OV7670[modifier | modifier le wikicode]

Les caméras OV7670 sont des caméras très bon marché : entre 5€ et 8€ sur Internet au moment de l'écriture de ces lignes. Elles sont configurables par un protocole très proche de l'i2c mais l'on doit récupérer soi-même les données du capteur par paquets de 8 bits.

Ce capteur vidéo n'est pas à proprement parler un shield. Il est d'autre part difficile à utiliser avec un Arduino dans sa version simple (sans mémoire externe).

Une première utilisation[modifier | modifier le wikicode]

Nous avons pris l'habitude dans ce chapitre de tester les shields avec des processeurs en 3,3V. Nous allons déroger à la règle ici car ces processeurs ne sont pas très adaptés à des essais de caméras. Cela ne veut pas dire que ce n'est pas possible puisqu'il est facile de trouver sur internet des exemples avec un processeur 32 bits.

Nous allons plutôt essayer un code VHDL tout fait pour commencer : il s'agit de saisir une image et de l'envoyer directement sur un écran vidéo.

Un code de ce genre peut être trouvé sous le titre "Digital Camera: OV7670 CMOS camera + DE2-115 FPGA board + LandTiger 2.0 board". En bas de la page en question se trouvent les liens pour télécharger les diverses versions du projet. Nous avons testé la première version. Pour cela, nous avons bien sûr passé plus de temps à câbler la caméra qu'à essayer ce code qui est absolument fonctionnel tout de suite. En effet nous possédons une carte DE2-115.

Aucun autre commentaire à part que la caméra se branche évidemment sur le connecteur d'extension. Pour savoir comment, il vous faut comparer la documentation du connecteur avec les contraintes pour les diverses entrées sorties de la caméra. C'est long mais pas difficile.

Le premier auteur qui semble avoir réalisé un code VHDL pour cette caméra bon marché semble être Mike Field dans son site (en) OV7670 camera

Une application simple[modifier | modifier le wikicode]

Le code de la section précédente est gourmand en mémoire interne au FPGA. Pour un cyclone IV 115 ce n'est pas un problème, mais nous avons l'intention d'utiliser des FPGA plus petits pour résoudre d'ailleurs des problèmes simples. Notre objectif est simplement de réaliser un suivi de lignes avec un Robot à l'aide de cette caméra.

Voir aussi[modifier | modifier le wikicode]

A regarder pour compléter ce chapitre[modifier | modifier le wikicode]