Foin de technique … Soyons des ARTISTES !

Mettre la salamandre en cage !

Quel  titre étrange n’est-il pas ? Si en plus je vous dis que nous allons faire du point de croix, la bizarrerie va atteindre un comble ! Et si on ajoutait : LOGO à GOGO !
Avec ce chapitre nous allons apprendre à créer un vrai dessin « artistique », c’est à dire une « peinture » réalisée point par point. Il s’agit de la première réelle application pour faire connaissance avec l’afficheur en mode graphique. Quand on dispose d’un tel composant, la tentation est grande d’agrémenter l’écran d’accueil avec un dessin. Ce peut être le symbole de notre entreprise, un LOGO qui nous est précieux etc. Dans ce chapitre, on va aborder le principe de création d’un dessin, il vous sera ensuite aisé dans vos programmes d’adapter les méthodes proposées dans ces lignes.
Vivant à la frontière des Landes, j’ai souvent dans ma prime jeunesse côtoyé ces magnifiques animaux que sont les salamandres noires et jaune fréquentant le sable noir des marécages. Ainsi, ce petit animal méconnu et inoffensif est devenu mon animal fétiche. La salamandre est ma signature, mon LOGO. Il est donc naturel qu’elle soit à l’honneur en page de démarrage du PICOLAB.

Nos premiers pas réellement graphiques avec le « PICOafficheur ».

Comme déjà abordé dans ces lignes, la première action consiste à définir exactement ce que l’on désire. Pour ma part, j’ai trouvé séduisant de placer la version du programme dans la zone jaune, comme montré sur la Fig.47, le titre PICOLAB en gros dans l’encadré bleu avec la signature en petit texte. (F6AAE est mon indicatif quand je pratiquais la radio amateur.) Puis, dans la zone inférieure gauche le petit dessin de

la salamandre … ni trop gros, ni dérisoire. La Fig.48 bien plus détaillée que la photographie ci-contre précise assez bien ce que l’on désire.
Comme le petit afficheur travaille intégralement par pixels et allume ces derniers sans éteindre ceux qui sont déjà positionnés, on va effacer le total, puis tracer le cadre, écrire les textes. Pour le LOGO, l’approche consiste à prévoir un dessin dont les coordonnées pourront être modifiées à votre convenance. Ainsi, en fonction de votre application, il vous sera facile de le placer où vous le désirez sur l’écran en ne modifiant que les valeurs de deux constantes XL et YL. Comme un dessin devient rapidement glouton en octets, dans ce chapitre nous allons explorer des pistes pour optimiser la taille occupée en mémoire. Le dessin désiré est représenté en bleu et en violet. Pour en souligner la place qu’il occupera en mémoire. En vert, rose et jaune est repéré le « cadre » d’encombrement total, et en rouge le pixel qui définit les coordonnées XL et YL du LOGO. On remarque sur la Fig.48,

que la zone du dessin empiète d’une ligne en haut sur le texte. Dans la pratique c’est faux, c’est le dessin qui est légèrement faux, le LOGO est juste en dessous du texte. Observez avec attention la Fig.49 qui représente avec précision ce que l’on désire. Les « tranches » vertes et roses du « cadre » ont pour but de regrouper les pixels par huit. Le dessin sera codé en mémoire sous forme d’octets, chaque BIT présentant l’état « 1 » correspond à un pixel à allumer, et à l’état « 0 » un pixel qui devra rester noir. La largeur du LOGO fait cinq octets plus deux pixels. Pas question de tasser la queue de l’animal, l’artiste est intransigeant sur ce point. La hauteur du dessin fait 24 lignes. Il est hors de question de consommer 24 octets de plus uniquement pour allumer les quelques pixels coloriés en violet. La solution économique est simple : On tracera le dessin bleu avec des pixels, et les deux lignes verticales violettes avec l’instruction display.drawLine(). Pour tracer l’animal, on va imposer à l’afficheur chaque pixel en procédant par un balayage horizontal, imbriqué dans un balayage vertical. Le petit programme expérimental P04_construire_la_Salamandre.ino va nous permettre d’aborder le principe d’un tel balayage qui exploite les instructions de décalage bit à bit.

PRINCIPE DU BALAYAGE.

Balayer, c’est déposer carrelage par carrelage et au bon endroit les divers éléments sur une surface qui représentera la « fresque ». Dans notre cas, chaque carrelage est informatiquement un petit rectangle allongé de huit petits carrés blancs ou noirs nommés PIXELs. Chaque élément allongé est nommé OCTET. Tous les carrelages qui vont composer notre œuvre d’art sont emballés dans un long paquet nommé byte Salamandre[120]. (Tableau « linéaire » de 5 x 24 = 120 Octets.) Informatiquement, le dessin du LOGO est préservé en mémoire sous forme d’un tableau d’OCTETs dans lesquels chaque PIXEL est matérialisé par un BIT. Si le BIT est à « 1 » il faudra allumer le PIXEL correspondant, s’il est à « 0 » il faudra l’éteindre. Avec la bibliothèque Adafruit_ssd1306syp.h l’instruction display.drawPixel(X, Y, État); permet d’initialiser indépendamment chaque BIT de la matrice de l’afficheur OLED. On indique ses coordonnées et l’état désiré.
Dans le programme codé en C++, l’image complète est une suite d’OCTETs dans lesquels nous avons imposé manuellement un « 0 » où un « 1 » pour que le total soit représentatif de la fresque que l’on veut transposer dans la mémoire de l’écran LCD.
Comme on peut le visualiser sur la Fig.50, traiter un OCTET du tableau informatique (Constitué de 120 OCTETs successifs dans la RAM.) consiste à « plaquer » les huit bits de ce dernier au bon endroit dans la grande matrice de RAM de 128 x 64 BITs de l’afficheur OLED.

Le BALAYAGE consiste à prendre un à un et dans l’ordre les OCTETS dans le tableau byte Salamandre[120] et à en déposer les huit BITs dans la mémoire RAM de l’afficheur. Dans ce but, il nous faut deux INDEX. L’index est le doigt de la main dont on se sert pour pointer un objet dans notre environnement et le désigner à quelqu’un. En informatique, ce mot INDEX est traditionnellement utilisé pour des identificateurs qui pointent des variables dans la mémoire du microcontrôleur. Par exemple, l’indice dans un tableau est par nature un index. Il pointe dans le tableau l’un des éléments particulier, celui sur lequel on veut agir. (On utilise souvent le mot de POINTEUR, qui a déjà été rencontré à profusion dans les programmes qui précèdent. Comme plus avant on va utiliser la notion de pointeur au sens du C++, c’est a dire de façon plus « typée », c’est volontairement que dans ce chapitre le mot INDEX qui est équivalent sera privilégié.)

Les précisions de vocabulaire étant dans notre sillage, revenons au principe du BALAYAGE. Encore un mot issu de notre quotidien. Balayer une surface consiste à en « traiter » la totalité en procédant par éléments dont les dimensions sont fonction de notre allonge et de la largeur du balais. C’est exactement ce que l’on fait informatiquement pour une image. Un premier INDEX va être initialisé sur le tout premier élément du tableau byte Salamandre[120]. Comme pour notre mental une image est à deux dimensions, autant raisonner comme des humains. La mémoire image devient alors une « macro matrice » de 5 COLONNEs et de 24 lignes. La Fig.50 met en évidence le fait qu’une COLONNE est constituée de macroéléments pour le balayage, et constituée d’OCTETs. Donc, chaque COLONNE sera en réalité pour l’image un groupe de huit « colonnes pixels ».

Pour effectuer un balayage simultané de la source (C’est à dire du tableau Salamandre.) et de la destination, (La mémoire de l’afficheur OLED.) il nous faut concrètement trois INDEX. Le premier va explorer le tableau en ligne de l’élément 0 à l’élément 119. Le deuxième va balayer horizontalement les 5 COLONNEs de la gauche vers la droite. Puis, arrivé tout à droite de la « macro matrice », il sera ramené à gauche en reprenant la valeur zéro. L’INDEX pour les lignes sera incrémenté, et l’on traitera alors la ligne suivante. Notez que l’on effectue l’exploration de la gauche vers la droite, et du haut vers le bas. C’est purement arbitraire, il serait totalement équivalent de faire du bas vers le haut, de la droite vers la gauche. Toutes les variantes sont autorisées. L’auteur de ces lignes est très influencé par la télévision de Papa, celle où les écrans étaient des tubes cathodiques. Le balayage image était effectué comme choisi arbitrairement dans ce programme. Quand on prend de l’âge …

À ce stade de l’exposé, vous avez compris que chaque OCTET du tableau Salamandre représente huit PIXELs qui dans l’image seront successifs. L’OCTET devient une sorte de fenêtre qui va se déplacer d’élément en élément dans Salamandre, et balayer COLONNEs/ligne dans la mémoire de l’afficheur. (Pour l’écriture, ligne est en lettres minuscules car elles désignent réellement des lignes de PIXELs dans l’image. Alors que COLONNEs est en majuscule car ce sont des groupements de huit « colonnes pixels ». Ainsi vous pourrez faire plus facilement la distinction.
Quand on va imbriquer dans deux boucles de type for(….) on imagine assez bien la « fenêtre jumelle » constituée d’un OCTET qui puise dans l’ordre des paquets de huit BITs dans le tableau, et qui par balayage ligne/COLONNE les dépose dans la RAM de l’afficheur LCD. C’est ici que l’on rencontre une petite difficulté informatique. Les trois INDEX étant conditionnés, le plus simple consisterait à lire l’OCTET dans le tableau, puis à l’écrire directement dans la RAM de l’afficheur. Sauf … que la bibliothèque Adafruit_ssd1306syp.h ne nous fournit pas d’instruction pour lire et écrire des OCTETS. On peut soit utiliser des caractères, mais leurs empreintes nous sont imposées, soit déposer un à un des PIXELs en précisant leurs coordonnées.

Déposer des OCTETs dans l’afficheur OLED.

Abusons une fois de plus d’un petit dessin, toujours plus facile à cerner qu’un long verbiage. La Fig.51 suppose que l’identificateur OCTET contient les huit « PIXELs » puisés dans le tableau par lecture de l’un de ses éléments. Ce schéma suppose également que les index COLONNE et ligne pointent les coordonnées du premier point image à placer dans la RAM de l’afficheur. Pour « déposer l’OCTET » dans l’image, il faut extraire BIT par BIT l’état du PIXEL, puis avec l’instruction display.drawPixel(…); le placer dans la RAM image. On passe ensuite au BIT suivant et l’on recommence huit fois. Inutile de préciser que l’on va encore employer un index qui cette fois va varier entre 0 et 7. Il reste à « isoler » l’état de chaque BIT. Le plus rationnel dans ce type de traitement consiste à utiliser les masques logiques et les opérateurs booléens. C’est un bon investissement sur le long terme, car incontournables pour gérer des ENTRÉES/SORTIES isolées, on va s’en resservir en outre pour optimiser la cadence d’échantillonnage de l’Oscilloscope numérique.

Les opérateurs LOGIQUES travaillant sur des BITs.

Le langage C++, comme tout système d’exploitation orienté microprocesseur du reste, met à notre disposition des opérateurs logiques qui permettent d’agir BIT à BIT dans un OCTET.

La Fig.52 présente quelques exemples. Comme un exposé complet sur le sujet sortirait largement du cadre de ce document, nous allons restreindre notre réflexion au ET logique.

Pour isoler la valeur d’un BIT dans un OCTET, il suffit de mettre à « 0 » tous les autres dans le masque logique. L’exemple de la Fig.53 focalisant sur le tout premier octet de la salamandre montre que le résultat de l’opération donne bien un « 1 » si le bit examiné est à l’état haut, et un « 0 » s’il est à l’état bas.

L’état du PIXEL étant connu, il suffit alors de le confier à l’instruction de décalage SHIFT qui le « déposera au bon endroit » dans la RAM de l’afficheur graphique :

Les opérateurs de décalage. (SHIFT.)

Impossible de les ignorer quand on programme « petite robotique ». À un moment où à un autre ils se montrent incontournables. Pour traiter les huit BITs de l’OCTET à déposer dans « l’image », il suffit d’effectuer huit fois un ET logique avec un masque qui va successivement prendre les valeurs 10000000, 01000000, 0010000000000001. Tout comptes faits, on voit encore une fois que le masque logique va à son tour balayer BIT à BIT l’OCTET de la gauche vers la droite. Il serait toutefois peu élégant d’écrire huit instructions différentes, avec pour chacune un masque logique adapté, alors que concrètement elles font toutes la même action.
L’idée plus judicieuse consiste à utiliser encore une boucle for(byte I=0; I < 8; I++) dans laquelle l’indice I va servir pour créer le masque logique. (L’indice I est encore un INDEX, il ne faut donc pas s’offusquer si souvent par tradition on utilise la lettre I pour le représenter.) Dans cette boucle, on va employer l’instruction de décalage SHIFT particulièrement agréable à utiliser avec le langage C++ de l’IDE d’Arduino. La symbolique de l’opérateur >> est affectée à un décalage vers la droite, alors que << est réservé pour « pousser » à gauche.

CIBLE = (OCTET >> 3); prend la valeur de OCTET, décale 3 fois à droite et dépose le résultat dans CIBLE.
Par exemple si OCTET valait 01011100, CIBLE recevra la valeur binaire 00001011. Les BITs ont été décalés trois fois vers la droite et « À gauche » on a introduit des zéros. Les trois BITs situés à droites sont définitivement « perdus ».

CIBLE = (OCTET << 2); prend la valeur de OCTET, décale 2 fois à gauche et dépose le résultat dans CIBLE.
Par exemple si OCTET valait 01011111, CIBLE recevra la valeur binaire 01111100. Les BITs ont été décalés deux fois vers la gauche et « À droite » on a introduit des zéros. Les deux BITs situés à gauche sont définitivement « perdus ».

Bonne nouvelle : Non seulement cette instruction va faire merveille pour construire le masque logique, mais également, décaler à gauche d’une position revient à multiplier par deux, décaler à droite revient à diviser par deux. On va profiter de cette particularité dans l’oscilloscope pour augmenter sa cadence d’échantillonnage.
En effectuant un >> avec une amplitude de 1, notre masque logique initial 10000000 va donc « naturellement » prendre les configurations successives correctes. Nous avons toutes les cartes en main, il ne reste plus qu’à jouer la partie :

La séquence de programme qui place le LOGO sur l’afficheur.

Finalement, quand on observe le listage du code source qui effectue toutes ces opérations, on constate que le principe de construction de l’image est infiniment plus laborieux à expliquer, que d’avoir à détailler les quelques lignes spécifiques du programme. La séquence de transposition de l’image est entièrement listée ci-dessous. Autant dire qu’elle se résume à peu de chose.

Ce listage met en évidence le choix de décaler à notre convenance de XL le LOGO horizontalement et d’YL verticalement pour le placer en position désirée sur l’écran. Les constantes XL et YL sont définies en tête du programme. Petite nuance par rapport aux développement qui précèdent, le masque logique reste constant à la valeur B10000000 , c’est OCTET que l’on décale à gauche d’un BIT. C’est une autre façon d’isoler chaque BIT de la gauche vers la droite.
REMARQUE :
Examinons l’écriture du tableau byte Salamandre[120] présenté en Fig.54 située ci-dessous. La valeur de chaque OCTET peut être fournie librement en décimal, en hexadécimal, en octal. Mais de loin, le plus simple consiste à choisir le BINAIRE pur symbolisé par la lettre « B » suivie de l’état de chaque BIT. Manuellement, au moment de rédiger le programme source, il est pour nous bien plus naturel de frapper des « 1 » quand sur le dessin on a positionné un point allumé, et un « 0 » quand on veut éteindre le point de l’image. Le tableau pourrait être précisé sur une seule ligne de programme, mais l’écran de l’ordinateur ne serait pas assez large pour la montrer entièrement. Donc, généralement on écourte nos lignes de code à une largeur qui nous séduit. Dans ce cas particulier, il est plus rationnel de regrouper 5 OCTETS par ligne de programme, et de les aligner les uns sous les autres. Il est ainsi tellement plus simple d’établir visuellement un rapport direct entre le listage du

programme, et notre petit dessin sur papier pour créer notre LOGO. Du reste, en reculant un peu de l’écran, on retrouve visuellement assez facilement l’idéogramme car avec un peu de recul, les « 0 » et les « 1 » n’ont pas des luminosités équivalentes. C’est d’autant plus flagrant que sur la Fig.54 le listage a été colorisé en établissant une correspondance directe avec le dessin de la Fig.49 en adoptant le bleu pour les PIXELs allumés, et le vert ou le rose pour les BITs éteints.

Balayer dans tous les sens.

Chaque fois que plusieurs arbitraires viennent s’introduire dans un programme, il existe toute une combinatoire de solutions disponibles pour le programmeur. Pour illustrer cette assertion, nous allons examiner l’éventail des possibilités pour tracer le LOGO. Le programme P05_Salamandre_dans_tous_les_sens.ino montre diverses possibilités pour concrétiser ce propos. Voici la liste des arbitraires relatifs au dessin et à la programmation :

Comme écrire la « matrice » représentant le dessin constitue une activité assez laborieuse, nous conserverons la table de la Fig.54 et n’explorerons pas les trois autres possibilités. Si vous le désirez, il vous sera relativement facile de la programmer en sens rétrograde par exemple et vous imposer à l’avance une attitude précise de la salamandre, c’est un bon petit exercice d’entrainement.
La table étant constante, il nous reste trois paramètres arbitraires. Nous savons que trois variables binaires conduisent à une combinatoire de huit possibilités. Dans ce chapitre, on ne désire pas un LOGO constant, mais au contraire, on préfère voir l’animal changer d’orientation. De plus, pour insister sur l’avantage que procure le paramétrage de la position XL et YL, dans cet essai les coordonnées seront des variables. Ainsi à chaque cas analysé la position de la salamandre changera sur l’écran. La Fig.55 présente les huit possibilités pour coder le source.

Programmer toutes ces possibilités ne présente aucune difficulté. C’est un peu fastidieux, car pour chaque cas il faut recalculer les coordonnées des deux lignes verticales violettes. Par ailleurs, en fonction du sens de lecture du tableau et du sens de balayage horizontal, on est amené à explorer l’OCTET en sens rétrograde. De toute façon, le téléversement du programme sera sans appel. Soit le dessin est correct, soit il affiche une mosaïque tout à fait artistique, mais pour le moins …  étrange.

Récréation artistique.

Créer un LOGO s’avère au final une opération assez élémentaire informatiquement parlant. Plusieurs étapes s’enchainent, certaines très valorisantes, d’autres bien plus indigestes. Tant que l’on utilise un afficheur de faible définition, tout tracer et coder manuellement reste une entreprise raisonnable. En revanche, si la mosaïque prend des proportions notables, coder la fresque n’est plus concevable au clavier. Il faut impérativement automatiser la création du tableau représentant le dessin. On s’arrange à avoir le LOGO sous forme d’un fichier BMP par exemple, puis par l’entremise d’un programme spécial, les points sont analysés sur toute la surface pour générer le tableau d’OCTETs. Nous n’aborderons pas cette facette de la programmation, car elle s’éloigne trop de notre préoccupation de base qui consiste à effectuer du mesurage avec une carte Arduino.
Un tutoriel complet sur le sujet est disponible sur http://forum.arduino.cc/index.php?topic=394550.0
Toutefois, avant de clore ce chapitre émoustillant sur l’affichage graphique, un petit résumé d’une approche méthodique à la création d’un petit LOGO n’est pas forcément inutile. Pour que vous puissiez appréhender rapidement le coté laborieux de ce type de programmation élémentaire, rapidement très fastidieux, je vous propose dans ce chapitre de faire afficher un dessin qui occupera toute la zone bleue de l’afficheur OLED. (La table à coder devient déjà très étendue …)

Approche méthodique de la création d’un LOGO.

1) Tracer en traits fins sur une grille telle que celle de la Fig.48 la fresque que l’on désire faire afficher sur l’écran. C’est l’étape la plus agréable,
x     celle ou l’artiste donne libre cours à son imagination.

2) Dans la grille, noircir au crayon les PIXELs qui se trouvent sous les lignes du dessin. Durant cette opération réalisée avec un crayon à papier
x     et une gomme, on affine la position des points allumés et éteints. Quand avec du recul le résultat est satisfaisant, on peut passer à l’étape
x     suivante. (Affutez les crayons de couleur !)

3) Sur la grille mettre en évidence les colonnes OCTETs par coloriage vert et rose.

4) Coder la table des OCTETS représentant le petit dessin. C’est cette étape qui constitue la partie indigeste de création d’un logo. Par
x     exemple, sur le petit programme P06_Une_petite_surprise.ino la fresque va occuper l’intégralité de la zone bleue. Le cadre extérieur sera
x     tracé avec l’instruction display.drawRect(0, 16, 128, 48,WHITE); le dessin se résumant à 46 lignes de 16 OCTETs chacune. C’est donc 736
x     OCTETs qu’il faut décrire, avec 5888 chiffres « 0 » ou « 1 » !

Pour franchir cette dernière étape sans trop y laisser la santé morale, il vaut mieux procéder en plusieurs fois. Par exemple vous décrivez environ deux COLONNEs puis passez à une autre activité. Plus tard vous reprenez deux « verticales » de plus. La façon la plus aisée consiste à imprimer le dessin créé durant l’étape n°3. Masquez le bas de ce dernier et la droite de la COLONNE avec du papier pour ne montrer que l’OCTET en cours d’écriture. Les « barbouillages » verts et rose délimitent assez distinctement les COLONNEs successives. Pour ma part, je commence par créer les 46 lignes de programme avec 16 fois une virgule, suivie de « B1 » suivie d’un espace. Il suffit de compléter avec les « 0 » et les « 1 ». Je vous laisse découvrir le petit dessin … à vous de deviner dans quelle région du monde nous a transporté cette petite surprise. (Il faut que je remplace les touches « 0 » et « 1 » de mon clavier ! ! !)

>>> Page suivante.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *