Licence CC BY-NC-SA

Votre premier texte sur le LCD !

Ça y est, on va pouvoir commencer à apprendre des trucs avec notre écran LCD ! Alors, au programme : afficher des variables, des tableaux, déplacer le curseur, etc. Après toutes ces explications, vous serez devenu un pro du LCD, du moins du LCD alphanumérique. :lol: Aller, en route ! Après ça vous ferez un petit TP plutôt intéressant, notamment au niveau de l’utilisation pour l’affichage des mesures sans avoir besoin d’un ordinateur. De plus, pensez au fait que vous pouvez vous aider des afficheurs pour déboguer votre programme !

Ecrire du texte sur le LCD

Afficher du texte

Vous vous rappelez quand je vous disais il y a longtemps "Les développeurs Arduino sont des gens sympas, ils font les choses clairement et logiquement !" ? Eh bien, ce constat se reproduit (encore) pour la bibliothèque LiquidCrystal ! En effet, une fois que votre écran LCD est bien paramétré, il nous suffira d’utiliser qu’une seule fonction pour afficher du texte ! Allez je vous laisse 10 secondes pour deviner le nom de la fonction que nous allons utiliser. Un indice, ça a un lien avec la voie série… C’est trouvé ? Félicitations à tous ceux qui auraient dit print(). En effet, une fois de plus nous retrouvons une fonction print(), comme pour l’objet Serial, pour envoyer du texte. Ainsi, pour saluer tous les zesteurs de la terre nous aurons juste à écrire :

lcd.print("Salut ca zeste ?");

et pour code complet avec les déclarations, on obtient :

#include "LiquidCrystal.h" // on inclut la librairie

// initialise l'écran avec les bonnes broches

// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !

LiquidCrystal lcd(11,10,5,4,3,2);

void setup() {
   lcd.begin(16, 2);
   lcd.print("Salut ca zeste ?");
}

void loop() {

}

Mais c’est nul ton truc on affiche toujours au même endroit, en haut à gauche !

Oui je sais, mais chaque chose en son temps, on s’occupera du positionnement du texte bientôt, promis !

Afficher une variable

Afficher du texte c’est bien, mais afficher du contenu dynamique c’est mieux ! Nous allons maintenant voir comment afficher une variable sur l’écran. Là encore, rien de difficile. Je ne vais donc pas faire un long discours pour vous dire qu’il n’y a qu’une seule fonction à retenir… le suspens est terrible… OUI évidemment cette fonction c’est print() !Décidément elle est vraiment tout-terrain (et rédacteur du tutoriel Arduino devient un vrai boulot de feignant, je vais finir par me copier-coller à chaque fois !) Allez zou, un petit code, une petite photo et en avant Guingamp !

    int mavariable = 42;
    lcd.print(mavariable);
Print pour afficher une variable
Combo ! Afficher du texte ET une variable

Bon vous aurez remarqué que notre code possède une certaine faiblesse… On n’affiche au choix qu’un texte ou qu’un nombre, mais pas les deux en même temps ! Nous allons donc voir maintenant une manière d’y remédier.

La fonction solution

La solution se trouve dans les bases du langage C , grâce à une fonction qui s’appelle sprintf() (aussi appelé "string printf"). Les personnes qui ont fait du C doivent la connaitre, ou connaitre sa cousine "printf". Cette fonction est un peu particulière, car elle ne prend pas un nombre d’arguments fini. En effet, si vous voulez afficher 2 variables vous ne lui donnerez pas autant d’arguments que pour en afficher 4 (ce qui parait logique d’une certaine manière). Pour utiliser cette dernière, il va falloir utiliser un tableau de char qui nous servira de buffer. Ce tableau sera celui dans lequel nous allons écrire notre chaine de caractère. Une fois que nous aurons écrit dedans, il nous suffira de l’envoyer sur l’écran en utilisant… print() !

Son fonctionnement

Comme dit rapidement plus tôt, sprintf() n’a pas un nombre d’arguments fini. Cependant, elle en aura au minimum deux qui sont le tableau de la chaine de caractère et une chaine à écrire. Un exemple simple serait d’écrire :

char message[16] = "";
sprintf(message,"J'ai 42 ans");
sprintf pour mettre une chaine dans une tableau de char

Au début, le tableau message ne contient rien. Après la fonction sprintf(), il possédera le texte "J’ai 42 ans". Simple non ?

J’utilise un tableau de 16 cases car mon écran fait 16 caractères de large au maximum, et donc inutile de gaspiller de la mémoire en prenant un tableau plus grand que nécessaire.

Nous allons maintenant voir comment changer mon âge en le mettant en dynamique dans la chaine grâce à une variable. Pour cela, nous allons utiliser des marqueurs de format. Le plus connu est %d pour indiquer un nombre entier (nous verrons les autres ensuite). Dans le contenu à écrire (le deuxième argument), nous placerons ces marqueurs à chaque endroit où l’on voudra mettre une variable. Nous pouvons en placer autant que nous voulons. Ensuite, il nous suffira de mettre dans le même ordre que les marqueurs les différentes variables en argument de sprintf(). Tout va être plus clair avec un exemple !

char message[16] = "";
int nbA = 3;
int nbB = 5;
sprintf(message,"%d + %d = %d", nbA, nbB, nbA+nbB);
sprintf pour enregistrer une (des) variables dans une chaine

Cela affichera :

3 + 5 = 8
Les marqueurs

Comme je vous le disais, il existe plusieurs marqueurs. Je vais vous présenter ceux qui vous serviront le plus, et différentes astuces pour les utiliser à bon escient :

  • %d qui sera remplacé par un int (signé)
  • %s sera remplacé par une chaine (un tableau de char)
  • %u pour un entier non signé (similaire à %d)
  • %% pour afficher le symbole '%' ;)

Malheureusement, Arduino ne les supporte pas tous. En effet, le %f des float ne fonctionne pas. :( Il vous faudra donc bricoler si vous désirez l’afficher en entier (je vous laisse deviner comment). Si jamais vous désirez forcer l’affichage d’un marqueur sur un certain nombre de caractères, vous pouvez utiliser un indicateur de taille de ce nombre entre le '%' et la lettre du marqueur. Par exemple, utiliser "%3d" forcera l’affichage du nombre en paramètre (quel qu’il soit) sur trois caractères au minimum. La variable ne sera pas tronquée s’il est plus grand que l’emplacement prévu. Ce paramètre prendra donc toujours autant de place au minimum sur l’écran (utile pour maitriser la disposition des caractères). Exemple :

int age1 = 42;
int age2 = 5;
char prenom1[10] = "Ben";
char prenom2[10] = "Luc";
char message[16] = "";
sprintf(message,"%s:%2d,%s:%2d",prenom1, age1, prenom2, age2);
Différents marqueurs de variable pour sprintf

À l’écran, on aura un texte tel que :

Ben:42,Luc: 5

On note l’espace avant le 5 grâce au forçage de l’écriture de la variable sur 2 caractères induits par %2d.

Exercice, faire une horloge
Consigne

Afin de conclure cette partie, je vous propose un petit exercice. Comme le titre l’indique, je vous propose de réaliser une petite horloge. Bien entendu elle ne sera pas fiable du tout car nous n’avons aucun repère réel dans le temps, mais ça reste un bon exercice. L’objectif sera donc d’afficher le message suivant : "Il est hh:mm:ss" avec 'hh' pour les heures, 'mm' pour les minutes et 'ss’ pour les secondes. Ça vous ira ? Ouais, enfin je ne vois pas pourquoi je pose la question puisque de toute manière vous n’avez pas le choix ! :diable: Une dernière chose avant de commencer. Si vous tentez de faire plusieurs affichages successifs, le curseur ne se replacera pas et votre écriture sera vite chaotique. Je vous donne donc rapidement une fonction qui vous permet de revenir à la position en haut à gauche de l’écran : home(). Il vous suffira de faire un lcd.home() pour replacer le curseur en haut à gauche. Nous reparlerons de la position curseur dans le chapitre suivant !

Solution

Je vais directement vous parachuter le code, sans vraiment d’explications car je pense l’avoir suffisamment commenté (et entre nous, l’exercice est sympa et pas trop dur). ;)

#include "LiquidCrystal.h" // on inclut la librairie

// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

int heures,minutes,secondes;
char message[16] = "";

void setup()
{
    lcd.begin(16, 2); // règle la taille du LCD : 16 colonnes et 2 lignes

    // changer les valeurs pour démarrer à l'heure souhaitée !
    heures = 0;
    minutes = 0;
    secondes = 0;
}

void loop()
{
    // on commence par gérer le temps qui passe...
    if(secondes == 60) // une minute est atteinte ?
    {
        secondes = 0; // on recompte à partir de 0
        minutes++;
    }
    if(minutes == 60) // une heure est atteinte ?
    {
        minutes = 0;
        heures++;
    }
    if(heures == 24) // une journée est atteinte ?
    {
        heures = 0;
    }

    // met le message dans la chaine à transmettre
    sprintf(message,"Il est %2d:%2d:%2d",heures,minutes,secondes);

    lcd.home();           // met le curseur en position (0;0) sur l'écran

    lcd.write(message);   // envoi le message sur l'écran

    delay(1000);          // attend une seconde
    // une seconde s'écoule...
    secondes++;
}

Et voici une solution interactive :
!(https://www.tinkercad.com/embed/gSGIbqyX8rB)
https://www.youtube.com/watch?v=0Ps2UjdHYCI

Se déplacer sur l’écran

Bon, autant vous prévenir, ce morceau de chapitre ne sera pas digne du nom de "tutoriel". Malheureusement, pour se déplacer sur l’écran (que ce soit le curseur ou du texte) il n’y a pas 36 solutions, juste quelques appels relativement simples à des fonctions. Désolé d’avance pour le "pseudo-listing" de fonctions que je vais faire tout en essayant de le garder intéressant…

Gérer l’affichage

Les premières fonctions que nous allons voir concernent l’écran dans son ensemble. Nous allons apprendre à enlever le texte de l’écran mais le garder dans la mémoire pour le réafficher ensuite. En d’autres termes, vous allez pouvoir faire un mode "invisible" où le texte est bien stocké en mémoire, mais pas affiché sur l’écran. Les deux fonctions permettant ce genre d’action sont les suivantes :

  • noDisplay() : fait disparaître le texte
  • display() : fait apparaître le texte (s’il y en a évidemment)

Si vous tapez le code suivant, vous verrez le texte clignoter toutes les secondes :

#include "LiquidCrystal.h" // on inclut la librairie

// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

void setup() {
    // règle la  taille du LCD
    lcd.begin(16, 2);
    lcd.print("Salut ca zeste ?");
}

void loop() {
    lcd.noDisplay();
    delay(500);
    lcd.display();
    delay(500);
}
Afficher ou non un texte sur le LCD : display et noDisplay

Utile si vous voulez attirer l’attention de l’utilisateur ! Une autre fonction utile est celle vous permettant de nettoyer l’écran. Contrairement à la précédente, cette fonction va supprimer le texte de manière permanente. Pour le réafficher, il faudra le renvoyer à l’afficheur. Cette fonction au nom évident est : clear() (bien sûr, il faut être un peu anglophone pour s’en douter ^^ ). Le code suivant vous permettra ainsi d’afficher un texte puis, au bout de 2 secondes, il disparaitra (pas de loop(), pas nécessaire) :

#include "LiquidCrystal.h" // on inclut la librairie

// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

void setup() {
    // règle la  taille du LCD
    lcd.begin(16, 2);
    lcd.print("Salut ca zeste ?");
    delay(2000);
    lcd.clear();
}
Effacer l’écran LCD : clear

Cette fonction est très utile lorsque l’on fait des menus sur l’écran, pour pouvoir changer de page. Si on ne fait pas un clear(), il risque d’ailleurs de subsister des caractères de la page précédente. Ce n’est pas très joli.

Attention à ne pas appeler cette fonction plusieurs fois de suite, par exemple en la mettant dans la fonction loop(), vous verrez le texte ne s’affichera que très rapidement puis disparaitra et ainsi de suite.

Gérer le curseur
Se déplacer sur l’écran

Voici maintenant d’autres fonctions que vous attendez certainement, celles permettant de déplacer le curseur sur l’écran. En déplaçant le curseur, vous pourrez écrire à n’importe quel endroit sur l’écran (attention cependant à ce qu’il y ait suffisamment de place pour votre texte). :P Nous allons commencer par quelque chose de facile que nous avons vu très rapidement dans le chapitre précédent. Je parle bien sûr de la fonction home() ! Souvenez-vous, cette fonction permet de replacer le curseur au début de l’écran.

Mais au fait, savez-vous comment est organisé le repère de l’écran ?

C’est assez simple, mais il faut être vigilant quand même. Tout d’abord, sachez que les coordonnées s’expriment de la manière suivante (x,y)(x,y). xx représente les abscisses, donc les pixels horizontaux et yy les ordonnées, les pixels verticaux. L’origine du repère sera logiquement le pixel le plus en haut à gauche (comme la lecture classique d’un livre, on commence en haut à gauche) et à pour coordonnées … (0,0) ! Eh oui, on ne commence pas aux pixels (1,1) mais bien (0,0). Quand on y réfléchit, c’est assez logique. Les caractères sont rangés dans des chaines de caractères, donc des tableaux, qui eux sont adressés à partir de la case 0. Il parait donc au final logique que les développeurs aient gardé une cohérence entre les deux.

Puisque nous commençons à 0, un écran de 16x2 caractères pourra donc avoir comme coordonnées de 0 à 15 pour xx et 0 ou 1 pour yy. Ceci étant dit, nous pouvons passer à la suite. La prochaine fonction que nous allons voir prend directement en compte ce que je viens de vous dire. Cette fonction nommée setCursor() vous permet de positionner le curseur sur l’écran. On pourra donc faire setCursor(0,0) pour se placer en haut à gauche (équivalent à la fonction "home()") et en faisant setCursor(15,1) on se placera tout en bas à droite (toujours pour un écran de 16x2 caractères). Un exemple :

#include "LiquidCrystal.h" // on inclut la librairie

// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

void setup()
{
    lcd.begin(16, 2);
    lcd.setCursor(2,1);        // place le curseur aux coordonnées (2,1)
    lcd.print("Texte centré"); // texte centré sur la ligne 2
}
Positionner le curseur sur l’écran LCD : setCursor
Animer le curseur

Tout comme nous pouvons faire disparaître le texte, nous pouvons aussi faire disparaître le curseur (comportement par défaut). La fonction noCursor() va donc l’effacer. La fonction antagoniste cursor() de son côté permettra de l’afficher (vous verrez alors un petit trait en bas du carré (5*8 pixels) où il est placé, comme lorsque vous appuyez sur la touche Insér. de votre clavier). Une dernière chose sympa à faire avec le curseur est de le faire clignoter. En anglais clignoter se dit "blink" et donc tout logiquement la fonction à appeler pour activer le clignotement est blink(). Vous verrez alors le curseur remplir le carré concerné en blanc puis s’effacer (juste le trait) et revenir. S’il y a un caractère en dessous, vous verrez alternativement un carré tout blanc puis le caractère. Pour désactiver le clignotement, il suffit de faire appel à la fonction noBlink().

#include "LiquidCrystal.h" // on inclut la librairie

// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

void setup()
{
    lcd.begin(16, 2);
    lcd.home();        // place le curseur aux coordonnées (0,0)
    lcd.setCursor();   // affiche le curseur
    lcd.blink();       // et le fait clignoter
    lcd.print("Curseur clignotant"); // texte centré sur la ligne 2
}
Faire clignoter le curseur : blink

Si vous faites appel à blink() puis à noCursor() le carré blanc continuera de clignoter. En revanche, quand le curseur est dans sa phase "éteinte" vous ne verrez plus le trait du bas.

Jouer avec le texte

Nous allons maintenant nous amuser avec le texte. Ne vous attendez pas non plus à des miracles, il s’agira juste de déplacer le texte automatiquement ou non.

Déplacer le texte à la main

Pour commencer, nous allons déplacer le texte manuellement, vers la droite ou vers la gauche. N’essayez pas de produire l’expérience avec votre main, ce n’est pas un écran tactile, hein ! ;) Le comportement est simple à comprendre. Après avoir écrit du texte sur l’écran, on peut faire appel aux fonctions scrollDisplayRight() et scrollDisplayLeft() vous pourrez déplacer le texte d’un carré vers la droite ou vers la gauche. S’il y a du texte sur chacune des lignes avant de faire appel aux fonctions, c’est le texte de chaque ligne qui sera déplacé par la fonction. Utilisez deux petits boutons poussoirs pour utiliser le code suivant. Vous pourrez déplacer le texte en appuyant sur chacun des poussoirs !

Pour ce montage j’ai du décaler mes fils de l’écran LCD par rapport à d’habitudes afin de libérer les entrées avec interruption sur les broches 2 et 3.

#include "LiquidCrystal.h" // on inclut la librairie

// les branchements
const int boutonGauche = 2; // le bouton de gauche
const int boutonDroite = 3; // le bouton de droite

// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,7,6,5,4);

// ------------------------------------------------------------------------------

void setup() {
    // règlage en entrées pullup
    pinMode(boutonGauche, INPUT);
    pinMode(boutonDroite, INPUT);
    digitalWrite(boutonGauche, HIGH);
    digitalWrite(boutonDroite, HIGH);

    // on attache des fonctions aux deux interruptions externes (les boutons)
    attachInterrupt(0, aGauche, RISING);
    attachInterrupt(1, aDroite RISING);

    // Réglage du LCD
    lcd.begin(16, 2); // règle la  taille du LCD
    lcd.print("Salut ca zeste ?");
}

void loop() {
    // pas besoin de loop pour le moment
}

// fonction appelée par l'interruption du premier bouton
void aGauche() {
    lcd.scrollDisplayLeft(); // on va à gauche !
}

// fonction appelée par l'interruption du deuxième bouton
void aDroite() {
    lcd.scrollDisplayRight(); // on va à droite !
}
Déplacer le texte : scrollDisplay

Et la démo sur simulateur :

!(https://www.tinkercad.com/embed/lF68lfZDFDD)

Déplacer le texte automatiquement

De temps en temps, il peut être utile d’écrire toujours sur le même pixel et de faire en sorte que le texte se décale tout seul (pour faire des effets zolis par exemple). ^^ Un couple de fonctions va nous aider dans cette tâche. La première sert à définir la direction du défilement. Elle s’appelle leftToRight() pour aller de la gauche vers la droite et rightToLeft() pour l’autre sens. Ensuite, il suffit d’activer (ou pas si vous voulez arrêter l’effet) avec la fonction autoScroll() (et noAutoScroll() pour l’arrêter). Pour mieux voir cet effet, je vous propose d’essayer le code qui suit. Vous verrez ainsi les chiffres de 0 à 9 apparaitre et se "pousser" les uns après les autres :

#include "LiquidCrystal.h" // on inclut la librairie

// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

void setup()
{
    lcd.begin(16, 2);
    lcd.setCursor(14,0);
    lcd.leftToRight();  // indique que le texte doit être déplacé vers la gauche
    lcd.autoscroll();   // rend automatique ce déplacement
    lcd.print("{");
    int i=0;
    for(i=0; i<10; i++)
    {
        lcd.print(i);
        delay(1000);
    }
    lcd.print("}");
}
Deplace le texte automatiquement : autoscroll

Créer un caractère

Dernière partie avant la pratique, on s’accroche vous serez bientôt incollable sur les écrans LCD ! En plus, réjouissez-vous ! je vous ai gardé un petit truc sympa pour la fin. En effet, dans ce dernier morceau, toute votre âme créatrice va pouvoir s’exprimer ! Nous allons créer des caractères !

Principe de la création

Créer un caractère n’est pas très difficile, il suffit d’avoir un peu d’imagination. Sur l’écran, les pixels sont en réalité divisés en grille de 5x8 (5 en largeur et 8 en hauteur). C’est parce que le contrôleur de l’écran connait l’alphabet qu’il peut dessiner sur ces petites grilles les caractères et les chiffres. Comme je viens de le dire, les caractères sont une grille de 5x8. Cette grille sera symbolisée en mémoire par un tableau de huit octets (type byte). Les 5 bits de poids faible de chaque octet représenteront une ligne du nouveau caractère. Pour faire simple, prenons un exemple. Nous allons dessiner un smiley, avec ses deux yeux et sa bouche pour avoir le rendu suivant :

0 0 0 0 0
X 0 0 0 X
0 0 0 0 0
0 0 0 0 0
X 0 0 0 X
0 X X X 0
0 0 0 0 0
0 0 0 0 0

Ce dessin se traduira en mémoire par un tableau d’octet que l’on pourra coder de la manière suivante :

byte smiley[8] = {
    B00000,
    B10001,
    B00000,
    B00000,
    B10001,
    B01110,
    B00000,
    B00000
};
Tableau de représentation d’un smiley

La lettre 'B' avant l’écriture des octets veut dire "Je t’écris la valeur en binaire". Cela nous permet d’avoir un rendu plus facile et rapide.

Oh le joli smiley !
Oh le joli smiley !
L’envoyer à l’écran et l’utiliser

Une fois que votre caractère est créé, il faut l’envoyer à l’écran, pour que ce dernier puisse le connaitre, avant toute communication avec l’écran (oui oui avant le begin()). La fonction pour apprendre notre caractère à l’écran se nomme createChar() signifiant "créer caractère". Cette fonction prend deux paramètres : "l’adresse" du caractère dans la mémoire de l’écran (de 0 à 7) et le tableau de byte représentant le caractère. Ensuite, l’étape de départ de communication avec l’écran peut-être faite (le begin). Ensuite, si vous voulez écrire ce nouveau caractère sur votre bel écran, nous allons utiliser une nouvelle (la dernière fonction) qui s’appelle write(). En paramètre sera passé un int représentant le numéro (adresse) du caractère que l’on veut afficher. Cependant, il y a là une faille dans le code Arduino. En effet, la fonction write() existe aussi dans une librairie standard d’Arduino et prend un pointeur sur un char. Le seul moyen de les différencier pour le compilateur sera donc de regarder le paramètre de la fonction pour savoir ce que vous voulez faire. Dans notre cas, il faut passer un int. On va donc forcer (on dit "caster") le paramètre dans le type "uint8_t" en écrivant la fonction de la manière suivante : write(uint8_t param). Le code complet sera ainsi le suivant :

#include "LiquidCrystal.h" // on inclut la librairie

// initialise l'écran avec les bonnes broches
// ATTENTION, REMPLACER LES NOMBRES PAR VOS BRANCHEMENTS À VOUS !
LiquidCrystal lcd(11,10,5,4,3,2);

// notre nouveau caractère
byte smiley[8] = {
    B00000,
    B10001,
    B00000,
    B00000,
    B10001,
    B01110,
    B00000,
};

void setup()
{
    lcd.createChar(0, smiley); // apprend le caractère à l'écran LCD
    lcd.begin(16, 2);
    lcd.write((uint8_t) 0); // affiche le caractère de l'adresse 0
}
Insertion et utilisation d’un caractère dans la memoire du LCD

Désormais, vous savez l’essentiel sur les LCD alphanumériques, vous êtes donc aptes pour passer au TP. ;)