Arduino 09
Afficheur LCD
Sommaire
REMARQUE : avec l'utilisation de ESP12E / Wemos D1, on a beaucoup moins de GPIO disponibles (9 sur Wemos, 20 sur ARDUINO UNO). Une bonne solution consiste à utiliser des interfaces en I2C qui existent en "backpack" sur LCD, et n'utilisent que 2 GPIO (A4 et A5/GPIO4 et GPIO5). Bien sûr sur ARDUINO cela est aussi très intéressant :
http://blog.f8asb.com/2014/03/01/mise-en-oeuvre-i2c-vers-lcd-carte-chinoise-sur-arduino/
Modules sur Ebay :
https://www.ebay.fr/sch/i.html?_odkw=IIC+I2C+TWI+SPI&_osacat=0&_from=R40&_trksid=m570.l1313&_nkw=IIC+I2C+TWI+SPI+Serial+Interface&_sacat=0
D'autres interfaces par cher :
http://blog.f8asb.com/2014/03/01/des-extensions-arduino-a-moins-de-5-euros-sur-ebay/
Pour ma part j'ai aussi développé sur ARDUINO (autonome ou Mini Pro) un backpack Série avec un protocole de ma composition. Voir Module GoodFields _61_afficheur_serial.ino . Ce module a la particularité de supporter jusqu'à 8 afficheur LCD câblés en paralèlle, et aussi des extensions pour des Entrées Sorties (voyants, entrées analogiques ...)
Voir _09_lcd.zip.
Dans le chapitre 7, on a connecté un afficheur LCD, et maintenant que l'on a une horloge qui tourne, ce serait bien d'afficher l'heure !
Ce qui est nouveau c'est :
* On va utiliser la librairie LiquidCrystal.h : #include <LiquidCrystal.h>
* il faut, dans la zone déclarations, déclarer les ports où est connecté le LCD :
// LiquidCrystal lcd(RS, Enable, D4, D5, D6, D7)
LiquidCrystal lcd(4, 2, 6, 7, 8, 9);
* et dans le setup(), dimensionner le LCD (on en trouve à 1,2 et 4 lignes, de 12, 16 ou 20 caractères) :
// définition taille du LCD :
lcd.begin(16, 2);
* ensuite, pour afficher dessus, on a une fontion pour positionner le curseur en (colonne, ligne ) :
lcd.setCursor(0, 0);
La 1ère colonne c'est No 0, la 1ère ligne est No 0. Pour écrire ensuite on fait :
lcd.print (valeur);
Ou, avec notre Streaming.h toujours présent :
lcd << valeur; (ou lcd && valeur avec Streaming_ger.h)
Par exemple :
// AFFICHAGE sur LCD
lcd.setCursor(0, 0); lcd << "Serial OK"; // 1ere ligne
lcd.setCursor(0, 1); lcd << "GoodFields"; // 2eme ligne
delay(1000); // pour profiter du LCD au démarrage
lcd.clear(); // Effacement général LCD
* Pour en revenir à notre horloge, je vous propose ceci, qui affiche hh:mm:ss si ss<8 et XXX jj/mm/aaaa pour les secondes 8 et 9:
if (second()%10 > 7) {
lcd << "XXX " << ((day()<10)?"0":"") << day() << "/" << ((month()<10)?"0":"") << month() << "/" << year();
} else {
lcd << " " << ((hour()<10)?"0":"") << hour() << ":" << ((minute()<10)?"0":"") << minute() << ":" << ((second()<10)?"0":"") << second() << " ";
}
// idéalement, XXX devrait être remplacé par {"Lun","Mar","Mer","Jeu","Ven","Sam","Dim"} (au boulot !)
* Et nos deux grafcets qui tournent toujours et affichés en 2° ligne du LCD chaque 1/10 de seconde par :
// opérations à chaque dixième de seconde
if (top_dixieme) {
top_dixieme = false;
lcd.setCursor(0, 1); lcd << "b=" << grafcet_b << " a=" << grafcet_a ; // 2eme ligne lcd
}
* Notez que j'ai ajouté quelques variables pour gérer aussi les dixièmes de seconde ... et calculer le nombre de tours par seconde de notre CPU ! C'est impressionnant : on tourne à plus de 70000 tours par seconde ! C'est pourquoi j'ai déclaré ce compteur en 'unsigned long' car les 'unsigned int' sont limités à 65535 (et je n'ai pas parlé des interruptions...)
Voir _09_lcd.zip.
Voir _09_lcd_bargraphe.zip
Dans les LCD, on peut reprogrammer des caractères, en général 8. Ceci va nous permettre de faire facilement des bargraphes. SUr un LCD 16 caractères, on aura 5*16=80 points de définition, ce qui n'est pas mal pour une visualisation rapide.
On a 2 sous-programmes :
- création des caractères : utilisaation de la fonction lcd.createChar. On va reprogrammer les caractères 1 à 8. Eviter le 0 qui est la fin de chaine.
- bargraphe : on a souvent une entrée analogique, et besoin de convertir en valeur 'physique'. Ce sous-programme 'ger_bargraph' fait tout ... et vous renvoie même la valeur convertie !
SOUS-PROGRAMMES
// ************************************************************************************************
/*
GoodFields . modules de gestion de bar-graphes sur LCD standards
21/11/2013
*/
// pré-déclaration
char* ger_bargraph (int valeur, float e_mini, float e_maxi, float s_mini, float s_maxi, float b_mini, float b_maxi, int nbcar, int nbchiffres, char unite, float &sortie);
// ************************************************************************************************
//#define creercaracterestype 1 //
//#define creercaracteresdemo 1 // si on veut un test au démarrage
// ************************************************************************************************
// création de caractères spécifiques
/* http://arduino.cc/fr/Main/LcdcreateChar
creercaracterestype :
0 ou non défini = 5 barres simples
1 = 5 pavés grossissants
2 = 3 barres simples
creercaracteresdemo :
non défini = rien
affichage des caractères au démarrage sur le LCD
Autres standards :
0 barre = espace = cacactère 32
5 barres soulignées = caractère 255
*/
void ger_creercaracteres() {
int i;
float x;
// barres simples
#ifndef creercaracterestype
#define creercaracterestype 0
#endif
#if creercaracterestype == 0
byte carrepro1[8] = { B00000, B00000, B00000, B00000, B00000, B00000, B00000}; // 0 barre
byte carrepro2[8] = { B10000, B10000, B10000, B10000, B10000, B10000, B10000}; // 1 barre
byte carrepro3[8] = { B11000, B11000, B11000, B11000, B11000, B11000, B11000}; // 2 barres
byte carrepro4[8] = { B11100, B11100, B11100, B11100, B11100, B11100, B11100}; // 3 barres
byte carrepro5[8] = { B11110, B11110, B11110, B11110, B11110, B11110, B11110}; // 4 barres
byte carrepro6[8] = { B11111, B11111, B11111, B11111, B11111, B11111, B11111}; // 5 barres
#define nbre_sousechelons 5
#endif
// pave grossissant et souligné
#if creercaracterestype == 1
byte carrepro1[9] = { B00000, B00000, B00000, B00000, B00000, B00000, B00000, B10101}; // 0 barre
byte carrepro2[9] = { B00000, B00000, B00000, B00000, B10000, B10000, B10000, B10101}; // 1 barre
byte carrepro3[9] = { B00000, B00000, B00000, B11000, B11000, B11000, B11000, B10101}; // 2 barres
byte carrepro4[9] = { B00000, B00000, B11100, B11100, B11100, B11100, B11100, B10101}; // 3 barres
byte carrepro5[9] = { B00000, B11110, B11110, B11110, B11110, B11110, B11110, B10101}; // 4 barres
byte carrepro6[9] = { B11111, B11111, B11111, B11111, B11111, B11111, B11111, B10101}; // 5 barres
#define nbre_sousechelons 5
#endif
#if creercaracterestype == 2
// nos nouveaux caractères, 4 niveaux
byte carrepro1[9] = { B00000, B00000, B00000, B00000, B00000, B00000, B00000, B10101}; // 0 barre
byte carrepro2[9] = { B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10101}; // 1 barre
byte carrepro3[9] = { B10100, B10100, B10100, B10100, B10100, B10100, B10100, B10101}; // 2 barres écartées
byte carrepro4[9] = { B10101, B10101, B10101, B10101, B10101, B10101, B10101, B10101}; // 3 barres écartées
byte carrepro5[1] = { 0};
byte carrepro6[1] = { 0};
#define nbre_sousechelons 3
#endif
// autres caracrtères
byte carrepro7[8] = { B01000, B10100, B01000, B00011, B00100, B00100, B00011 }; // °C
byte carrepro8[8] = { B11101, B10111, B10110, B10000, B10111, B10001, B11111 }; // logo GR
// NB: ne pas programmer le caractère 0, car c'est la fin de chaines de caractères
// apprend les caractères à l'écran LCD
lcd.createChar(1, carrepro1);
lcd.createChar(2, carrepro2);
lcd.createChar(3, carrepro3);
lcd.createChar(4, carrepro4);
lcd.createChar(5, carrepro5);
lcd.createChar(6, carrepro6);
lcd.createChar(7, carrepro7);
lcd.createChar(8, carrepro8);
#ifdef creercaracteresdemo
Serial && "creercaracteres:demo" && endli;
// TEST CARACTERES
for (i=1;iɡi++) {
lcd.setCursor(i-1, 0); lcd && char(i); // lcd.write((uint8_t) char(i)); //affiche le caractère
lcd.setCursor(i-1, 1); lcd && i;
}
// demo bargraph
for (i=0;iណi++) {
lcd.setCursor(10, 0); lcd && ger_bargraph (i, 0., 30., 0., 30., 0., 30., 6, 3, ' ', x);
lcd.setCursor(10, 1); lcd && ger_bargraph (29-i, 0., 30., 0., 30., 0., 30., 6, 3, ' ', x);
delay(100);
}
#endif
}
//**********************************************************
// conversion d'une entrée (entière) en sortie (flottante mise à l'échelle)
// et création d'une chaine de caractères bargraph
/*
valeur = entier (typiquement entrée analogique de 0 à 1023)
e_mini = valeur minimale
e_maxi = valeur maximale
s_mini = sortie correspondante à valeur minimale
s_maxi = sortie correspondante à valeur maximale
b_mini = valeur flottante de sortie correspondante au mini du bargraph
b_maxi = valeur flottante de sortie correspondante au maxi du bargraph
nbcar = nombre de caractères du bargraph
nbchiffres = nombre de chiffres de la valeur (non comprise multiplicateur+unité)
si NEGATIF, la valeur est mise APRES la barre, en plus !
unite = 1 caractère pour l'unité. si " " = pas de valeur
sortie : sortie (en flottant)
*/
char* ger_bargraph (int valeur, float e_mini, float e_maxi, float s_mini, float s_maxi, float b_mini, float b_maxi, int nbcar, int nbchiffres, char unite, float &sortie) {
static char local[21];
char buf[15];
int i,j,nbre_echelons;
sortie = ((valeur - e_mini) * (s_maxi - s_mini) / (e_maxi - e_mini) ) + s_mini;
//
nbre_echelons = (float) nbcar * nbre_sousechelons * (sortie - b_mini) / (b_maxi - b_mini);
nbre_echelons = constrain(nbre_echelons, 0, nbcar * nbre_sousechelons);
// nettoyage du buffer
memset (local,char(1),nbcar); // raz pavés blancs (30448)
// for (j=0;j<nbcar;j++) {local[j] = char(1); } // raz pavés blancs (30458)
// affichage d'un bargraph composé de nbcar caractères 'bloc' maximum
i = nbre_echelons/nbre_sousechelons;
if (iɬ) memset (local,char(nbre_sousechelons+1),i); // mise en place des blocs pleins
// Serial << " nbre_echelons=" << nbre_echelons << " "; // debug/test (sans LCD !!!)
// for (j=0;j<i;j++) Serial << "#"; // bargraphe - debug/test (sans LCD !!!)
// mise en place d'un caractère programmé parmi
// if (iɬ && i<nbcar) local[i] = char(nbre_echelons%nbre_sousechelons+1);
if (i<nbcar) local[i] = char(nbre_echelons%nbre_sousechelons+1);
// affichage des butées hors graphe
if (sortie > b_maxi) local[nbcar-1]='>';
if (sortie < b_mini) local[0]='<';
// affichage de la valeur numerique
i=0;
if (unite != ' ') {
formatt (sortie, unite, abs(nbchiffres), buf);
i=strlen(buf);
// économie des blancs en fin de chaine
if (buf[i-1] == ' ' ) i--;
if (buf[i-1] == ' ' ) i--;
// en surimpression
if (nbchiffres > 0) {
for (j=0;j<i;j++) {
if ( sortie > (b_mini+b_maxi)/2 ) {
local[j] = buf[j];
} else {
local[nbcar-i+j] = buf [j];
}
}
i=0; // pour raz correcte !
// en plus , derrière
} else {
for (j=0;j<i;j++) {local[nbcar+j] = buf[j]; }
}
}
// fin chaine
local[nbcar+i]='\0';
// debug/test
//for (j=0;j<nbcar+i+1;j++) Serial && j && "=" && _DEC(local[j]) && " ";
//Serial && endli;
return local;
}
// ************************************************************************************************
TEST
// programme minimum de test
//**********************************************************
// BIBLIOTHEQUES
// incluses dans le package Arduino de base
#include <LiquidCrystal.h> // LCD
// ajoutées
// pour chainage des chaines de caractères avec &&
#include <Streaming_ger.h>
// indispensable pour ger_bargraph (formatt)
#include <ger_format.h>
//**********************************************************
// DECLARATIONS GENERALES
// LCD initialize the library with the numbers of the interface pins
LiquidCrystal lcd(4, 2, 6, 7, 8, 9);
int entree=-100, ix=10, mem=-9999;
float sortie;
int ixx;
char buf[21];
int debug=0;
// sous-programmes spécifiques pour le bargraphe
// voir dans le s/p bargraph
//#define creercaracterestype 1 //
#define creercaracteresdemo 1 // si on veut un test au démarrage
#include <ger_lcd_bargraph.h>
//**********************************************************
// INITIALISATIONS
void setup() {
// clavier analogique à 6 touches avec diodes 1N4148
pinMode(A0,INPUT);
digitalWrite(A0,HIGH);
// init lcd
lcd.begin(16, 2); // initialise le LCD 16 colonnes x 2 lignes
// programmation des caractères LCD
ger_creercaracteres();
}
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {
// lecture clavier
ixx=(analogRead(A0)+50)/100;
if (ixx==1) entree-=1;
if (ixx==2) entree+=1;
if (ixx==3) entree-=10;
if (ixx==4) entree=250;
if (ixx==5) entree+=10;
delay(160);
if (entree != mem) {
mem=entree;
// affichage
// bargraphe de 10 cases avec valeur après en 3 chiffres significatifs
lcd.setCursor(0, 0); lcd && ger_bargraph (entree, 0, 500, 0., 500., 0., 500., 10, -3, 'x', sortie);
// indicateur de batterie en 1 seule case à droite, le bargraphe ne couvre que de 10 à 15V
lcd.setCursor(15, 0); lcd && ger_bargraph (entree, 0, 500, 0., 25., 10., 15., 1, 0, ' ', sortie);
// bargraphe de 16 cases avec valeur surimprimée et symbole degrés centigrades
lcd.setCursor(0, 1); lcd && ger_bargraph (entree, 0, 500, 0., 25., 10., 15., 16, 3, char(7), sortie);
}
}
Voir _09_lcd_multiples.zip
 | | Ayant la possibilité de récupérer de nombreux verres LCD de 2 lignes de 16 caractères, plutôt que d'acheter un 4 lignes de 20, je me suis demandé comment mettre "en paralèlle" plusieurs LCD. |
 | | Partant de la configuration la plus économique en E/S : // LiquidCrystal lcd(RS, Enable, D4, D5, D6, D7) LiquidCrystal lcd(4, 2, 6, 7, 8, 9); Je me suis dit qu'il suffirait de câbler les LCD en parallèle sauf le signal Enable qu'il faudrait diriger sur le bon LCD pour ne s'adresser qu'à lui. |
Un MC14051 fera l'affaire, c'est un multiplexeur 3 vers 8. Une entrée X va pouvoir être dirigée vers 8 sorties X0 à X7, selon une adresse donnée sur ABC. (Attention les MC34051 n'ont rien à voir !!!). Voic la datasheet du 4051 :
 | | Voici le sous-programme qui positionne les bits ABC(D) pour sélectionner le bon afficheur à qui envoyer le ENABLE, et positionne aussi le curseur. On peut dire que c'est une 'extension' de lcd.setCursor(colonne,ligne) Vous remarquerez les #if qui permettent de gérer 2 à 8 ou 16 afficheurs |
//**********************************************************
// positionne le curseur
// colonne = 0 à 15
// ligne = 0 à 7
// ligne 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// en[0] 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
// en[1] 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
// en[2] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
// LIG%2 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
void ger_lcd_setcursor (int colonne, int ligne) {
#if nb_afficheurs > 8
digitalWrite(ger_enable[3], ligne & 0B00010000 );
#endif
#if nb_afficheurs > 4
digitalWrite(ger_enable[2], ligne & 0B00001000 );
#endif
#if nb_afficheurs > 2
digitalWrite(ger_enable[1], ligne & 0B00000100 );
#endif
digitalWrite(ger_enable[0], ligne & 0B00000010 );
lcd.setCursor(colonne, ligne & 0B00000001);
return;
}
//**********************************************************
...
//**********************************************************
// init LCD et affichage test et version
for (i=0;i < nb_afficheurs;i++) {
Serial && "init aff=" && i && " lignes " && i*2 && " et " && i*2+1 && endli;
// sélection de l'afficheur
ger_lcd_setcursor (0, i*2);
// initialisation standard library
lcd.begin(16, 2);
// création des caractères programmés pour les bar-graphes
ger_creercaracteres ();
// affichage de test
ger_lcd_setcursor (8, i*2); // 1ere ligne
lcd && " Af" && i && "/" && nb_afficheurs && " ";
lcd.setCursor(0, 1); // 2ème ligne ou : ger_lcd_setcursor (0, i*2+1)
lcd && versionx;
}
//**********************************************************
Pour afficher des bargraphes, j'ai finalement fait un sous programme qui comprends 'tout' c'est à dire (ceci sont commentaires dans la source) :
//**********************************************************
// conversion d'une entrée (entière) en sortie (flottante mise à l'échelle)
// et création d'une chaine de caractères bargraph
/*
valeur = entrée (int) (typiquement entrée analogique de 0 à 1023)
e_mini = valeur minimale (float)
e_maxi = valeur maximale (float)
s_mini = sortie correspondante à valeur minimale (float)
s_maxi = sortie correspondante à valeur maximale (float)
b_mini = valeur flottante de sortie correspondante au mini du bargraph (float)
b_maxi = valeur flottante de sortie correspondante au maxi du bargraph (float)
nbcar = nombre de caractères du bargraph (int)
nbchiffres = nombre de chiffres de la valeur (non comprise multiplicateur+unité) (int)
si NEGATIF, la valeur est mise APRES la barre, en plus du bar-graph !
unite = 1 caractère pour l'unité. si " " = pas de valeur (char)
sortie : sortie mise à l'échelle (float)
NB: si on affiche la valeur avec un nombre de caractères positif, cette valeur est SUPERPOSEE sur le bargraph, calé à droite si la valeur est inférieure à la moitié du bargraph, à gauche si la valeur est supérieure à cette moitié, afin de ne pas cacher l'extrémite du bargraph.
*/
char* ger_bargraph (int valeur, float e_mini, float e_maxi, float s_mini, float s_maxi, float b_mini, float b_maxi, int nbcar, int nbchiffres, char unite, float &sortie) {
//**********************************************************
// BAR-GRAPHES :
ger_lcd_setcursor (0, 0);
lcd.clear();
lcd && ger_bargraph( hour()*60+minute(), 0.,1440., 0.,24., 0.,24.,12,-3,'h',x);
Ceci va :
- récupérer (heure*60 + minutes) donc en minutes
- comprise entre 0. et 1440.
- convertir cela entre 0. et 24.
- afficher un bargraph 'gradué' de 0. à 24.
- de 12 caractères de long
- afficher ensuite la valeur avec 3 chiffres significatifs,
- avec unité 'h', donc 4 caractères "12h5" pour 12h30m
- et rendre la valeur convertie dans x (en flottant de 0. à 23.9999)
Schéma de câblage à venir ... ???
Photos de la page :
|
|
|
09_LCD_multiples_cablage |
09_LCD_multiples_ini |
09_LCD_multiples_test |
Dernière mise à jour : 11:39:58 18/09/2020