MAX7219 Treiber zur Ansteuerung von 8x8 LED-Matrix und 8fach-7Segment-Display verwenden

Um eine 4fach-7Segment-Anzeige direkt anzusteuern, braucht es, wie in dieser Anleitung gezeigt zusätzlich 8 Widerstände, ein 74HC595 Schieberegister und jede Menge Leitungen. Für eine 8fach 7Segment-Anzeige, die dann aus 64 einzelnen LEDs besteht, verdoppelt sich der Aufwand sogar noch.

Das gleiche gilt für eine 8x8-LED-Matrix, wie ich sie hier beschrieben habe. Auch hier sind 64 einzelne LEDs anzusteuern, was zwei 74HC595 benötigt.

Außer dem Hardware-Aufwand ist auch noch ein ständig laufendes Programm vonnöten, dass das Multiplexing bewerkstelligt. Auf dem Raspberry Pi habe ich dabei immer wieder Ungleichmäßgikeiten festestellen müssen, wenn noch andere Programme im Hintergrund liefen.


Sehr viel eleganter und mit weniger Bauteilen und Leitungen kann man ein 8fach-7Segment-Display oder eine 8x8-LED-Matrix mit einem speziellen Chip, einem LED Treiber, ansteuern.

Ein gebräuchlicher LED-Treiber ist der 7219 von Maxxim, diesen gibt es in den Bauformen DIP (also sockelbar) und als SMD.



Er hat folgende Vorteile / Eigenschaften: Besonders schön ist hier, dass man das anzuzeigende Bitmuster nur einmal in den 7219 hereinschieben muss und sich dann im Programm um weitere Dinge kümmern kann, weil der 7219 das Multiplexing und die weitere Anzeige übernimmt, und zwar so, dass alle LEDs schön gleichmäßig leuchten, was - wir erinnern uns - ja sonst nur mit reichlich Vorwiderständen möglich war.


Mit dem 7219 muss man im Prinzip nur die Pins der Anzeige an die 16 Pins DIGIT 0 bis 7 und SEGMENT A bis G und DP (Digitalpunkt) anschließen, V+ mit +5V Spannung versorgen, Masse an GND anschließen und die SPI-Leitungen des Arduino an CLK, CS (LOAD) und DIN (Data in). Über den noch übrige Pin ISET wird ein Referenzwiderstand mit 10 kΩ für die maximale Grundhelligkeit angeschlossen; hier könnte man z. B. einen Fotowiderstand einsetzen, um eine automatische Helligkeitsanpassung an das Tageslicht zu erreichen.

Die prinzipielle Beschaltung einer 8fach-7Segment-Anzeige zeigt das Bild rechts. Wichtig zu wissen ist, dass der MAX7219 zur Zusammenarbeit mit Common Cathode-Anzeigen vorgesehen ist. Es gibt auch noch Common Anode-Anzeigen, die nicht mit dem 7219 verwendet werden können.

Darüber müssen wir uns allerdings nicht allzugroße Gedanken machen, wenn wir gleich ein fertiges Modul kaufen, was sehr zu empfehlen ist und was preislich evtl. sogar günstiger kommt. Diese Module sind gleich mit einer 8fach-7Segment-Anzeige oder einer 8x8-LED-Matrix-Anzeige, einem MAX7219 und den obligatorischen Widerständen und Kondensatoren bestückt. Wir haben dann jeweils nur noch 5 Leitungen an den Arduino anzuschließen.



Beide Module haben links die Eingangspins und rechts die Ausgangspins. Wenn man nun die rechten Pins des 1. Moduls mit den linken Pins des 2. Moduls verbindet, die Module also kaskadiert, dann kann man ähnlich wie beim 74HC595 Schieberegister die Daten durchschieben. Bildlich gesprochen wird der 1. Block links in das 1. Modul geschoben. Der 2. Block wird wieder links in das 1. Modul geschoben, was dann Block 2 beinhaltet und der alte 1. Block wird weitergeschoben in das 2. Modul. Ein evtl. 3. eingeschobener Block schiebt das Ganze wieder eins weiter. Man schiebt also soviele Blöcke ein, wie man Module aneinander gereiht hat, so dass jedes versorgt ist. Zu beachten ist, dass im Modul ganz rechts der zuerst geschobene Block steht. Man "schreibt" also blockweise von rechts nach links.

In das 1. Modul werden die Blöcke über SPI eingeschoben. Dies geschieht über die Eingangspins mit den Anschlüssen (von oben nach unten):
Die wie beschrieben an einen Arduino angeschlossen werden. Es können auch andere Port benutzt werden. Diese müssen dann nur im Source-Code entsprechend eingetragen werden.

Als Plattform benutze ich aber das hier beschriebene Prototype Shield mit Mini-Breadboard, auf dem D8 bis D13 schon mit LEDs und Schaltern bestückt sind, die uns für die Schaltung noch nützlich werden dürften.

Der MAX7219 wird über 16-bittige Befehle über die SPI-Schnittstelle angesteuert, wobei das 1. Byte den Befehl und das 2. Byte die Daten enthält.

Da es nicht soviele Befehle gibt, werden nur die unteren 4 Bits des 1. Bytes ausgewertet. Die Bitordnung beim Datenbyte lautet: Most significant Bit links, Least significant Bit rechts.



Die Befehle sind wie folgt definiert:



No-Op steht dabei für No Operation, also "Nichtstun" und ist praktisch bei Kaskadierung, wenn ein Modul seinen Zustand behalten soll.

Digit 0 bis Digit 7 gibt die Stelle des 7-Segment-Displays bzw. der Zeile der 8x8-Matrix an.

Decode Mode Schaltet den Decode-Mode an, bei dem nur die unteren 4 Datenbits (D3-D0) berücksichtigt werden, um einen BCD-Code auszugeben (siehe Datenblatt).

Intensity steuert die Helligkeit der Anzeige in 16 Stufen (0 bis 15), die über die Bits D3-D0 übergeben werden. Dazu nutzt der 7219 Pulsweitenmodulation (PWM).

Mit dem Shutdown-Befehl kann der MAX7219 in den Ruhemodus versetzt werden. Das Display geht aus, die Speicherinhalte bleiben vorhanden und werden beim Aufwecken wieder angezeigt. Hiermit kann Strom gespart werden oder der Befehl wird dazu benutzt, ein Blinken zu generieren.

Display Test checkt, ob alle Segmente heil sind, indem es alle LEDs mit maximaler Helligkeit leuchten lässt.

Auch die Kommunikation mit dem 7219 über da Protokoll ist nicht sonderlich schwierig:

Bibliothek LedControl


Das Protokoll und die Lowlevel-Befehle zu benutzen, können wir uns aber auch sparen, denn diese Mühe haben sich bereits Andere gemacht und es existieren einige Bibliotheken, um LED-Anzeigen über den MAX7219 anzusprechen.

Ich habe mich dabei für die Bibliothek LedControl von Eberhard Fahle entschieden, weil diese sowohl für Segmentanzeigen als auch Matrizen taugt, speichersparend programmiert ist, aber doch über die nötigsten Funktionen zum Ansprechen der Anzeigen bietet. Außerdem gibt es eine ausführlichere Dokumentation, was bei Bibliotheken leider nicht immer Usus ist.

Die Bibliothek ist über den Bibliotheksverwalter in der Arduino-IDE schnell installiert (siehe Bild). Den Gebrauch kann man wohl am besten mit zwei kleinen Beispielprogrämmchen erklären.

Source-Code für 8fach-7Segment-Anzeige

//////////////////////////////////////////////////////// // (C) 2019 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include "LedControl.h" // Lib für 8fach-7Segment-Anzeige mit MAX7219 LedControl lc=LedControl(7,5,6,1); // Anzeige initialisieren // 7 = DIN // 5 = CLK // 6 = CS / LOAD // 1 = 1 MAX7291 void setup() { lc.shutdown(0,false); // MAX7219 aufwecken (schläft zu Beginn) lc.setIntensity(0,8); // Helligkeit setzen (0 bis 15) lc.clearDisplay(0); // Display leeren } void segText(String strg) { // schreibt String bis 8 Zeichen auf Segmentanzeige, Punkte werden eingeschoben unsigned int siz=strg.length(); char c; int p; int i; p=7; i=0; while (p>=0) { // nächster Buchstabe ein Punkt, Komma, Doppelpunkt? if (i < siz-1) { c=strg[i+1]; if (c=='.' || c==',' || c=='.' || c==':' || c==';') { lc.setChar(0,p,strg[i],true); siz++; i+=2; p--; continue; } } if (i < siz) { lc.setChar(0,p,strg[i],false); } else { lc.setChar(0,p,' ',false); } i++; p--; } } void segBin (uint8_t* bin) { // schreibt 8 Zeichen mit Segmentdefinitionen auf Segmentanzeige, (Punkte durch +0x80) uint8_t c; int p; int i; p=7; i=0; while (p>=0) { c=bin[i]; lc.setRow(0,p,c); i++; p--; } } void loop() { // Definition von Anzeigen, die undefinierte Zeichen enthalten // 0x01 = Mitte // 0x02 = oben links // 0x04 = unten links // 0x08 = unten // 0x10 = unten rechts // 0x20 = oben rechts // 0x40 = oben // 0x80 = Punkt // // 40 // __ // 2 |__| 20 (Mitte=1) // 4 |__| 10 // // 8 uint8_t arduino[8] = {0x77,0x05,0x3d,0x1c,0x10,0x15,0x1D,0x00}; // A r d u i n o uint8_t wwwqulde[8] = {0x2b,0x2b,0xab,0x73,0x3e,0x8e,0x3D,0x4f}; // w w w. q u l. d e while (1) { // Texte anzeigen segText ("25.01.16:57"); delay(2000); segText ("c-o-o-1"); delay(2000); segBin (arduino); delay(2000); segBin (wwwqulde); delay(2000); // Fade in and out lc.setIntensity(0,0); segText ("c-o-o-1"); for (int w=0; w<5; w++) { for (int i=0; i<16; i++) { lc.setIntensity(0,i); delay (50); } for (int i=16; i>=0; i--) { lc.setIntensity(0,i); delay (50); } } lc.setIntensity(0,8); // alles Blinken lassen segText ("c-o-o-1"); for (int w=0; w<5; w++) { lc.shutdown(0,true); delay (500); lc.shutdown(0,false); delay (500); } delay(2000); // 1. Stelle blinken lassen segText ("25.01.16:57"); for (int w=0; w<5; w++) { lc.setChar(0,7,' ',false); delay (500); lc.setChar(0,7,'2',false); delay (500); } // 2. Stelle blinken lassen segText ("25.01.16:57"); for (int w=0; w<5; w++) { lc.setChar(0,6,' ',true); delay (500); lc.setChar(0,6,'5',true); delay (500); } } //end while 1 } //end loop() Der Quellcode sollte soweit selbsterklärend sein. Kommentare an den entsprechenden Stellen dürften schwieriger zu verstehende Passagen erklären. Durch den Befehl LedControl lc=LedControl([Ports]) wird ein Objekt lc erzeugt, auf das wir nachfolgend zugreifen können, um das Display zu manipulieren.

Die selbstgeschriebene Funktion segText() schreibt einen beliebigen Text auf das Display. Punkte werden einfach mit in den String eingefügt und dann an den entsprechenden Stellen gesetzt. Allerdings sind nicht alle Buchstaben vorhanden, sondern nur die Ziffern und die weiteren Zeichen "ABCDEFHLP-_" und das Leerzeichen.

Möchte man andere Zeichen benutzen, muss man auf die Funktion segBin() zurückgreifen, der 8 Bytes mit den Segmentdefinition übergeben wird. So kann man dann jede beliebige Konstellation darstellen.

In der Loop()-Funktion werden dann ein paar Dinge dargestellt. Hier findet sich auch eine kleine Erklärung, wie einzelne Zeichen als Bytedefinition zu definieren sind.

Hier eine kleine Demonstration des 8fach-7Segment-Anzeige-Demo-Programmes als Video:



Source-Code für 8x8-LED-Matrix-Anzeige

//////////////////////////////////////////////////////// // (C) 2019 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include "LedControl.h" // Lib für 8fach-7Segment-Anzeige mit MAX7219 LedControl lc=LedControl(7,5,6,1); // Anzeige initialisieren // 7 = DIN // 5 = CLK // 6 = CS / LOAD // 1 = 1 MAX7291 void setup() { lc.shutdown(0,false); // MAX7219 aufwecken (schläft zu Beginn) lc.setIntensity(0,8); // Helligkeit setzen (0 bis 15) lc.clearDisplay(0); // Display leeren } void showBin (uint8_t* bin) { // schreibt 8 Bytes mit Zeilendefinitionen zu 8 bit auf Matrix uint8_t c; int p; int i; lc.clearDisplay(0); p=7; i=0; while (p>=0) { c=bin[7-i]; lc.setRow(0,p,c); i++; p--; } } void showBinCol (uint8_t* bin) { // schreibt 8 Bytes mit Spaltendefinitionen zu 8 bit auf Matrix uint8_t c; int p; int i; p=7; i=0; while (p>=0) { c=bin[7-i]; lc.setColumn(0,p,c); i++; p--; } } void scroll (char *text[8], uint16_t cols, uint16_t verzoegerung) { // cols=Anzahl von Spalten gesamt uint8_t colArray[8] = {0, 0, 0, 0, 0, 0, 0, 0}; //ersten Screen reinschieben for (uint8_t c=0; c<7; c++) { for (uint8_t i=0; i<7;i++) { colArray[i]=colArray[i+1]; } uint8_t colByte=0; for (uint8_t row=0; row<8; row++) { if (text[row][c] == '#') colByte += 1<<(7-row); } colArray[7]=colByte; showBinCol(colArray); delay (verzoegerung); } // gesamten Text durchscrollen for (uint16_t col=0; col<cols-8; col++) { for (uint16_t c=col; c<col+8; c++) { // 8 ZeilenBytes holen uint8_t colByte=0; for (uint8_t row=0; row<8; row++) { if (text[row][c] == '#') colByte += 1<<(7-row); } colArray[c-col] = colByte; } showBinCol(colArray); delay (verzoegerung); } // letzten Screen auch noch rausschieben for (uint8_t c=0; c<8; c++) { for (uint8_t i=0; i<7;i++) { colArray[i]=colArray[i+1]; } colArray[7]=0; showBinCol(colArray); delay (verzoegerung); } } void loop() { while (1) { lc.clearDisplay(0); // die Dots der Reihe nach durchschalten for (int i=0; i<64; i++) { uint8_t x=i%8; uint8_t y=i/8; lc.setLed(0,y,x,true); delay (25); } for (int i=0; i<64; i++) { uint8_t x=i%8; uint8_t y=i/8; lc.setLed(0,y,x,false); delay (25); } delay(1000); // kleine Warte-Animation // 0 1 2 3 4 5 6 7 // . . # # # # . . 0 // . # . . . . # . 8 // # . . . . . . # 16 // # . . . . . . # 24 // # . . . . . . # 32 // # . . . . . . # 40 // . # . . . . # . 48 // . . # # # # . . 56 lc.clearDisplay(0); uint8_t dots[20] = {2,3,4,5, 14, 23, 31, 39, 47, 54, 61,60,59,58, 49, 40, 32, 24, 16, 9}; for (uint8_t w=0; w < 10; w++) { for (int i=0; i<20; i++) { uint8_t x=dots[i]%8; uint8_t y=dots[i]/8; lc.setLed(0,y,x,true); delay (20); lc.setLed(0,y,x,false); } } lc.clearDisplay(0); delay(1000); // ein 8x8-Matrix-Bild anzeigen uint8_t pic[8] = { 0b01100110, 0b10011001, 0b10000001, 0b10000001, 0b10000001, 0b01000010, 0b00100100, 0b00011000 }; showBin (pic); delay(2000); // Fade in and out for (int w=0; w<5; w++) { for (int i=0; i<16; i++) { lc.setIntensity(0,i); delay (50); } for (int i=16; i>=0; i--) { lc.setIntensity(0,i); delay (50); } } lc.setIntensity(0,8); // alles Blinken lassen for (int w=0; w<5; w++) { lc.shutdown(0,true); delay (500); lc.shutdown(0,false); delay (500); } lc.clearDisplay(0); delay(1000); // einen Scroll-Text durchlaufen lassen char *scrollText[8] = { "###...##.##....####....####....####...#..............#...#...#####..####.......####....#####", ".#...#..#..#..#....#..#....#..#....#..#..............#...#...#......#...#......#...#...#....", ".#...#.....#..#.......#....#..#....#..#..............#...#...#......#...#......#....#..#....", ".#...#.....#..#.......#....#..#....#..#......#####...#...#...####...####.......#....#..####.", ".#...#.....#..#.......#....#..#....#..#..............#...#...#......#...#......#....#..#....", ".#....#...#...#.......#....#..#....#..#..............#.#.#...#......#...#......#....#..#....", ".#.....#.#....#....#..#....#..#....#..#..............##.##...#......#...#..##..#...#...#....", "###.....#......####....####....####...#####..........#...#...#####..####...##..####....#####" }; scroll (scrollText,92,50); delay(1000); } //end while 1 } //end loop() Ich habe mich bei der Demonstration an der Demo der direkt Ansteuerung eines 1088BS, die ich schon einmal mit dem Raspberry Pi vorgeführt hatte, orientiert. Diese Videos sollten sich ähneln und man sieht, dass die Ansteuerung mit dem MAX7219 der direkten in Nichts nachsteht, im Gegenteil sogar einige zusätzliche Features wie das Ein-/Ausblenden bietet.

Auch für die MAX7219-Version der 8x8-Matrix gibt es natürlich ein Video:



Mein Fazit ist, dass die Ansteuerung mit einem LED-Driver wie dem MAX7219 sehr viel einfacher ist und jede Menge Einzelteile, Arbeit und Zeit beim Aufbau der Schaltung spart. Gerade die fertig gelöteten Module, die Anzeige und 7219 schon komplett enthalten, sind eine tolle Sache, die auch nicht allzusehr ins Geld gehen und sich auf jeden Fall lohnen.