Connecter un afficheur LCD sous Linux - Linux Magazine France No 8

Connecter un afficheur LCD sous Linux

Après plusieurs montages exploitant le port parallèle, voici quelque chose d'un peu plus ardu. Il s'agit d'interfacer un afficheur à cristaux liquides avec votre PC sous Linux.

L'idée de départ de cet article était basée sur un logiciel nommé LcdProc permettant de faire fonctionner un afficheur Matrix Orbital sur le port série. LcdProc est un logiciel très complet sachant également fonctionner en réseau. Malheu reusement, les prix des afficheurs LCD supportés font du montage un gadget très onéreux. De plus, l'utilisation de LcdProc ferait sortir l'article de son cadre didacticiel.

Principe de base

Nous ne reviendrons pas sur la gestion du port parallèle, cette partie a déjà grandement été couverte par les numéros précédents. Le type d'afficheur LCD utilisé est basé sur le contrôleur HD44780. En effet, l'utilisation directe du composant LCD est laissée à la discrétion d'une puce qui sert de traducteur entre nous et l'affichage.

Dans un même type d'afficheur LCD, on peut faire la distinction entre chaque modèle en fonction du nombre de lignes et du nombre de caractères par ligne. Dans notre exemple, nous utiliserons un afficheur d'une ligne de 16 caractères (ceci n'est pas tout à fait exact puisqu'il s'agit en fait de deux fois huit caractères). Je pense que ceci devrait être largement suffisant pour s'initier à son interface. Le coût de ce genre de modèle dépassant rarement la centaine de francs, nous pourrons l'expérimenter en toute tranquillité.

Le brochage des afficheurs basé sur un HD44780 est toujours (plus ou moins) standardisé, comme suit :

1 - Masse

2 - Vcc (alim.)

3 - Vo

4 - Sélection de registre (Register Select)

5 - Lecture/écriture

6 - Activation de signal (Enable Signal)

7 à 14 - Entrée des données (D0 à D7)

Optionnel :

15 - Masse rétro-éclairage

16 - Alim. rétro-éclairage

Passons sur les broches 1, 2, et 7 à 14 dont l'utilisation est (relativement) évidente.

La broche Vo permet de contrôler le contraste de l'affichage selon le schéma en figure 1. Nous avons utilisé une méthode plus cavalière pour notre exemple, en utilisant une résistance (je pense que cela n'est pas tout à fait correct mais ça marche).

Le signal Register Select (4) permet de choisir entre ce que l'on pourrait appeler le mode commandes et le mode données. Selon l'état de cette broche, on peut contrôler l'afficheur (effacement, positionnement du curseur, programmation, etc.) ou simplement envoyer des caractères destinés à être affichés.

Le signal lecture/écriture permet, comme son nom l'indique, de choisir entre la réception des informations (position du curseur par exemple) et l'envoi de données au HD44780. Dans notre exemple, cette broche sera directement reliée à la masse car nous ne ferons qu'écrire sur le composant.

Le signal envoyé sur la broche 6 peut être comparé au fonctionnement de la touche [Entrée] d'un clavier. En effet, pour afficher un caractère, on envoie les données sur les broches 7 à 14, puis on active brièvement Enable Signal. Et ce n'est qu'à ce moment que le caractère apparaît sur l'afficheur. Il en va de même pour le mode commande, une commande ne sera "validée" que par ce signal.

Maintenant que ce tour d'horizon se termine, nous pouvons résumer le montage au schéma donné en figure 2.

Partie logicielle

D'après les informations données ci-dessus, on peut écrire l'algorithme suivant :

chaîne:=”BONJOUR MONDE" ;

compteur:=0;

initialise_LCD;

tantque (compteur < taille_de(chaîne)) {

envoyer_sur_7_à_14(chaine[compteur]);

envoyer_sur_6(1);

attendre(1ms);

envoyer_sur_6(0);

compteur:=compteur+1;

}

Malheureusement, cet algorithme ne correspond pas au mode de fonctionnement de l'afficheur. Nous l'avons dit en début d'article, il ne s'agit pas en réalité d'un afficheur d'une ligne de 16 caractères mais d'une ligne de 2 fois 8 caractères. En clair, dans la mémoire de l'afficheur LCD, les 8 premiers caractères sont stockés respectivement aux adresses [0][1][2][3][4][5][6][7]. Les 8 caractères suivants sont stockés bien plus loin aux adresses hexa [40][41][42][43][44][45][46][47]. Il nous est donc nécessaire de passer en mode commande entre le 8e et 9e caractère pour changer la position du curseur.

Le code

Voici un exemple de source en C permettant d'afficher un texte sur le montage. Dans un premier temps, nous incluons les en-têtes adéquats, définissons quelques constantes et déclarons nos variables :

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <asm/io.h>

#define PORTADDRESS 0x378 /* Adresse du port parallèle*/

#define DATA PORTADDRESS+0 /* port données */

#define STATUS PORTADDRESS+1 /* port d'état */

#define CONTROL PORTADDRESS+2 /* port de contrôle */

char init[10]; /*tableau contenant les valeurs d'initialisation du HD44780*/

int count; /*simple compteur pour les différentes boucles*/

int len; /*longueur du texte*/

Ensuite, pour simplifier la réutilisation du code, nous définissons des fonctions. La première initialise l'afficheur et les valeurs d'initialisation sont tirées de la documentation de l'afficheur (aka doc. HD44780) :

void initlcd(void) {

init[0] = 0x0F; /* Reset LCD */

init[1] = 0x01; /* Efface LCD */

init[2] = 0x38; /* mode 8 Bits */

outb(inb(CONTROL) & 0xDF, CONTROL); /* init du port de contrôlel */

/* Passe en mode commande (Select Printer=Register Select), broche 17 */

outb(inb(CONTROL) | 0x08, CONTROL);

/*boucle pour l'envoi de toutes les valeur définies dans le tableau init[] */

for (count = 0; count <= 2; count++) {

outb(init[count], DATA); /* on envoie une valeur sur DATA */

outb(inb(CONTROL) | 0x01, CONTROL); /* on valide (Strobe=Enable)*/

usleep(2); /* pause de 2ms */

outb(inb(CONTROL) & 0xFE, CONTROL);

usleep(2); /* pause de 2ms */

}

/* nous venons d'envoyer Reset, Efface, mode 8 bits en validant l'envoi à chaque fois.

Nous pouvons à présent repasser en mode données */

outb(inb(CONTROL) & 0xF7, CONTROL); /* Stop mode commande */

}

Notre seconde fonction se chargera d'envoyer du texte sur l'afficheur. Cette fonction accepte un argument (le texte à afficher) :

void envoilcd(char texte[10]) {

len = strlen(texte); /* on calcule la longueur du texte */

/* Et on boucle du premier au dernier caractère de la chaîne */

for (count = 0; count < len; count++)

{

outb(texte[count], DATA); /* envoi d'un caractère */

outb(inb(CONTROL) | 0x01, CONTROL); /* validation */

usleep(1); /* on attend 1ms */

outb(inb(CONTROL) & 0xFE, CONTROL);

usleep(1); /* on attend 1ms */

}

}

Enfin, il nous reste une fonction à définir, celle qui permet de placer le curseur où bon nous semble. Un argument est passé à la fonction (la nouvelle position du curseur) :

void passelcd(int pos) {

/* on passe en mode commande */

outb(inb(CONTROL) | 0x08, CONTROL);

outb(128+pos, DATA); /* le 8e bit informe d'un choix d'adresse */

outb(inb(CONTROL) | 0x01, CONTROL); /* validation */

usleep(2);

outb(inb(CONTROL) & 0xFE, CONTROL);

usleep(2);

outb(inb(CONTROL) & 0xF7, CONTROL); /* Fin du mode commande */ }

A présent, il ne nous reste plus qu'à écrire la fonction main du programme. Nous avons à notre disposition tout ce qu'il faut pour travailler :

void main(void)

{

/* Avant toutes choses, il faut demander au système la permission d'utiliser le port. C'est indispensable sinon... SEGFAULT ! */

if(ioperm(PORTADDRESS,3,1)) {perror("ioperm"); exit(1);}

initlcd(); /* on initialise l'afficheur */

envoilcd("BONJOUR" ); /* on envoie le premier texte */

passelcd(0x40); /* on place le curseur à la position 40h */

envoilcd("MONDE !" ); /* et on envoie le second texte */`

}

Voilà, il ne vous reste plus qu'à compiler le programme en n'oubliant pas l'option -O2 :

gcc lcd.c -olcd -O2 -Wall

Bien sûr, ce programme n'est qu'un exemple simpliste. Il ne fait qu'afficher un texte statique sur l'afficheur LCD. En ajoutant quelques lignes de code, on peut facilement afficher des informations récupérées dans les fichiers du répertoire /proc.

Exemple :

char dummy[4096]; // variable pipo pour éliminer les mots

char file[]="/proc/meminfo"; //nom du fichier

FILE *fp; //pointeur sur fichier

char valeur[10];

if (system(dummy)) exit (1);

fp = fopen (file,"r"); //ouvre le fichier/proc/meminfo

if (!fp) perror("/proc/meminfo"); //erreur en ouverture ?

initlcd(); /* on initialise de l'afficheur */

/* on élimine les 17 premiers mots */

if (!fscanf(fp,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",

dummy,dummy,dummy,dummy,dummy,

dummy,dummy,dummy,dummy,dummy,

dummy,dummy,dummy,dummy,dummy,

dummy,dummy)) perror("moi");

while (!feof(fp)){ // tant que pas de fin de fichier

/* on choisit les mots à stocker */

fscanf(fp,"%s%s%s\n",dummy,valeur,dummy);

envoilcd(valeur); /* on envoie le texte */

}

fclose(fp); //et puis on ferme le fichier

}

Bien sûr, ce petit bout de code n'est qu'un exemple tiré des sources sur mon disque. Ne vous étonnez donc pas si tout cela semble confus. N'hésitez pas à nous envoyer vos créations dérivées de cet article et des idées de montages si vous en avez.

 


© Copyright 2000 Diamond Editions/Linux magazine France. - Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".