Wer mit dem Raspberry Pi rumspielt, hat ja meist seine ganz eigenen Ideen, was er oder sie anstellen will. Eines meiner ersten Projekte sollte die Nutzung eines kleinen Displays am Pi sein.
Nachdem ich meine ersten Versuche mit dem Pi gemacht hatte, und Reichelt mir dankenswerterweise ungefähr 2kg toten Baum (f.k.a. den Katalog) zugeschickt hatte, fand ich beim Stöbern die Displays von Electronic Assembly, genauer gesagt die DOG-Serie: Das sind kleine, “grafische” Displays in verschiedenen Größen bzw. Auflösungen.
Mein ursprünglicher Plan, ein Standard 16×2-Zeichen-Display für den ersten Test zu verwenden wurde also über Bord geschmissen und ich machte meine ersten Gehversuche mit dem EA-DOGM mit einer Auflösung von 128×64 Pixeln.
Im folgenden Artikel möchte ich dieses Projekt vorstellen.
Planung
Bevor man da so richtig ans Basteln geht, steht erstmal Planung im Vordergrund. Ein Blick in das Datenblatt des EA-DOGM zeigt, dass man zum Betrieb zwei Modi verwenden kann: Erstens den sogenannten single supply, bei dem die Stromversorgung für das Display mit 3,3V erfolgt oder aber den dual supply, der eine externe Spannungsquelle mit ~12V erfordert. Für den Betrieb mit dem Raspberry Pi bietet sich der single supply geradezu an, da man über die GPIO-Pins des Pi ja genau 3,3V beziehen kann. Das wäre also schon mal geklärt.
In diesem Modus werden 9 Kondensatoren mit jeweils 1µF benötigt. Im Schaltplan des Datenblattes sind “normale” Kondensatoren eingezeichnet, aber da ich ein Elektronik-DAU bin, habe ich bei Reichelt leider Elektrolytkondensatoren bestellt. Zum Glück funktioniert das Display auch damit, wenn man die Elkos in der richtigen Polung nutzt. Mehr dazu im Schaltplan.
Bei den DOG-Displays wird eine LED-Hintergrundbeleuchtung empfohlen. Dafür werden ggf. noch Vorwiderstände benötigt (siehe auch dazu das Datenblatt). Mein Testaufbau benutzt das Display DOGM128W-6, welches auch ohne Beleuchtung auskommt (ich habe allerdings eine eingebaut, für die Zukunft).
Hardware – Display verlöten
Hat man alles zusammen, entsteht folgender Schaltplan, der quasi 1:1 dem Datenblatt entnommen ist.
Aus dem Schaltplan sollte man sich jetzt zumindest eine Skizze für das Platinen-Layout generieren, meine sah dann so aus:
Bitte beachtet, dass ich axiale Elkos gekauft hatte, weil ich sie ursprünglich liegend verbauen wollte. Aus Platzgründen sind sie nun doch stehend und benötigen daher den oben zu sehenden Platz!
Ok, soldering time. Das Problem an dem Dasein als IT-Nerd ist die Tatsache, dass man zwar programmieren kann, aber nicht löten. Daher dauerte das Löten der Platine ungefähr 12 (in Worten: ZWÖLF) Stunden. Aber wie gesagt, ich übe noch.
Zeit für eine Sieges-Zigarre!
Anschluss an den Raspberry Pi
Ok, verkabeln wir das Baby! Durch den Pin-Header können wir das Display nun relativ einfach mit dem Raspberry Pi verbinden. Ich habe dafür Female-Female-Jumperkabel verwendet. Den Anschluss für die Spannungsversorgung habe ich mit Pin 1 verbunden, den Masse-Anschluss mit Pin 6. Die Datenleitungen habe ich mit den Pins 16, 18, 22, 24 und 26 verbunden. Laut Dokumentation des Displays handelt es sich um ein SPI-Interface, allerdings können wir für unsere Zwecke jeden GPIO-Pin verwenden.
Bitte beachtet, dass meine Schaltung noch mehr Pins enthält, die für den Betrieb des Displays keine direkte Bedeutung haben. Es handelt sich dabei um eine Schaltung, um den Pi mit Strom zu versorgen. Ignorieren wir das an dieser Stelle einfach mal.
Zeit alles einzuschalten! Verbindet den Pi mit dem Netzteil und bootet euer System. In meinem Fall ist das ein Raspbian, aber jedes Betriebssystem sollte funktionieren.
Erstellt am Besten einen Ordner, um die ganzen Files nicht aus dem Auge zu verlieren und erstellt darin eine Datei demo.py. Zunächst wollen wir eine saubere Arbeitsumgebung erzeugen, deswegen erstellen wir ein Skript als Wrapper:
[python]#!/usr/bin/python
# coding: utf-8
import sys
import signal
import RPi.GPIO as GPIO
def handler(sig, frame):
print ‘You pressed Ctrl+C, I\’m cleaning up GPIO…’
GPIO.cleanup()
sys.exit(0)
if __name__ == ‘__main__’:
signal.signal(signal.SIGINT, GPIO.cleanup)
print ‘Demo finished!’
GPIO.cleanup()[/python]
Nun aber an’s Eingemachte: Ich habe als Startpunkt bereits eine fertige Python-Klasse gefunden, und zwar hier. Diese wurde adaptiert und als Ausgangspunkt für die weitere Entwicklung genutzt. Zunächst erstellen wir eine Klasse DOG und benutzen den Konstruktor, um die GPIO-Pins zu initialisieren. Als erste Operation müssen wir das LCD initialisieren (mit der Initialisierungssequenz laut Datenblatt). Dazu brauchen wir natürlich erstmal die Möglichkeit, Befehle und Daten an das Display zu senden.
[python]class DOG():
"""
"""
def __init__(self, si, clk, a0, cs, res):
"""
"""
self.si = si
self.clk = clk
self.a0 = a0
self.cs = cs
self.res = res
GPIO.setmode(GPIO.BOARD)
GPIO.setup(self.si, GPIO.OUT)
GPIO.setup(self.clk, GPIO.OUT)
GPIO.setup(self.a0, GPIO.OUT)
GPIO.setup(self.cs, GPIO.OUT)
GPIO.setup(self.res, GPIO.OUT)
def send_byte(self, data):
"""
"""
if type(data) == type(‘a’):
data = ord(data)
GPIO.output(self.cs, 0)
for i in range(8):
if data&128 > 0:
GPIO.output(self.si, 1)
else:
GPIO.output(self.si, 0)
data = data<<1
GPIO.output(self.clk, 0)
time.sleep(0.00001)
GPIO.output(self.clk, 1)
time.sleep(0.00001)
GPIO.output(self.cs, 1)
time.sleep(0.01)
def send_cmd(self, cmd):
"""
"""
GPIO.output(self.a0, 0)
self.send_byte(cmd)
time.sleep(0.001)
def send_cmd_seq(self, seq):
"""
"""
for i in seq:
self.send_cmd(i)
def send_data(self, data):
"""
"""
GPIO.output(self.a0, 1)
self.send_byte(data)
def send_data_seq(self, seq):
"""
"""
for i in seq:
self.send_data(i)[/python]
Ob gesendete Daten reine Nutzdaten oder etwa ein Befehl sind, wird durch den Pegel am Eingang A0 des Displays festgelegt: Bei Daten liegt dort ein high-Pegel an, bei Befehlen ein low-Pegel.
Wir senden nun die Initialisierungssequenz, dafür erstellen wir aber eine weitere Methode in der Klasse:
[python]
def init_lcd(self):
"""
"""
GPIO.output(self.res, 0)
time.sleep(0.01)
GPIO.output(self.res, 1)
time.sleep(0.01)
init_seq = [ 0x40, 0xA1, 0xC0, 0xA6, 0xA2, 0x2F, 0xF8, 0x00, 0x27, 0x81, 0x16, 0xAC, 0x00, 0xAF ]
self.send_cmd_seq(init_seq)[/python]
Diese Methode rufen wir einfach im Konstruktor noch auf. Für den Start wäre es noch gut, wenn wir das Display komplett leeren könnten, schließlich wollen wir doch eine weiße Leinwand.
Dazu möchte ich kurz erklären, wie das Display mit Daten umgeht und wie man eigentlich an eine bestimmte Stelle etwas schreiben kann.
Das Display hat eine Auflösung von 128×64 Bildpunkten. Diese sind aber nicht komplett einzeln adressierbar. Das Display ist in 8 Zeilen, sogenannte Pages eingeteilt. Jede Page umfasst 8 Zeilen, in Pixeln gesprochen. Die Spalten, Columns genannt, sind einzeln addressierbar. Schreibt man an eine bestimmte Adresse, also Page und Column ein Byte, wird dies bitweise von unten betrachtet auf die Bildpunkte geschrieben.
Wir erstellen uns eine Methode, mit der wir den internen Cursor des Displays bewegen können und gleich noch eine Methode, mit der wir das gesamte Display nullen, also leeren.
[python]
def set_pos(self, page, column):
"""
"""
self.send_cmd(0xB0 + page)
self.send_cmd(0x10 + ((column&0xF0)>>4))
self.send_cmd(0x00 + (column&0x0F))
def clear_lcd(self):
"""
"""
for i in range(8):
self.set_pos(i, 0)
self.send_data_seq([0x00]*128)[/python]
Jetzt haben wir alles zusammen, um die Demo füllen zu können. Hier nochmal die gesamte Klasse:
[python]class DOG():
"""
"""
def __init__(self, si, clk, a0, cs, res):
"""
"""
self.si = si
self.clk = clk
self.a0 = a0
self.cs = cs
self.res = res
GPIO.setmode(GPIO.BOARD)
GPIO.setup(self.si, GPIO.OUT)
GPIO.setup(self.clk, GPIO.OUT)
GPIO.setup(self.a0, GPIO.OUT)
GPIO.setup(self.cs, GPIO.OUT)
GPIO.setup(self.res, GPIO.OUT)
self.init_lcd()
self.clear_lcd()
def set_pos(self, page, column):
"""
"""
self.send_cmd(0xB0 + page)
self.send_cmd(0x10 + ((column&0xF0)>>4))
self.send_cmd(0x00 + (column&0x0F))
def clear_lcd(self):
"""
"""
for i in range(8):
self.set_pos(i, 0)
self.send_data_seq([0x00]*128)
def init_lcd(self):
"""
"""
GPIO.output(self.res, 0)
time.sleep(0.01)
GPIO.output(self.res, 1)
time.sleep(0.01)
init_seq = [ 0x40, 0xA1, 0xC0, 0xA6, 0xA2, 0x2F, 0xF8, 0x00, 0x27, 0x81, 0x16, 0xAC, 0x00, 0xAF ]
self.send_cmd_seq(init_seq)
def send_byte(self, data):
"""
"""
if type(data) == type(‘a’):
data = ord(data)
GPIO.output(self.cs, 0)
for i in range(8):
if data&128 > 0:
GPIO.output(self.si, 1)
else:
GPIO.output(self.si, 0)
data = data<<1
GPIO.output(self.clk, 0)
time.sleep(0.00001)
GPIO.output(self.clk, 1)
time.sleep(0.00001)
GPIO.output(self.cs, 1)
time.sleep(0.01)
def send_cmd(self, cmd):
"""
"""
GPIO.output(self.a0, 0)
self.send_byte(cmd)
time.sleep(0.001)
def send_cmd_seq(self, seq):
"""
"""
for i in seq:
self.send_cmd(i)
def send_data(self, data):
"""
"""
GPIO.output(self.a0, 1)
self.send_byte(data)
def send_data_seq(self, seq):
"""
"""
for i in seq:
self.send_data(i)[/python]
Und nun erstellen wir mal eine Demo, die ein paar Linien auf das Display malt:
[python]
if __name__ == ‘__main__’:
signal.signal(signal.SIGINT, GPIO.cleanup)
lcd = DOG(16, 18, 22, 26, 24)
for i in range(8):
data = 1
for j in range(4):
lcd.set_pos(i, 0)
lcd.send_data_seq([data]*128)
data += pow(4, j+1)
for i in range(64):
for j in range(8):
lcd.set_pos(j, i*2)
lcd.send_data_seq([ 0xFF, 0x00 ])
lcd.clear_lcd()
print ‘Demo finished!’
GPIO.cleanup()
[/python]
So sieht das Ganze bei mir aus:
Ihr müsst den Code natürlich nicht unbedingt hier abtippen, ich habe auf GitHub ein entsprechendes Repository! 😉
Dies beendet zunächst das Experiment, ein DOGM an den Raspberry Pi anzuschließen! Im Repository liegt noch etwas mehr Code, zum Beispiel um Text auf dem Display auszugeben, oder auch Grafiken. Dies werde ich in einem weiteren Blog behandeln, um die entsprechenden Methoden zu erläutern.
Ich hoffe, ihr hattet ähnlich viel Erfolg bei eurem Experiment! Viel Spaß mit eurem Pi und eurem Projekt!
Hallo
_MOSI = 23 # PIN 36
DOG_CLK = 25 # PIN 37
DOG_A0 = 22 # PIN 38
DOG_CS = 24 # PIN 40
DOG_RES = 17 # PIN 39
du sagst
Datenleitungen habe ich mit den Pins 16, 18, 22, 24 und 26 verbunden.
aber welcer ist welcer ?
Gruss
Hallo, die EA DOG-Serie zeichnet sich durch die Eigenschaft aus, dass sie über den I2C-Bus angesteuert werden kann. Vom Aufwand her ist das eine serielle Schnittstelle auf 2 Drähten plus Stromversorgung. Zudem können durch die Adressierbarkeit 4 Display angesteuert werden. Das blockiert auch keine weiteren GPIO-Ports. EA hat dazu einen C-Code zur Initialisierung bereitgestellt http://www.lcd-module.de/fileadmin/downloads/development%20service/DOGXL240_UC1611S/DOGXL240_UC1611S.zip. Pech nur, weil ich das aufgrund fehlender Kenntnisse nicht für den RasPi umsetzen kann.
Guten Abend!
Ich habe ein Problem mit meinem Attiny4313 und dem EA DOGM128-6.
muss ich beim Anschließen (Verdrahten ) was beachten?
Meine Hintergrund Beleuchtung funktioniert alles andere,….. Schweigen im Walde.
ICh wollte mir die Franzis Wetter Station ein wenig Größer Bauen,
Und dafür dann auch ein Größeres Display.
Nur….Sch!……! Sorry.
Hast du werte die ich Messen und Vergleichen könnte?.
Führt das Display im Reset Mode einen Selbstest durch? da könnte man wenigstens Sehen ob sich was tut?