Arduino 09arduino_goodfields_64.jpgAfficheur LCD
 
Sommaire

Afficheur  LCD Afficheur LCD Bargraphe  sur LCD Bargraphe sur LCD LCD_multiples LCD_multiples
codes  programmes 09 codes programmes 09
La page principale ... La page principale ...

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 ...)

   
Vers le début Vers Page 2 Vers sommaire Afficheur LCD
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.

   
Vers le début Vers Page 2 Vers sommaire Bargraphe sur LCD
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 !

Le codage standard Le codage standard 'page 850' très répandu


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);

    }

}

   
Vers le début Vers Page 3 Vers Page 1 Vers sommaire LCD_multiples
Voir _09_lcd_multiples.zip

cliquez pour agrandir : photo/09_LCD_multiples_ini.jpg 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.
cliquez pour agrandir : photo/09_LCD_multiples_cablage.jpg 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 :
CMOS-4051-4052-4053.pdf CMOS-4051-4052-4053.pdf

cliquez pour agrandir : photo/09_LCD_multiples_test.jpg 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 ... ???

   
Vers le début Vers Page 4 Vers Page 2 Vers sommaire codes programmes 09
_09_lcd.zip _09_lcd.zip _09_lcd_bargraphe.zip _09_lcd_bargraphe.zip _09_lcd_multiples.zip _09_lcd_multiples.zip
la suite ... la suite ... La page principale ... La page principale ...

Photos de la page :
cliquez pour agrandir : photo/09_LCD_multiples_cablage.jpg cliquez pour agrandir : photo/09_LCD_multiples_ini.jpg cliquez pour agrandir : photo/09_LCD_multiples_test.jpg
09_LCD_multiples_cablage 09_LCD_multiples_ini 09_LCD_multiples_test

Dernière mise à jour : 11:39:58 18/09/2020