Vu le nombre d'écran LCD couleurs en circulation dans les différents appareils électroniques utilisés dans la vie quotidienne, le prix de ces écrans devient abordable pour les électroniciens amateurs. Plusieurs sources d'approvisionnement sont possibles. Directement en démontant des appareils mobiles déclassés, en furetant dans les stocks de pièces détachées, notamment sur Ebay. On peut encore trouver des revendeurs tels Sparkfun qui non seulement fournit les écrans LCD mais peut aussi fournir des circuits imprimés déjà équipés permettant de s'affranchir de tous les problèmes d'interfacage mécanique.(Break-out Board)
L'écran LCD utilisé ci-dessous, est un écran de GSM Nokia. Sa référence la plus commune sur le web est LCD Nokia 6100 de part sa présence au sein du GSM Nokia 6100. Ce type d'écran est aussi utilisé dans d'autres modèles de la marque.
Il existe sur le web, un forum de référence pour ce type d'écran où pas mal d'informations sont rassemblées. Le site de Sparkfun regorge aussi d'exemples mis à disposition par d'autres internautes.
Qu'importe la source, vous avez votre LCD en main, on peut commencer... Oui et non, avant d'intégrer votre LCD dans votre dernière création, il reste à déterminer le type de contrôleur présent dans l'écran. Il existe deux familles : une à base du contrôleur Phillips PCF8833 et l'autre à base du contrôleur Epson S1D15G00. Sur le forum de référence cité ci-dessus, on peut trouver des éléments de détermination basés sur les caractéristiques physiques de l'écran (couleur du PCB Flex, numéros et autocollants présents sur l'écran). Celui acheté chez Sparkfun mi-avril 2009 est basé sur un contrôleur graphique Epson. (Flex Vert,sticker noté GE et sticker rouge sur la feuille de protection de l'écran, numéro GE128128 à l'arrière). Hélas, les deux types de contrôleurs ne se programment pas de la même manière. La suite de cette page sera consacrée uniquement au contrôleur Epson.
Au moins quelque chose de commun aux deux types de contrôleurs graphiques, le hardware !
![]() |
Pour permettre une interface facile entre notre montage et l'écran LCD, on peut utiliser le Breakout Board proposé par Sparkfun. Outre la connectique avec l'écran, le PCB est équipé d'une LED RGB et de deux push buttons pouvant améliorer l'interface entre l'utilisateur et le montage. Un étage de puissance est prévu pour pouvoir générer les tensions nécessaires à l'écran. Une alimentation uniquement en 3.3 volts est possible même si elle n'est pas optimale comme décrit plus bas. Le schéma de ce PCB est disponible ici. |
Ci-dessous, la liste des bornes accessibles sur le PCB de Sparkfun :
|
Les interconnections avec le microcontrôleur se limitent à 4 pins ( CS-SCK-DIO-RESET ). Si le microcontrôleur est alimenté en 3,3 volts, ces pins peuvent être directement connectées sur les broches du PCB LCD. Si ce n'est pas le cas, il faut prévoir une adaptation des niveaux de tensions. Un simple pont diviseur à base de résistances peut suffire. (1k8 - 3k3 pour du 5 Volts, par exemple). La tension Vbatt appliquée peut descendre jusqu'à 3,3 Volts mais à cette tension, l'étage de puissance a un rendement assez exécrable. Mieux vaut prévoir une tension plus élevée (7 volts max) pour réduire les courants.
Avant de parler d'initialisation, il faut définir le protocole de communication entre le PIC et l'écran. Celui-ci utilise un protocole de type SPI (2 fils : une ligne clock et une ligne data). Le protocole de l'écran utilisant 9 bits, on n'utilisera un SPI software plutôt que d'utiliser le module SPI hardware du PIC limité à 8 bits.Le bit 9 étant utilisé pour informer l'écran de l'envoi d'une commande (bit = 0) ou d'une donnée (bit = 1). Sur le PCB développé par Sparkfun, le signal Din n'est pas implémenté; on a donc qu'un accès en écriture à l'écran.
Ci dessous, le code utilisé pour communiquer avec l'écran.
|
************************ **** Envoi Commande **** ************************ void gspi_command(unsigned char dat) { LCD_CS = 0; // Chip Select - Sélection de l'écran SPI_DO = 0; // on sort un 0 sur la ligne data (9ième bit=0 => commande) SPI_CLK = 0; // envoi de la clock Delay_us(1); SPI_CLK = 1; Delay_us(1); gspi_do(dat); // Envoi des 8 bits suivants LCD_CS = 1; // On libère l'écran } ************************ **** Envoi Donnée ****** ************************ void gspi_data(unsigned char dat) { LCD_CS = 0; // Chip Select - Sélection de l'écran SPI_DO = 1; // on sort un 1 sur la ligne data (9ième bit=1 => donnée) SPI_CLK = 0; // envoi de la clock Delay_us(1); SPI_CLK = 1; Delay_us(1); gspi_do(dat); // Envoi des 8 bits suivants LCD_CS = 1; // On libère l'écran } ************************************** **** Envoi 8 bits complémentaires **** ************************************** void gspi_do(unsigned char dat) { int i; i = 0; for(i = 1; i <= 8; i++) // boucle de 8 itérations, une pour chaque bit { if (dat > (unsigned)127) // test du bit le plus significatif { SPI_DO = 1; // si le bit=1, DO=1 } else { SPI_DO = 0; // si le bit=0, DO=0 } dat = dat << 1; // déplace les bits de dat d'une case vers la droite SPI_CLK = 0; // On envoie la clock Delay_us(1); SPI_CLK = 1; Delay_us(1); } } |
|||
Un remarque quand même : les delais de 1 µsec (delay_us(1)) utilisés dans ces boucles sont surement trop long... On peut sans problème les diviser par 5. Les communications ont été ralenties à des fins de debug.
Comme toujours les écrans LCD nécessitent une séquence d'initialisation avant de pouvoir les utiliser. La première chose à faire est d'effectuer un reset hardware. Pour ce faire, rien de bien sorcier, il suffit de mettre la pin reset à l'état bas puis à l'état haut comme dans le code ci-dessous.
|
// Reset Hardware LCD_RESET = 0; delay_ms(100); LCD_RESET = 1; delay_ms(100); |
|||
Vient ensuite la commande DISCTL qui permet d'effectuer tous les contrôles concernants les horloges internes du chip LCD mais aussi de déterminer le nombre de lignes utilisées. La commande DISCTL utilise 4 paramètres. Le premier permet de régler le facteur de division de l'horloge interne. Le deuxième permet de sélectionner le nombre de blocs utilisés ( 4 lignes par bloc ) via la formule suivante : ( nombre de lignes/4 )- 1. Le troisième paramètre permet de choisir le nombre de lignes qui seront "highlighted". Le dernier paramètre permet de régler s'il y a dispersion ou non. Ci-dessous, le code utilisé et les paramètres choisis.
|
// commande DISCTL gspi_command(DISCTL); gspi_data(0x00); // div par 2 (defaut) et periode de switching par défaut gspi_data(0x20); // 130 lignes utilisées gspi_data(0x00); // pas de lignes inversément "highlighted" gspi_data(0x00); // no dispersion |
|||
La commande suivante permet de sélectionner le mode de scanning des sorties. Cela dépend de la manière ont le contrôleur EPSON est connecté avec le module LCD. Et donc peu d'influence dans notre cas. La seule valeur de paramètre qui fonctionne est 1. les deux commandes suivantes permettent, respectivement, d'allumer l'oscillateur interne (OSCON) et de sortir du mode SLEEP (SLPOUT).
|
gspi_command(COMSCN); gspi_data(0x01); //P1: 0x01 = Scan 1->80, 160<-81 gspi_command(OSCON); gspi_command(SLPOUT); |
|||
La suite des commandes à envoyer permet de régler le contraste de l'écran (VOLCTR) et d'allumer les différents étages de puissance de l'écran LCD (PWRCTR). Le réglage du contraste peut être difficile. Pour choisir la valeur optimale, j'ai effectué une boucle testant différentes valeurs (valeurs de 0 à 63). Le deuxième paramètre de la commande VOLCTR est fixé à 0x03; seule valeur donnant un résultat correct et lié à un rapport de résistances internes. La commande PWRCTR envoyée (on allume tout !), il faut laisser un delai de 100msec pour permettre aux différentes tensions de s'établir.
|
gspi_command(VOLCTR); gspi_data(0x30); gspi_data(0x03); delay_ms(100); gspi_command(PWRCTR); gspi_data(0x0f); delay_ms(100); |
|||
La commande suivante est DISINV qui permet d'obtenir des couleurs correctes sur l'écran. Vient ensuite la commande DATCTL permettant de définir le nombre de couleurs utilisées, la manière de séquencer les RGB ainsi que la position de l'écran ( retournement à 180°). Trois paramètres sont nécessaires pour cette dernière commande. Le premier concerne le sens de l'écran, le second, le séquencement RGB et le troisième le nombre de bits consacrés aux couleurs. Une dernière commande qui peut avoir son importance : DISON ... pour allumer l'écran.
|
gspi_command(DISINV); gspi_command(DATCTL); gspi_data(0x00); gspi_data(0x00); gspi_data(0x02); gspi_command(DISON); |
|||
On entre enfin dans le vif du sujet ! Une des fonctions élémentaire consiste en savoir allumer un pixel dans la couleur désirée. Pour ce faire, il faut déterminer la ligne et la colonne de l'écran où allumer le pixel et donc accéder à l'adresse RAM où stocker les information de couleur. Nous aurons donc besoin des commandes PASET ( choix de la ligne), CASET (choix de la colonne) et RAMWR (écriture en RAM).
|
void LCDSetPixel(int x, int y, int color) { // Choix de l'adresse de la ligne (commande 0x2B) gspi_command(PASET); gspi_data(x); gspi_data(x); // Choix de l'adresse de la colonne (commande 0x2A) gspi_command(CASET); gspi_data(y); gspi_data(y); // On allume le pixel (2nd pixel ignoré) gspi_command(RAMWR); gspi_data((color >> 4) & 0xFF); gspi_data(((color & 0xF) << 4) | ((color >> 8) & 0xF)); gspi_data(color & 0xFF); gspi_command(NOP);} |
|||
Un petit mot d'explication concernant les commandes PASET et CASET. Ces deux commandes permettent de définir un rectangle dont la position et les longueurs des côtés sont définis par les deux points extremes d'une des diagonales. Petit dessin pour mieux comprendre :

Dans le cas de l'écriture d'un point, les points définis par PASET et CASET sont identiques. L'utilisation de la commande RAMWR permet d'écrire directement dans la mémoire du contrôleur. Si les points définis dans les commandes CASET et PASET sont différents, on définit un rectangle permettant très facilement de dessiner des caractères ou autre dessin. L'énorme avantage de la commande RAMWR, permet d'envoyer séquentiellement les valeurs RGB des pixels sans devoir se préoccuper des adressages mémoire. En effet, on commence dans le coin inférieur gauche et puis, automatiquement, le contrôleur incrémente la position au pixel suivant. Une fois arrivé en bout de ligne, une incrémentation vers la ligne suivante est effectuée. (Voir schéma ci-dessus). On voit tout de suite, l'économie de commande par rapport à un dessin pixel par pixel. Un exemple d'application est présenté dans le paragraphe suivant.
Sur un écran LCD graphique, écrire, c'est avant tout dessiner. Chaque caractère, doit être préalablement dessiné. On utilise un tableau dans lequel chaque ligne regroupe les différentes séries de pixels à allumer pour un même caractère.

Dans le tableau ci-dessous, on retrouve un exemple de table de caractères utilisée dans un programme affichant des températures. La première ligne de données du tableau fournit la hauteur et la largeur du caractère ainsi que le nombre d'octets nécessaires pour le dessiner.
|
const unsigned char FONTRED1 [26][8] = { 0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00, 0x3E,0x63,0x63,0x6B,0x63,0x63,0x3E,0x00, // 0 0x18,0x38,0x58,0x18,0x18,0x18,0x7E,0x00, // 1 0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00, // 2 0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00, // 3 0x0E,0x1E,0x36,0x66,0x7F,0x06,0x0F,0x00, // 4 0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00, // 5 0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00, // 6 0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00, // 7 0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00, // 8 0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00, // 9 0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00, // + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, // , 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00, // - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //space 0x7E,0x5A,0x18,0x18,0x18,0x18,0x3C,0x00, // T 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00, // e 0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00, // m 0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78, // p 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00, // e 0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00, // r 0x00,0x00,0x3C,0x06,0x3E,0x66,0x3B,0x00, // a 0x08,0x18,0x3E,0x18,0x18,0x1A,0x0C,0x00, // t 0x00,0x00,0x66,0x66,0x66,0x66,0x3B,0x00, // u 0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00, // r 0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00 // e }; |
|||
Reste donc maintenant à utiliser la commande RAMWR telle que décrite ci-dessus pour afficher le caractère à l'endroit voulu sur l'écran. Pour ce faire, après avoir défini l'endroit (x,y) où afficher, il faut parcourir la table des caractères afin de retrouver l'état de chaque pixel composant le caractère. Un choix de couleur est possible pour l'impression du caractère mais aussi pour la couleur de fond.
|
void LCDPutChar(char c, unsigned char x, unsigned char y,unsigned int fColor, unsigned int bColor) { unsigned char i,j,nCols,nRows,PixelRows,Mask; unsigned int Word0; unsigned int Word1; nCols = FONTRED[0][0]; nRows = FONTRED[0][1]; //Définition du cadre dans lequel dessiner le caractère // Addresse des lignes gspi_Command(PASET); gspi_data(x); gspi_data(x + nRows - 1); // Adresse des colonnes gspi_command(CASET); gspi_data(y); gspi_data(y + nCols - 1); // On écrit le caractère, ligne par ligne, de haut en bas, en // sélectionnant la couleur de fond ou la couleur du caractère gspi_command(RAMWR); for (i =0 ; i <=nRows - 1; i++) { PixelRow = FONTRED[c][i]; Mask = 0x80; for (j = 0; j < nCols; j += 2) { // si le bit du pixel = 1, couleur caractère sinon couleur de fond // attention on traite deux pixels à la fois if ((PixelRow & Mask) == 0) Word0 = bColor; else Word0 = fColor; Mask = Mask >> 1; if ((PixelRow & Mask) == 0) Word1 = bColor; else Word1 = fColor; Mask = Mask >> 1; gspi_data((Word0 >> 4) & 0xFF); gspi_data(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF)); gspi_data(Word1 & 0xFF); } } gspi_command(NOP); } |
|||
Cette routine n'affiche qu'un seul caractère à la fois, il est tout a fait possible d'imaginer une routine permettant d'afficher une chaîne de caractères à partir de celle-ci. Une boucle appelle LCDPutChar en incrémentant la position des caractères mais aussi la position des caractères de la chaîne dans la table. Attention aussi à ne pas dépasser les 131 pixels...