Roues lumineuses et bavardes

roueled_message.jpg - 34554 Bytes
Cliquer sur l'image pour la vidéo (20 Mo) en MPEG1.

Initialement ce projet avait pour but d’initier mon fils à la programmation des microcontrôleurs pour l’arracher à ses jeux vidéos.
Il s’agit d’afficher un message lumineux avec un minimum de ressources et c’est bien là le gros du défi. Bien sûr on voit pléthore de banderoles lumineuses avec des matrices de LEDs multicolores, mais je lui soumets l’idée de faire la même chose uniquement avec 7 LEDs !
« Je ne voie pas comment on peut faire un message avec si peu de loupiotes » me dit-il approximativement en hochant les épaules.

L’idée :
Regarde comment sont réalisés les caractères sur l’afficheur du lecteur DVD. Chaque lettre des mots qu’il affiche est composée d’une grille de point lumineux. Regardons de plus près, la grille fait 7 points de haut et 5 de large. Il est possible de reproduire tous les caractères de l’alphabet avec cette matrice de points et bien d’autre chose d’ailleurs puisqu’il y a 2^(7x5)= 34 milliards de symboles affichables !
Maintenant imagine que les 7 LEDs dont je te parlais compose une colonne de cette grille.
Tu allumes cette colonne comme cela 0000000_ tu attends un petit peu puis tu déplaces la colonne d’une épaisseur de LED et tu allumes ___0___0 tu attends de la même durée précédemment et tu déplaces encore ta colonne ainsi de suite 3 fois et tu finis par afficher 0000000_, tu viens d’afficher un « A »:
0000000_
___0___0
___0___0
___0___0
0000000_ (Il faut pencher la tête à droite de 90°)

« Oui, sur le papier mais je ne vais jamais me souvenir de ce qui a été allumé ». Oui, mais si tu recommences cette séquence plusieurs fois et rapidement, ton œil, de part la persistance rétinienne, gardera l’information en mémoire pour toi.
« C’est quoi la persistance rétinienne ? ». Et bien quand tu éclaires intensément la rétine brièvement c’est à dire que tu lui balances des photons qui se transforment en charges électriques et bien ces charges vont mettre un certain temps pour s’écouler dans le nerf optique car elles ne peuvent voyager qu’à une certaine vitesse. « Je n’ai rien compris ! ». Bon, si tu te tapes avec un marteau sur le doigt, tu as mal même si le marteau n’est plus là, pour l’œil c’est pareil, le marteau c’est la lumière, la douleur (persistante) c’est le souvenir de la lumière.

Pour avoir une idée de la valeur de ta persistance rétinienne on va éclairer fortement une LED pendant 100µs (100 millionièmes de seconde) et répéter cet éclat lumineux très rapidement. Puis on va diminuer la fréquence de répétition jusqu’à percevoir un scintillement de lumière : on note une période de 29 ms : durée approximative de notre persistance rétinienne. Même pour un œil humain « rapide » elle est au-dessus de 20 ms (valeur usuellement retenue).
Il faudra donc que l’on répète la séquence du message au moins toutes les 29 ms si l’on veut avoir un message qui ne scintille pas.

Les essais :
Pour l’initiation au microcontrôleur nous utilisons le Pickit de microchip (~30 euros).
Le microcontrôleur choisi pour les essais est le PIC16F684. Sa mémoire de type Flash permet un bon nombre de programmation. Il comporte un oscillateur intégré jusqu’à 8 Mhz et tous ses ports peuvent piloter directement une LED avec un courant de 20 mA. Nous câblons sur un petit circuit pré percé 8leds et une résistance de 180 ohms associées, un optotrigger et sa LED infra rouge, le µP et un condensateur de découplage (100µF faible ESR).

roueled_pre.jpg - 57995 Bytes

Le circuit est placé sur une roue en plastique de Ø 12 cm montée via un roulement à bille sur un socle en alu. Un petit drapeau permet de couper le barrage optique de l’optotrigger à chaque tour.

roueled_maquette.jpg - 62786 Bytes

Les Leds ont un Ø de 3 mm on peut donc former avec la roue PI x 120mm / 3mm = 126 colonnes lumineuses. Avec 5 colonnes pour le caractère et une pour l’espace entre caractère on a 126 / 6 = 21 lettres pour le message.

Le programme consiste à :
a) détecter la coupure du barrage optique de l’optotrigger,
b) de déclencher un chronomètre (timer1 ) après la coupure du barrage,
c) Initialiser le pointeur d’affichage de colonne (col=0),
d) de relever le temps que met la roue pour faire un tour après une deuxième coupure du barrage (tcy=timer1),
e) de remettre à 0 ce chronomètre après le deuxième passage (timer1=0),
f) de diviser le temps mesuré pour un tour par 126 colonnes (ta=tcy/126),
g) de chercher la valeur pointée par « col » dans un tableau (mat) qui permettra aux LEDs d’afficher la première colonne,
h) d’attendre pendant un temps « ta » avec un autre chronomètre (timer0),
i) d’incrémenter le pointeur de colonne (col ),
j) et de recommencer en g) jusqu’à ce
k) qu’un autre tour soit détecté (barrage) ce qui nous conduira en c).

Un peu plus dans le détail :
L’osillateur interne du µC est réglé sur 4 Mhz soit 1µs par cycle.
L’optotrigger déclenche une interruption « INT » (port RA2) à chaque tour.
Timer1 à une profondeur de 16 bits et un prescaler réglé sur 8 soit 8µs par incrément d’un bit soit une amplitude de 8µs x 2^16 = 0.5 seconde.
Imaginons que Timer1 mesure un temps de 0.3 seconde pour un tour soit 10010010 01111100 en binaire (0.3s/8µs=37500). En décimal la suppression du chiffre de droite d’un nombre binaire correspond à une divisions par 10. En binaire cela correspond à une division par 2. Plutôt que de diviser le nombre par 126 nous allons le diviser par 128 opération ultra simple puisqu’il s’agit de supprimer les 7 derniers chiffres (128=2^7) de Tmr1 pour obtenir cette division. Pour l’exemple : 37500 / 128 = 292 en décimal soit 100100100 en binaire !
L’astuce est sympa mais on peut faire encore plus fort. Le résultat de cette division donne des nombres allant jusqu’à 9 bits. Ces nombres ne sont pas facilement manipulable avec un µC qui possède un bus de donnée de 8 bits. L’astuce consiste donc à ramener cette valeur sur une mantisse de 8 bits ce qui implique une division supplémentaire par 2. Il suffit donc de programmer le prescaler du timer0 à une valeur double de celle du timer 1 pour que le délai soit juste et compatible avec des nombres huit bits. Dans ce contexte la valeur de pause d’affichage d’une colonne de LED correspond exactement au 8 bits de poids fort du Timer1. Simple non ? « Non ! ». Reprenons l’exemple si dessus, la pause d’affichage doit être de 292 x 8µs = 2.3 ms
Si je prends les 8 bits de poids fort du Timer1 10010010 soit 146 en décimal mais que je double la valeur du prescaler j’obtiens : 146 x 16µs = 2.3 ms !
Le Timer0, d’une profondeur de 8 bits, aura un prescaler réglé sur 16 soit 16µs par incrément d’un bit.
Le temps de pause d’une colonne de LED sera simplement l’octet de poids fort du Timer1.

Pour lire un message nous décomposons les mots syllabes par syllabes. Ces syllabes n’excèdent pas 4 lettres soit 4 x 6 = 24 colonnes de LEDs. Il ne faut donc pas que l’affichage de ces 24 colonnes n’excèdent les 20 ms de la persistance rétinienne. La durée minimale d’une rotation de notre roue pour que le message soit lisible sera au maximum de 20ms x 128/24 = ~ 0.1s soit 10 tours par seconde.

Pour prendre le montage en action en photo ou en vidéo il faut régler l’obturateur entre 1/30 et 1/50 de seconde (de 33 à 20 ms). Si l’obturateur n’est pas « full frame » mais « progressive scan » ou « interlaced » vous risquer d’être surpris du résultat, mais ceci est une autre histoire ...

Pour composer le code d’assemblage du texte à afficher nous nous sommes aider d’un tableur. Chaque cellule d’un tableau de 8x128 représente un point graphique de la bannière lumineuse. En plaçant des « 1 » aux endroits où l’on veut allumer une LED, une formule de type :
=" retlw h’"&DECHEX((K2*128+K3*64+K4*32+K5*16+K6*8+K7*4+K8*2+K9);2)&"’" vous donnera la ligne de code à recopier directement dans le fichier d’assemblage pour obtenir les 128 « retlw h’3C’ » (3C pour 00111100 première colonne d’un « O ») nécessaire au tableau « mat ».

Et le VTT dans tout ça ?
On peut fixer ce montage sur un rayon de la roue avant par exemple.

roueled_rayon.jpg - 53950 Bytes

Comme l’édition des messages n’est pas aisée (inscrit dans le code) l’idée d’utiliser une interface communicante à fait son chemin. J’ai proposé d’utiliser l’Hyperterminal présent dans tous les Windows en mode ANSI pour l’édition du texte via la liaison série RS232 du PC. Solution d’autant plus simple qu’elle ne nécessite pas de notion de programmation dans l’OS, que les terminaux ANSI existent dans tous les systèmes d’exploitation et utilisent une interface très répandue dans les périphériques de µC/PC (port « com »).

roueled_kit.jpg - 72907 Bytes

Pour cela on change de cheval de bataille, je choisie un PIC16F876 qui possède une interface série, de la mémoire Flash et EEPROM et qui a l’avantage de traîner en nombre dans le tiroir de mon bureau en boîtier SO.
Le choix d’un quartz oscillant à 14.7456 Mhz donne une liaison dépourvue d’erreur à 9600 bauds.
Pour reproduire tous les caractères ASCII nous avons utilisé la font d’un afficheur LCD retranscrit dans un tableau Excel pour obtenir le code comme précédemment. 7 LEDS sont suffisantes et sont raccordées au portB du µC.

Pour faire la détection du tour de roue on utilise un ILS (interrupteur à lames souples) du même type que celui qui actionne le tachymètre sur le guidon. L’avantage c’est qu’il ne consomme aucune énergie contrairement à l’optotrigger (polarisation et LED alimenté en continu). L’aimant est fixé sur le fourreau de la fourche.

roueled_aimant.jpg - 55914 Bytes

L’interface série permet d’éditer un message de 256 caractères stockés dans l’EEPROM.
Le format de communication est 9600 bauds, 1 bit de start, 1 bit de stop, pas de parité, pas de contrôle de flux.
La gestion se fait par interruption (PIR1,RCIF) de l’USART.

Le TIMER1 à un prescaler de valeur max 8 ce qui donne une amplitude de 2^16 x 8 x 271ns = 0.14 ms valeur insuffisante car cela correspond à une vitesse de 26 pouces x 0.0254 m x PI / 0.14 ms = 15 m/s soit 53 km/h.
Une amplitude 8 fois supérieure pour obtenir une vitesse de seuil de 6.6 km/h sera obtenue par un compteur auxiliaire (atH) incrémenté à chaque interruption de débordement du TIMER1.
Pour avoir le temps que doit être afficher un segment de 3mm situé à un rayon de 250 mm :
2xPIx250 / 3 = 523 soit à peu près un tour divisé par 512 soit 9 décalages.
Le rapport des prescalers du timer1/timer0 est de 1/4 soit 2 décalages supplémentaires. Ramener à un modulo de 8 seulement 3 décalages sont nécessaire sur atH/TMR1H pour obtenir la valeur du Timer0 dans TMR1H.

En voyant les yeux exorbités de mon fils devant ces explications j’ai pris le relais pour la programmation et la réalisation du circuit imprimé. Voici le code du programme en assembleur, le schéma, l’implantation, la nomenclature et le dessin du circuit.

roueled_piles.jpg - 51815 Bytes
Le bloc de piles sur le moyeu.

La consommation électrique est de l'ordre de 40 mA ce qui permet d'imaginer une alimentation avec 2 piles boutons lithium rechargeables type CR3032. On aurait une autonomie d'environ 100mAH/40mA = 2H30 et pas de fils qui trainent.
roueled_conso.gif - 7052 Bytes
Mesure du courant en observant la tension aux bornes d'une résistance de 10 ohms en série avec l'alimentation.

roueled_detail.jpg - 56058 Bytes
Les encoches des têtes de vis sont faites avec une scie à métaux avec 3 lames parallèles.

Les voies d’améliorations :
- analyser les accélérations / décélération,
- synchroniser plusieurs circuits entre eux,
- avoir un message lisible des deux cotés de la roue,
- ajouter des effets (clignotement, pause, graphique ).

Ca vous inspire ? Alors à vous de jouer.