![]() |
Raspberry-Pi-Projekte: Einfaches Text-LCDProf. Jürgen Plate, Christian Steiner, Serhat Erbas, Nguyen Duc Khanh |
Einfache Text-Display mit zwei bis vier Zeile zu je 20 bis 40 Zeichen und HD44780 Controller werden meist parallel angeschlossen. Dazu werden vier oder 8 Datenleitungen und drei Steuerleitungen benötigt. So belegt ein solches Display minimal sieben GPIO_Ports. Deshalb wird hier auf ein Display mit angebauten I2C-Interface beschrieben, das so nur zwei Leitungen braucht. Die Wahl fiel auf das SainSmart IIC/I2C/TWI Serial 2004 20x4 LCD, das über Amazon bestellt wurde. Es gibt aber auch Steckplatinen ohne Display, die ähnliche Funktionaität besitzen.


Um das LCD Display an den Raspberry Pi anzuschliessen sind nur vier Leitungen notwendig:

Um die I2C-Adresse des LCD zu finden, kann der Befehl i2cdetect -y 1 verwendet werden, wozu Root-Berechtigung nötig ist. In diesem Fall ist die I2C-Adresse 0x3F. Für die Programmierung des LCD wurde die Programmiersprache Python verwendet. Es ist auch möglich, das LCD in C oder einer anderen Programmierprache zu betreiben. Um auf dem Raspberry Pi den I2C-Bus verwenden zu können, muss er frei geschatet sein (siehe Raspberry Pi: I2C-Konfiguration und -Programmierung). Der nachfolgende Code-Ausschnitt zeigt ausschnittsweise den I2C-Treiber von Python für grundlegende Funktionen:
import smbus
from time import sleep
class i2c_device:
def __init__(self, addr, port=1):
self.addr = addr
self.bus = smbus.SMBus(port)
# Write a single command
def write_cmd(self, cmd):
self.bus.write_byte(self.addr, cmd)
sleep(0.0001)
# Write a command and argument
def write_cmd_arg(self, cmd, data):
self.bus.write_byte_data(self.addr, cmd, data)
sleep(0.0001)
# Write a block of data
def write_block_data(self, cmd, data):
self.bus.write_block_data(self.addr, cmd, data)
sleep(0.0001)
# Read a single byte
def read(self):
return self.bus.read_byte(self.addr)
# Read
def read_data(self, cmd):
return self.bus.read_byte_data(self.addr, cmd)
# Read a block of data
def read_block_data(self, cmd):
return self.bus.read_block_data(self.addr, cmd)
Das nachfolgende Bild zeigt das Display-Register des HD44780. Zu beachten ist, dass die zweite Zeile des Displays, der dritten Zeile im Register entspricht. Für den Lineshift-Befehl müssen dann die entsprechenden Werte verwendet werden.

Der LCD-Treiber beinhaltet alle Kommandos die das LCD verwendet. Die Kommandos können dazu im Datenblatt des HD44780 nachgelesen werden. Es gibt z. B. Funktionen, um einen Text am Display anzuzeigen oder auch den Inhalt zu löschen. Näheres kann dem Datenblatt des HD44780 entnommen werden. Der LCD-Treiber verwendet dabei den bereits angesprochenen I2C-Treiber. Er wird mittels import lcddriver in eigene Python-Programme eingebunden.
import i2c_lib
from time import sleep
# LCD Address
ADDRESS = 0x3f
# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80
# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00
# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00
# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00
# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00
# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00
En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit
class lcd:
#initializes objects and lcd
def __init__(self):
self.lcd_device = i2c_lib.i2c_device(ADDRESS)
self.lcd_write(0x03)
self.lcd_write(0x03)
self.lcd_write(0x03)
self.lcd_write(0x02)
self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
self.lcd_write(LCD_CLEARDISPLAY)
self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
sleep(0.2)
# clocks EN to latch command
def lcd_strobe(self, data):
self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
sleep(.0005)
self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
sleep(.0001)
def lcd_write_four_bits(self, data):
self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
self.lcd_strobe(data)
# write a command to lcd
def lcd_write(self, cmd, mode=0):
self.lcd_write_four_bits(mode | (cmd & 0xF0))
self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))
# put string function
def lcd_display_string(self, string, line, offset):
if line == 1:
#0x80 | 0x00 = 0x80
#self.lcd_write(0x80 + offset)
self.lcd_write((LCD_SETDDRAMADDR | 0x00) + offset)
if line == 2:
#0x80 | 0x40 = 0xC0
#self.lcd_write(0xC0 + offset)
self.lcd_write((LCD_SETDDRAMADDR | 0x40) + offset)
if line == 3:
#0x80 | 0x14 = 0x94
#self.lcd_write(0x94 + offset)
self.lcd_write((LCD_SETDDRAMADDR | 0x14) + offset)
if line == 4:
#0x80 | 0x54 = D4
#self.lcd_write(0xD4 + offset)
self.lcd_write((LCD_SETDDRAMADDR | 0x54) + offset)
for char in string:
self.lcd_write(ord(char), Rs)
# clear lcd and set to home
def lcd_clear(self):
self.lcd_write(LCD_CLEARDISPLAY)
self.lcd_write(LCD_RETURNHOME)
Der anzuzeigende Text, die Zeile und der Zeilenversatz werden als Kommandozeilenparameter an das Python-Programm übergeben. Zuerst kommt der auszugebene String, dann die gewünscht Zeile (beginnend bei 1) und zum Schluss der Offset in der Zeile (beginnend bei 0).
import lcddriver
import sys
lcd = lcddriver.lcd()
try :
lcd.lcd_display_string(str(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]))
except:
print("Usage: \"Output-String\" \"Line in decimal\" \"Offset in decimal\" ")
print("Example: \"Hello World!\" 1 0")
import lcddriver
import threading
from datetime import datetime
from time import sleep
threads = []
class DisplayTimeThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.running = True
def run(self):
while self.running:
try:
self.printTime()
sleep(0.25)
except:
self.running = False
def printTime(self):
lcd.lcd_display_string(datetime.now().strftime('%b %d'), 2, 7)
lcd.lcd_display_string(datetime.now().strftime('%H:%M:%S'), 3, 6)
#END DisplayTimeThread(threading.Thread):
#MAIN PROGRAM
lcd = lcddriver.lcd()
lcd.lcd_clear()
timeThread = DisplayTimeThread()
threads.append(timeThread)
timeThread.start()
for t in threads:
t.join()