Make Magazin 3/2017
S. 64
Grundlagen
Aufmacherbild

Im Display-Library-Labyrinth

Als wir unserem beliebten Projekt Shootduino ein zweites Leben schenken wollten, erlebten wir unser blaues Wunder bei Beschaffung und Inbetriebnahme des passenden Displays. Jetzt räumen wir die Stolpersteine zum Einsatz dieser (und anderer) OLEDs für Sie aus dem Weg.

Wenn Sie schon mal eine unserer Maker Faires besuchten, dann haben Sie bestimmt auch schon am Shootduino gespielt. Neben dem Asteroids-Clone mit Bildröhre im Steam-Punk-Design lockt die kleine Spielkonsole immer wieder neue Besucher an. Auch wenn jedes Smartphone bessere Grafik und mehr Auswahl bietet: das kleine Spiel fasziniert. Unsere Idee war , eine Platine anzubieten, sodass der Kabelverhau entfällt und es Einsteigern (mit wenig Löterfahrung) leichter gelingt, ihr erstes Arduino-Projekt zu realisieren. Auch hoffen wir, dass sich dann doch noch Leser finden, die sich berufen fühlen, eigene Mini-Spiele zu entwickeln, denn wir hatten damals davon geträumt, dass unsere Leser viele tolle Ideen haben und diese miteinander über unser Forum oder anderweitig austauschen.

Platinenentwurf

Der Entwurf einer Platine für das einfache Projekt ist keine große Herausforderung. Da ich mit dem ursprünglichen Projekt nicht vertraut war, habe ich mich am Artikel orientiert und mich nach dem dort genannten 0,96"-OLED-Display (Organic Light Emitting Diode) umgesehen. OLED-Bildschirme bieten gegenüber den herkömmlichen Flüssigkristalldisplays (Liquid Crystal Display, LCD) den Vorteil eines sehr hohen Kontrasts, da sie ohne Hintergrundbeleuchtung auskommen. Nichtleuchtende Flächen sind wirklich schwarz und selbst bei hellem Tageslicht sind die leuchtenden Punkte sehr gut zu erkennen. Hauptnachteil ist der relativ hohe Preis gegenüber anderen Typen.

Monochrome (schwarz/weiß) 1,3"-OLED von Adafruit (Artikel-Nummer 938)

Das erste Problemchen kam auf, als sich die Frage nach den Abmessungen des Displays und der Platzierung der Bohrlöcher und Pins stellte. Vernünftig wäre es gewesen, sich das passende Display zu besorgen und nachzumessen. Stattdessen habe ich die Abmessungen benutzt, die auf irgendeiner Webseite standen. Im ursprünglichen Artikel kamen zwei Displays zum Einsatz. Es wird die kleine Variante angesprochen, aber der Autor zeigt auch ein Display mit 1,3 Zoll, welches auch auf unserem Exponat für die Maker Faires zum Einsatz kommt. Und damit begann die Odyssee in die Welt der Mini-OLED-Displays und Arduinos Konzept zur Code-Verwaltung.

Erstes falsches Display

Erstaunlich ist es, dass es genau die gesuchte OLED (Artikel-Nummer 326 bei Adafruit für die 0,96"-Variante) und auch die große Variante nicht aus alternativen Quellen wie ebay, Sparkfun oder bei Amazon gibt. Bei ebay und anderen Anbietern finden sich aber durchaus OLEDs in der gewünschten Größe und mit Ansteuerung über I2C (Inter-Integrated Circuit, IIC). Die von Adafruit lassen sich zusätzlich noch per SPI (Serial Peripheral Interface) ansteuern, was aber im konkreten Fall nicht genutzt wird. Aus welcher Quelle die Abmessungen für das Display stammten, die ich für die Platine nutzte, ist im Nachhinein nicht mehr festzustellen gewesen. Fakt ist aber, dass es zum abgebildeten Footprint führte.

Platine für den Shootduino mit Bestückungsdruck für 0,96"-Display
Zuerst montiertes 0,96"-Display – anfangs noch ohne Funktion. Auf die falsche Darstellung wird im Text noch eingegangen.

Als die Platinen ankamen, sollte das in der Zwischenzeit besorgte Display montiert werden. Die horizontale Position der Bohrlöcher für die Befestigung der kleinen Platine passten schon mal nicht, was aber noch zu verkraften gewesen wäre, denn die hätten nachträglich noch gebohrt werden können. Nachdem alles bestückt war, wollte das Display aber schlichtweg nichts anzeigen. Als Fehlerursache kommen natürlich einige Punkte wie Platinenfehler, defektes Display oder Fehler bei der Arduino-Programmierung in Frage, aber auf das Naheliegende kommt man oft erst später. An dieser Stelle die Frage an Sie: Finden Sie den Fehler anhand der Fotos auf dieser Seite? Immerhin dürften Sie schon eine Vorahnung haben, wo er zu finden ist, angesichts des Themas, um das es hier geht.

Die Lösung und ein neues Problem

Das einfache Display rechts wurde durch die Verpolung beschädigt.

Es ist trivial und deshalb so leicht zu übersehen: Die Anschlüsse für VCC (Versorgungsspannung) und Masse (GND) sind vertauscht. Zum Glück ist nichts passiert und weder Display noch Arduino Uno sind abgeraucht. Im Nachhinein ist es natürlich völlig klar, dass man die Pins kontrollieren sollte. Um diese Erkenntnis schlauer, wird ein neues Display besorgt beziehungsweise aus der Bastelkiste gekramt – dieses Mal mit der „richtigen“ Pinanordnung. Um eins vorwegzunehmen: Bei allen probierten Displays sind die zwei Pins für I2C nie vertauscht gewesen – warum auch immer das so ist.

Das gelieferte 1,3"-Display und SH1106 mit falscher Pinanordnung – anders als im Angebot. Macht es da noch etwas, dass es blaue anstatt weiße Pixel sind?

Beim Ausprobieren hat sich dafür zwischendurch eines der Displays verabschiedet. Einmal falsch gepolt an die Betriebsspannung gehängt, und es roch unangenehm und das Display war in die ewigen Jagdgründe eingegangen. Ein Blick auf die Rückseite zeigt, dass sich nicht nur die Pinbelegung unterscheidet, sondern auch die Elektronik. Beim defekten Display gibt es nur ein paar Widerstände und Kondensatoren für die Ladungspumpen, um die OLED-Technik zu betreiben. Auf dem anderen Display befindet sich etwas mehr Technik. Eine Schutzdiode für Bruchteile von Cents auf der Display-Platine hätte uns vor Schaden bewahren können.

Sobald ein Display mit der richtigen Pinanordnung eingebaut war, wurde dann immerhin sofort etwas angezeigt. Das Adafruit-Logo (welches im Spiel beibehalten wurde, weil deren Bibliotheken (Libraries) benutzt wurden) war sichtbar. Allerdings wurde nur die obere Hälfte vom Logo und auch des Spielfeldes gezeigt. Es sah so aus, als würde jede zweite Displayzeile übersprungen werden.

Bibliotheken-Irrsinn

Was jetzt folgte, war eine langwierige Odyssee durch die Bibliotheken, die für das Spiel genutzt werden. Es müssen die passenden Libraries installiert und vor allem angepasst werden. Wir haben uns entschieden, Ihnen in einem Online-Artikel zu zeigen, wie Sie mit Bibliotheken arbeiten und wo Sie im konkreten Fall die Einstellungen finden.

Zu klein und falsch kopiert

Jetzt kam die Präsentation bei den Redaktions-Kollegen und die machten keinen Hehl daraus, dass ihnen das Display zu winzig sei. Es gibt zwar nicht viel zu sehen, aber das wollten sie größer haben. Kein Problem, denn es gibt ja auch noch die 1,3"-Variante. Das Adafruit-Original passt nicht auf die Platine, da es acht Pins besitzt, weil es neben I2C auch noch SPI beherrscht. Deutsche Händler offerierten zwar das Display mit der gewünschten Pinanordnung mussten dann aber eingestehen, dass sie nicht liefern können. Also wurden gleich 20 Stück in China per Express-Lieferung bestellt. Wieder einmal zeigte sich, dass Chinesen einfach alles kopieren – auch die Fotos für die Online-Angebote. Es stellte sich nämlich prompt heraus, dass die gelieferte Ware nicht der Abbildung entsprach und wieder VCC und GND vertauscht waren.

Bibliothek für SH1106

Mit ein paar Jumpersteckern wurde die gelieferte OLED aber probeweise mit der Platine verbunden … und blieb schwarz. Ursache dafür ist der angeblich kompatible Display-Controller, der die Ansteuerung der Pixel übernimmt. In den kleinen Displays und dem von Adafruit werkelt ein SSD1305 und im billigen großen Display steckt ein SH1106 (gelegentlich falsch als SSH1106 bezeichnet) – angeblich sollen beide kompatibel sein. Wie wenig „kompatibel“ und Displaytreiber in Einklang zu bringen sind, ist vielen Anwendern eventuell von den älteren Text-LCDs, dem Controller HD44780 und seinen Derivaten her bekannt. Kompatibel ist in dem Zusammenhang eher mit „so ungefähr ähnlich“ gleichzusetzen. Die Library von Adafruit, die wir bereits für das Display genutzt und angepasst haben, bietet keine Unterstützung für den Controller SH1106. Im Online-Beitrag haben wir Ihnen gezeigt, wie Sie Bibliotheken aus anderen Quellen installieren, was jetzt noch einmal gebraucht wird.

Exemplarisch soll die Library benutzt werden, die Sie unter https://github.com/shondll/Adafruit_SSD1306 herunter laden können. Klicken Sie dort auf Clone or download und wählen Sie dann Download ZIP, um das Archiv auf Ihrem PC zu speichern. Beenden Sie am besten die Arduino IDE und löschen Sie aus dem Verzeichnis \<Benutzername>\Arduino\libraries den Ordner mit der gleichnamigen Bibliothek, wenn Sie diese bereits installiert hatten. Anschließend entpacken Sie das heruntergeladene Archiv in diesen Ordner, sodass dort der Unterordner Adafruit_SSD1306-master entsteht. Sie können ihn in Adafruit_SSD1306 umbenennen, was aber nicht relevant ist. Wenn Sie sich die Datei Adafruit_SSD1306.h ansehen, stellen Sie fest, dass es dort eine weitere Präprozessor-Direktive gibt:

   
#define SH1106_128_64
//   #define SSD1306_128_64
//   #define SSD1306_128_32
//   #define SSD1306_96_16

Allerdings scheint der Autor der modifizierten Library noch etwas übersehen zu haben, denn es wird keine Initialisierung des SH1106-Controllers durchgeführt. In der Funktion Adafruit_SSD1306::begin in der Datei Adafruit_SSD1306.cpp wird nur geprüft, ob und welcher SSD1306-Controller eingestellt wurde. Ist SH1106_128_64 gesetzt, wird kein Code ausgeführt. Das kann im Einzelfall gutgehen (eventuell steht aber auch das Bild auf dem Kopf oder Ähnliches), aber besser ist es, wenn wir noch eine kleine Änderung vornehmen und aus der Zeile (251) #if defined SSD1306_128_64 die Anweisung

#if defined SSD1306_128_64 ||

defined SH1106_128_64

machen, sodass die in der if-Bedingung aufgeführten Befehle bei beiden Display-Typen ausgeführt werden.

Für den Shootduino lässt sich der Quellcode leider so noch immer nicht übersetzen, denn der Compiler stellt fest, dass die Routine swap() nicht definiert ist. Diese soll den Inhalt zweier Variablen vertauschen und ist eventuell irgendwo anders definiert, aber in diesem Projekt (noch) nicht. Das lösen wir, indem Sie in der Adafruit_SSD1306.h vor der Zeile class Adafruit_SSD1306 : public Adafruit_GFX { diese Zeile ergänzen:

#define swap(a, b) { int16_t t = a; a = b; b = t; }

Lahme Grafik

Pinout der OLED-Displays von Adafruit. Bei diesem Modell muss immer zusätzlich auch die Reset-Leitung angesteuert werden.
Rückseite der Adafruit-OLED mit Jumper-/Kurzschlussbrücken zur Protokolleinstellung

Ohne den direkten Vergleich aufgrund der bisherigen Exzesse wäre es vermutlich gar nicht aufgefallen: Auf dem großen Billig-Display wirkt das Spiel irgendwie lahm. Die Sterne, Schüsse und Asteroiden fliegen wie durch zähen Sirup. Nach anfänglichem Rätselraten findet sich die Ursache bei einer weiteren Analyse des Codes in Adafruit_SSD1306.cpp. und dort der Funktion Adafruit_SSD1306::display() (etwa ab Zeile 476). Hier gibt es eine Unterscheidung, an welchen Display-Controller die Bilddaten gesendet werden sollen. Wenn es der SH1106 ist, müssen die Pixeldaten blockweise übertragen werden. Dabei handelt es sich um sogenannte pages (Seiten), in die der Datenspeicher unterteilt ist, wie es bei vielen Speicherbausteinen gemacht wird. Die notwendige Adressierung kostet entsprechend Zeit und bremst die Darstellung. Beim SSD1306 kann hingegen das RAM in einem Zug beschrieben werden. Für Spiele und ähnliche Anwendungen mit vielen schnellen Änderungen auf dem Display eignen sich die Modelle mit SH1106 also weniger gut.

Reset und Adressierung

Einstellen der Bus-Adresse

Kehren wir noch einmal zum Display von Adafruit zurück, welches ja noch nichts angezeigt hat, um dieses in Betrieb zu nehmen – es gilt nämlich noch zwei weitere Hürden zu meistern. Eine Eigenart bei den Modellen, die per SPI oder I2C angesteuert werden können, ist, dass sie ein Reset-Signal benötigen. Vom Arduino ist für diesen Fall ein freier I/O-Pin an die entsprechende Leitung am Display zu führen. Die erforderlichen Codezeilen sind dafür auch alle schon in der Bibliothek vorhanden. Lediglich dem Konstruktor Adafruit_SSD1306 muss im Hauptprogramm (Shootduino.ino) die Pinnummer mit übergeben werden (Standard ist Pin 4).

const uint8_t OLED_RESET = 4;

Adafruit_SSD1306 display(OLED_RESET);

Auf der Rückseite der Multi-Protokoll-Displays ist vorher das gewünschte Datenprotokoll zu konfigurieren. Für I2C sind die zwei Brücken SJ1 und SJ2 zu schließen – zum Beispiel mit einem Klecks Lötzinn. Möchten Sie das Display via SPI ansteuern, bleiben die zwei Kurzschlussbrücken offen.

Ein Blick auf die Rückseite des No-Name-1,3"-Displays gibt einen Hinweis darauf, warum das Display von Adafruit mit dem bisher genutzten Code trotzdem dunkel bleibt. Es gibt eine Kurzschlussbrücke zwischen der Beschriftung „0X78“ und „0X7A“, die mit einem SMD-Widerstand von 4,7 kΩ in der Stellung „0X78“ bestückt ist. Fehlende Dokumentation muss in dem Fall durch Erfahrung ausgeglichen werden: Es liegt nahe, dass es sich hier um die Einstellung der Geräteadresse für den I2C-Bus handeln dürfte. Im Quellcode von Shootduino.ino ist auch ganz am Anfang eine Konstantendeklaration enthalten, die dazu passt:

const uint8_t I2C_ADDRESS_DISPLAY = 0x3C;

Obwohl im Code die Adresse 3CHex steht und auf dem Display 78Hex eingestellt ist, funktioniert das China-Display, nicht aber das von Adafruit. Ein erneuter Blick in die Header-Datei Adafruit_SSD1306.h bringt uns der Lösung näher (Zeile 42):

#define SSD1306_I2C_ADDRESS 0x3C

// 011110+SA0+RW - 0x3C or 0x3D

// Address for 128x32 is 0x3C

// Address for 128x64 is 0x3D (default)

or 0x3C (if SA0 is grounded)

Das Adafruit-Display benötigt eine andere Adresse:3DHex. Diese wird aber im Hauptprogramm eingestellt, weil dort der Aufruf der Initialisierungsroutine display.begin(SSD1306_SWITCHCAPVCC, I2C_ADDRESS_DISPLAY) (Zeile 327) mit der am Anfang der Datei festgelegten Konstante (Zeile 18) erfolgt. So modifiziert, funktioniert auch endlich die teure 1,3"-OLED. —fls