Ein 2 x 16 Zeichen LCD mit den GPIO-Pins programmieren

LCD10Ein zweizeiliges LCD kostet inklusive Versand knapp 3 EUR. Oft genug reichen 2 x 16 Zeichen völlig aus, um die wichtigsten Informationen darzustellen. Ob eine IP-Adresse, ein MP3-Titel, eine Benachrichtigung über ein Systemereignis oder Messwerte – einen kompletten Bildschirm braucht es oft nicht. LCDs mit einem HD44780-Controller sind sehr verbreitet. Es gibt viele Projekte, die ein solches Display für den RasPi verwenden. Das LCD wird dabei meist mit Hilfe von Bibliotheken angesprochen, die das Programmieren des Controllers abnehmen. Die python-Bibliothek RPLCD ist zum Beispiel so eine Bibliothek. In diesem Beitrag soll gezeigt werden, wie das LCD über die GPIO-Pins direkt auf der Ebene des Microcontrollers angesprochen wird. Der Quell-Code für das C-Programm ist auf gitHub abgelegt: https://github.com/rheikvaneyck/HD44780.

Vorbereitung

LCDFür das Projekt wird ein LCD mit einem HD44780-Controller gebraucht, eine Stiftleiste, zwei Widerstände (3,3 kOhm + 560 Ohm) und natürlich ein RasPi. Um das LCD nutzen zu können, muss zunächst die Steckleiste auf 16 Pins gekürzt  und an das LCD gelötet werden. Ein kleines Video gibt einen 10 s-Tipp dazu: Steckbrücke am LCD fixieren, Lötspitze zum Anwärmen kurz an den Pin halten und Lötzinn zugeben. Fertig.

Anschließend wird das Ganze verkabelt, wie in der nächsten Abbildung gezeigt.

HD44780_Steckplatine

Nach dem Zusammenstecken sollte alles noch mal geprüft werden, damit RasPi oder LCD keinen Schaden nehmen. Dann kann der RasPi angeschaltet werden.Das LCD zeigt sofort 16 weiße Blöcke in der ersten Zeile. Das ist die Standardkonfiguration nach dem Einschalten.

Nun braucht es nur noch den Quell-Code. Der Code ist in C geschrieben und nutzt die bcm2835-Bibliothek zum Ansprechen der GPIO-Pins. Um die aktuelle Version zu installieren, wird sie mit wget von der Webseite des Entwicklers geladen und compiliert:

wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.37.tar.gz
tar zxvf bcm2835-1.37.tar.gz
cd bcm2835-1.37
./configure
make
sudo make check
sudo make install

Nun kann der Quell-Code für das C-Programm mit git von gitHub geladen werden:

git clone https://github.com/rheikvaneyck/HD44780.git
cd HD44780
make

Wenn alles geklappt hat, bringt der folgende Aufruf einen Text auf das LCD:

sudo ./lcd_test -1 "Hallo Welt" -2 "Wie geht's?"

Das Programm verfügt noch über zwei Test-Sequenzen, die einen Text anzeigen und die Cursor-Funktionen vorführen, sowie alle verfügbaren Zeichen auf das Display bringen. Das Ergebnis der Test-Sequenzen 'sudo ./lcd_test -t' und 'sudo ./lcd_test -s' zeigt das folgende Video:

Man kann also nicht nur Text auf das Display bringen, der Microcontroller hat auch Funktionen, um den Cursor zu setzen, die Anzeige nach links oder rechts zu verschieben oder das Display komplett zu löschen. Wie das genau auf Ebene des Microcontrollers programmiert wird, zeigt der nächste Abschnitt.

Den HD44780 mit C programmieren

Das C-Programm macht nichts anderes, als die entsprechenden GPIO-Pins im richtigen Moment an- und auszuschalten. Der Microcontroller des LCD interpretiert die an den Pins anliegende Spannung als Bits, die bei HIGH eine 1 und bei LOW eine 0 ergeben. Auf einen zeitlich genau abgestimmten Impuls auf dem Pin E, der mit dem GPIO-Pin 24 erzeugt wird, liest der Microcontroller die Datenleitungen ein. Die Bits der Datenleitungen D4, D5, D6 und D7 werden mit den GPIO-Pins 22, 18, 16 und 12 gesetzt.

Wie läuft die Kommunikation zwischen RasPi und LCD also ab? Über einen Morse-Code auf den GPIO-Pins: Werden die GPIO-Pins 22, 18, 16 und 12 auf HIGH, HIGH, LOW, HIGH gesetzt und anschließend ein Impuls auf dem GPIO-Pin 24 erzeugt, liest der Microcontroller die Bits 1101 ein. Die dabei anliegende Spannung auf dem LCD-Pin RS – die wiederum mit dem GPIO-Pin 26 gesetzt wird – entscheidet darüber, ob die Bits als Kommando (Registereintrag) oder als Daten interpretiert werden sollen. Um das LCD zu programmieren, muss alles nur noch in der richtigen Reihenfolge abgespielt werden. Der Impuls besteht übrigens aus einem LOW, dann 5 ms HIGH und anschließend mindestens 5 ms LOW. Unmittelbar nach diesem Signal werden die Datenleitungen ausgelesen.

void pulse_e(void)
{
// Toogle ENABLE pin in us
// E_DELAY = 1
// E_DATA_PULSE = 5000
// E_CMD_PULSE = 5000
bcm2835_delayMicroseconds(E_DELAY);
bcm2835_gpio_set(LCD_E); // GPIO-Pin 24 HIGH
bcm2835_delayMicroseconds(E_DATA_PULSE);
bcm2835_gpio_clr(LCD_E); // GPIO-Pin 24 LOW
(LCD_DATA) ? bcm2835_delayMicroseconds(E_DATA_PULSE) : bcm2835_delayMicroseconds(E_CMD_PULSE);
}

Jetzt muss man nur noch die Daten schreiben. Der Microcontroller will eigentlich immer ein ganzes Byte. Das sind 8 Bit. In der Standardkonfiguration ist der HD44780 im 4-Bit Modus er erwartet daher zwei Halbbytes hintereinander. Für ein Byte werden daher zwei Einlesevorgänge hintereinander ausgelöst. Das zeigt die folgende Funktion:

void write_to_lcd(uint8_t byte, uint8_t mode)
{
  switch (mode) {
  case LCD_CMD:
    bcm2835_gpio_clr(LCD_RS);
      // fprintf(stdout, "Set RS to cmd mode\n");
      break;
  case LCD_DATA:
    bcm2835_gpio_set(LCD_RS);
    // fprintf(stdout, "Set RS to data mode for '%c'\n", byte);
    break;
  default:
    // fprintf(stderr, "mode for setting GPIO pin unknown: %i\n", mode);
    exit(EXIT_FAILURE);
  }
  // write high bits
  bcm2835_gpio_clr(LCD_D4);
  bcm2835_gpio_clr(LCD_D5);
  bcm2835_gpio_clr(LCD_D6);
  bcm2835_gpio_clr(LCD_D7);

  if ((byte & 0x10) == 0x10) {
    bcm2835_gpio_set(LCD_D4);
  }
  if ((byte & 0x20) == 0x20) {
    bcm2835_gpio_set(LCD_D5);
  }
  if ((byte & 0x40) == 0x40) {
    bcm2835_gpio_set(LCD_D6);
  }
  if ((byte & 0x80) == 0x80) {
    bcm2835_gpio_set(LCD_D7);
  }

  // Toogle ENABLE pin in us
  pulse_e();

  // write low bits
  bcm2835_gpio_clr(LCD_D4);
  bcm2835_gpio_clr(LCD_D5);
  bcm2835_gpio_clr(LCD_D6);
  bcm2835_gpio_clr(LCD_D7);
  if ((byte & 0x01) == 0x01) {
    bcm2835_gpio_set(LCD_D4);
  }
  if ((byte & 0x02) == 0x02) {
    bcm2835_gpio_set(LCD_D5);
  }
  if ((byte & 0x04) == 0x04) {
    bcm2835_gpio_set(LCD_D6);
  }
  if ((byte & 0x08) == 0x08) {
    bcm2835_gpio_set(LCD_D7);
  }
  pulse_e();
  }

Ob ein GPIO-Pin auf HIGH oder LOW gesetzt werden soll prüfen die if-Abfragen, indem sie jedes Bit mit dem UND-Operator „&“ prüfen. Ist ((byte & 0x04) == 0x04) wahr, dann ist das dritte Bit gesetzt (0100) und die Datenleitung D6 wird auf HIGH gesetzt.

Welche Register-Bytes welche Kommandos ausführen, liefert das Datenblatt des HD44780. Die Kommandos sind dort zum Beispiel auf Seite 24 aufgelistet. Um Zeichen auf das Display zu bringen, wird gemäß Beschreibung auf Seite 25 der RS-Pin auf HIGH gesetzt und die Datenleitungen entsprechend der Bits des Zeichencodes geschalten. Der Rest ist Fleißarbeit und kann aus dem Quell-Code des C-Programms gelesen werden.

, , , , ,

  1. #1 von Juergen B. am 1. Oktober 2017 - 16:01

    Hallo,
    ich hätte gerne gewusst wo ich die GPIO-Pins für die Ansteuerung des LC-Display ändern kann, da ich bei meinen Testboard andere GPIO’s verwende.
    MfG
    Juergen B.

    • #2 von mnasarek am 3. Oktober 2017 - 18:16

      Hi Jürgen, die Pins für das Programm habe ich in der Header-Datei (*.h) festgelegt. Dort findet sich ein Block mit folgenden Definitionen:
      // Define GPIO pin to LCD pin mapping

      #define LCD_RS RPI_V2_GPIO_P1_26

      #define LCD_RW RPI_V2_GPIO_P1_07

      #define LCD_E RPI_V2_GPIO_P1_24

      #define LCD_D4 RPI_V2_GPIO_P1_22

      #define LCD_D5 RPI_V2_GPIO_P1_18

      #define LCD_D6 RPI_V2_GPIO_P1_16

      #define LCD_D7 RPI_V2_GPIO_P1_12

      RPI_V2_GPIO_P1_24 heißt, Pin-header 1 gpio Nummer 24. Das kannst du so anpassen, dass es für Dich passt.

      • #3 von Juergen B. am 4. Oktober 2017 - 13:23

        Hallo,
        ich verwende für den Anschluss RS = GPIO04 und für den Anschluss E = GPIO17.
        Dabei erhalte ich folgende Meldung:
        RPI_V2_GPIO_P1_04 undeclared (…..
        RPI_V2_GPIO_P1_17 undeclared (….
        Auch bei E = GPIO25 erhalte ich diese Meldung.
        Warum kann ich diese GPIO’s nicht verwenden ?
        Für die Datenleitungen verwende ich GPIO18,22,23,24, das scheint zu funktionieren.
        MfG
        Juergen B.

      • #4 von mnasarek am 4. Oktober 2017 - 14:29

        Welchen RasPi setzt du denn ein? Ich schaue mir das an.

      • #5 von Juergen B. am 4. Oktober 2017 - 15:51

        Hallo,
        ich benutze den Raspberry Pi Modell B, Siftleiste 40-polig, Broadcom BCM2836 (hier könnte der Fehler liegen ?)
        MfG
        Juergen B.

      • #6 von mnasarek am 4. Oktober 2017 - 17:43

        Ok. Den habe ich auch. Ich schaue dann noch mal in die alternative Belegung ob das unter bestimmten Umständen anders zugewiesen wird.

  1. Die serielle Schnittstelle (UART) in Skripten programmieren | Raspberry Pi Lab

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s

%d Bloggern gefällt das: