LCD-Display für den Raspberry Pi

Programmierung des HD44780-Controllers zur Anzeige von Text auf einem 4×20 Zeichen LCD Display.

Der gängigste Controller der mehrzeiligen Zeichen-Displays ist der HD44780. Es gibt verschiedene Displays (2*8, 2*16, und 4*20), die über diesen Controller angesteuert werden. In diesem Tutorial wird der Aufbau eines 20×4 Zeichen Displays und dessen Programmierung mit Phyton und Java gezeigt.

 

 

Das HD44780 Display

Anschlussplan

Aufbau

Das Display verfügt über 16 Kontakte. Es kann sowohl über 4 Bit als auch über 8 Bit gesteuert werden. Um GPIO-Pins zu sparen, habe ich mich für die 4-Bit-Variante entschieden. Somit müssen auch 4 Kabel weniger angelötet werden. Die Belegung der 16 Kontakte lautet wie folgt:

  1. Vss: Versorgungsspannung GND (Masse).
  2. Vcc: Versorgungsspannung +5V.
  3. V0: Kontrastspannung. Hier kann eine Spannung zwischen 0 und 5 Volt angelegt werden, um den Kontrast festzulegen.
  4. RS: Register. Hierüber erfolgt die Wahl des Registers. 0: Befehlsregister, 1: Datenregister.
  5. R/W: Read/Write. Das Display kann auch Daten ausgeben. Dies ist für unsere Zwecke nicht nötig und sogar gefährlich. Die GPIO-Pins des Pis vertragen nur 3,3 Volt, der Output des LCD-Moduls liegt aber bei 5 Volt, also eine gute Quelle für einen Überspannungsschaden.
  6. E: Enable (Takt).
  7. Datenleitung 0. Brauchen wir nicht, weil wir im 4-Bit-Modus arbeiten.
  8. Datenleitung 1. Brauchen wir nicht, weil wir im 4-Bit-Modus arbeiten.
  9. Datenleitung 2. Brauchen wir nicht, weil wir im 4-Bit-Modus arbeiten.
  10. Datenleitung 3. Brauchen wir nicht, weil wir im 4-Bit-Modus arbeiten.
  11. Datenleitung 4.
  12. Datenleitung 5.
  13. Datenleitung 6.
  14. Datenleitung 7.
  15. A: Anode der LED-Hintergrundbeleuchtung.
  16. K: Kathode der LED-Hintergrundbeleuchtung.

Für den Aufbau benötigt man ein Breadboard, einige Kabel, Lötsifte für das Display und einen Lötkolben mit dem nötigen Zubehör. Grundlegende Kenntnissse im Bereich Elektrotechnik sollte man mitbringen. Ein Kurzschluss bzw eine Überspannung an den GPIO-Pins zerstört den Raspberry Pi. Deshalb die Schaltung, bevor man Spannung anlegt, nochmals auf richtige Verkabelung überprüfen.

Eine gute Beschreibung des HD44780-Controllers finden Sie [hier].

Phyton Script


#!/usr/bin/python
import time
import RPi.GPIO as GPIO

# Zuordnung der GPIO Pins (ggf. anpassen)
DISPLAY_RS = 7
DISPLAY_E  = 8
DISPLAY_DATA4 = 25 
DISPLAY_DATA5 = 24
DISPLAY_DATA6 = 23
DISPLAY_DATA7 = 18




DISPLAY_WIDTH = 40   # Zeichen je Zeile
DISPLAY_LINE_1 = 0x80   # Adresse der ersten Display Zeile
DISPLAY_LINE_2 = 0xC0   # Adresse der zweiten Display Zeile
DISPLAY_CHR = True
DISPLAY_CMD = False
E_PULSE = 0.00005
E_DELAY = 0.00005

def main():
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(DISPLAY_E, GPIO.OUT)
  GPIO.setup(DISPLAY_RS, GPIO.OUT)
  GPIO.setup(DISPLAY_DATA4, GPIO.OUT)
  GPIO.setup(DISPLAY_DATA5, GPIO.OUT)
  GPIO.setup(DISPLAY_DATA6, GPIO.OUT)
  GPIO.setup(DISPLAY_DATA7, GPIO.OUT)

  display_init()

  lcd_byte(DISPLAY_LINE_1, DISPLAY_CMD)
  lcd_string("Hallo")
  lcd_byte(DISPLAY_LINE_2, DISPLAY_CMD)
  lcd_string("Welt!")

  time.sleep(5)

  lcd_byte(DISPLAY_LINE_1, DISPLAY_CMD)
  lcd_string("Dein Display")
  lcd_byte(DISPLAY_LINE_2, DISPLAY_CMD)
  lcd_string("funktioniert! :)")  

  time.sleep(5)
  GPIO.cleanup()





def display_init():
  lcd_byte(0x33,DISPLAY_CMD)
  lcd_byte(0x32,DISPLAY_CMD)
  lcd_byte(0x28,DISPLAY_CMD)
  lcd_byte(0x0C,DISPLAY_CMD)  
  lcd_byte(0x06,DISPLAY_CMD)
  lcd_byte(0x01,DISPLAY_CMD)  

def lcd_string(message):
  message = message.ljust(DISPLAY_WIDTH," ")  
  for i in range(DISPLAY_WIDTH):
    lcd_byte(ord(message[i]),DISPLAY_CHR)

def lcd_byte(bits, mode):
  GPIO.output(DISPLAY_RS, mode)
  GPIO.output(DISPLAY_DATA4, False)
  GPIO.output(DISPLAY_DATA5, False)
  GPIO.output(DISPLAY_DATA6, False)
  GPIO.output(DISPLAY_DATA7, False)
  if bits&0x10==0x10:
    GPIO.output(DISPLAY_DATA4, True)
  if bits&0x20==0x20:
    GPIO.output(DISPLAY_DATA5, True)
  if bits&0x40==0x40:
    GPIO.output(DISPLAY_DATA6, True)
  if bits&0x80==0x80:
    GPIO.output(DISPLAY_DATA7, True)
  time.sleep(E_DELAY)    
  GPIO.output(DISPLAY_E, True)  
  time.sleep(E_PULSE)
  GPIO.output(DISPLAY_E, False)  
  time.sleep(E_DELAY)      
  GPIO.output(DISPLAY_DATA4, False)
  GPIO.output(DISPLAY_DATA5, False)
  GPIO.output(DISPLAY_DATA6, False)
  GPIO.output(DISPLAY_DATA7, False)
  if bits&0x01==0x01:
    GPIO.output(DISPLAY_DATA4, True)
  if bits&0x02==0x02:
    GPIO.output(DISPLAY_DATA5, True)
  if bits&0x04==0x04:
    GPIO.output(DISPLAY_DATA6, True)
  if bits&0x08==0x08:
    GPIO.output(DISPLAY_DATA7, True)
  time.sleep(E_DELAY)    
  GPIO.output(DISPLAY_E, True)  
  time.sleep(E_PULSE)
  GPIO.output(DISPLAY_E, False)  
  time.sleep(E_DELAY)   

if __name__ == '__main__':
  main()
  

Java mit Pi4J


package myLCD;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class Display {
  
  // LCD Commands
  private static final int LCD_CLEARDISPLAY = 0x01;
  
  public final static int LCD_ROW_1 = 0x80;
  public final static int LCD_ROW_2 = 0xC0;
  public final static int LCD_ROW_3 = 0x94;
  public final static int LCD_ROW_4 = 0xD4;

  private final static int LCD_COLUMNS = 20;
  private final static int LCD_BITS = 4;

  private final static int BEFEHLS_REGISTER = 0;
  private final static int DATEN_REGISTER = 1;
  private final static int SLEEP_TIME = 1;
  
  final GpioController gpio = GpioFactory.getInstance();
  GpioPinDigitalOutput rs, e, d1, d2, d3, d4;

  public Display() {

    rs = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_11, "Register",
        PinState.LOW); // 0->Befehlsregister, 1->Datenregister
    e = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_10, "MyLED",
        PinState.LOW); // Taktleitung

    d1 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_06, "Datenbit1",
        PinState.LOW); //
    d2 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_05, "Datenbit2",
        PinState.LOW); // GPIO 22, Board-Nr=15
    d3 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_04, "Datenbit3",
        PinState.LOW); // GPIO 13, Board-Nr=16
    d4 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01, "Datenbit4",
        PinState.LOW); // GPIO 14, Board-Nr=18
    lcdInit();
  }

  private void lcdInit() {
    lcdByte(0x33, BEFEHLS_REGISTER);//?
    lcdByte(0x32, BEFEHLS_REGISTER);//?
    lcdByte(0x28, BEFEHLS_REGISTER);//FunctionSet, DL=0 -> 4 Bit; N=1 -> 2Lines
    lcdByte(LCD_CLEARDISPLAY, BEFEHLS_REGISTER);//Clear
    lcdByte(0xC, BEFEHLS_REGISTER);//Display On, Cursor aus, Blinken aus
    lcdByte(0x06, BEFEHLS_REGISTER);//Entry-Mode-Set, I/D=1 -> increment
    lcdByte(LCD_CLEARDISPLAY, BEFEHLS_REGISTER);//Clear
  }

  public synchronized void writeString(int zeile, String message) {
    lcdByte(zeile, BEFEHLS_REGISTER);//Zu schreibende Zeile im Display setzen
    char[] chars = message.toCharArray();
    for (char c : chars) {
      lcdByte(c, DATEN_REGISTER);
    }
  }

  private synchronized void lcdByte(int bits, int mode) {
    if (mode == BEFEHLS_REGISTER) {
      rs.low();// Befehl
    } else {
      rs.high();// Daten
    }
    // Datenleitungen auf low setzen
    d4.low();
    d3.low();
    d2.low();
    d1.low();

    // Ein Byte wird mit zweimal 4 Bit uebertragen. Zuerst wird der High- ,
    // dann der Low-Nibble übertragen
    // High-Nibble (Bits D7 - D4)
    if ((bits & 16) == 16) {
      d1.high();
    }
    if ((bits & 32) == 32) {
      d2.high();
    }
    if ((bits & 64) == 64) {
      d3.high();
    }
    if ((bits & 128) == 128) {
      d4.high();
    }

    try {
      Thread.sleep(SLEEP_TIME);// 10ms warten

      // Taktleitung auf HIGH und anschliessend auf LOW setzen (Uebernahme
      // der 4 Datenbits)
      e.high();
      ;
      Thread.sleep(SLEEP_TIME);// 10ms warten
      e.low();

      // Datenleitungen auf low setzen
      d4.low();
      d3.low();
      d2.low();
      d1.low();

      // LOW-Nibble (Bits D3 - D0)
      if ((bits & 1) == 1) {
        d1.high();
      }
      if ((bits & 2) == 2) {
        d2.high();
      }
      if ((bits & 4) == 4) {
        d3.high();
      }
      if ((bits & 8) == 8) {
        d4.high();
      }

      Thread.sleep(10);// 10ms warten

      // Taktleitung auf HIGH und anschliessend auf LOW setzen (Uebernahme
      // der 4 Datenbits)
      e.high();
      
      Thread.sleep(SLEEP_TIME);// 10ms warten
      e.low();
      Thread.sleep(SLEEP_TIME);// 10ms warten
    } catch (InterruptedException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }
  }
}
    

CPU-Temperatur anzeigen


package myLCD;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import myLCD.Display;

public class RaspCPU_Temperatur extends Thread {
  private Display display;
  private Process result;
  private BufferedReader output;
  private String line;
  private String[] lines;
  private int lcdRow;

  public RaspCPU_Temperatur(Display d, int lcdRow) {
    this.display = d;
    this.lcdRow = lcdRow;
  }

  @Override
  public void run() {
    while (true) {
      try {
        result = Runtime.getRuntime().exec(
            "/opt/vc/bin/vcgencmd measure_temp");
        output = new BufferedReader(
            new InputStreamReader(result.getInputStream()));
        line = output.readLine();
        lines = line.split("=");
        // System.out.println(lines[1]);
        display.writeString(lcdRow, "Prozessortemp: "
            + lines[1]);
        yield();
        sleep(10000);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

  

CPU-Temperatur anzeigen main()


package myLCD;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.NetworkInterface;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.StringTokenizer;

import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;

public class TestDisplay {

  public static void main(String[] args) throws Exception {
    Display display = new Display();

    Process result = Runtime.getRuntime().exec("netstat -rn");
    BufferedReader output = new BufferedReader(new InputStreamReader(
        result.getInputStream()));
    String line = output.readLine();
    while (line != null) {
      if (line.startsWith("0") == true)
        break;
      line = output.readLine();
    }
    StringTokenizer st = new StringTokenizer(line);
    st.nextToken();
    String gateway = st.nextToken();
    display.writeString(Display.LCD_ROW_2, "GW: " + gateway);

    //Raspberry CPU Temperatur auf Display schreiben
    RaspCPU_Temperatur cpuTemp = new RaspCPU_Temperatur(display, Display.LCD_ROW_2);
    cpuTemp.start();
    
    
    // Temperatur in Zeile 3 schreiben
    new TemperaturMitPCF5891(display, Display.LCD_ROW_3);

    SimpleDateFormat datumFormatter = new SimpleDateFormat("dd.MMM.YYYY");
    SimpleDateFormat zeitFormatter = new SimpleDateFormat("HH:mm:ss");
    String datum = datumFormatter.format(new Date());
    // update time
    while (true) {      
      display.writeString(Display.LCD_ROW_4,
          datum + " " + zeitFormatter.format(new Date()));
      Thread.sleep(750);
    }
  }
}