In diesem Tutorial lernst du, wie du Parola-Zonen-Animationen auf einem MAX7219 LED-Matrix-Display koordinierst. Die MD_Parola Bibliothek ist eine beliebte Bibliothek, die das Programmieren von Animationen wie das Scrollen von Text sehr einfach macht. Sie ermöglicht es dir außerdem, ein Display in mehrere Zonen zu unterteilen und in diesen Zonen verschiedene Animationen laufen zu lassen.
Allerdings ist die Koordination von Animationen in mehreren Zonen nicht einfach. Zum Beispiel: Wie startet man eine Animation in einer Zone neu, ohne die Animation in einer anderen Zone zu beeinflussen? Oder wie startet man alle Animationen erst dann neu, wenn alle Animationen abgeschlossen sind, auch wenn sie mit unterschiedlichen Geschwindigkeiten laufen?
Dieses Tutorial gibt dir ein Code-Framework an die Hand, mit dem du solche Animationsszenarien einfach umsetzen kannst.
Benötigte Teile
Ich habe für dieses Projekt ein Arduino Uno verwendet, aber jedes andere Arduino-Board oder ein ESP8266/ESP32-Board funktioniert ebenfalls. Außerdem habe ich ein MAX7219 LED-Matrix-Display mit 4 Modulen benutzt. Der Code und die Verdrahtung sind für Displays mit einer anderen Anzahl von Modulen im Wesentlichen gleich.

MAX7219 LED-Matrix-Display

Arduino Uno

Dupont-Kabelset

USB-Kabel für Arduino UNO
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.
MAX7219 LED-Display mit Arduino verbinden
In diesem Tutorial verwenden wir ein MAX7219 LED-Matrix-Display, um Animationen anzuzeigen. Der MAX7219 LED-Display-Treiber kommuniziert mit dem Arduino über SPI (Serial Peripheral Interface). Daher ist das Erste, was du tun musst, das Display über die SPI-Schnittstelle mit dem Arduino zu verbinden.
Verdrahtung
Das folgende Diagramm zeigt dir die Verdrahtung zwischen dem MAX7219-Display und einem Arduino Uno. Achte darauf, dass du das Display an der Eingangsseite (DIN) und nicht an der Ausgangsseite (DOUT) anschließt.

Beginne damit, die SPI-Schnittstellenpins zu verbinden: Pin 11 des Arduino wird mit dem DIN-Pin des Displays verbunden. Dann verbinde Pin 3 mit CS und Pin 13 mit CLK. Zum Schluss verbinde 5V mit VCC und GND mit GND. Die folgende Tabelle zeigt die Verbindungen noch einmal:
| Display | Arduino |
|---|---|
| VCC | 5 V |
| GND | GND |
| DIN | 11 (MOSI) |
| CS | 3 (SS) |
| CLK | 13 (SCK) |
Die oben genannten Pins sind für Hardware SPI , was schneller ist als Software-SPI, aber du musst dafür bestimmte Pins verwenden, die je nach Board unterschiedlich sind. Für mehr Details siehe die MAX7219 LED dot matrix display Arduino tutorial .
Als Nächstes schreiben und testen wir etwas Beispielcode, um sicherzustellen, dass die Verbindungen korrekt sind.
MD_Parola und MD_MAX72XX Bibliotheken installieren
Zuerst musst du die MD_MAX72XX und die MD_Parola Bibliothek installieren. Öffne den Arduino IDE und gehe zu Tools > Manage Libraries , um den Bibliotheks-Manager zu öffnen. Suche nach „MD_MAX72XX“ und die entsprechenden Bibliotheken werden angezeigt. Der Screenshot unten zeigt dir die beiden Bibliotheken (gelb markiert), nachdem sie installiert wurden.

Die MD_MAX72XX-Bibliothek ist im Wesentlichen die Treibersoftware für das MAX7219 LED-Display. Die MD_Parola-Bibliothek nutzt sie, um Animationen wie Scroll- und Sprite-Text-Effekte zu erstellen. Wenn du mehr über die verfügbaren Animationen erfahren möchtest, schau dir die MAX7219 LED dot matrix display Arduino tutorial an.
Testcode
Der folgende Code ist ein minimales Beispiel, um die Verdrahtung und Funktion des Displays zu testen. Er scrollt einfach den Text „Test“ über das Display.
#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3
MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
void setup() {
disp.begin();
disp.setIntensity(0);
disp.displayClear();
disp.displayText("Test", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
void loop() {
if (disp.displayAnimate()) {
disp.displayReset();
}
}
Wenn du diesen Code hochlädst und ausführst, solltest du auf deinem MAX7219-Display folgende Ausgabe sehen:

Für mehr Details zu diesem Testcode schau dir das Control Parola Animations on MAX7219 LED Display Tutorial an, in dem wir denselben Testcode verwenden.
Beachte: Wenn du einen anderen Typ von MAX7219 LED-Display verwendest oder ein Display mit mehr oder weniger Modulen hast, musst du die folgenden Definitionen anpassen:
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4
Ebenso musst du, wenn du Software-SPI statt Hardware-SPI verwendest, die Art und Weise ändern, wie das Display-Objekt erstellt wird:
MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
Mehr Informationen dazu findest du in der MAX7219 LED dot matrix display Arduino tutorial .
In den folgenden Abschnitten schauen wir uns Zonen genauer an, wie man sie erstellt und verwendet und – am wichtigsten – wie man Animationen koordiniert, die in verschiedenen Zonen laufen.
Was sind Parola-Zonen
Die Parola-Bibliothek behandelt ein MAX7219 LED-Display als eine Abfolge von 8×8 LED-Modulen, nummeriert von 0 bis n-1 . Eine „Zone“ ist ein Abschnitt aufeinanderfolgender Module. Jede Zone kann unabhängig von anderen Zonen gesteuert werden. Das folgende Beispiel zeigt ein MAX7219 LED-Display mit vier Modulen (0…3) und zwei Zonendefinitionen.

Zone 0 umfasst die Module 0 und 1, und Zone 1 umfasst die Module 2 und 3. Beachte, dass die Nummerierung der Module bei 0 beginnt und von der Eingangsseite (rechte Seite) des Displays aus gezählt wird. Innerhalb jeder Zone kannst du verschiedene Animationen mit unterschiedlichen Schriftarten, Texteffekten, Texten, Geschwindigkeiten und sogar Helligkeiten laufen lassen.
Zonen sollten sich nicht überlappen, können aber unterschiedlich lang sein und der Zonenindex muss nicht in derselben Reihenfolge wie die Module sein. Zum Beispiel zeigt das folgende Beispiel eine weitere gültige Zonendefinition. Hier haben wir drei Zonen (0,1,2), aber sie sind unterschiedlich lang und die Zonenindizes sind nicht in Reihenfolge (0,2,1).

Wie definiert man Parola-Zonen
Angenommen, wir wollen die folgenden Display-Zonen definieren. Zone 0 läuft von Modul 0 bis Modul 1, und Zone 1 läuft von Modul 2 bis Modul 3:

In der setup-Funktion rufst du zuerst begin() mit der Gesamtanzahl der Zonen (hier 2) auf. Dann definierst du die einzelnen Zonen, indem du setZone(z, start, end) aufrufst. Das erste Argument z ist die Zonen-ID (0 oder 1, in unserem Fall), gefolgt vom Index des ersten und letzten Moduls dieser Zone.
#define MAX_ZONES 2
void setup() {
disp.begin(MAX_ZONES);
disp.setZone(0, 0, 1);
disp.setZone(1, 2, 3);
}
Wie bereits erwähnt, sollten sich Zonen nicht überlappen, aber sie können es. Wenn sie sich überlappen, musst du jedoch sicherstellen, dass überlappende Zonen nicht gleichzeitig Animationen ausführen, sonst bekommst du seltsame Artefakte.
Sobald du die Zonen definiert hast, kannst du diesen Zonen Animationen zuweisen. Zum Beispiel definiert die folgende Codezeile eine Animation für Zone 0, die den Text „Left“ nach links scrollt. Mehr dazu im nächsten Abschnitt.
disp.displayZoneText(0, "Left", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
Wie animiert man Parola-Zonen
Die meisten Parola-Animationsfunktionen gibt es in zwei Versionen. Eine Version arbeitet auf dem gesamten Display, die andere innerhalb einer bestimmten Zone. Sieh dir die Parola Documentation für einen Überblick an.
Zum Beispiel kannst du die Helligkeit des gesamten Displays oder einer bestimmten Zone einstellen, indem du setIntensity entweder ohne oder mit Zonenindex aufrufst.
setIntensity(intensity); // entire display setIntensity(z, intensity); // zone z
Das gilt auch für die meisten anderen Funktionen zum Setzen von Schriftarten, Texteffekten, Texten, Geschwindigkeiten usw. Der folgende Codeausschnitt zeigt, wie man in zwei Zonen unterschiedliche Texte anzeigt und animiert. In der ersten Zone (0) scrollen wir den Text „0“ nach rechts, in der zweiten Zone (1) scrollen wir den Text „1“ nach links.
#define SPEED 100
void setup() {
disp.begin(MAX_ZONES);
disp.setZone(0, 0, 1);
disp.setZone(1, 2, 3);
disp.displayZoneText(0, "0", PA_CENTER, SPEED, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
disp.displayZoneText(1, "1", PA_CENTER, SPEED, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
In der loop-Funktion rufen wir dann einfach wie gewohnt displayAnimate() auf, um herauszufinden, ob eine der beiden Animationen abgeschlossen ist, und setzen dann das Display für alle Zonen mit displayReset() zurück.
void loop() {
if (disp.displayAnimate()) {
disp.displayReset();
}
}
Beachte, dass du auch einzelne Zonen zurücksetzen kannst, indem du displayReset(z) mit einer Zonen-ID aufrufst. Das werden wir später noch nutzen.
void loop() {
if (disp.displayAnimate()) {
disp.displayReset(0);
disp.displayReset(1);
}
}
Das funktioniert gut, vor allem, weil beide Animationen hier mit derselben Geschwindigkeit (100) laufen. Wenn die Animationen aber mit unterschiedlichen Geschwindigkeiten laufen oder Texte unterschiedlicher Länge haben, wird eine Animation vor der anderen fertig. Wie gehen wir mit diesem Fall um und legen fest, was dann passieren soll?
Diese Koordination zwischen Animationen in mehreren Zonen ist das Thema des nächsten Abschnitts.
Wie koordiniert man Parola-Zonen
Wie oben erwähnt, ruft der Standard-Parola-Loop displayAnimate() auf, der true zurückgibt, wenn eine Animation in irgendeiner Zone abgeschlossen ist. Du kannst herausfinden, welche abgeschlossen ist, indem du getZoneStatus(z) mit der Zonen-ID z aufrufst.
Wenn du zum Beispiel zwei Zonen hast, könntest du die Animationen in beiden Zonen neu starten, wenn beide Animationen abgeschlossen sind, indem du folgenden Code schreibst:
void loop() {
if (disp.displayAnimate()) {
if(disp.getZoneStatus(0) && disp.getZoneStatus(1)) {
disp.displayReset();
}
}
}
Das funktioniert gut und ist perfekt für diesen einfachen Fall. Aber wenn du etwas komplexere Szenarien oder mehr Zonen hast, wird der Code schnell unübersichtlich. Angenommen, wir haben zwei Animationen in zwei Zonen und eine Animation ist schneller als die andere. Das ergibt mindestens sechs verschiedene mögliche Animationsszenarien:
- Beide Animationen laufen nur einmal
- Erste Animation läuft einmal, die andere wiederholt sich
- Zweite Animation läuft einmal, die andere wiederholt sich
- Beide Animationen werden wiederholt, wenn beide fertig sind
- Schnellere Animation wiederholen, während die langsamere noch läuft
- Langsamere Animation neu starten, wenn die schnellere fertig ist
In den folgenden Abschnitten werden wir diese sechs Animationsszenarien umsetzen. Um die Sache zu vereinfachen, definieren wir zuerst eine Hilfsfunktion getStatus() .
getStatus-Funktion
Anstatt getZoneStatus(z) für jede Zone aufzurufen, gibt die folgende getStatus() Funktion einen Bitvektor zurück, der den Status aller Zonen auf einmal anzeigt.
byte getStatus() {
static byte status = 0;
disp.displayAnimate();
for (int z = 0; z < MAX_ZONES; z++) {
disp.getZoneStatus(z) ? bitSet(status, z) : bitClear(status, z);
}
return status;
}
Intern iteriert die Funktion über alle Zonen und setzt den Zonenstatus im status Byte. Das bedeutet, dass die maximale Anzahl an Zonen auf 8 begrenzt ist, was aber normalerweise mehr als ausreichend ist.
Das folgende Bild zeigt ein Beispiel für einen Bit- bzw. Statusvektor, den getStatus() zurückgibt. In diesem Beispiel sind die Animationen in Zone 1 und Zone 3 abgeschlossen:

Wenn du getStatus() im Hauptloop aufrufst, kannst du den Statusvektor ganz einfach ausgeben.
void loop() {
Serial.println(getStatus(), BIN);
}
Beachte, dass getStatus() intern auch displayAnimate() aufruft, sodass du das im Loop nicht mehr extra machen musst.
Wir werden getStatus() in den folgenden Abschnitten verwenden, um die sechs verschiedenen Animationsszenarien umzusetzen.
Codebeispiel mit zwei Zonen
Fangen wir mit einem Codebeispiel an, das alles enthält – außer der Implementierung der loop-Funktion.
#include "MD_Parola.h"
#include "MD_MAX72xx.h"
#include "SPI.h"
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define MAX_ZONES 2
#define CS_PIN 3
MD_Parola disp = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
byte getStatus() {
static byte status = 0;
disp.displayAnimate();
for (int z = 0; z < MAX_ZONES; z++) {
disp.getZoneStatus(z) ? bitSet(status, z) : bitClear(status, z);
}
return status;
}
void setup() {
disp.begin(MAX_ZONES);
disp.setZone(0, 0, 1);
disp.setZone(1, 2, 3);
disp.setIntensity(0);
disp.displayClear();
disp.displayZoneText(0, "0", PA_CENTER, 100, 0, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
disp.displayZoneText(1, "1", PA_CENTER, 200, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
void loop() {
// TODO
}
In diesem Code binden wir zuerst die benötigten Bibliotheken ein und definieren die Konstanten für das MAX7219-Display. Wenn du einen anderen Displaytyp oder eine andere Anzahl von Modulen hast, musst du diese Konstanten anpassen.
Als Nächstes erstellen wir das Display-Objekt disp und implementieren die getStatus() Funktion wie oben gezeigt.
In der setup-Funktion definieren wir zwei Animationen in zwei Zonen. Die erste Animation in Zone 0 scrollt den Text „0“ nach rechts. Die zweite Animation in Zone 1 scrollt den Text „1“ nach links. Das Bild unten zeigt die beiden Zonen mit ihren Indizes.

Beachte, dass die erste Animation in Zone 0 schneller läuft (speed=100) als die zweite Animation (speed=200) in Zone 1. Das kurze Video unten zeigt, wie das aussieht.

Wir können den Code oben für alle sechs Animationsszenarien gleich lassen und müssen nur die Implementierung der loop-Funktion anpassen.
Beide Animationen laufen nur einmal
Fangen wir mit dem einfachsten Fall an. Wir wollen, dass beide Animationen nur einmal laufen, bis sie abgeschlossen sind. In diesem Fall muss die loop-Funktion einfach nur getStatus() aufrufen und sonst nichts tun.
void loop() {
getStatus();
}
Das brauchst du wahrscheinlich nicht oft, aber wenn du das umsetzt und laufen lässt, sieht es so aus:

Beachte jedoch: Da das Video in einer Schleife läuft, sieht es so aus, als würde die Animation neu starten. Das passiert aber tatsächlich nicht – die Animation läuft nur einmal und das Display bleibt danach für immer leer. Vielleicht nützlich für eine Startnachricht, die nur einmal angezeigt werden soll.
Erste Animation läuft einmal, die andere wiederholt sich
Im nächsten Szenario lassen wir die erste Animation in Zone 0 nur einmal laufen, aber die zweite wiederholt sich. Das heißt, immer wenn die Animation in Zone 1 abgeschlossen ist, müssen wir das Display für diese Zone zurücksetzen. Unten ist der Code für die loop-Funktion in diesem Szenario.
void loop() {
if(getStatus() & 0b00000010) {
disp.displayReset(1);
}
}
Wir holen uns den Status und führen eine bitwise AND (&)-Operation aus, um zu prüfen, ob die Animation in Zone 1 fertig ist. Falls ja, rufen wir displayReset(1) für diese Zone auf, um die Animation zu wiederholen. Der folgende Clip zeigt die resultierende Animation:

Auch hier sieht es im Video so aus, als würde die erste Animation wiederholt, aber das ist tatsächlich nicht der Fall.
Zweite Animation läuft einmal, die andere wiederholt sich
Im nächsten Szenario tauschen wir die Rollen der ersten und zweiten Animation. Wir lassen die zweite Animation nur einmal laufen und wiederholen die erste. Der Code der loop-Funktion ist im Wesentlichen derselbe wie zuvor, wir ändern nur die betrachtete Zone.
void loop() {
if(getStatus() & 0b00000001) {
disp.displayReset(0);
}
}
Der Videoclip unten zeigt die entsprechende Animation.

Beide Animationen werden wiederholt, wenn beide fertig sind
Jetzt warten wir, bis beide Animationen abgeschlossen sind, und setzen dann beide zurück. Das ist ganz einfach. Wir müssen nur prüfen, ob beide Status-Bits gesetzt sind. Beachte, dass wir in diesem Fall kein bitweises UND (&) machen, sondern auf Gleichheit (=) prüfen!
void loop() {
if(getStatus() == 0b00000011) {
disp.displayClear();
disp.displayReset();
}
}
Im folgenden Clip siehst du, wie die Animation in diesem Fall aussieht.

Schnellere Animation wiederholen, während die langsamere noch läuft
Da die beiden Animationen mit unterschiedlichen Geschwindigkeiten laufen, müssen wir entscheiden, was passieren soll, wenn die schnellere Animation fertig ist. Wollen wir die schnellere Animation sofort neu starten oder warten, bis die langsamere fertig ist? Den zweiten Fall haben wir im Abschnitt oben behandelt.
Unten ist der Code für den ersten Fall. Wir prüfen einfach für jede Zone, ob sie abgeschlossen ist, und starten dann die entsprechende Animation neu.
void loop() {
if(getStatus() & 0b00000001) {
disp.displayReset(0);
}
if(getStatus() & 0b00000010) {
disp.displayReset(1);
}
}
Hier ist der Videoclip, der dieses Szenario in Aktion zeigt.

Langsamere Animation neu starten, wenn die schnellere fertig ist
Das letzte Animationsszenario ist der umgekehrte Fall des vorherigen. In diesem Szenario wollen wir die langsamere Animation neu starten, sobald die schnellere Animation abgeschlossen ist. Beachte, dass der Code sich deutlich vom vorherigen unterscheidet.
void loop() {
if(getStatus() & 0b00000001) {
disp.displayReset(1);
disp.displayClear(1);
disp.displayReset(0);
}
}
Insbesondere müssen wir Zone 1 löschen, da wir die Animation in Zone 1 neu starten, bevor sie fertig ist. Wenn du das Display nicht löschst, bleiben Artefakte zurück.
Der Videoclip unten zeigt diese Animation. Beachte, dass das linke Scrollen der „1“ unterbrochen und zurückgesetzt wird, sobald das rechte Scrollen der „0“ abgeschlossen ist.

Fazit
In diesem Tutorial hast du gelernt, wie man Parola-Animationen koordiniert, die in mehreren Zonen laufen. Die obigen Beispiele verwenden nur zwei Zonen und sind relativ einfach. Sobald du jedoch mehr als zwei Zonen hast, wird die getStatus() Funktion deinen Code deutlich vereinfachen.
Du kannst zum Beispiel alle möglichen Zonen-Status in einer switch Anweisung auflisten und entsprechend behandeln. Hier ist ein Beispiel für zwei Zonen, das du leicht auf mehr Zonen erweitern kannst.
void loop() {
switch (getStatus()) {
case 0b00000001:
...
break;
case 0b00000010:
...
break;
case 0b00000011:
...
break;
}
}
Wir haben diese Konstruktion in den obigen Codebeispielen nicht verwendet, da sie bei zwei Zonen den Code nicht wirklich vereinfacht, aber bei mehr Zonen schon.
Wenn du mehrere Animationen verketten oder sie über externe Eingaben steuern möchtest, schau dir das Control Parola Animations on MAX7219 LED Display Tutorial an.
Wenn du einzelne Pixel der LED-Matrix ansteuern möchtest, ist die Parola-Bibliothek nicht die richtige Wahl. Verwende stattdessen FastLED oder etwas Ähnliches. Ein Beispiel findest du im Beitrag Game of Life on a Dot Matrix Display with MAX7219 .
Ich hoffe, du findest dieses Tutorial hilfreich. Hinterlasse gerne einen Kommentar, wenn du Fragen hast.

