Raspberry PI + LCD – Avis

Dans cet article, je vais tenter de comparer 2 technologies de LCD ou du moins, 2 manières de piloter un écran LCD avec le Raspberry PI.

Mon article concerne aussi bien les Raspberry PI A/B/A+/B+/B2/B3/0 … autant dire toutes.

En effet, je vais utiliser, vous l’aurez compris les GPIOs du Raspberry PI

Le besoin

Le Raspberry est un appareil qui a révolutionné le monde de l’embarqué pour les néophytes. Il permet de faire énormément de petits ou moyens projets ou solutions, il permet même de créer des produits complets de manière très simple…

Cependant, dans énormément de cas, il manque souvent un écran, ne serait-ce que pour afficher l’adresse IP (quand on est en DHCP) ou connaître le statut et l’état de l’appareil.

A moins de passer par un moniteur HDMI, cette tâche n’est pas si évidente qu’on pourrait le penser.

Je ne parlerai pas non plus dans cet article des écrans TFT (promis j’en parle prochainement) qui eux ont un rôle plus avancé. Non, je veux parler des petits (ou gros) écrans LCD…

Bon ok, je parle de ceux la :

20x4-line-lcd-display-with-yellow-backlight-hd44780-for-all-arduinoraspberry-piavrarmpic8051-etc-0-2 1602-16x2-lcd-16-x-2-module-hd44780-green-display-diy-arduino-other-mcu-650x489 hd44780 s-l300

Oui ceux-là. On en voit dans tous les tutoriels.

En fait, il en existe de toutes les tailles, les formes (mouais… carré ou rectangulaire quoi) , en 2 lignes de 8, 2 lignes de 20, 4 lignes de 20 etc ….et toutes les couleurs (bleu sur blanc, noir sur blanc, blanc sur noir, jaune pipi …) afin de s’adapter le mieux possible à votre produit.

Le HD44780

Quand on parle de ce type d’écran, on devrait plutôt dire contrôleur, le HD44780 qui existe depuis de nombreuses années et qui est je pense le plus répandu et le moins cher.

Malgré tout ces avantages, je trouve que l’interface (bus datas) de cet écran commence à être obsolète et assez lourd.

En effet, au minimum, pour piloter cet écran, il vous faudra utiliser 7 GPIOs (en mode 4bits) et 11 GPIOs (en mode 8bits) de votre Raspberry PI. Autant dire que sur les premières versions des Raspberry A/B, vous mangez la moitié des GPIOs juste pour afficher environ 32 caractères.

ecran LCD raspberry montage

Voilà l’exemple d’un montage.

Personnellement, face à ce montage, je trouve que l’affichage d’un caractère est assez cher payé (pas en € mais en ressource utilisé).

Tiens, d’ailleurs combien coûte un écran LCD à base de HD44780. Hé bien, la réponse est : ça dépend mais les premiers prix (petit format) commence à environ 2€.

Exemple là

ou là

Le ST7032

Je vais vous présenter un autre contrôleur existant, le ST7032, qui est, aussi, beaucoup utilisé sur les écrans LCD mais qui utilise une interface plus évoluée, j’ai nommé l’I2C.

Niveau choix, dans les écrans, j’avoue qu’il existe beaucoup moins de modèles que le HD44780 mais les formats les plus classiques existent et répondent à la plupart des besoins.

Je pense, d’ailleurs, que c’est le seul inconvénient de ce contrôleur (avec le prix un peu…).

Les avantages

Les avantages de ce contrôleur sont directement lié aux avantages de l’interface I2C. Donc, voici les avantages de cette technologie sur les bus datas:

  • Seulement 2 pattes pour le flot de données (SCL « la clock », SDA « les datas »)
  • Les 2 pattes sont « collaboratives » . c’est à dire que l’on peut les réutiliser pour d’autres appareils I2C

L’I2C fonctionne en mode Maître/Esclave comme sur le schéma suivant:

Raspberry I2C

Le maître, c’est le Raspberry et l’esclave, l’écran. Mais sur le même bus, vous pouvez intégrer une horloge RTC sauvegardé, des capteurs etc…

Chaque esclave est en fait muni d’une adresse I2C. Si le maître pose une question à une adresse, seul l’appareil concerné répondra et les autres seront en attente.

Je simplifie volontairement mais vous pouvez aller consulter le wiki de l’interface I2C si vous êtes curieux.

Bref… avec cette technologie d’écran, vous ne prenez pas en otage les GPIOs de votre Raspberry PI !!!

Vous n’aurez besoin que des pattes suivantes :

  • SDA
  • SCL
  • GND
  • 3.3 ou 5V en fonction des modèles
  • le RST de l’écran vers le 3.3 du raspberry
  • 1 GPIO pour piloter le backlight

raspberry_lcd_i2c

Concernant la patte qui va piloter le backlight, je fais un peu le violent car je le connecte directement au Raspberry (avec une petite résistance quand même). Cependant j’ai vu que les GPIOs du Raspberry pouvait fournir entre 2 et 16mA. Cela fait 1 an que mon écran a été installé, pour le moment pas de souci mais il est bien entendu préférable de passer par un transistor…

Concernant le prix (~= 2-3€):

C’est parfois un peu plus cher mais surtout moins répandu :

sur ebay

sinon une boutique qui en vend : BuyDisplay

Bon ok, passons au code alors:

Le code

Pré-requis:

  • il faut installer la librairie wiringpi (pour le pilotage des GPIOs) apt-get install wiringpi
  • il faut installer le module i2c-dev dans /etc/modules
  • Dans /etc/modprobe.d/raspi-blacklist.conf il faut enlever la blacklist i2c-bcm2708
  • il faut installer les outils i2c : sudo apt-get install i2c-tools

Méthode en C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <wiringPi.h>

#define LCD_ADDRESS (0x3e)
#define LCD_CONTRAST 100

#define LCD_RS_CMD (0x00)
#define LCD_RS_DATA (0x40)
#define LCD_CMD_CLEAR (0x01)
#define LCD_CMD_HOME (0x03)
En-tête

Voici les inclusions

const int backlight = 18;

void LCD_init(int fd);
void LCD_write(unsigned char rs, unsigned char data, int fd);
void LCD_clear(int fd);
void LCD_setCursor(unsigned char col, unsigned char row, int fd);
void LCD_putc(unsigned char c, int fd);
void LCD_puts(char *str, int fd);
Déclaration des fonctions

les fonctions

void LCD_init(int fd)
{
 usleep(40);
 // Function Set.8bit bus mode, 2-line mode,normal font,normal instruction mode.
    LCD_write(LCD_RS_CMD, 0b00111000, fd);
    // Function Set.extension instruction mode..
    LCD_write(LCD_RS_CMD, 0b00111001, fd);
    // Internal OSC frequency(extension instruction mode)...
    LCD_write(LCD_RS_CMD, 0b00010100, fd);
    // Contrast set(extension instruction mode)..........4bit...
    LCD_write(LCD_RS_CMD, 0b01110000 | (LCD_CONTRAST & 0xF), fd);
    // Power/ICON/Contrast set(extension instruction mode).
    // .... On,booster On,.........2bit...
    LCD_write(LCD_RS_CMD, 0b01011100 | ((LCD_CONTRAST >> 4) & 0x3), fd);
    // Follower control.internal follower on, 
    LCD_write(LCD_RS_CMD, 0b01101100, fd);
    // .....
    usleep(300);
    
    // Function Set.normal instruction mode.
    LCD_write(LCD_RS_CMD, 0b00111000, fd);
    // Display On/Off.Display On....
    LCD_write(LCD_RS_CMD, 0b00001100, fd);
    // Clear Display.
    LCD_write(LCD_RS_CMD, 0b00001100, fd);
    // .....
    usleep(2);
}

void LCD_write(unsigned char rs, unsigned char data, int fd)
{
    unsigned char buf[2];
 
    if (rs == LCD_RS_CMD || rs == LCD_RS_DATA)
    {
        // LCD_RS_CMD ..........LCD_RS_DATA .........
        
  buf[0] = rs;
  buf[1] = data;
  if (write(fd, buf, 2) != 2)
  {
   printf("Error writeing to i2c slave1\n");
  }        
    }
    else
    {
        // rs....LCD_RS_CMD,LCD_RS_DATA...........
    }
}

void LCD_clear(int fd)
{
    LCD_write(LCD_RS_CMD, LCD_CMD_CLEAR, fd);
    usleep(2);
    LCD_write(LCD_RS_CMD, LCD_CMD_HOME, fd);
    usleep(2);
}

void LCD_setCursor(unsigned char col, unsigned char row, int fd)
{
    unsigned char offset[] = {0x00, 0x40};
    
    if (row > 1)    row = 1;
    if (col > 16)    col = 16;
    
    LCD_write(LCD_RS_CMD, 0x80 | (col + offset[row]), fd);
}

void LCD_putc(unsigned char c, int fd)
{
    LCD_write(LCD_RS_DATA, c, fd);
}

void LCD_puts(char *str, int fd)
{
    int i;
    for (i = 0; i < 16; i++)
    {
        if (str[i] == 0x00)
        {
            break;
        }
        else
        {
            LCD_putc((unsigned int)str[i], fd);
        }
    }
}
les fonctions

le programme principal

int main(int argc, char **argv)
{
 int lcd;       // ............
 char *i2cFileName = "/dev/i2c-1"; // I2C..........
 int lcdAddress = LCD_ADDRESS;  // I2C LCD ......
 wiringPiSetupGpio();
 pinMode(butPin, INPUT);      // Set button as INPUT 
 pinMode(backlight,OUTPUT);   
 pullUpDnControl(butPin, PUD_DOWN);
 
 //printf("***** start i2c lcd test program *****\n");
 
 // I2C....Read/Write........
 if ((lcd = open(i2cFileName, O_RDWR)) < 0)
 {
  printf("Faild to open i2c port\n");
  exit(1);
 }
 
 // ...........
 if (ioctl(lcd, I2C_SLAVE, lcdAddress) < 0)
 {
  printf("Unable to get bus access to talk to slave\n");
  exit(1);
 }
 
 usleep(100);

 // LCD....
 LCD_init(lcd);
 
 // ........
    LCD_setCursor(0, 0, lcd);
    LCD_puts("INIT ...", lcd);
}
le programme principal
	 gcc -o lcd i2c_lcd.c -l wiringpi
Makefile

 

Méthode en bash

#!/bin/bash
function usage {
    echo "Usage: $0 [-ic] [-p pos] message" > /dev/stderr;
    echo " -i : LCD init, -c : Clear Screen" > /dev/stderr
    echo " -p : position (0:top left, 40:bottom left)" > /dev/stderr
    exit 1
}
[ $# = 0 ] && usage

while getopts "icp:" flag; do
    case $flag in
	\?) usage ;;
	i)  i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x78 0x5e 0x6c i
	    sleep 0.25
	    i2cset -y 1 0x3e 0 0x0c 0x01 0x06 i
	    sleep 0.05
	    ;;
	c)  i2cset -y 1 0x3e 0 0x01 ;;
	p)  i2cset -y 1 0x3e 0 $((OPTARG+128)) ;;
    esac
done
shift $((OPTIND-1))
[ $# = 0 ] && exit

LANG=C
MSG=`echo -n "$1" | perl -pe '$_=join" ",map{ord }split//'`
#echo $MSG
i2cset -y 1 0x3e 0x40 $MSG i

Conclusion

Voici comment intégrer un écran LCD en I2C sur votre Raspberry PI. Je trouve personnellement que cette méthode est plus élégante que l’autre méthode (sur le plan pratique et visuel).

Bien entendu, si vous n’avez pas besoin d’utiliser les autres GPIOs disponibles du Raspberry PI, l’intérêt reste plus limité.

Mais désormais, vous n’aurez plus d’excuse pour ne pas économiser les GPIOs de votre Raspberry PI.

[Total : 1    Moyenne : 1/5]

Leave a Reply

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *