LED Matrix Display ansteuern

Über den Beitrag

In diesem Beitrag möchte ich euch Methoden vorstellen, wie ihr ein LED Matrix Display (engl.: Dot Matrix Display) ansteuern könnt. Das ist natürlich nicht der erste Beitrag zu diesem Thema im Netz, aber vielleicht gibt es doch noch das eine oder andere Neue für euch zu entdecken. Im Einzelnen behandele ich die folgenden Themen: 

Als Appetizer hier schon mal ein kleines Video:

Laufschriften für ein 8x8x4 LED Matrix Display

Was ist ein LED Matrix Display und wie funktioniert es?

8x8 LED Matrix Display mit Reihen-Eingang und Spalten-Ausgang, Rot: LED (0/0)
8×8 LED Matrix Display mit Reihen-Eingang und Spalten-Ausgang, Rot: LED (0/0)

Ein LED Matrix Display ist ein LED Dioden Array. Jeweils ein Eingangs- bzw. Ausgangspin verbindet die LEDs einer Reihe (Row) bzw. einer Spalte (Column). Ob die Reihen oder die Spalten die Ein- oder Ausgänge sind, variiert von Display zu Display. 

Durch Anlegen einer Spannung an einen Eingangspin und durch Verbinden eines Ausgangspins mit Ground bringt ihr genau eine LED am Schnittpunkt zum Leuchten.

Wenn ihr keinen Ansteuerungs-IC wie den MAX7219 oder MAX7221 verwendet, dann müsst ihr Vorwiderstände einsetzen. Die benötigte Größe hängt von den technischen Daten eurer Displays ab. Meistens liegt ihr aber bei 5 Volt mit 330 Ohm schon richtig. 

Noch eine Anmerkung zum Schema oben: normalerweise findet man in Datenblättern die Reihen- und Spaltennummern von 1 bis 8. Gewöhnt euch lieber die Nummerierung 0 bis 7 an. Denn dann denkt ihr gleich „richtig“ in Bits und Bytes und das ist bei der Ansteuerung hilfreich.

Und noch etwas: wenn ich eine LED Position (einen Dot) in diesem Beitrag als Wertepaar (a/b) angebe, dann ist a die Reihe und b die Spalte. Also nicht wie x und y im Koordinatensystem. 

Woher bekommt Ihr LED Matrix Displays?

LED Matrix Displays gibt es in vielen verschiedenen Ausfertigungen. Dabei variiert die Anzahl der LEDs (Dots), die Größe der Displays und die Farbe der LEDs. Darüber hinaus gibt es sie mit integriertem Ansteuerungs-IC und teilweise auch im Verbund, z.B. als 8 x 8 x 4. Sucht in dem Online-Shop eures Vertrauens nach „LED Matrix Display“ oder „Dot Matrix“. Die Preise variieren recht stark mit der Herkunft und der Menge. 

Direkte Ansteuerung per Microcontroller oder Arduino

Ich verwende als Beispiel das oben schematisch abgebildete 8×8 LED Matrix Display mit Reihen-Eingang und Spalten-Ausgang. Vorab: die Verdrahtung macht keinen Spaß, da acht Eingänge, acht Ausgänge und acht Widerstände verbunden werden müssen. Außerdem sind die Reihen und Spalten den Pins ohne erkennbares System zugeordnet. Da müsst ihr ziemlich viel suchen. Teilweise ist auch nicht offensichtlich welches der Pin 1 ist. In diesem Fall müsst ihr erstmal ein wenig ausprobieren. 

Der ganze erste Abschnitt dient denn auch mehr der Didaktik als der Praxis. Ansteuerungs-ICs wie der MAX7219 machen das Leben leichter. Und noch komfortabler sind die Displays mit integrierter Ansteuerung. Da komme ich dann später zu. Trotzdem sollte man erstmal das Prinzip verstanden haben.

Beschaltung

Bei 16 benötigten Ein- und Ausgängen müssen auf dem Arduino auch die analogen Pins mit einbezogen werden. Oder ihr verwendet eine Portweiterung wie den MCP23017

So sieht die Beschaltung schematisch und praktisch aus:

Beschaltung für ein 8x8 LED Matrix Display
Beschaltung (schematisch) für ein 8×8 LED Matrix Display…
Verkabelung in der Praxis - eine ziemliche "Wurschtelei"
…und so sieht es in der Praxis aus – eine ziemliche „Wurschtelei“

Ein kleiner Beispielsketch

Im folgenden Beispielsketch werden zunächst die Arduino Pins für die Reihen und Spalten definiert. Die Eingänge (Reihen) werden auf LOW gesetzt, die Ausgänge (Spalten) auf HIGH. Damit sind alle LEDs aus. Eine LED an der Stelle „r/c“ wird angeschaltet, wenn die Reihe „r“ auf HIGH gesetzt wird und die Spalte „c“ auf LOW.

Als erste kleine Spielerei gibt es ein Lauflicht. Dabei geht jede LED einmal kurz an. Dann wird die Diagonale von (0/0) bis (7/7) zum Leuchten gebracht.

#define r0 2 // pin for row 0
#define r1 3 // pin for row 1
#define r2 4 // ...
#define r3 5
#define r4 6
#define r5 7
#define r6 8
#define r7 9
#define c0 10 // pin for column 0
#define c1 11 // pin for column 1
#define c2 12 // ...
#define c3 13
#define c4 A0
#define c5 A1
#define c6 A2
#define c7 A3

int row[] = {r0, r1, r2, r3, r4, r5, r6, r7};
int column[] = {c0, c1, c2, c3, c4, c5, c6, c7};
               
void setup() {
  for(int i = 0; i<=7; i++){
    pinMode(row[i], OUTPUT);
  }
  for(int i = 0; i<=7; i++){
    pinMode(column[i], OUTPUT);
  }
  clearDisplay();
}

void loop() {
  for(int i=0; i<=7; i++){
    for(int j=0; j<=7; j++){
      switchLED(row[i],column[j],1);
      delay(100);
      switchLED(row[i],column[j],0);
    }
  }
}

void clearDisplay(){
   for(int i = 0; i<=7; i++){
      digitalWrite(row[i],LOW); 
  }
  for(int i = 0; i<=7; i++){
    digitalWrite(column[i], HIGH);
  } 
}

void switchLED(int r, int c, bool ON){
  if(ON){
    digitalWrite(r, HIGH);
    digitalWrite(c, LOW);
  }
  else{
    digitalWrite(r, LOW);
    digitalWrite(c, HIGH);
  }
}

 

Unerwünschte Querbeeinflussung

Ersetzt im letzten Sketch einmal die Sketch Hauptschleife durch die folgende:

void loop() {
  switchLED(row[0],column[0],1);
  delay(1000);
  switchLED(row[0],column[1],1);
  delay(1000);
  switchLED(row[1],column[0],1);
  delay(1000);
  clearDisplay();
}

 

Eigentlich soll der Sketch die Dots (0/0), (0/1) und (1/0) anschalten. Das tut er zwar auch, aber mit dem Dot (1/0) wird auch (1/1) angeschaltet. Falls euch nicht klar sein sollte, warum das so ist, schaut dazu noch einmal in das Schema des 8×8 Displays. Dann sollte die Problematik offensichtlich sein. 

Die Lösung: Multiplexing

Ersetzt nun noch einmal die Hauptschleife durch die folgende:

void loop() {
  switchLED(row[0],column[0],1);
  delay(1);
  switchLED(row[0],column[0],0);
  switchLED(row[0],column[1],1);
  delay(1);
  switchLED(row[0],column[1],0);
  switchLED(row[1],column[0],1);
  delay(1);
  switchLED(row[1],column[0],0);
}

 

In diesem Fall leuchten tatsächlich nur die Dots (0/0), (0/1) und (1/0), da jeder Dot separat für kurze Zeit angeschaltet wird. Ein Flackern sieht man nicht, dafür ist der Wechsel zu schnell.

Die Grenzen des einfachen Multiplexings

Zunehmendes Multiplexing in dieser Form führt zu einer Abnahme der Intensität. Im folgenden Beispiel ist der Dot (7,7) immer an, der Rest der Diagonale ist „gemultiplext“. Dass der Rest von Reihe und Spalte 7 auch leuchten, ist ein Nebeneffekt.

Zu beachten: Die Sketchzeilen 1 und 2 gehören noch ins Setup. 

  switchLED(row[7],column[7], 1);
}

void loop() {
  for(int i=0; i<=6; i++){
    switchLED(row[i],column[i],1);
    delay(1);
    switchLED(row[i],column[i],0);  
  }
}

 

Auf dem Foto erkennt man die Unterschiede nicht so gut wie in der Realität. LEDs sind nicht einfach zu fotografieren. Dennoch könnt ihr vielleicht erahnen, dass der Dot (7,7) heller ist. 

"Immer an" vs. "gemultiplext"
„Immer an“ vs. „gemultiplext“

Auch für dieses Problem gibt es eine Lösung. Über Pulsweitenmodulation (PWM, siehe mein Beitrag über Timer) lässt sich die Intensität je nach Anzahl der „gemultiplexten“ LEDs gewichten. Darauf gehe ich aber jetzt nicht näher ein und möchte die bequemere und allgemein übliche Lösung vorstellen.

Ansteuerung mit dem MAX7219 / MAX7221

Alle eben genannten Probleme lassen sich mit den Ansteuerungs-ICs MAX7219 oder MAX7221 lösen. Da beide fast identisch sind, nenne ich ab jetzt nur den MAX7219, meine aber beide.

Primär ist der MAX7219 für die Ansteuerung von 7-Segment Displays konzipiert. Er lässt sich aber auch für die Ansteuerung von LED Matrix Displays oder losen LED Arrangements verwenden. Das ist auch nicht weiter verwunderlich, da die Ansteuerung mehrerer 7-Segment Anzeigen im Grunde dieselbe Herausforderung wie die Ansteuerung eines LED Matrix Displays darstellt.

Pinout und technische Eigenschaften des MAX7219

Pinout des MAX7219 / MAX7221; CS gilt nur für den MAX7221
Pinout des MAX7219 / MAX7221; CS gilt nur für den MAX7221

Der MAX7219 besitzt 24 Pins:

  • Die Pins SEG X liefern die positive Spannungsversorgung
    • Ob diese mit den Reihen- oder Spaltenpins verbunden werden, hängt davon ab, wie herum die LEDs im Display verbaut sind. 
  • Die Pins DIG X schalten nach GND.
  • Der MAX7219 wird per SPI angesteuert:
    • DIN ist der Data Input, also MOSI (Master Out, Slave In).
    • Ein MISO (Master In, Slave Out) gibt es nicht; es ist also eine One-Way Kommunikation.
    • CLK: Clock Pin.
    • LOAD / CS: Chip Select Pin.
  • Wenn ihr mehrere Displays verwendet, wird DOUT mit dem DIN des nächsten Displays verbunden.
  • An VCC dürfen 4 bis 5.5 Volt verwendet werden
  • An ISET wird die Höhe des Stroms für die LEDs eingestellt. Ich habe 10 kOhm verwendet. Im Datenblatt des MAX7219 gibt es eine grafische Darstellung, welcher Strom mit welchem Widerstand bei welcher Spannung bereitgestellt wird. 

Verwendung der Bibliothek LedControl

Zur Ansteuerung des MAX7219 verwende ich die Bibliothek LedControl von Eberhard Fahle, die ihr von Github oder direkt über die Bibliotheksverwaltung der Arduino IDE installieren könnt. Eine sehr gute Einführung gibt es hier.

Die Bibliothek erwartet eigentlich den Spannungseingang an den Spalten und den Spannungsausgang an den Reihen. Das ist bei dem von mir verwendeten Modell anders herum. Man kann einfach entsprechend andersherum verkabeln (Segx an die Reihenpins, Digx an die Spaltenpins), muss dann aber nachher noch etwas umdenken.

Diese LED Matrix Display Struktur erwartet die Bibliothek LedControl
Diese LED Matrix Display Struktur erwartet die Bibliothek LedControl

Der MAX7219 besitzt nicht sonderlich viele Register. Deshalb lässt er sich auch ohne all zu viel Aufwand ohne Bibliothek ansteuern. Wie das geht, zeige ich am Ende dieses Beitrages. 

Was die Komplexität der Verkabelung des LED Matrix Displays angeht, bringt der MAX7219 noch keinen großen Vorteil, aber immerhin braucht ihr die Vorwiderstände nicht:

Verkabelung eines 8x8 LED Matrix Displays mit dem MAX7219
Verkabelung eines 8×8 LED Matrix Displays mit dem MAX7219
8x8 LED Matrix Display mit MAX7219 am Arduino - Fritzing Schema
Verkabelung LED Matrix Display – MAX7219, schematisch

Zu beachten ist, dass die oben abgebildete Verkabelung spezifisch für das von mir verwendete LED Matrix Display ist. Vielleicht sind die Anschlüsse eures Displays gleich, vielleicht aber auch nicht.

Ein kleiner Beispielsketch

#include <LedControl.h>
//12= data pin(DIn), 11= CLK Pin, 10= Load/CS Pin, 1 = num of devices
LedControl lc88=LedControl(12,11,10,1); 

void setup(){
  lc88.shutdown(0,false); // Wake up! 0= index of first device;
  lc88.setIntensity(0,2);
  lc88.clearDisplay(0);
  delay(500);
}

void loop(){
  for(int row=0; row<=7; row++){
    lc88.setLed(0,row,0,true);
    delay(250); 
  }
  for(int col=0; col<=7; col++){
    lc88.setLed(0,0,col,true);
    delay(250); 
  }
  delay(500);
  lc88.clearDisplay(0);
  delay(2000); 
}

 

An diesem Beispiel sollten die meisten Funktionen der Bibliothek schon klar werden:

  • zunächst wird ein Display Objekt erzeugt und dabei die Anschluss Pins und die Zahl der Einzeldisplays festgelegt
  • shutdown(display, false) weckt das Display; mit true geht es schlafen
  • setIntensity(display, value) legt die Helligkeit der Dots fest mit 0 <= value < 16
  • clearDisplay(display) schaltet alle Dots eines Displays aus
  • setLed(display, row, col, true/false) schaltet genau einen Dot in Reihe row und Spalte col an oder aus.  

Weitere nützliche Funktionen, die ihr selber ausprobieren könnt, sind:

  • setRow(display, row, val) – die Reihe row eines Displays wird mit dem Wert val belegt, z.B. 0b11110000 -> die ersten vier LEDs sind an, die letzten vier aus 
  • setColumn(display, col, val) – wie setRow, nur eben zum Setzen von Spalten
    • diese Funktion ist erheblich langsamer als die setRow-Funktion, da sie die setLed-Funktion (achtmal pro setColumn) verwendet. 

Da ich bei meinem LED Matrix Display die SEG und die DIG Anschlüsse vertauschen musste, sind auch Reihen und Spalten vertauscht. Eigentlich sollte der Sketch erst die Spalte 0 und dann die Reihe 0 füllen. Offensichtlich ist es anders herum: 

LedControl_Max7219_test.ino: Spalten und Zeilen sind an diesem Display vertauscht
LedControl_Max7219_test.ino: Spalten und Zeilen sind an diesem Display vertauscht

Auf dem Bild oben seht ihr, dass die obere Reihe schon komplett leuchtet und nun die linke Spalte vervollständigt wird.

Eine Lösung lautet, das Display einfach um 90° gegen den Uhrzeigersinn zu drehen. Dann ist der Dot (0/0) unten links (wie in einem Koordinatenkreuz) und nicht oben links. Ihr müsst dann bei der Zeilennummerierung umdenken.

Verwendung von Displays mit integriertem MAX7219

8x8x1 LED Matrix Display

Wer es bequem haben möchte, dem rate ich zur Verwendung von Displays mit integriertem MAX7219. Die Verkabelung solcher Teile sieht schon deutlich angenehmer aus:

Verkabelung von Displays mit integriertem MAX7219 - auch das Kaskadieren ist einfach
Verkabelung von Displays mit integriertem MAX7219 – auch das Kaskadieren ist einfach

Wo der Nullpunkt (0/0) liegt, kann unter Umständen überraschend sein. Probiert es aus. Dieses LED Matrix Display macht es wie erwartet:

8x8 LED Matrix Display mit integriertem MAX7219
8×8 LED Matrix Display mit integriertem MAX7219

Hinweis: wenn ihr alle LEDs eines 8×8 Matrix Displays gleichzeitig leuchten lasst, dann kann der Strombedarf  mehrere 100 mA pro Display betragen. Bei Verwendung mehrerer Displays könnt ihr dann sehr locker die Limits des Arduinos überschreiten. Mehr als 500 mA solltet ihr einem Arduino UNO bzw. eurem USB Anschluss nicht zumuten. Setzt ggf. eine separate Stromquelle ein.  

8x8x4 LED Matrix Display

Wollt ihr mehrere Displays aneinanderhängen, kommt für euch vielleicht ein Verbunddisplay infrage. Vor allem sind 8x8x4 Displays weit verbreitet. Auch hier seid ihr unter Umständen überrascht, wo sich der Dot (0,0) befindet. Zum Testen habe ich den folgenden kurzen Sketch verwendet:

#include "LedControl.h"
LedControl lc884=LedControl(12,11,10,4);

unsigned long delaytime=3000;

void setup() {
  for(int i=0;i<4;i++){
    lc884.shutdown(i,false);
    lc884.setIntensity(i,8);
    lc884.clearDisplay(i);
  }
}

void loop() { 
  lc884.setRow(0,0,B01111111);
  lc884.setRow(1,1,B00111111);
  lc884.setRow(2,2,B00011111);
  lc884.setRow(3,3,B00001111);  
}

 

Eigentlich hätte ich gerne das Display 0 links und das Display 4 rechts. Außerdem hätte ich gerne den Nullpunkt oben links und eine Reihenabbildung, die der Reihenfolge der Bits entspricht. D.h. ein 0b00001111 soll vier Dots einer Reihe rechts leuchten lassen und nicht links. Ich habe zwei Displays aus unterschiedlichen Quellen getestet und mit beiden konnte ich nicht alle Wünsche erfüllen. Mit einer umgekehrten Displayreihenfolge konnte ich am ehesten leben: 

8x8x4 LED Matrix Display: meine Ausrichtung
8x8x4 LED Matrix Display: meine Ausrichtung

Display 0 ist hier rechts, dafür ist aber der Rest so wie ich es will. Diese Anordnung verwende ich im Folgenden für die Entwicklung der Laufschrift. Wenn euer Display die Dinge anders abbildet, dann müsst ihr die Sketche entsprechend anpassen.

Programmierung einer Laufschrift (Banner)

Vorbereitung: Kreieren und Darstellen von Buchstaben

Buchstaben, Zahlen und Zeichen sind auf einem Blatt kariertem Papier schnell entwickelt. Dann schreibt ihr das Ergebnis zeilenweise im Byteformat in Arrays. Im folgenden Sketch seht ihr Beispiele für A,r,d,u,i,n und o. Ich habe die Buchstaben recht schmal gemacht, da ich sie später noch „zusammenschieben“ möchte, sodass der ganze Arduino Schriftzug auf einmal auf das Display passt.

Die Buchstaben werden zunächst nacheinander auf dem Display 0 dargestellt. Dann machen wir den ersten Schritt in Richtung Laufschrift, indem wir die Buchstaben Display-weise von rechts nach links durchlaufen lassen. Mein Bezugsdisplay ist dabei das nicht sichtbare, virtuelle Display -1. 

#include "LedControl.h"
LedControl lc884=LedControl(12,11,10,4);

unsigned long delayTime=500;

byte a[8]={B00000000,B01100000,B10010000,B10010000,B10010000,B11110000,B10010000,B10010000};
byte r[8]={B00000000,B00000000,B00000000,B10100000,B11010000,B10000000,B10000000,B10000000};
byte d[8]={B00000000,B00010000,B00010000,B00010000,B01110000,B10010000,B10010000,B01110000};
byte u[8]={B00000000,B00000000,B00000000,B10010000,B10010000,B10010000,B10010000,B01110000};
byte i[8]={B00000000,B00000000,B01000000,B00000000,B01000000,B01000000,B01000000,B01000000};
byte n[8]={B00000000,B00000000,B00000000,B10100000,B11010000,B10010000,B10010000,B10010000};
byte o[8]={B00000000,B00000000,B00000000,B01100000,B10010000,B10010000,B10010000,B01100000};

void setup() {
  for(int i=0;i<4;i++){
    lc884.shutdown(i,false);
    lc884.setIntensity(i,8);
    lc884.clearDisplay(i);
  }
}

void loop() { 
  oneMatrix();
  fourMatrices();
  delay(1000);
}

void oneMatrix(){
  displayCharAndWait(a,0);
  displayCharAndWait(r,0);
  displayCharAndWait(d,0);
  displayCharAndWait(u,0);
  displayCharAndWait(i,0);
  displayCharAndWait(n,0);
  displayCharAndWait(o,0);
}

void displayCharAndWait(byte* x, byte displayNumber){
  lc884.clearDisplay(displayNumber);
  for(int j=0; j<=7;j++){
    lc884.setRow(displayNumber,j,x[j]);
  }
  delay(delayTime);
  lc884.clearDisplay(displayNumber); 
}


void fourMatrices(){
  for(int j=0; j<=10; j++){
    int currentMatrix = -1;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(a,currentMatrix+j);
    }
    currentMatrix--;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(r,currentMatrix+j);
    }
    currentMatrix--;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(d,currentMatrix+j);
    }
    currentMatrix--;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(u,currentMatrix+j);
    }
    currentMatrix--;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(i,currentMatrix+j);
    }
    currentMatrix--;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(n,currentMatrix+j);
    }
    currentMatrix--;
    if( ((currentMatrix+j)>=0) && ((currentMatrix+j)<4) ){
      displayChar(o,currentMatrix+j);
    }
   
    delay(delayTime);
    for(int i=0; i<=4; i++){
      lc884.clearDisplay(i);
    }
  }  
}

void displayChar(byte *x, int displayNumber){
  lc884.clearDisplay(displayNumber);
  for(int j=0; j<=7;j++){
    lc884.setRow(displayNumber,j,x[j]);
  }
}

 

Wie das im Ergebnis aussieht, könnt ihr in dem Video zu Beginn des Beitrages sehen. Hier ein Ausschnitt: 

Ausschnitt der Laufschrift
Ausschnitt der Laufschrift

Ein Zwischenschritt: statische Darstellung als unsigned long Array

Das Durchlaufen „Display für Display“ war einfach. Aber es ist auch noch nicht besonders schick. Schöner wäre ein Verschieben „Dot für Dot“. Zugegebenermaßen habe ich mich damit schwerer getan als ich dachte. Ich zeige ein paar Lösungen. Vielleicht denke ich auch zu kompliziert – wenn ihr einfachere Methoden habt, immer her damit!

Für meinen Lösungsweg Nr. 1 kommt hier erst einmal ein Zwischenschritt. Zunächst habe ich die Buchstaben so zusammengefasst, dass sie auf das Viererdisplay passen. Dazu habe ich die Zwischenräume auf ein Mindestmaß reduziert und alle 0ten, 1sten, 2ten….7ten Reihen in jeweils einem unsigned long Wert zusammengefasst. Da eine unsigned long Variable 32 bits hat, entspricht das der Breite des 4er Displays. Bei der späteren Darstellung auf dem Display muss man den Wert dann allerdings wieder in bytes „aufdröseln“. Das macht die Funktion displayBanner().

#include "LedControl.h"
LedControl lc884=LedControl(12,11,10,4);

long delayTime=500;

unsigned long banner[8]={0b00000000000000000000000000000000,
                         0b01100000000001000000000000000000,
                         0b10010000000001000000100000000000,
                         0b10010101000001010010001010001100,
                         0b10010110100111010010101101010010,
                         0b11110100001001010010101001010010,
                         0b10010100001001010010101001010010,
                         0b10010100000111001110101001001100};
               
void setup() {
  for(int i=0;i<4;i++){
    lc884.shutdown(i,false);
    lc884.setIntensity(i,8);
    lc884.clearDisplay(i);
  }
}

void loop() { 
  displayBanner(); delay(500);
}

void displayBanner(){
  byte currentMatrix[8];
  for(int j=0; j<4; j++){  
    for(int i=0; i<8; i++){
      currentMatrix[i] = (((banner[i])>>(j*8)) & 0b11111111);
    }
    displayMatrix(currentMatrix,j);
  }
}

void displayMatrix(byte *matrix, int matrixNumber){
  for(int i=0; i<=7;i++){
    lc884.setRow(matrixNumber,i,matrix[i]);
  }
}

 

Und so sieht das Ergebnis aus: 

Der "kondensierte" Arduino Schriftzug
Der „kondensierte“ Arduino Schriftzug

Das unsigned long Array dotweise laufen lassen

Der Vorteil der Darstellung als unsigned long array ist, dass die Verschiebung wesentlich leichter über Binäroperationen zu programmieren ist als Byte für Byte. Das Banner wird schrittweise nach links verschoben und auf die Einzeldisplays aufgeteilt. Beim Nachvollziehen des Sketches vergesst nicht, dass sich das Display 0 rechts befindet.

Als kleines Feature habe ich noch eingebaut, dass das Banner kurz stoppt, wenn es vollständig auf dem Display ist. Es wird kurz heller, dann wieder dunkler und läuft dann aus dem Display. Das Ergebnis seht ihr im Video zu Beginn des Beitrages. 

#include "LedControl.h"
LedControl lc884=LedControl(12,11,10,4);

long delayTime=150;

unsigned long banner[8]={0b00000000000000000000000000000000,
                         0b01100000000001000000000000000000,
                         0b10010000000001000000100000000000,
                         0b10010101000001010010001010001100,
                         0b10010110100111010010101101010010,
                         0b11110100001001010010101001010010,
                         0b10010100001001010010101001010010,
                         0b10010100000111001110101001001100};
               
void setup() {
  for(int i=0;i<4;i++){
    lc884.shutdown(i,false);
    lc884.setIntensity(i,8);
    lc884.clearDisplay(i);
  }
}

void loop() { 
  calcCurrentBanner(); delay(500);
}

void calcCurrentBanner(){
  unsigned long currentBanner[8];
  for(int i=32; i>=0; i--){
    for(int j=0; j<8; j++){
      currentBanner[j] = (banner[j])>>i;
    }
    displayBanner(currentBanner);
    delay(delayTime);
  }
  stopAndHighlight();
  for(int i=0; i<33; i++){
    for(int j=0; j<8; j++){
      currentBanner[j] = (banner[j])<<i;
    }
    displayBanner(currentBanner);
    delay(delayTime);  
  }
}

void displayBanner(unsigned long *cb){
  byte currentMatrix[8];
  for(int j=0; j<4; j++){  
    for(int i=0; i<8; i++){
      currentMatrix[i] = (((cb[i])>>(j*8)) & 0b11111111);
    }
    displayMatrix(currentMatrix,j);
  }
}

void displayMatrix(byte *matrix, int matrixNumber){
  for(int i=0; i<=7;i++){
    lc884.setRow(matrixNumber,i,matrix[i]);
  }
}

void stopAndHighlight(){
  for(int i=8; i<16; i++){
    for(int j=0;j<4;j++){
      lc884.setIntensity(j,i);
      delay(20);
    }
  }
  delay(1000);
  for(int i=15; i>=8; i--){
    for(int j=0;j<4;j++){
      lc884.setIntensity(j,i);
      delay(20);
    }
  }
}

 

Gößere Banner laufen lassen

Nun passt das, was ihr vielleicht als Banner über das Display laufen lassen wollt, nicht unbedingt auf das Display bzw. in ein unsigned long Array. Aber das geht auch. Als Beispiel nehme ich den ungekürzten Arduino Schriftzug, der sich über sieben Einzeldisplays erstreckt. Diesen verteile ich in ein zweidimensionales unsigned long Array „bannerPart[2][8]“. Dabei verschwende ich natürlich ein wenig Speicherplatz, da ich das Array nicht ganz nutze.  

Die Übernahme der einzelnen Buchstaben in das Array bannerPart habe ich diesmal automatisiert. Das geschieht im Setup in den ersten beiden for-Schleifen. In calcCurrentBanner() finden sich drei for-Schleifen, die für drei Phasen stehen. In der ersten Phase wird das erste Bannerteil hineingeschoben. Während der zweiten Phase geht das erste Teil hinaus und die frei werdenden Bereiche werden mit dem zweiten Bannerteil aufgefüllt. In der dritten Phase wird das zweite Bannerteil hinausgeschoben. 

#include "LedControl.h"
LedControl lc884=LedControl(12,11,10,4);

long delayTime=150;
byte banner[7][8]={{B00000000,B01100000,B10010000,B10010000,B10010000,B11110000,B10010000,B10010000},
                   {B00000000,B00000000,B00000000,B10100000,B11010000,B10000000,B10000000,B10000000},
                   {B00000000,B00010000,B00010000,B00010000,B01110000,B10010000,B10010000,B01110000},
                   {B00000000,B00000000,B00000000,B10010000,B10010000,B10010000,B10010000,B01110000},
                   {B00000000,B00000000,B01000000,B00000000,B01000000,B01000000,B01000000,B01000000},
                   {B00000000,B00000000,B00000000,B10100000,B11010000,B10010000,B10010000,B10010000},
                   {B00000000,B00000000,B00000000,B01100000,B10010000,B10010000,B10010000,B01100000}};

unsigned long bannerPart[2][8];

void setup(){
  unsigned long b1, b2, b3;
  for(int i=0; i<8; i++){
    unsigned long b1, b2, b3;
    b1 = ((unsigned long)(banner[0][i]))<<24;
    b2 = ((unsigned long)(banner[1][i]))<<16;
    b3 = ((unsigned long)(banner[2][i]))<<8;
    bannerPart[0][i] = b1 + b2 + b3 + banner[3][i];
  }
   for(int i=0; i<8; i++){
    unsigned long b1, b2, b3;
    b1 = ((unsigned long)(banner[4][i]))<<24;
    b2 = ((unsigned long)(banner[5][i]))<<16;
    b3 = ((unsigned long)(banner[6][i]))<<8;
    bannerPart[1][i] = b1 + b2 + b3;
  }
 
  for(int i=0;i<4;i++){
    lc884.shutdown(i,false);
    lc884.setIntensity(i,8);
    lc884.clearDisplay(i);
  }
}

void loop() { 
  calcCurrentBanner(); 
  delay(500);
}

void calcCurrentBanner(){
  unsigned long currentBanner[8];
  for(int i=32; i>=0; i--){
    for(int j=0; j<8; j++){
      currentBanner[j] = (bannerPart[0][j])>>i;
    }
    displayBanner(currentBanner);
    delay(delayTime);
  }
  for(int i=1; i<=32; i++){
    for(int j=0; j<8; j++){
      currentBanner[j] = ((bannerPart[0][j])<<i) + ((bannerPart[1][j])>>(32-i));
    }
    displayBanner(currentBanner);
    delay(delayTime);
  }
  for(int i=1; i<=24; i++){
    for(int j=0; j<8; j++){
      currentBanner[j] = (bannerPart[1][j])<<i;
    }
    displayBanner(currentBanner);
    delay(delayTime);
  }
}

void displayBanner(unsigned long *cb){
  byte currentMatrix[8];
  for(int j=0; j<4; j++){  
    for(int i=0; i<8; i++){
      currentMatrix[i] = (((cb[i])>>(j*8)) & 0b11111111);
    }
    displayMatrix(currentMatrix,j);
  }
}

void displayMatrix(byte *matrix, int matrixNumber){
  for(int i=0; i<=7;i++){
    lc884.setRow(matrixNumber,i,matrix[i]);
  }
}

 

Das Ergebnis findet ihr wieder im Video.

Wenn ihr eigene Banner mit einer anderen Breite kreiert, dann müsst ihr einige Werte im Sketch anpassen. Ich hatte offengestanden keine Lust mehr den Sketch zu verallgemeinern.

Weitere Methoden

Das Verschieben mittels Byte Arrays ist etwas komplizierter als man zunächst meinen sollte. Zumindest habe ich mich damit ein wenig schwergetan. Ihr findet den Sketch am Schluss des Beitrages.

Die mit Abstand einfachste Methode wäre ein bool Array, in dem jeder Dot eine separate Variable ist. In dieses Array könnte man auch die vier leeren Displays zu Beginn und die vier leeren Displays am Ende integrieren. Macht 4 + 7 (für das Arduino Banner) + 4 = 15 Einzeldisplays. Daraus würde ein 120 x 8 Array mit 960 bool Werten entstehen. Das Durchlaufen des Banners wäre so sehr einfach zu programmieren. Aber leider belegt jede bool Variable ein ganzes Byte. Diese Verschwendung von Speicherplatz ist mir dann doch zu groß.

LED Matrix Display Ansteuerung ohne MAX7219 Bibliothek

Wie schon erwähnt, hat der MAX7219 nicht übermäßig viele Register und kann deswegen recht leicht auch ohne Bibliothek angesteuert werden. Im nächsten Sketch könnt ihr sehen wie das geht.

Ich möchte den Sketch aber auch nicht im Detail erläutern, da der Beitrag sowieso schon so lang ist. Wenn ihr euch das Datenblatt danebenlegt, sollte er nicht so schwer zu verstehen sein. Wenn ihr trotzdem Fragen habt, fragt! Nur ein paar Anmerkungen:

  • Der Sketch verwendet die Standard SPI Anschlüsse für MOSI und CLK (beim UNO Pin 11 bzw. 13). Ihr müsst also ein wenig umstöpseln.
  • MISO gibt es nicht, der MAX7219 ist nicht gesprächig.
  • Da es kein MISO gibt, lässt sich der Zustand der Dots nicht abfragen.
  • Da man bei der setLed Funktion nur einzelne Dots ändern möchte, aber nur ganze Reihen Bytes schreiben kann, muss der Sketch sozusagen Buch führen. Das macht er über das Array dots.
#include <SPI.h>

const int slaveSelectPin = 10;
const int totalDevs = 4;
const int colsPerDev = 8;
const int rowsPerDev = 8;
byte dots[totalDevs][rowsPerDev] = {0}; // in diesem Array wird der Zustand der Dots gespeichert

byte a[8]={B00000000,B01100000,B10010000,B10010000,B10010000,B11110000,B10010000,B10010000};

void setup(){
  SPI.begin();
  pinMode(slaveSelectPin, OUTPUT);
  digitalWrite(slaveSelectPin, HIGH);
  for(int device=0; device<totalDevs; device++){
    wakeUp(device);                  // Normal Operation (shutdown beenden)
    displayTest(device,500);
    writeRegister(device,0x09,0x00); // Decode: 00 = Einzeldots, kein decoding
    setIntensity(device, 6);
    writeRegister(device,0x0B,0x07); // alle Reihen werden angezeigt
    clearDevice(device);
  }
  delay(500);
}

void loop(){
  setRow(0,0,B11111000);
  delay(1000);
  setLED(0,0,5,0);
  delay(1000);
  setLED(0,0,7,0);
  delay(1000);
  displayChar(1, a);
}  


void clearDevice(int device){
  for(int row=0; row<=rowsPerDev; row++){  //alles ausschalten
    writeRegister(device,byte(row),0x00);
  }
}

void setIntensity(int device, int intensity){
  writeRegister(device,0x0A,intensity);
}

void displayTest(int device, int testTime){
  writeRegister(device,0x0F,0x01); //Display Test ein
  delay(testTime);
  writeRegister(device,0x0F,0x00); //Display Test aus
}

void wakeUp(int device){
  writeRegister(device,0x0C,0x01);
}

void shutDown(int device){
  writeRegister(device,0x0C,0x00);
}

void setRow(int device, byte row, byte data){
  dots[device][row] = data;
  writeRegister(device, row+1, data); 
}

void setLED(int device, byte row, byte col, bool on){
  byte reg = row; 
  byte dataToSend;
  byte mask;
  if(on){
    mask = (1<<(col));
    dots[device][row] |= mask;
  }
  else{
    mask = ~(1<<(col));
    dots[device][row] &= mask;
  }
  dataToSend = dots[device][row];
  writeRegister(device, reg+1, dataToSend);
}

void displayChar(int device, byte *character){
  for(int i=0; i<=7; i++){
    dots[device][i] = character[i];
    writeRegister(device,i+1,character[i]);
  }
}


void writeRegister(int device, byte reg, byte data){
  digitalWrite(slaveSelectPin, LOW);
  for(int i=0; i<(totalDevs-device); i++){  
    SPI.transfer(0x00);
    SPI.transfer(0x00);
  }
  SPI.transfer(reg);
  SPI.transfer(data);
  for(int i=0; i<device; i++){
    SPI.transfer(0x00);
    SPI.transfer(0x00);
  }
  digitalWrite(slaveSelectPin, HIGH);
}

 

Zu guter Letzt…

…noch der Banner Sketch auf Basis der Byte Arrays. Hier habe ich als Denkmodell ein virtuelles Display verwendet, welches das physikalische Display quasi von rechts nach links durchwandert (siehe Schema unten). Ein bisschen wie ein Filmstreifen, der im Projektor hinter dem Objektiv entlang läuft.

Grau: Virtuelles Display, Blau: physikalisch vorhandenes Display
Grau: Virtuelles Display, Blau: physikalisch vorhandenes Display

Ich habe den Sketch so verfasst, dass ihr ihn leicht auf andere Displaygrößen anpassen könnt. Ich habe ihn z.B. auf zwei hintereinandergeschalteten 8x8x4 Matrix Display laufen lassen (dazu musste lediglich die numberOfPhysicalDisplays von vier auf acht geändert werden:

Anwendung auf ein 2x8x8x4 Display
Anwendung auf ein 2x8x8x4 Display

Der Sketch ist recht kompakt, aber einigermaßen schwer „verdaulich“. Ich habe versucht, ihn durch Kommentare halbwegs verständlich zu gestalten. Viel Spaß beim Nachvollziehen meiner Logik!

#include "LedControl.h"

const unsigned int delayTime = 150;
const int numberOfCharacters = 7;   // = A,r,d,u,i,n,o
const int numberOfPhysicalDisplays = 4;  // 4 Matrix Displays
const int displayWidth = 8;  // 8x8 Format
const int displayHeight = 8;
int numberOfSteps;            // Anzahl Verschiebeschritte
int numberOfVirtualDisplays;  // Anzahl virtuelle Displays 

LedControl lc884=LedControl(12,11,10,numberOfPhysicalDisplays);

byte banner[7][8]={{B00000000,B01100000,B10010000,B10010000,B10010000,B11110000,B10010000,B10010000},
                   {B00000000,B00000000,B00000000,B10100000,B11010000,B10000000,B10000000,B10000000},
                   {B00000000,B00010000,B00010000,B00010000,B01110000,B10010000,B10010000,B01110000},
                   {B00000000,B00000000,B00000000,B10010000,B10010000,B10010000,B10010000,B01110000},
                   {B00000000,B00000000,B01000000,B00000000,B01000000,B01000000,B01000000,B01000000},
                   {B00000000,B00000000,B00000000,B10100000,B11010000,B10010000,B10010000,B10010000},
                   {B00000000,B00000000,B00000000,B01100000,B10010000,B10010000,B10010000,B01100000}};

void setup(){
  numberOfVirtualDisplays = numberOfPhysicalDisplays + numberOfCharacters;  // das virtuelle Gesamtdisplay besteht aus 4 leeren Displays + Anzahl der Zeichen
  numberOfSteps = numberOfVirtualDisplays * displayWidth;
  for(int i=0;i<numberOfPhysicalDisplays; i++){
    lc884.shutdown(i,false);
    lc884.setIntensity(i,8);
    lc884.clearDisplay(i);
  }
}

void loop(){
  for(int i=0; i<= numberOfSteps; i++){
    calcVisibleBannerPart(i);
  }
}

void calcVisibleBannerPart(int step){
  int currentFirstVirtualDisplay = step/displayWidth; // Erstes virtuelles Display, dass auf dem physikalischen Display abgebildet wird
  int bitPosition = step % displayWidth; // Bit-Position, an der die virtuellen Displays auf zwei benachbarte physikalische Displays verteilt werden
  byte matrixToDisplay[8]; // abzubildende Matrix

  for(int currentPhysicalDisplay=0; currentPhysicalDisplay<numberOfPhysicalDisplays; currentPhysicalDisplay++){ // Gehe die physikalischen Displays durch

    if( (currentFirstVirtualDisplay + currentPhysicalDisplay) == (numberOfPhysicalDisplays - 1)){
      for(int i=0; i < displayHeight; i++){
         matrixToDisplay[i] = banner[0][i] >> (8 - bitPosition);
      }
      displayMatrix(matrixToDisplay,(numberOfPhysicalDisplays-1 - currentPhysicalDisplay));  // mein 0tes Display ist rechts!
    }


    if( ((currentFirstVirtualDisplay + currentPhysicalDisplay) > (numberOfPhysicalDisplays - 1)) && (currentFirstVirtualDisplay + currentPhysicalDisplay) < (numberOfCharacters + numberOfPhysicalDisplays - 1) ){
      for(int i=0; i < displayHeight; i++){
         matrixToDisplay[i] = (((banner[currentFirstVirtualDisplay + currentPhysicalDisplay - numberOfPhysicalDisplays][i]) << bitPosition) + ((banner[currentFirstVirtualDisplay + currentPhysicalDisplay - numberOfPhysicalDisplays+1][i]) >> (8- bitPosition))) ;
      }
      displayMatrix(matrixToDisplay,(numberOfPhysicalDisplays-1 - currentPhysicalDisplay));
    }
    
    
    if( (currentFirstVirtualDisplay + currentPhysicalDisplay) == (numberOfCharacters + numberOfPhysicalDisplays-1)){
      for(int i=0; i < displayHeight; i++){
         matrixToDisplay[i] = banner[numberOfCharacters-1][i] << bitPosition;
      }
      displayMatrix(matrixToDisplay,(numberOfPhysicalDisplays-1 - currentPhysicalDisplay));
    }
  }
  delay(delayTime);
}
  
void displayMatrix(byte *matrix, int matrixNumber){
  for(int i=0; i<=7;i++){
    lc884.setRow(matrixNumber,i,matrix[i]);
  }
}

 

26 thoughts on “LED Matrix Display ansteuern

  1. Hallo,

    ich habe das Programm für die beiden Streifen in einen MEGA geladen (brauche einen sehr großen Speicher). Nach dem Start liefen beide Streifen parallel und nicht hintereinander.
    Wahrscheinlich lag das an der Zeile:

    const int numberOfPhysicalDisplays = 4 // 4 Matrix Displays

    Nach der Änderung auf:

    const int numberOfPhysicalDisplays = 8; // 4 Matrix Displays

    Dann war die Anzeige in Reihe.

    Das ist aber eigentlich nicht mein Problem. Ich möchte einzelne Punkte auf 4 Streifen setzen und wieder Löschen.
    Falls jemand mal ein Beispiel in der Schublade hat . . .

    M.f.G.

    Hans-Ulrich

    1. Hallo Ulrich, einzelne Punkte sind doch eigentlich nicht besonders schwierig. Mit setLed(display, row, col, true/false) schaltest du sie an oder aus. Oder habe ich das Problem noch nicht verstanden? Sonst müsstest du vielleicht nochmal genauer beschreiben was du vor hast. VG, Wolfgang

  2. Hallo Wolle,

    ich bin ein absoluter Anfänger welches dieses Thema betrifft; Deine Anleitung ist wirklich klasse und sehr gut verständlich.

    MfG

    Patrick

      1. Moin moin Wolfgang und Alle in der Runde.

        Ich habe ein paar BLAUE 4 in 1 8x 32 Max7219 (1088AB) Module in die Hände bekommen.

        Betreibe ein rotes 3 in 1 Modul, das an einem Arduino Nano mit BME-P280 hängt und mir Druck, Temper…… anzeigt.

        Nun habe ich das Rote mit dem Blauen 4 in 1 Modul ausgetauscht. Doch das Ergebnis ist nicht berauschend.

        Die Anzeige scrollt die Werte / Text nach links, wie es soll.
        Nicht Kopf stehen und nicht gespiegelt ;-))))

        Doch der Durchlauf ist irgend wie falsch,
        Als wenn er etwas verzögert dem Beginn auf der 1.’n auf der 2.’n auch von vorn anfäng und danach auf dem 3, auch und auf dem 4.

        Stecke ich die rote 4 in 1 an läuft alles wieder, Tadel los.

        Ich bin nicht der Spezi und wende mich nach erfolgloser Suche zwecks einer Lösung zur „Blauen“ LED-Matrix 8×8 mit Maxim 7219 (1088AB) an Dich und die Runde..

        Nun hoffe ich, das Ihr mir weiter helfen könnt.

        Mehr als den Sketch anfügen, der die rote DotMatrix einwand frei antreibt, kann ich leider nicht dazu beitragen.

        Grüße
        Paul Preuß

        Und das ist der (für die rote Matrix) funktionierende Sketch:

        //8×32 Dot Matrix Max7219 4 x 1088AS
        #include
        #include
        #include
        #include

        #define HARDWARE_TYPE MD_MAX72XX::FC16_HW

        #define DATA_PIN 11
        #define CLK_PIN 13
        #define CS_PIN 10
        #define MAX_DEVICES 4

        MD_Parola disp = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

        Adafruit_BME280 bme;

        int temp, hum, pres; String tempString, humString, presString;

        void setup()
        {

        disp.begin(); bme.begin(0x76, &Wire);

        }

        void loop()
        {

        disp.displayClear();
        disp.displayText(„Temperatur“, PA_CENTER, 100, 100, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
        while (!disp.displayAnimate());
        delay(500);
        for (int i = 1; i <= 1; i++) //i=1; i<=1; i++)

        temp = bme.readTemperature();
        tempString = " " + String(temp) + " 'C"; // "*C " oder "~C"
        disp.print(tempString);
        delay(4000);

        disp.displayClear();
        disp.displayText("Luftfeuchte", PA_LEFT, 100, 100, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
        while (!disp.displayAnimate());
        delay(500);
        for (int i = 1; i <= 1; i++) //i=1; i<=1; i++)

        hum = bme.readHumidity();
        humString = " " + String(hum) + " %";
        disp.print(humString);
        delay(4000);

        disp.displayClear();
        disp.displayText("Luftdruck", PA_LEFT, 100, 100, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
        while (!disp.displayAnimate());
        delay(500);
        for (int i = 1; i <= 1; i++) //i=1; i<=1; i++)
        {
        pres = bme.seaLevelForAltitude(45, bme.readPressure()) / 100;
        presString = "" + String(pres) + " hp";
        disp.print(presString);
        delay(4000);
        }
        }

        1. Bin noch einmal und noch einmal alles durch gegangen und antworte mir mal selbst;-)
          Den ersten Hinweis fand ich auf dem PCB unter einem 1088AB und den Hinweis auf die Lösung in der MD_MAX72xx.h

          Die Zeile:
          #define HARDWARE_TYPE MD_MAX72XX::FC16_HW habe ich deaktiviert
          und dafür
          #define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW gesetzt.

          Nun funzen die Blauenauch, wie sie sollen.

          Sorry an Alle die schon angesetzt haben, aber vielleicht interessiert oder hilftes es ja sogar Jemanden, der diese Module verwenden möchte.

          Grüße
          Paul

      2. Hallo Wolfgang,

        Ich soll eine Teststellung für Fundino One und die komplexen Module des dazugehörigen Sets anbauen. Wir verleihen die Sets und es soll bei Rückgabe eine Kontrolle auf Funktionsfähigkeit erfolgen. Außerdem soll es möglich sein, an der Testplatine selbst programmierte Sketche im Rahmen eines Projektes im ersten Semester zu testen. Das sind meist Anfänger ohne Programmierkenntnisse. Für diesen Teil habe ich mir ein Selbstbau-Kit ohne eingelöteter Matrix und dem MAX7219 bestellt.

        Bis auf die Ansteuerung der 8×8- Matrix habe ich alles fertig. In einem passiven Testmodus, der über einen Schalter die dazu gehörigen Bustreiber aktiviert, werden die Anzeigen mit einem Spannungsregler (ohne Arduino) an einem USB- Eingang betrieben.
        Es werden da immer alle Segmente oder sollen die 64 LEDs der Matrix gleichzeitig angesteuert werden.

        Für die Ansteuerung der 8×8- Matrix mit dem Arduino wollte ich erst das Lauflicht von Dir nutzen,. Als das nicht die gewünschten Ergebnisse zeigte, wollte ich jeweils eine Reihe anschalten und wie bei einer Balkenanzeige die Spalten nacheinander aktivieren.

        Allerdings leuchtet bei aktivierter Reihe 0 die Reihe 4 dunkler mit, obwohl bei abgezogener Matrix am Arduino die Belegungen von r0 = D2 = High und c0 = D10 = LOW korrekt anliegen. Stecke ich die Anzeige an, wechselt r4 auf High.

        Das Verhalten ist mir nicht erklärlich, denn wenn ich direkt 5V und GND anlege, leuchtet nur die LED (0;0).

        Hast Du eine Erklärung?

        Mit freundlichen Grüßen
        Wolf

        1. Hallo Wolf, die kurze und ehrliche Antwort lautet schlicht: Nein, keine Ahnung! Merkwürdig, dass ausgerechnet diese eine Reihe mitleuchtet. Schwierig auf die Entfernung eine Lösung zu finden. Ich müsste die ganze Schaltung sehen und den Sketch dazu. Wenn du magst, kannst du mir mehr Informationen an Wolfgang. Ewald @wolles-elektronikkiste.de senden. Ansonsten probiere ich bei solchen Phänomenen einfach systematisch aus. Z.B. Was passiert bei Aktivierung anderer Reihen? Leuchtet dann auch immer Reihe 4 mit? Oder andere Reihen? Gibt es da Muster? VG, Wolfgang

          1. Hallo Wolfgang,

            Danke für die schnelle Antwort! Es leuchten auch eine andere LED mit, wenn es ich beispielsweise die r4 zwangsweise auf GND lege. Ob es ein Exemplarfehler ist, teste ich am Dienstag . Software habe ich 1:1 von Dir kopiert, um das zu testen. Beschäftige mich erst seit 2 Tagen mit der Matrix.

            Für meinen Testplatz soll die Ansteuerung mit dem MAX7219 bzw. auch ohne Software erfolgen.Auf der Platine verbinde ich die Spalten mit GND und schalte vielleicht mit einem Drehschalter die Reihen einzeln zu, um nicht den Strombedarf zu hoch werden zu lassen. Ich dachte nur, dass Dir bei Deinen Tests so etwas vielleicht schon mal untergekommen ist.

            Falls ich doch noch die Ursache ermittle, melde ich mich noch einmal.

            Viele Güße
            Wolf

              1. Hallo Wolfgang,

                wie versprochen, meine Rückmeldung. Die LED- Matrix war defekt, da wahrscheinlich hei der Ausleihe von den Studenten etwas falsch gemacht wurde. Dummerweise funktionierte auch die 2. Matrix nicht, aber mit einem anderen Fehler. Jetzt läuft alles und natürlich auch das Lauflicht. Trotzdem nochmals Danke!

                Viele Grüße!
                Wolf

                1. Schön, das freut mich! Danke für die Rückmeldung. Es ist immer hilfreich zu erfahren, wenn ein Problem am Ende gelöst wurde.

  3. Hallo Wolfgang
    toller Beitrag- finde ich echt spitze!
    Funktioniert tadellos – leider habe ich dann ein Problem wenn ich zwei LED 8x8x4 hintereinander schalte beginnt die Laufschrift zu flackern. Habe alle Bauteile getauscht – selbes Problem wieder.
    Hast du ne Idee woran das liegen könnte?
    Verwednde keinen Uno sondern einen ESP8266…
    LG
    Ewald

    1. Merkwürdig, ich habe es ja auch mit 2x(8x8x4) probiert, da ging es ohne Flackern. Und eigentlich ist ein ESP8266 ja schneller als ein Arduino. Also sollte zumindest auf der Seite nichts haken. Vielleicht eine Frage der Stromversorgung? Wenn du die Schrift ganz langsam laufen lässt, flackert es dann auch? Oder wenn du mal die Helleigkeit herunterregelst?

  4. Hallo Wolle, es gibt auch Display mit 4 x 8x8x4 DOT. Diese sind übereinander angeordnet. Also Matrix 4 x 4 Stück in 8×8 DOT Ausführung. Wie könnte man dort die Ansteuerung realisieren? ich will auf diesem Display Symbole die ich vorher fest erstelle, zur Anzeige bringen. In Summe sind das dann 32 x 32 DOT. Die 4 fach Anzeigen sind fertig verdrahtet und montiert. Bei Amazon heißen die 32×32 16 in 1 DOT Matrix…

    1. Hallo Camillo, das Prinzip ist immer dasselbe. Am besten nimmst du dir kariertes Papier und malst dort die Zeichen in 32 x 32 Kästchen auf. Dann teilst du das in die 16 8×8 Displays auf. Viel Schreibarbeit.

      Wie die Displays angeordnet sind, also wo sich Display 0 und wo sich Display 31 befindet, musst du schlicht ausprobieren. Auch, wo in einzelnen der Displays der Punkt 0,0 bzw. 8,8 ist, musst du schlicht mal systematisch ausprobieren.

      1. Danke für deine Antwort. Prinzipell kann ich die einzelnen Bytes als DOT Grafik festlegen. Leider werden diese aber nicht richtig angezeit. Nur 8 Display. Danach wiederholt sich das Grafik Muster.
        Habe den Sketch „Display _ static _short “ von dir verwendet. Welche Konfiguration muss ich noch bezüglich der Anzahl der Elemente machen? Habe LedControl lc884=LedControl(12,11,10,4); in 12,11,10,16
        geändert sowie unsigned long banner[8] in 32. Danach die 32 Stück 8Bit Datenfelder (Arrays) fest definiert.Ich brauche dann nur noch die Hilfe zur Konfiguration der Schleife die abgearbeitet wird. Welchen Schleifenzähler muss ich wie ändern?

        Gruß Camillo

        1. Hallo, das ist ein bisschen schwer für mich aus der Ferne zu beurteilen. Ich kenne dein Display ja nicht. Zumindest werde es 16 Displays sein, die du ansteuern musst. Aber die können ja z.B. so organsiert sein:
          0-1-2-3
          4-5-6-7
          8-9-10-11
          12-13-14-15

          Oder so:
          0-4-8-12
          1-5-9-13
          2-6-10-14
          3-7-11-15

          Oder sonst irgendwie. Das kann ich ja nicht wissen. Deswegen schrieb ich, dass du erstmal systematisch herausbekommen solltest, wo welches Display ist.

          Wenn du Glück hat und ich vermute, das wird so sein, dann sind die Displays wie im ersten meiner Optionen angeordnet. Dann nimmst du deine 32 x 32 Zeichen und teilst sie in 4 8×32 banner auf. Nenne sie z.b. banner0, banner1, banner2, banner3.

          Und dann machst du 4 mal das, was im Sketch 884_display_static_short.ino steht. Der Zähler j ist die Matrix. D.h. für banner0 durchläufst du j=0 bis j=3, für banner1 j=4 bis j=7, usw.

          Du bis bei j=0 bis j=3 geblieben, also 4 Displays und hast versucht 32 Zeilen hineinzuschreiben. Aber nach 8 Zeilen ist Schluss. Die 9. Zeile gehört zu anderen Displays.

          Schwer zu erklären und wahrscheinlich schwer zu verstehen. Ich hoffe, du kannst mir ungefähr folgen…

  5. …es klappt hervorragen. Ich weiß nicht, wozu man dann noch eine Bibliothek benötigt.

    Noch mal vielen Dank für Deine Arbeit. Wenn meine Wetterstation fertig ist, schicke ich mal ein Foto. (Der Code ist bestimmt nicht so überwältigend)

    Gruß Harald!

  6. Hallo Wolfgang
    das hat mir sehr geholfen. Ich habe gerade einen ESP32 angeschlossen. Es geht wirklich super.

    Eine Frage habe ich aber noch, warum musste ich die PIN’s für SPI nicht definieren. Wurden die Standard-Pin’s der Bibliothek SPI.h genommen?

    SS habe ich auf PIN 5 gesetzt weil bei mir CS auf 5 war.

    1. Hallo Harald,

      wie hast du denn das LedControl Objekt erzeugt? Eigentlich übergibst du da die SPI Pins, also z.B.:

      LedControl lc884=LedControl(12,11,10,4);

      VG, Wolfgang

      1. Hallo Wolfgang,
        ich habe ja gar kein Objekt erzeugt, sondern Dein Beispiel ohne Bibliothek genommen. Da am ESP32 der PIN 5 SS ist, klappt es eigentlich sehr gut. Ich habe mit Variablen für die Zeichen erzeugt und schreibe mit der Funktion displayChar(int device, byte *character).

        Leider bekommen ich keine Anzeige, wenn ich die Funktion setLED(int device, byte row, byte col, bool on) nutzen will. Ich wollte damit eine Art Kurve für den Verlauf des Luftdruckes zeigen.

        In meinem Beispiel sieht der Befehl so aus:

        setLED(0, B0001000, B0001000, 1);

        Habe ich da einen Denkfehler?

        Vielen Dank und Gruß Harald!

        1. Ach so, wir reden also über den Sketch „884_display_MAX7219_without_library.ino“. Das war mir nicht so klar. Um auf die erste Frage zurückzukommen: wenn man SPI initialisiert und keine Pins übergibt, dann werden die vordefinierten SPI Pins genommen.

          Zur zweiten Frage: die setLED() Funktion spricht nur eine einzelne LEDs an. Z.B. würde setLED(0,3,6,1) die LED in der Reihe 3, Spalte 6 am Display 0 einschalten. Bei einem 8×8 Display gehen die LEDs von 0,0 bis 7,7. Wenn du B0001000, B0001000 übergibst, dann ist das die LED 8,8 – und die gibt es nicht!

          1. Ah, alles klar. Mein Fehler war, dass ich in bit gedacht haben und nicht in Byte!!

            Probiere es gleich aus!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.