Skip to Content

Seitenweises Neuzeichnen des E-Paper-Displays

Seitenweises Neuzeichnen des E-Paper-Displays

In diesem Tutorial lernst du, wie man mit einem Arduino eine seitenweise Aktualisierung eines E-Paper-Displays durchführt und warum dies bei Mikrocontrollern mit kleinem Speicher oft notwendig ist.

Im Vergleich zu PCs oder Mobiltelefonen verfügen Mikrocontroller wie der Arduino über deutlich weniger Speicher. Dieser begrenzte Speicher erschwert die Verarbeitung großer Bilder oder komplexer Grafiken. E-Paper-Displays benötigen oft viel Speicher, um den gesamten Bildschirminhalt zu speichern. Beim Aktualisieren des Displays steht möglicherweise nicht genug RAM zur Verfügung, um das vollständige Bild zu halten.

Die seitenweise Aktualisierung löst dieses Problem. Anstatt den gesamten Bildschirminhalt auf einmal in den Speicher zu laden, teilt man den Bildschirm in kleinere Abschnitte oder „Seiten“ auf. Diese können dann nacheinander geladen und aktualisiert werden. Diese Methode reduziert den Speicherverbrauch und ermöglicht flüssigere Updates.

In den folgenden Abschnitten lernst du, wie du eine seitenweise Aktualisierung für ein E-Paper-Display mit einem Arduino Uno durchführst.

Benötigte Teile

Du benötigst ein E-Paper-Display. Für dieses Tutorial habe ich ein monochromes 2,9-Zoll-Display mit einer Auflösung von 296×128 Pixeln gewählt. Du kannst aber auch ein E-Paper mit einer anderen Größe verwenden.

Als Mikrocontroller funktioniert jeder Arduino oder ESP32/ESP8266, aber um den Bedarf an seitenweisen Aktualisierungen wirklich zu verstehen, empfehle ich einen Arduino Uno. Sein begrenzter Speicher macht diese Technik selbst bei kleinen E-Paper-Displays unverzichtbar.

2,9″ E-Paper-Display

Arduino

Arduino Uno

USB Data Sync cable Arduino

USB-Kabel für Arduino UNO

Dupont wire set

Dupont-Kabelset

Half_breadboard56a

Breadboard

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Was ist eine seitenweise Aktualisierung

Die seitenweise Aktualisierung ist eine Technik aus der Grafikprogrammierung, besonders in speicherarmen Umgebungen wie Mikrocontrollern. Sie ermöglicht es, ein Display durch das Zeichnen von Inhalten in separaten „Seiten“ oder Abschnitten zu aktualisieren.

Schauen wir uns ein konkretes Beispiel an. Das 2,9″ E-Paper-Display, das wir in diesem Tutorial verwenden, hat eine Auflösung von 128×296 Pixeln. Angenommen, jeder Pixel ist 1 Bit für ein monochromes Display (schwarz, weiß), benötigen wir (128×296)/8 = 4736 Bytes, um das gesamte Pixelarray (Bild, Frame) im RAM zu speichern.

Normalerweise wird das anzuzeigende Bild zuerst in einem Display-Puffer im RAM des Mikrocontrollers gespeichert und dann an den E-Paper-Controller gesendet, um den Bildschirminhalt zu aktualisieren. Ein Arduino Uno hat jedoch nur 2 KB SRAM = 2048 Bytes, was bedeutet, dass wir kein komplettes Bild im RAM vorbereiten können. Anders gesagt: Auf einem Arduino Uno können wir keinen Display-Puffer erstellen, der groß genug ist, um ein 128×296-Bild zu halten.

Seitenweise Aktualisierung

Hier kommt dieseitenweise Aktualisierungins Spiel. Anstatt das gesamte Bild auf einmal vorzubereiten und zu senden, teilen wir das Bild in kleinere Abschnitte, sogenannte „Seiten„, die in den RAM des Mikrocontrollers passen. Wir verarbeiten dann eine Seite nach der anderen, senden sie an den E-Paper-Controller, der die Seiten zusammensetzt und das Display aktualisiert, wenn das Bild komplett ist. Siehe zur Veranschaulichung das folgende Bild:

Paged Redraw of an Image
Seitenweise Aktualisierung eines Bildes

Wenn du die GxEPD2 Bibliothek verwendest, spiegelt sich dieser Prozess im Code wider. Für eine seitenweise Aktualisierung rufst du zuerst firstPage() auf, um die Aktualisierung zu starten. Dann rufst du wiederholt nextPage() in einer Schleife auf, bis alle Seiten an das Display übertragen wurden.

  epd.firstPage();
  do {
    ...  // Graphics code
  } while (epd.nextPage());

Innerhalb der Schleife definierst du die Grafikoperationen, die du ausführen möchtest, um den anzuzeigenden Inhalt/das Bild zu erstellen. Die seitenweise Aktualisierung ist natürlich ineffizient, da du denselben Inhalt mehrfach erzeugen musst, aber E-Paper sind langsam beim Aktualisieren, selbst bei Teilaktualisierungen (< 0,3 Sekunden), sodass das kaum ins Gewicht fällt.

Pufferhöhe

Während eine Seite ein beliebiger rechteckiger Abschnitt sein könnte, verwendet die GxEPD2Bibliothek Seiten in Form von Streifen. Die Länge des Streifens entspricht der Breite des Displays, die Höhe hängt vom verfügbaren Speicher ab. Bei einem monochromen Display kannst du die Höhe heines Streifens oder des Display-Puffers wie folgt berechnen,

h = buff / (width / 8)

wobei buff die Größe in Bytes des Display-Puffers ist, die du dir leisten kannst, und width die Breite des Displays.

Für E-Paper mit 4 Graustufen oder 3 oder 4 Farben benötigen wir 2 Bit pro Pixel, und die Formel lautet:

h = (buff / 2) / (width / 8)

Ein Arduino hat 2048 Bytes RAM, also wäre die maximale Höhe hfür das Display im Hochformat

h = 2048 / (128 / 8) = 128Pixel

Allerdings teilen sich der Display-Puffer und der Code denselben Speicher, und wir können nicht die vollen 2048 Bytes nutzen. Wenn du die Hälfte des Speichers für den Display-Puffer verwendest, erhalten wir

h = 1024 / (128 / 8) = 64Pixel,

was bei einer Displayhöhe von 296 Pixeln bedeutet, dass wir 296 / 64 = 5 Seiten hätten.

Je nach Größe des Codes und verfügbarem RAM deines Mikrocontrollers musst du die Puffergröße anpassen. Wenn du beim Kompilieren die folgende Fehlermeldung erhältst, ist dein Puffer zu groß:

Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint. 
data section exceeds available space in board.
Compilation error: data section exceeds available space in board

In den nächsten Abschnitten verbinden wir das E-Paper-Display mit dem Arduino und schreiben Code, um zu demonstrieren, wie eine seitenweise Aktualisierung implementiert wird.

Installation der GxEPD2-Bibliothek für E-Paper

Bevor wir auf dem E-Paper zeichnen oder schreiben können, müssen wir zwei Bibliotheken installieren. Die Adafruit_GFX Bibliothek ist eine Kern-Grafikbibliothek, die eine gemeinsame Menge an Grafikgrundfunktionen (Text, Punkte, Linien, Kreise usw.) bereitstellt. Und die GxEPD2 Bibliothek liefert die Grafiktreiber-Software, um ein E-Paper über SPI zu steuern.

Öffne den Library Manager, suche nach „Adafruit_GFX“ und „GxEPD2“ und klicke auf „INSTALL“. Nach der Installation sollten die Bibliotheken im Library Manager wie folgt erscheinen.

Adafruit_GFX and GxEPD2 libraries in Library Manager
Adafruit_GFX- und GxEPD2-Bibliotheken im Library Manager

Anschluss des E-Paper-Displays an den Arduino

Das folgende Schaltbild zeigt, wie du die SPI-Schnittstelle des E-Paper-Displays mit einem Arduino verbindest. Die meisten Pins können frei konfiguriert werden, aber für die DIN- und CLK-Leitungen müssen SPI-spezifische Pins verwendet werden. Beim Arduino Uno ist DIN an Pin 11 und CLK an Pin 13.

Connecting E-Paper to Arduino Uno via SPI
Anschluss des E-Paper an Arduino Uno über SPI

Die folgende Tabelle listet alle weiteren Verbindungen auf, die du herstellen musst.

E-Paper-DisplayArduino UNO
CS/SS4
SCL/SCK/CLK13
SDA/DIN/MOSI11
BUSY7
RES/RST6
DC5
VCC3,3 V
GNDG

Seitenweise Aktualisierung für E-Paper mit Arduino Uno

Wir werden das folgende Testbild auf dem E-Paper-Display zeichnen:

Test Picture
Testbild

Unten findest du den entsprechenden Code. Schau ihn dir zuerst kurz an, dann besprechen wir die Details.

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// CLK = 13
// DIN = 11

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
   epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));

void setup() {
  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();

  epd.firstPage();
  do {
    epd.fillScreen(GxEPD_WHITE);
    epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
    epd.fillTriangle (100, 120, 180, 120, 140, 60, GxEPD_BLACK);
    epd.setCursor(200, 60); epd.print("TEST");
  } while (epd.nextPage());

  epd.hibernate();
}

void loop() {}

Wir beginnen mit dem Einbinden der benötigten Grafikbibliothek und der Definition einiger Konstanten für die SPI-Pins. Denk daran, dass DIN- und CLK-Pins festgelegt und spezifisch für deinen Mikrocontroller sind, während die anderen geändert werden können.

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// CLK = 13
// DIN = 11

Als Nächstes definieren wir die Größe des Display-Puffers BUFF und die Seitenhöhe HIGHT. Die Höhe wird über ein Makro berechnet, das das Display-Objekt EPD als Parameter nimmt und dessen Breiteigenschaft (EPD::WIDTH) verwendet, um die Seitenhöhe zu bestimmen.

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

Die Berechnung folgt der zuvor besprochenen Formel. Für ein dreifarbiges Display würdest du stattdessen folgende Formel verwenden:

#define HEIGHT(EPD) ((BUFF / 2) / (EPD::WIDTH / 8))

Wir können nun das HIGHTMakro verwenden, um das Display-Objekt epd zu erstellen. Das sieht zwar komplex aus, aber es nimmt einfach die Pin-Konstanten, die Höhe und einen Displaytyp als Template-Parameter, um das Display zu erzeugen.

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
   epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));

Wenn dein Display nichts anzeigt oder fehlerhafte Texte/Bilder zeigt, ist entweder die Verkabelung falsch oder der falsche Displaytyp gewählt.

Die Readme for GxEPD2 Bibliothek listet alle unterstützten Displays auf, und du findest die Details in den Header-Dateien, z.B. GxEPD2.h. Suche den Displaytreiber, der zu deinem Display passt. Schau dir auch das Interfacing Arduino with E-ink Display Tutorial an, in dem wir ein dreifarbiges E-Paper mit Arduino und ESP32 verbinden.

Setup-Funktion

In der setupFunktion setzen wir zuerst einige Grafikparameter wie die Ausrichtung (Hochformat), die Textgröße, die Textfarbe und den Aktualisierungsmodus (Vollbild).

  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();

Es gibt auch eine „Teilaktualisierung“, die wir hier nicht verwenden. Wenn du mehr darüber erfahren möchtest, schau dir das Partial Refresh of e-Paper Display Tutorial an.

Schließlich kommen wir zum Teil, wo die seitenweise Aktualisierung durchgeführt wird. Wir starten mit dem Aufruf von firstPage(), um die Aktualisierung einzuleiten. Dann zeichnen wir wiederholt das Rechteck, das Dreieck und den Text in einer do-while loop, bis nextPage() false zurückgibt.

  epd.firstPage();
  do {
    epd.fillScreen(GxEPD_WHITE);
    epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
    epd.fillTriangle (100, 120, 180, 120, 140, 60, GxEPD_BLACK);
    epd.setCursor(200, 60); epd.print("TEST");
  } while (epd.nextPage());

Diese Schleife füllt im Wesentlichen den Display-Puffer mit dem Inhalt einer Seite, sendet diese Seite an das E-Paper-Display, wo der Controller die Seiten zusammensetzt, bis das Bild komplett ist.

Du wirst nicht sehen, wie das Display Seite für Seite aktualisiert wird. Stattdessen wartet der Display-Controller, bis alle Seiten empfangen wurden, und führt dann eine vollständige Aktualisierung des Displays durch. Siehe den kurzen Videoclip unten:

Full refresh with Paged Redraw
Vollständige Aktualisierung mit seitenweiser Aktualisierung

Übrigens, wenn du die tatsächliche Seitenhöhe wissen möchtest, die das Display verwendet, kannst du in der Setup-Funktion die folgende Funktion aufrufen, um sie herauszufinden:

  Serial.println(epd.pageHeight());

Loop-Funktion

Die loopFunktion in diesem Beispiel ist leer, da keine periodische Inhaltsaktualisierung erfolgt.

void loop() {}

Aber nehmen wir an, du möchtest eine Digital Clock on e-Paper Display implementieren, dann würde die loopFunktion den Code für die seitenweise Aktualisierung enthalten, anstatt die setupFunktion.

Zeichenfunktion

Bei komplexeren Anwendungen ist es besser, eine Zeichenfunktion anstelle der do-while loopfür die seitenweise Aktualisierung zu verwenden. Die GxEPD2 Bibliothek erlaubt es dir, eine Zeichenfunktion zu definieren und dann epd.drawPaged(...)für das seitenweise Zeichnen aufzurufen. Hier ist das Codegerüst:

void draw(const void* pv) {
  ... // Graphics code
}

void setup() {
  ...
  epd.drawPaged(draw, 0);
  epd.hibernate();
}

void loop() {}

Die Zeichenfunktion draw()nimmt einen Zeiger pv, mit dem du Parameter übergeben kannst, wenn du epd.drawPaged(draw, 0)aufrufst. Du kannst aber auch einfach 0als Parameterzeiger übergeben, wenn du das nicht brauchst.

Die Verwendung von Zeichenfunktionen für seitenweise Aktualisierungen macht den Code lesbarer und erweiterbar. Hier ist unser Testcode nochmal, diesmal mit einer Zeichenfunktion anstelle der do-while loop:

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// SCL(SCK)=13,
// SDA(MOSI)=11

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
  epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));


void draw(const void* pv) {
  epd.fillScreen(GxEPD_WHITE);
  epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
  epd.fillTriangle(100, 120, 180, 120, 140, 60, GxEPD_BLACK);
  epd.setCursor(200, 60); epd.print("TEST");
}

void setup() {
  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();
  epd.drawPaged(draw, 0);
  epd.hibernate();
}

void loop() {}

Und das war’s! Jetzt weißt du, was eine seitenweise Aktualisierung ist und wie man sie implementiert.

Fazit

In diesem Tutorial hast du gelernt, wie man eine seitenweise Aktualisierung eines monochromen E-Paper-Displays mit einem Arduino Uno durchführt. Wenn du statt eines monochromen ein dreifarbiges E-Paper oder einen ESP32 hast, schau dir das Interfacing Arduino with E-ink Display an.

Wenn du mehr über Teil- vs. Vollaktualisierung erfahren möchtest, empfehle ich unser Partial Refresh of e-Paper Display Tutorial.

Außerdem wird eine Kombination aus Seitenaktualisierung sowie Teil- und Vollaktualisierung von E-Paper-Displays unterschiedlicher Größe in den Tutorials Temperature Plotter on e-Paper Display, Digital Clock on e-Paper Display und Analog Clock on e-Paper Display verwendet. Wenn du Anwendungsbeispiele brauchst, findest du sie dort.

Wenn du weitere Fragen hast, kannst du sie gerne im Kommentarbereich stellen.

Viel Spaß beim Basteln ; )