Tutoriel Arduino cube LED
Rapport de projet : Matrice de LED 3D
IMA4 2012-2013
Matthias De Bie - Pierre-Jean Petitprez
Table des mati?res
Remerciements 2
Introduction 3
1 Solutions techniques 4
1.1 Partie matØrielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.1 Carte de commande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.2 Carte de Nunchuck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.3 Carte d’alimentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.4 Carte de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2 Partie logicielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 Description du programme . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.2 Choix de l’Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.3 Choix des tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.4 Gestion de la mØmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.5 Gestion du Nunchuck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.6 Gestion de l’a chage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.7 Gestion du serpent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2 Conception assistØe par ordinateur 11
3 Conception du cube de LED 12
4 Pour aller plus loin 13
4.1 MatØriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.2 Logiciel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Conclusion 15
A Carte Ølectronique 16
B Programme Arduino 17
B.1 Fichier principal () . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
B.2 Gestion du Nunchuck : . . . . . . . . . . . . . . . . . . . . . . . . 19
B.3 Gestion de l’envoi la matrice : . . . . . . . . . . . . . . . . . . . . . 21
B.4 Gestion du tableau symbolisant la matrice : A . . . . . . . . . . . . . 23
B.5 Gestion du serpent : . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Merci M. BoØ et M. Vantroys, nos tuteurs de projet, pour l’aide qu’ils nous ont apportØe.
Merci M. Flamen pour son aide inestimable et les nombreux conseils qu’il a pu nous offrir. Sans lui nous n’aurions ni matrice de LED ni carte Ølectronique digne de ce nom.
Merci aux Øl?ves IMA qui nous ont soutenus et qui ont approuvØ notre projet.
Merci Øgalement aux enseignants Polytech’Lille qui pour certains ont manifestØ de l’intØrŒt pour notre projet.
Dans le cadre du projet de n de 4?me annØe nous avons dØcidØ de rØaliser une matrice de LED en 3 dimensions.
Figure 1 Le cube de 512 LED
Ce projet permettra d’habiller les reprØsentations o cielles de Polytech’Lille et du dØpartement IMA, telles que la journØe portes ouvertes. Le contexte technique est le suivant, nous disposions en dØbut de projet d’un ensemble de LED rouges non montØes, d’un Arduino (Uno ou MØga), du logiciel Altium Designer 2010 et de toutes les machines nØcessaire la fabrication de cartes Ølectroniques. Sur une pØriode de 15 semaines, soit environ 70 heures encadrØes auxquelles il faut ajouter le temps passØ en dehors des crØneaux encadrØs, nous avons donc proposØ les solutions techniques, rØalisØ des prototypes, con u les programmes et les cartes Ølectroniques, schØmatisØ l’implantation du cube de LED, assemblØ l’ensemble des composants et rØalisØ des tests fonctionnels . La seule partie dont nous ne nous sommes pas occupØs est le montage proprement parler de la matrice de 512 LED.
Dans ce rapport, nous revenons sur l’Ølaboration de ce projet. Nous discuterons sur les solutions envisagØes, tant matØrielles que logicielles, puis nous prØsenterons celles que nous avons utilisØes.
1 Solutions techniques
1.1 Partie matØrielle
Le travail sur la partie matØrielle consistait en la conception de di Ørentes cartes. Voici une reprØsentation simpli Øe du montage complet prØvu :
Figure 1.1.1 SchØma simpli Ø du montage prØvu
1.1.1 Carte de commande
La premi?re carte est une carte capable de commander les 512 LED gr ce un Arduino, sachant qu’un Arduino Uno ne poss?de que 19 sorties dont 13 numØriques. Plusieurs solutions nous ont semblØ viables, voici lesquelles :
- La premi?re solution a ØtØ d’utiliser uniquement des dØcodeurs 3 vers 8 pour la sØlection des Øtages et 4 vers 16 pour la sØlection des LED. Malheureusement nous ne disposions plus assez de broches libres pour brancher un adaptateur Nunchuck sur Arduino Uno.
- La deuxi?me solution a ØtØ d’utiliser des circuits intØgrØs contenant 8 bascules D. toutes les entrØes des bascules Øtaient reliØes par un bus de donnØes commun (8 bits de donnØes) et les bascules mØmorisaient un mot prØsent sur le bus lors d’un front d’horloge qui lui Øtait propre. Cette solution faisait appel 19 broches au moins et Øtant donnØ qu’il fallait brancher un adaptateur Nunchuck en plus sur les broches analogiques il nous manquait de l’espace.
- La derni?re est une solution originale qui utilise 8 registres dØcalages couplØs 8 bascules D de 8 bits. Les registres sont chargement sØrie et dØchargement parall?le, le principe est donc de charger les 8 registres avec 8 bits de mani?re sØrie pour ensuite les stocker et les a cher, pendant le temps d’a chage les autres sØries de 8 bits sont injectØes dans les registres. Cette solution utilise au maximum 13 broches de l’Arduino et au minimum 6 broches car les registres dØcalages ont une sortie sØrie qui permet de les assembler ensemble pour former un seul registre dØcalage de 64 bits.
Pour ces trois solutions la sØlection d’un Øtage de LED est toujours rØalisØe par un dØmultiplexeur 3 vers 8 qui permet de relier toutes les cathodes d’un Øtage la masse via des transistors.
Nous nous somme donc basØs sur le syst?me qui utilise des registres dØcalage car il permet d’utiliser la vitesse d’exØcution de l’Arduino dØfaut du nombre de sorties qui est vraiment limitØ. En tout il nous faut donc 13 sorties pour le contr le de la matrice : 8 pour les entrØes sØries, 2 pour les signaux d’enable des registres et 3 pour le multiplexage des couches. L’alimentation des composants de commande (ceux qui n’alimentent pas directement les LED) est directement desservie par l’Arduino. Le typon de cette carte est disponible en annexe A.0.1.
Choix des composants :
- (x8) 74HCT595 8-bit Serial in/Parallel out Shift Register
- (x8) UDN2981 8-Channel Transistor Array Darlington Type Source
- (x1) 74HCT238 3-to-8 line Decoder
- (x1) ULN2803 - 8-Channel Transistor Array Darlington Type Sink
1.1.2 Carte de Nunchuck
Figure 1.1.2 La carte Wiichuck telle que prØsentØe chez Sparkfun
La deuxi?me carte est une simple carte d’adaptation du connecteur Nunchuck de mani?re la brancher sur les pattes analogiques de l’Arduino comme un shield Arduino classique. Il se trouve que cette carte Øtait dØj disponible dans les stocks de l’Øcole car elle existe dans le commerce, vendue sous le nom de "Wiichuck", nØanmoins nous avons tout de mŒme crØØ un typon pour pouvoir tirer une autre carte de notre propre fabrication.
Cette carte est en rØalitØ tr?s simple, car elle ne fait que relier les pistes utilisØes de la prise femelle du Nunchuck avec des broches permettant de se connecter sur un Arduino.
Figure 1.1.3 Le connecteur femelle du Nunchuck
1.1.3 Carte d’alimentation
La 3?me carte est prØvue pour alimenter l’ensemble du syst?me partir d’une seule prise Ølectrique, de cette mani?re il est plus facile de dØplacer l’ensemble (plut t que de prØvoir un bloc d’alimentation de test pour chaque branchement occasionnel). Ce bloc est assez simple et il permet de disposer de deux tensions, 12V et 5V respectivement pour l’Arduino et pour l’alimentation des LED. L’alimentation des LED doit pouvoir assurer un courant de 1A environ lorsque le maximum des LED est allumØ. Le schØma de cette carte est basØ sur un rØgulateur L7805CV qui permet de transformer une tension 12V en 5V en maintenant une tension stable et permettant un courant de 1,5A.
Figure 1.1.4 SchØma de la carte d’alimentation
En raison de manque de temps et de contraintes techniques, cette carte n’a pu Œtre rØalisØe. Pour les tests, nous avons utilisØ une alimentation 5V sØparØe du bloc d’alimentation de l’Arduino.
1.1.4 Carte de test
Pour pouvoir tester et surtout vØri er le bon fonctionnement de notre syst?me nous avons eu besoin de crØer une carte temporaire qui permet d’adapter un circuit intØgrØ de type CMS
(Composant MontØ en Surface) en un circuit DIP (Dual Inline Package - composant traversant).
Figure 1.1.5 Carte d’adaptation de CMS
1.2 Partie logicielle
Du c tØ logiciel, il nous faut programmer l’Arduino de sorte qu’il puisse commander la matrice, mais Øgalement gØrer une partie de Snake. La majoritØ du code est disponible en annexe, et la totalitØ des programmes est Øgalement disponible sur la page wiki su projet (https:// ?title=Matrice_de_LED_3D).
1.2.1 Description du programme
Le but nal est de jouer un jeu de type Snake , mais la di Ørence des Snake habituels, celui-ci se joue en 3D. Pour ce faire, le programme Arduino se dØcoupe en plusieurs chiers (que l’on peut assimiler des biblioth?ques) a n de rendre le programme modulaire : un chier principal contenant les fonctions indispensables pour l’Arduino que sont setup() exØcutØe une fois au dØmarrage et loop() qui tourne en boucle, un chier pour les fonctions de gestion du Nunchuck, un chier pour les fonctions de gestion de l’a chage et en n un chier pour les fonctions de l’algorithme du Snake. Ainsi il su t de modi er un seul chier pour adapter le programme d’autres supports.
1.2.2 Choix de l’Arduino
D?s le dØbut du projet, nous avons pu remarquer que le nombre de sorties et la RAM d’un Arduino Uno seraient les di cultØs contourner. Un moyen simple de les contourner aurait ØtØ d’utiliser un Arduino Mega, qui o re une cinquantaine de sorties numØriques et une mØmoire RAM disponible de 8 ko. Cependant, utiliser un Arduino Mega semblait disproportionnØ au vu du projet. C’est pourquoi nous nous sommes tournØs vers un Arduino Uno, qui o re beaucoup moins de sorties et de mØmoire, mais qui semblait plus appropriØ pour ce projet. De plus, optimiser le programme ainsi que les connexions matØrielles dans le but d’utiliser la version Uno fut une tr?s bonne expØrience.
1.2.3 Choix des tableaux
L’utilisation de tableaux est indispensable car il faut un moyen de stocker les valeurs que vont prendre les LED de la matrice, en l’occurrence "0" ou "1" symbolisant l’Øtat Øteint ou allumØ. Utiliser un seul tableau global nous a vite semblØ impossible, car il fallait avoir la possibilitØ de rØaliser des opØrations sur le serpent. C’est pourquoi le serpent est reprØsentØ par un vecteur de 512 cases (la taille maximale qu’il peut atteindre correspond au nombre de LED disponibles). Dans un premier temps nous pensions stocker pour chaque case ses coordonnØes sur la matrice (en mode [X][Y][Z], mais cela revenait crØer un tableau de 512 lignes ayant chacune 3 colonnes, soit un total de 1536 octets et cela rendait plus di cile l’algorithme. Au nal nous avons optØ pour un vecteur de 512 cases contenant chacune un nombre compris entre 0 et 511 qui correspond au numØro de la LED. Ceci rend tr?s simple l’algorithme, car ainsi on ne manipule qu’une seule case.
Ensuite, notre premier algorithme utilisait 3 tableaux dont 2 uniquement pour le Snake, l’un pour l’Øtat actuel du Snake et un deuxi?me qui re oit l’Øtat suivant, puis celui-ci Øtait recopiØ dans le premier. Ceci n’Øtait Øvidemment pas tr?s optimisØ, c’est pourquoi le deuxi?me tableau a tr?s vite ØtØ supprimØ, et les calculs pour dØ nir l’Øtat suivant se font directement sur le seul tableau restant, en commen ant par la n pour Øviter les Øcrasements des valeurs. La suppression d’un tableau a simplement obligØ ajouter deux variables globales, une pour symboliser la tŒte du serpent et une autre contenant la taille du serpent.
Le deuxi?me tableau est utilisØ pour stocker les Øtats de chaque LED (allumØe ou Øteinte) pour ensuite envoyer ces donnØes la carte de commande. Celui-ci poss?de 64 octets, en e et une LED est codØe sur un bit, et 64*8=512.
Voici une reprØsentation schØmatique de la mani?re donc la matrice et le serpent sont gØrØs, pour l’exemple nous prenons uniquement la premi?re couche de la matrice ( gure 1.2.1).
Figure 1.2.1 Exemple avec la premi?re couche de 64 LED
Les disques rouges reprØsentent les LED allumØes par le serpent, les disques rouge foncØ reprØsentent les LED Øteintes.
Chaque LED poss?de un numØro, compris entre 0 et 511 pour la matrice 3D, de 0 63 pour la premi?re couche. Si la LED 9 est la tŒte du serpent, alors le tableau reprØsentant celui-ci contiendra ces valeurs (exprimØes en dØcimal) :
[9][10][11][19][27][28][29][37][45]
Une fois la mise jour e ectuØe, le tableau contenant les Øtats des LED vaudra donc, exprimØ en binaire, et en considØrant que le bit de poids faible est gauche :
[00000000][01110000][00010000][00011100][00000100][00000000][00000000]
1.2.4 Gestion de la mØmoire
La premi?re version du code avait ØtØ dØveloppØe sans prendre en compte la limitation en mØmoire RAM de l’Arduino. En e et, en utilisant deux tableaux de 1024 octets chacun (le premier reprØsentant le serpent l’Øtape n et le second l’Øtape n+1) et un troisi?me tableau de 512 octets pour la matrice (dans lequel un octet repØsentait une LED), on dØpassait tr?s largement les 2 ko de mØmoire disponible. C’est pourquoi le code a ØtØ repensØ a n d’optimiser au mieux l’utilisation des tableaux et des variables. Le vecteur reprØsentant le serpent doit pouvoir contenir des valeurs comprises entre 0 et 511, or un octet est trop petit pour coder les valeurs supØrieures 255. Chaque entier est donc codØ sur deux octets, soit un total de 1024 octets pour ce tableau.
Sachant que la RAM de l’Arduino Uno est de 2 ko, et qu’il est prØfØrable de ne pas dØpasser environ 1,5 ko sous peine de voir appara tre des probl?mes de gestion des variables et de pile, il ne reste au maximum que 512 octets disponibles pour stocker le tableau qui reprØsente la matrice et Øgalement les nombreuses autres variables qui prennent une place non nØgligeable.
Etant donnØ la place perdue en utilisant des octets complets pour stocker l’Øtat des LED, nous sommes partis dans une autre direction : associer un bit une LED, ce qui ram?ne la taille du tableau associØ la matrice 64 octets. Ceci a rendu le code lØg?rement plus complexe en utilisant des masques et autres opØrations bit bit au lieu d’une simple a ectation par indice, mais cela a l’avantage d’Œtre tr?s compact et optimisØ.
1.2.5 Gestion du Nunchuck
Ce qui rend ce Snake di Ørent mis part son a chage 3D, c’est aussi l’utilisation du Nunchuck de Nintendo pour le contr ler. Le joystick analogique permet de dØplacer le serpent dans le plan horizontal, alors que l’accØlØrom?tre permet de dØplacer le serpent verticalement. Ce Nunchuck dialogue avec l’Arduino gr ce au protocole I2C (utilisation de la biblioth?que Wire.h intØgrØe nativement dans l’Arduino IDE).
Dans un premier temps nous pensions Øgalement contr ler le Snake avec une Wiimote connectØe en Bluetooth. Cependant la connexion requise, Bluetooth HID, n’est pas supportØe nativement par Arduino. La solution aurait ØtØ de prendre un shield Bluetooth externe mais cela compliquait le montage. Nous nous sommes donc limitØs l’utilisation d’un Nunchuck connectØ en laire. L’utilisation du connecteur appelØ Wiichuck (voir 1.1.2) dans le commerce permet de connecter facilement un Nunchuck sur un Arduino en reliant les 4 pistes utilisØes sur le connecteur du Nunchuck sur les broches analogiques A0 A3 de l’Arduino a n de satisfaire aux exigences du protocole I2C. Ceci oblige donc con gurer ces broches de mani?re ce qu’elles alimentent le Nunchuck : il su t de rediriger l’alimentation et la masse sur les broches A3 et A2, les deux autres sont utilisØes pour les donnØes et l’horloge.
1.2.6 Gestion de l’a chage
A n de rendre l’a chage totalement indØpendant du reste du programme, nous utilisons une interruption temporelle qui exØcute la fonction de mise jour de la matrice. Pour ce faire, nous utilisons la biblioth?que MsTimer2 () qui permet d’accØder facilement au timer nommØ Timer2 et aux interruptions liØes ce timer. Ainsi nous pouvons xer la pØriode d’appel cette fonction qui est la seule modi er directement les sorties de l’Arduino, et donc la commande de la matrice.
Cette fonction g?re les registres dØcalage et la sØlection des couches de la matrice.
1.2.7 Gestion du serpent
Au niveau de la gestion du serpent, le programme se dØcoupe en plusieurs phases : d’abord on calcule la nouvelle position de la tŒte du serpent en fonction de la direction ordonnØe par le Nunchuck, puis on vØri e si cette nouvelle position correspond une pomme, dans ce cas elle est indiquØe comme mangØe et une nouvelle pomme est introduite. Ensuite on vØri e si la tŒte du serpent entre en collision avec la queue, ce qui mettra n au programme et relance une nouvelle partie. En n, en cas de non-collision, le serpent est mis jour dans sa globalitØ.
Les interruptions n’autorisent pas l’utilisation de fonctions de pause telles que "delay()". De plus, le timer utilisØ pour l’a chage n’o re qu’une seule interruption. La fonction faisant avancer le serpent devant elle aussi Œtre exØcutØe pØriodiquement (la pØriode dØpendant de la vitesse du serpent), nous avons choisi de tester chaque tour si le nombre de millisecondes ØcoulØes depuis la derni?re exØcution est supØrieur la pØriode voulue. Ceci n’est pas tr?s e cace algorithmiquement, il faudrait utiliser un second timer pour cette fonction. Cependant, il faut absolument que les valeurs provenant du Nunchuck soient compl?tes avant d’exØcuter la fonction de dØplacement du serpent. Utiliser une interruption pourrait alors poser probl?me, moins d’implØmenter des sØcuritØs telles que des sØmaphores, qui rendraient alors le code plus complexe.
2 Conception assistØe par ordinateur
La conception est la partie inØvitable et nØcessaire qui prime sur toutes les autres puisque sans elle la crØation d’un circuit sur epoxy n’est pas possible. Le but de la conception est de produire un mod?le imprimØ de la carte, avec les pistes, les trous, les vias, les annotations Pour ce faire il est utile de crØer un schØmatique qui introduit les di Ørents composants et leurs routages sur un schØma, ce schØma n’est pas utilisable tel quel mais il permet par la suite d’avoir une aide visuelle pour placer les pistes, aussi appelØe "chevelu".
Nous avons dØcidØ d’utiliser le logiciel Altium pour rØaliser notre carte car il s’agit d’une plate-forme e cace avec de nombreux outils adaptØs la CAO et nous avons plusieurs fois eu recours ce dernier pendant nos cours Polytech Lille. Dans notre projet le recours ce type d’outils Øtait indispensable car l’Øchelle des composants et des pistes Øtait tr?s petite, en e et nous utilisons des composants CMS.
C’est l’une des phases qui a demandØ le plus de notre temps car une conception de circuit aussi complet ne se fait pas sans quelques prØcautions. Parmi les choses vØri er :
- La taille des pistes doit Œtre au moins Øgale 27mil
- La taille des vias et des trous en extØrieur doit Œtre de 80mil et intØrieur 16mil minimum Ces deux r?gles sont nØcessaires la fois pour Øviter une erreur de prØcision lors de l’impression mais aussi pour assurer que les pistes puissent supporter le courant.
- Les pistes ne doivent pas Œtre angle droit car les contraintes mØcaniques pourraient les dØtacher
- le placement et le routage des composants doit Œtre simple et fonctionnel, surtout lorsqu’il y a des composants montØs en surface et traversants
- Il est prØfØrable de diviser l’alimentation s’il existe une partie commande et une partie puissance
- L’espace qu’occupe la carte doit Œtre rØduit surtout s’il s’agit d’un projet embarquØ
- viter au maximum les vias sur les cartes double face
Pour suivre toutes les r?gles ci dessus nous avons dß remanier le projet plusieurs reprises voire mŒme reprendre les routages depuis le dØbut.
Lors de la conception d’une telle carte, un logiciel comme Altium a toujours besoin d’une librairie dans laquelle il pourra trouver les schØmas, le brochage et les "footprints" d’un composant. Malheureusement pour nous aucune librairie connue du logiciel ne contenait ce dont nous avions besoin. Il a donc Øtait nØcessaire de recrØer une librairie personnalisØe pour rØpondre nos besoins.
La crØation d’une librairie s’adresse habituellement des professionnels et comprend toutes les informations possibles sur un composant depuis son nom jusqu’ une reprØsentation 3D de l’espace physique utilisØ dans un bo tier. Il Øtait Øvident que nous n’avions pas besoin de toutes les informations, nous avons donc seulement reprØsentØ le schØma et le footprint comprenant la position des pattes imprimer sur le circuit. Notre librairie personnalisØe s’est donc vue dotØe de deux composants (le registre dØcalage 74HCT595 et le dØmultiplexeur 74HCT238).
3 Conception du cube de LED
La partie ma tresse de notre projet, le cube de LED, a ØtØ parmi les plus longues rØaliser car il s’agit d’un travail tr?s minutieux et qui requiert une dextØritØ et une expØrience de la soudure que nous n’avons pas. C’est pour cela que la crØation de la matrice a ØtØ con Øe M. Flamen. NØanmoins nous avons enti?rement dØcrit le schØma de principe et le design global du cube. l’intention de M. Flamen nous avons crØØ un guide au format PDF pour expliquer les caractØristiques de notre projet et la mani?re dont cette matrice devait Œtre construite.
Comme expliquØ prØcØdemment, notre matrice est composØe de 512 LED soit 8x8x8 LED. Cette con guration serait tr?s di cilement rØalisable s’il fallait relier chacune des LED, pour remØdier cette di cultØ nous avons fait appel aux propriØtØs Ølectroniques des LED en combinaison avec la logique du cube. Nous avons dØcidØ de dØ nir quelle LED s’allume en fonction de si elle est connectØe une source de tension d’un c tØ et une masse de l’autre. Ainsi toutes les LED sont reliØes par leur anode par groupe de 8 verticalement ce qui nous donne 64 groupes, et toutes les LED sont reliØes par leur cathode par groupe de 64 horizontalement soit 8 groupes qui forment 8 Øtages de LED empilØs verticalement.
Voici un schØma pour illustrer le principe de sØlection d’une LED :
Figure 3.0.2 Exemple de sØlection d’une LED
Les traits rouges sont les anodes, les traits noirs sont les cathodes. En sØlectionnant une anode commune 8 LED et une cathode commune 64 LED, on peut allumer la LED dont l’anode et la cathode correspondent celles sØlectionnØes.
ThØoriquement on peut donc allumer 64 LED au maximum en une seule fois, par exemple les 64 LED du premier Øtage. Cette limite ne peut pas Œtre contournØe sans utiliser plus de ls, or comment rØsoudre ce probl?me partir de l ?
La rØponse est simple et repose sur la facultØ que l’homme a de ne pas pouvoir voir plus de 42 images par seconde environ (cette valeur peut changer en fonction de l’individu mais dans l’ensemble cette valeur su t). En e et si l’homme ne peut voir plus de 42 images par secondes, alors il devient facile de le tromper avec une illusion. Si une LED est allumØe seulement 1 fois sur 8 une frØquence supØrieure 42 Hz, alors l’ il humain ne per oit pas la di Ørence entre le moment oø elle est allumØe et le moment oø elle est Øteinte, il ne per oit qu’une baisse de luminositØ globale. Cette caractØristique est due la persistance rØtinienne et c’est ce principe que nous utilisons dans notre syst?me. L’a chage est dØcomposØ en 8 Øtapes qui se rØp?tent indØ niment, Øtage apr?s Øtage les LED sont reliØes la masse par leur cathode et il su t de les relier la source par leur anode pour allumer celles qui doivent l’Œtre.
Ainsi en synchronisant gr ce un micro-contr leur on peut dØterminer quelles LED sont allumØes n’importe quel endroit de la matrice, et ce sans que l il humain ne se rende compte de l’alternance e ectuØe.
4 Pour aller plus loin
A la n de ces quelques semaines, nous n’avons pas pu mener terme tout ce qui Øtait prØvu, notamment en raison d’une arrivØe tr?s tardive de composants commandØs. A n d’Œtre compl?tement opØrationnel, ce projet nØcessiterait quelques heures de travail supplØmentaires.
4.1 MatØriel
Au niveau matØriel, il manque quelques cartes. En e et, il nous restait peu de temps pour la rØaliser, et il valait mieux passer ce temps naliser proprement la carte de commande et sa connexion la matrice gr ce des nappes de type Parallel ATA ou IDE telles qu’on en trouve dans les ordinateurs. Il a fallu sertir ces nappes sur les connecteurs et surtout y souder les rØsistances sans lesquelles le courant serait trop important et pourrait endommager les LED.
Pour les tests, nous avons donc alimentØ les composants gr ce un gØnØrateur 5V. NØanmoins, la carte telle que prØvue initialement serait tr?s simple rØaliser, car nous avons tout le matØriel nØcessaire : le rØgulateur L78005CV, les condensateurs de dØcouplage, les rØsistances
Ensuite, la plaque sur laquelle la matrice doit Œtre posØe doit aussi Œtre rØalisØe, il s’agit d’une carte reliant la nappe d’alimentation aux anodes et cathodes des LED. Pour le moment cette connexion se fait par ls volants.
En n, dans un souci de protection et d’esthØtique, il est prØvu que la matrice soit enfermØe dans un co rage de Plexiglas. Ce co rage n’a pas pu Œtre fabriquØ lors de ce projet.
4.2 Logiciel
Au niveau logiciel, nous avons pensØ de nombreuses fonctionnalitØs supplØmentaires, mais nous nous sommes concentrØs sur le principal.
Premi?rement, il faut revoir la fonction permettant d’envoyer les donnØes la matrice, car nous n’avons pas eu le temps de la programmer de fa on optimale et l’a chage ne rØpond pas toujours correctement. Une fois l’a chage totalement fonctionnel, on peut imaginer de nombreuses applications possibles.
Ensuite, on peut s’intØresser la fa on d’amØliorer le programme principal, c’est- -dire le jeu Snake. Une fonctionnalitØ initialement prØvue Øtait une sØlection de di cultØ, qui changeait la vitesse de dØplacement du serpent. Ceci est en fait simple rØaliser, il su t de crØer une fonction "menu" au dØmarrage qui permet de sØlectionner la di cultØ, par exemple en tournant le nunchuck dans un sens ou dans un autre.
On peut Øgalement penser un moyen de sauvegarder les scores. Ceci est possible en utilisant des variables sauvegardØes dans la mØmoire ash de l’Arduino, qui n’est pas volatile.
Lors de ce projet, nous avons dß mener du dØbut la n le dØveloppement de cette matrice 3D ainsi que des cartes et des programmes permettant de la commander. La conception et la rØalisation de toutes les parties de ce projet nous ont menØ utiliser nos connaissances en Ølectronique et en informatique, et nous ont permis de dØvelopper de nouvelles connaissances, notamment sur la mani?re de concevoir un circuit imprimØ.
MŒme si le projet n’a pu Œtre compl?tement terminØ faute de temps, nous avons con u une base su sante pour permettre d’autres Øtudiants ou professeurs de reprendre le projet et d’y ajouter les fonctionnalitØs manquantes, dans l’optique d’une prØsentation lors d’Øv?nements l’Øcole comme les journØes Portes Ouvertes.
Annexe A : Carte Ølectronique
Figure A.0.1 Carte de commande. Faces supØrieure et infØrieure
Annexe B : Programme Arduino
Ce programme se dØcompose en plusieurs chiers, ceci a n de maximiser la modularitØ. Les chiers d’entŒte ("headers") sont volontairement absents car inutiles ici.
Annexe B.1 : Fichier principal ()
#include <Wire .h>
#include <MsTimer2 .h>
#include "Nunchuck . h"
#include " Affichage . h"
#include "Snake . h"
#include "Cube . h"
#include <Arduino .h>
// Declarations variables pour le nunchuck const byte DEADZONEX = 30; const byte DEADZONEY = 20; const byte DEADZONEZ = 10; const byte DEADZONEJOYX = 30; const byte DEADZONEJOYY = 30; byte cpt , choix=4; byte data_nunchuck [ 6 ] ; int accelX , accelY , accelZ , accelXCentre , accelYCentre , accelZCentre ; int joyX , joyY , joyXCentre , joyYCentre ; byte boutonZ , boutonC ;
// Declarations variables pour l ’ affichage const byte NB_X_MAX = 8; const byte NB_Y_MAX = 8; const byte NB_Z_MAX = 8; const int NB_LED = 512; const byte NB_OCTETS = NB_LED/8; byte matrix [NB_OCTETS] ; int inc = 0;
// Declarations variables pour le snake const byte numX = 8; const byte numY = 8; const byte numZ = 8; byte direc ; // direction du snake int snake [NB_LED] ; byte startpoint = 10; int snakehead = 0; int snakesize = 1; int eat = 0; int apple = 0; byte c o l l i s i o n = 0; int SnakeSpeed = 400;
unsigned long l a s t M i l l i s = 0;
/??????????????????????????????????/
void setup () {
setup_cube ( ) ; // si on u t i l i s e le Cube 3D
// setup_affichage () // si on u t i l i s e la matrice 2D de Sparkfun nunchuck_init ( ) ;
MsTimer2 : : set (20 , interrup_aff_cube );
// periode de 20 ms, appel de la fonction commandant le cube MsTimer2 : : start ( ) ;
SnakeNewGame ( ) ;
}
void loop () {
nunchuck_read_data ( ) ; nunchuck_reinit_Z ( ) ;
SnakeNewDirectionJoystick ( ) ; Snake ( ) ; delay (80);
}
Annexe B.2 : Gestion du Nunchuck :
#include "Nunchuck . h"
#include "Arduino . h" #include <Wire .h>
void nunchuck_setpowerpins (){
// Uses port C ( analog in ) pins as power & ground for Nunchuck
//From Wiichuck demo , Tod E. Kurt , 2008
#define pwrpin PORTC3
#define gndpin PORTC2
DDRC |= _BV( pwrpin ) | _BV( gndpin );
PORTC &=~ _BV( gndpin );
PORTC |= _BV( pwrpin ); delay (100); // wait for things to s t a b i l i z e
}
void nunchuck_init (){
nunchuck_setpowerpins ( ) ;
Wire . begin ( ) ;
/? I n i t i a l i s a t i o n du nunchuck ?/
Wire . beginTransmission (NUNCHUCK_ADDRESS);
Wire . write (0xF0 ); Wire . write (0x55 );
Wire . endTransmission ( ) ;
Wire . beginTransmission (NUNCHUCK_ADDRESS);
Wire . write (0xFB);
Wire . write (0x00 ); Wire . endTransmission ( ) ;
nunchuck_calibrate ( ) ;
}
void nunchuck_read_data (){
// on demande 6 octets au nunchuck Wire . requestFrom (NUNCHUCK_ADDRESS, 6);
cpt = 0;
// tant qu ’ i l y a des donnees
while(Wire . available ()){
// on recupere les donnees data_nunchuck [ cpt ] = Wire . read ( ) ; cpt++;
}
// on r e i n i t i a l i s e le nunchuck pour la prochaine demande
Wire . beginTransmission (NUNCHUCK_ADDRESS);
Wire . write (0x00 );
Wire . endTransmission ( ) ;
if ( cpt >= 5){
// on extrait les donnees joyX = data_nunchuck [ 0 ] ? joyXCentre ; joyY = data_nunchuck [ 1 ] ? joyYCentre ; accelX = (( data_nunchuck [ 2 ] << 2) + (( data_nunchuck [ 5 ] >> 2) & 0x03 ))
? accelXCentre ; //MSB sur data [2] + LSB sur data [5]
accelY = (( data_nunchuck [ 3 ] << 2) + (( data_nunchuck [ 5 ] >> 4) & 0x03 ))
? accelYCentre ;
accelZ = (( data_nunchuck [ 4 ] << 2) + (( data_nunchuck [ 5 ] >> 6) & 0x03 ))
? accelZCentre ;
boutonZ = data_nunchuck [ 5 ] & 1; //boutons Z et C : 0 si appuye , 1 si relache boutonC = (data_nunchuck [ 5 ] >> 1) & 1;
}
}
void nunchuck_calibrate (){
accelXCentre = 0; accelYCentre = 0; accelZCentre = 0; joyXCentre = 0; joyYCentre = 0; nunchuck_read_data ( ) ; // le premier tour est toujours faux
delay (100);
nunchuck_read_data ( ) ; accelXCentre = accelX ; accelYCentre = accelY ; accelZCentre = accelZ ; joyXCentre = joyX ; joyYCentre = joyY ;
}
void nunchuck_reinit_Z (){ if (boutonZ==0) nunchuck_calibrate ( ) ;
}
Annexe B.3 : Gestion de l’envoi la matrice :
#include "Cube . h"
void setup_cube (){
pinMode(SHCP, OUTPUT); pinMode(STCP, OUTPUT); pinMode(S0 , OUTPUT); pinMode(S1 , OUTPUT); pinMode(S2 , OUTPUT); pinMode(S3 , OUTPUT); pinMode(S4 , OUTPUT); pinMode(S5 , OUTPUT); pinMode(S6 , OUTPUT); pinMode(S7 , OUTPUT); pinMode(A, OUTPUT); pinMode(B, OUTPUT); pinMode(C, OUTPUT);
}
void interrup_aff_cube (){
short j , k ;
// Boucle des lignes (8 lignes , 64 LEDs) for ( j =0;j <8; j++){
// Boucle des LEDs (8 LEDs)
// u t i l i s a t i o n du tableau matrix digitalWrite (S0 , matrix [( inc ?8)+( j ) ] ) ; digitalWrite (S1 ,( matrix [( inc ?8)+( j )] >> 1) & 1); digitalWrite (S2 ,( matrix [( inc ?8)+( j )] >> 2) & 1); digitalWrite (S3 ,( matrix [( inc ?8)+( j )] >> 3) & 1); digitalWrite (S4 ,( matrix [( inc ?8)+( j )] >> 4) & 1); digitalWrite (S5 ,( matrix [( inc ?8)+( j )] >> 5) & 1); digitalWrite (S6 ,( matrix [( inc ?8)+( j )] >> 6) & 1); digitalWrite (S7 ,( matrix [( inc ?8)+( j )] >> 7) & 1);
// Front montant de l ’ horloge des registres a decalage digitalWrite (SHCP,HIGH); digitalWrite (SHCP,LOW);
}
// Demultiplexeur if ( inc !=0){ | |||
digitalWrite (A,( inc==1 | | inc==3 | | inc==5 | | inc==7) ? HIGH : LOW); |
digitalWrite (B,( inc==2 | | inc==3 | | inc==6 | | inc==7) ? HIGH : LOW); |
digitalWrite (C,( inc==4 | | inc==5 | | inc==6 | | inc==7) ? HIGH : LOW); |
} else{
digitalWrite (A,LOW); digitalWrite (B,LOW); digitalWrite (C,LOW); }
// Front montant du signal de chargement des bascules D digitalWrite (STCP,HIGH); digitalWrite (STCP,LOW); inc++; if ( inc >7)inc =0;
}
Annexe B.4 : Gestion du tableau symbolisant la matrice : A
#include " Affichage . h"
#include "Snake . h"
// Les 3 fonctions ci?dessous s ’ u t i l i s e n t si on veut u t i l i s e r le programme
// sur une matrice 2D (8x8 ). Ne pas oublier de mettre NB_LED a 64 dans ce cas .
/?
char spi_transfer ( v o l a t i l e char data ){
SPDR = data ; // Start the transmission
while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
{
}; }
void setup_affichage (){
byte clr ;
pinMode(DATAOUT,OUTPUT); pinMode(SPICLOCK,OUTPUT); pinMode(CHIPSELECT,OUTPUT);
digitalWrite (CHIPSELECT,HIGH); // disable device
SPCR = B01010001 ; //SPI Registers
SPSR = SPSR & B11111110 ; //make sure the speed is 125KHz clr=SPSR; clr=SPDR; delay (10);
}
void affichage_envoi_byte (){
// delay (100); digitalWrite (CHIPSELECT,LOW); // enable the ChipSelect on the backpack delayMicroseconds (100); for ( int i =0; i<NB_OCTETS; i++){ for ( int j =0; j <8; j++){ spi_transfer (( matrix [ i ] >> j ) & 1);
}
}
digitalWrite (CHIPSELECT,HIGH); // disable the ChipSelect on the backpack //delayMicroseconds (500);
} ?/
void clearMatrix_byte () {
// remise a 0 de la matrice for ( int i =0; i<NB_OCTETS; i++){
matrix [ i ] = 0;
}
}
int puissance ( int a , int b){
// La fonction puissance de math . h n ’ est pas f i a b l e (2^7 = 127 ??) int c = 1; for ( int i =0;i<b ; i++) c ?= a ; return c ;
}
void updateMatrix_byte (){ //Stocke dans le tableau matrix les leds
// allumees par le serpent et la pomme
// Dans matrix un bit = une led clearMatrix_byte ( ) ;
int i = 0;
while( i<snakesize && i<NB_LED){
matrix [ snake [ i ]/8] |= puissance (2 ,( snake [ i ] % 8));
i++;
} matrix [ apple /8] |= puissance (2 ,( apple %8));
}
Annexe B.5 : Gestion du serpent :
#include "Arduino . h"
#include "Snake . h"
#include "Nunchuck . h"
#include " Affichage . h"
/??? snake new direction ???/ void SnakeNewDirectionJoystick (){
// Directions //0 = haut
//1 = droite
//2 = bas
//3 = gauche
// 4 et 5 pour la 3e direction if (joyX>DEADZONEJOYX) direc = 3;
else if (joyX<?DEADZONEJOYX) direc = 1; else if (joyY>DEADZONEJOYY) direc = 0; else if (joyY<?DEADZONEJOYY) direc = 2; else if (axeY<?DEADZONEY) direc = 4; else if (axeY>DEADZONEY) direc = 5;
}
/??? snake new game ???/ void SnakeNewGame(){
clearMatrix_byte ( ) ;
snakesize = 1; // t a i l l e du serpent : 1 LED
snakehead = startpoint ; snake [ 0 ] = snakehead ; SnakeNewApple ( ) ; direc = 1; // choix arbitraire d ’une direction pour commencer c o l l i s i o n = 0;
updateMatrix_byte ( ) ;
}
/??? generate apple ???/ void SnakeNewApple (){
apple = random(NB_LED); for ( int i = 0; i < snakesize ; i++){ //pour eviter que la nouvelle pomme
//n ’ apparaisse sous le snake if ( snake [ i ] == apple ) { SnakeNewApple ( ) ;
}
}
}
/??? snake check apple ???/ void SnakeCheckApple (){
if ( snakehead == apple ){ eat = 1; // flag indiquant que la pomme est mangee
if ( snakesize<NB_LED) snakesize++; // on agrandit le snake SnakeNewApple ( ) ;
}
}
/??? snake check c o l l i s i o n ???/ void SnakeCheckCollision (){
for ( int i = 1; i < snakesize ; i++) {
if ( snake [ i ] == snakehead ) {
c o l l i s i o n = 1;
}
}
}
/??? snake calculate new head position ???/ void SnakeNewHead(){ if ( direc==2){ //down
if (( snakehead + numX) > ((numY ? numX ? 1))) snakehead = snakehead % numX;
else snakehead += numY;
}
else if ( direc==1){ // right
if (( snakehead ? ( snakehead % numX)) == ((( snakehead+1) ? ( snakehead+1) % numX))) snakehead += 1;
else snakehead = snakehead ? (numX ? 1);
}
else if ( direc==0){ //up if (( snakehead ? numX) < 0) snakehead += (numY ? 1) ? numX;
else snakehead ?= numX;
}
else if ( direc==3){ // l e f t
if ( snakehead == 0) // probleme avec 0 ?> on ne peut pas a l l e r a la case ?1
// ?> i l faut a l l e r a la case 7 snakehead = numX?1;
else if (( snakehead ? ( snakehead % numX)) == ((( snakehead ?1) ? ( snakehead ?1) % numX))) snakehead ?= 1;
else snakehead += numX ? 1;
}
else if ( direc == 4){ //3e direction vers le haut : changement d ’ etage
snakehead += numX ? numY;
if ( snakehead > NB_LED) snakehead ?= NB_LED;
}
else if ( direc == 5){
snakehead ?= numX ? numY;
if ( snakehead < 0) snakehead += NB_LED;
}
}
/??? snake move ???/ void Snake (){ if ( m i l l i s ()? l a s t M i l l i s >= SnakeSpeed ){
SnakeNewHead ( ) ;
SnakeCheckApple ( ) ; SnakeCheckCollision ( ) ; if ( c o l l i s i o n == 0){ // si pas de c o l l i s i o n
for ( int i=snakesize ?1; snake [ 0 ] = snakehead ; | i >0; i ??) snake [ i ] = snake [ i ?1]; | |||||
if ( eat == 1) eat = 0; updateMatrix_byte ( ) ; } else { | //remise a 0 du | flag | indiquant | si | la pomme est | mangee |
endGame ( ) ; // fin du jeu } l a s t M i l l i s = m i l l i s ( ) ; | si c o l l i s i o n |
}
}
/??? End game ???/ void endGame(){ delay (1000);
SnakeNewGame ( ) ;
}