LED Matrix: Praktische Umsetzung & Programmierung

Nachdem wir bereit kennen gelernt haben, wie eine LED-Matrix speziell für das Multiplexing geplant wird, werden wir heute konkret. Ich habe eine 8×8 Matrix aus LEDs gebaut und werde euch anhand dieses Beispiels zeigen, wie die Matrix angeschlossen und angesteuert wird.
Falls ihr die theoretische Planung noch nicht gelesen habt, solltet ihr einen Blick darauf werfen. Dort sind einige Grundlagen erwähnt, ohne die ihr diesen Artikel nicht unbedingt versteht.

Specs

LED Matrix Zeilen

Die Kathoden der LEDs sind elektrisch in Zeilen miteinander verbunden

Hier sind die Eckdaten für meine Matrix:

  • 8 Spalten
  • 8 Zeilen
  • Flußstrom einer LED: 20mA
  • Flußspannung: 2,0V

Die Matrix zusammenzubasteln ging recht einfach. Ich habe die LEDs reihenweise eingesetzt und die Kathoden (Minuspol) zur Seite gebogen, sodass die Kathoden einer Zeile verbunden sind.

Die Pins wurden leicht gekürzt und direkt aneinander gelötet (siehe Bild rechts). Dies habe ich mit allen Zeilen so gemacht, bis alle Kathoden in Spalten miteinander verbunden sind.

LED Matrix mit Zeilen und Spalten

Über den zeilenweise verbundenen Kathoden liegt eine zweite Lochrasterplatine zur Isolation, die Anoden sind in Spalten miteinander verbunden.

Danach kam der schwierigste Teil: Als Isolation zwischen Kathoden und Anoden habe ich eine zweite Lochrasterplatine auf die Anoden (Pluspol) gefädelt. Diese Platine liegt also zwischen den negativen Pins der LEDs und den Anoden, welche ich spaltenweise umgebogen habe.

Die Pins wurden vor dem Löten in den Reihen und Spalten jeweils etwas gekürzt, sodass sich nur 2 Pins gleichzeitig überlagern.

Mit genügend Flussmittel und Lötzinn wurden die Pins direkt aneinander gelötet, für die Matrix selbst ist also keine aufwendige Verkabelung nötig, was Kosten und Nerven spart.

Die Matrix besteht also nur aus 64 LEDs, 2 Lochrasterplatinen und etwas Lötzinn. Die LEDs habe ich billig über eBay erstanden, gekostet hat mich die gesamte Matrix < 2 EUR gekostet(!).

Interfacing

Das Anschließen der Matrix an einen Mikrocontroller oder Steuerchip ist ebenfalls sehr einfach. Im Fall der 8×8 Matrix braucht man „nur“ 16 Kabel, welche von den Ausgängen der Steuereinheit an einen Transistor führen. Der Transistor wird nun entweder an den Pluspol der Stromversorgung und den Pluspol der LED-Gruppe angeschlossen, oder negativ zu negativ.
Ob eure Spalten positiv sind und Reihen negativ oder Umgekehrt ist theoretisch erst einmal egal. Je nachdem wie ihr euch entscheidet, müsst ihr nur in der Software einen kleinen Teil ändern.

Es sollte aber schon entschieden werden, wie herum gemultiplext werden soll. Ich nehme das „gewohnte“ Zeilen-Multiplexing. Das heißt, dass ich nur eine Zeile gleichzeitig einschalte. In dieser Zeile werden dann die gewünschten Spalten eingeschaltet. Spalten und Zeile werden wieder ausgeschaltet, und die nächste Zeile wird angezeigt.
Daher wissen wir, dass in einer Spalte immer nur 1 LED gleichzeitig an ist.
Diese Information ist nun von Vorteil, denn es muss immer noch ein Vorwiderstand in den Schaltkreis eingefügt werden, sonst werden die LEDs durch zu hohe Spannung zerstört.
Der Widerstand berechnet sich durch R=U/I.

LED Matrix Interfacing

Die LED-Matrix ist an einen Arduino Mega angeschlossen. Im Hintergrund sind die Transistoren und Widerstände erkennbar.

Da wir nun glücklicherweise wissen, dass I für eine Spalte immer konstant ist (hier 20mA, da nur eine LED leuchtet), können wir den passenden Widerstand ausrechnen. (Würde der Widerstand vor/nach einer Zeile angebracht wäre die Zeile je nachdem wie viele LEDs leuchten heller oder dunkler)
Wie sich später noch zeigen wird, kann dieser Widerstand etwas niedriger gewählt werden, da die LEDs einer Zeile nur 1/8 der normalen Zeit an sind, also auch nur 1/8 des Lichtes abgeben. Dies kann durch eine höhere Spannung kompensiert werden. Aber Vorsicht: Gerade für Testläufe, in denen die Software noch nicht ganz fehlerfrei ist, sollte noch nicht zu viel Spannung gegeben werden. Hängt das Programm, sind die LEDs eventuell doch die ganze Zeit an, haben keine Zeit um abzukühlen und brennen aufgrund zu hoher Spannung durch.

Zurück zum Thema: Ich verwende also einen Vorwiderstand von (5-2)V/0,02A = ~150 Ohm in jeder Spalte. Dadurch ist sicher gestellt, dass jede Spalte gleich hell leuchtet.

Programmierung

Nun bringen wir endlich Bilder auf das Display!

Die Theorie ist: Wir schließen den Stromkreis für eine Zeile, schließen dann den Stromkreis für die Spalten, die in dieser Zeile leuchten sollen, warten ein bisschen, und öffnen den Stromkreis für Spalten und Zeilen wieder. Das wird für jede Zeile der Matrix gemacht.
Diese Anwendung schreit geradezu nach For-Schleifen!

Die Anwendung lässt sich mit folgendem Pseudo-Code ausdrücken:

FÜR JEDE ZEILE R

	schließe Stromkreis für die Zeile

	FÜR JEDE SPALTE C

		soll die LED in dieser Spalte leuchten?
			ja: schließe Stromkreis für die Spalte

	ENDE SPALTENDURCHLAUF

	FÜR JEDE SPALTE
		öffne Stromkreis für die Spalte
	ENDE SPALTENDURCHLAUF

	öffne Stromkreis für die Zeile

ENDE ZEILENDURCHLAUF

Erklärung: Die äußere For-Schleife zählt r für die Zeilen (rows) durch. Die Zeile wird eingeschaltet, dann wird die innere For-Schleife für jede Spalte (column, c) durchlaufen. Soll eine Spalte leuchten, wird der Output auf HIGH gestellt, sodass der Transistor den Stromkreis schließt.
Danach werden alle Outputs für die Spalten wieder auf LOW gestellt, und die Zeile ebenfalls. Dies wiederholt sich nun so oft, bis alle Zeilen durchlaufen sind.

Wie weiß das Programm, welche Spalten leuchten müssen?
Unsere Matrix hat 8 Zeilen und 8 Spalten. Also 2 Dimensionen mit je 8 Elementen. Die Dimensionen (Zeile/Spalte) lässt sich ganz einfach mit einem Array ausdrücken:

char display[8][8] = {
{ 1, 0, 0, 1, 1, 0, 0, 1 }, //display [0]
{ 1, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 1, 0, 0, 0, 0, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 0, 0, 0, 0, 1, 1 },
{ 1, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 0, 0, 1, 1, 0, 0, 1 }, //display [7]
};

Hier wurde ein zweidimensionales Array dargestellt. Um das ganze noch etwas zu optimieren, bedienen wir uns der Tatsache, dass die Matrix genau 8 Spalten hat, und ein char genau 8 Bits.
Dadurch fällt die zweite Dimension des Arrays weg, und wir benutzen die Bits des Char-Arrays als zweite Dimension. Hört sich auf jeden Fall ziemlich Cool an.
Also:
Anstatt für jede Zeile ein Array aus Chars anzulegen, welche die Spalten repräsentieren, können wir die 8 Bits in einem Char nehmen, und die Spalten direkt damit repräsentieren:

char display[8] = {	B10011001,	//dezimal: 153, hex: 99
					B10100101,
					B11000011,
					B10000001,
					B10000001,
					B11000011,
					B10100101,
					B10011001 };

In unserer zweiten For-Schleife müssen wir dann nur prüfen, welches Bit in dem (Spalten-)Char 1 ist. Und das machen wir mit einer Bit-Maske und dem bitweisen AND.
Die Bitmaske ist für uns 00000001. Wenn wir damit ein Char AND-verknüpfen, gibt es nur 2 Ergebnisse: 00000000 und 00000001. Nur das letzte Bit kann sich ändern, da die restlichen Bits der Maske 0 sind und mit AND verknüpft werden. Das letzte Bit des Ergebnisses wird also nur 1, wenn das letzte Bit des Chars eine 1 ist.
Wir haben also eine Methode, um das letzte Bit eines Chars zu prüfen:

IF(display[R] & B00000001)
	ja: das Letzte Bit von display[R] ist eine 1

Diese Methode können wir noch nicht ganz einsetzen: Es wird schließlich immer nur das letzte Bit geprüft. Eine If-Bedingung gilt in C als Wahr, wenn der Wert des Operanden genau gleich 1 ist. Binär also 00000001. Wir könnten jetzt das Bit in unserer Maske für jeden Durchlauf 1 Stelle weiter nach links verschieben um alle Spalten abzutesten, allerdings erhalten wir als Ergebnis dann 1,2,4,8,16,32,64 oder 128, falls das entsprechende Bit im Char eine 1 ist. Also schieben wir nicht die Maske nach Links, sondern die zu prüfenden Bits nach rechts:

FÜR JEDE SPALTE C

	Ist (display[R] >> C) & B00000001 wahr?
		ja: schließe Stromkreis für die Spalte

ENDE SPALTENDURCHLAUF
LED Matrix Anzeige

Je nach Schriftgröße passt leider nur ein Buchstabe auf die Matrix. Hier: Ein C

Mit dieser Schleife können also nun die einzelnen Spalten geprüft werden. Das Bild ergibt sich durch die Bits im Array. Die Elemente im Array repräsentieren die Zeilen (rows), die Bits in den Elementen repräsentieren die Spalten.
Wir können nun also ein statisches Bild auf die LED-Matrix zaubern!

Da 8×8 LEDs nur beschränkt Platz für z.B. einen Text bieten, wäre eine Laufschrift sinnvoll. Wie dies zu realisieren ist, könnt ihr euch schon vorab auf GitHub ansehen in meiner Repository: TacticalCode/LED_matrix_scrolltext.
Den Code und dessen genauen Ablauf werde ich in einem separaten Post erklären.
Zur Übersicht hier noch ein Beispiel für ein C-Programm, welches ein Statisches Bild auf die LED-Matrix zeichnet:
Beachte: Die Pin-Nummern in diesem Beispiel sind nur theoretisch!

// Anzahl der Zeilen und Spalten in der Matrix
const int num_rows = 8;
const int num_cols = 8;

// Bild, welches angezeigt werden soll
const char display[num_rows] = {	B10011001,
						B10100101,
						B11000011,
						B10000001,
						B10000001,
						B11000011,
						B10100101,
						B10011001 };

// Bitmaske zur Prüfung der einzelnen Bits im Char
const char bitmask = B000001;

void setup()
{
	// Zeilen auf Output und LOW stellen
	for(int i = 0; i < num_rows; i++)
	{
		pinMode(start_rows + (2*i), OUTPUT);
		digitalWrite(start_rows + (2*i), LOW);
	}

	// Spalten auf Output und LOW stellen
	for(int i = 8; i < num_cols+8; i++)
	{
		pinMode(i, OUTPUT);
		digitalWrite(i, LOW);
	}
}

void loop()
{
	// Für jede Zeile r...
	for(int r = 0; r < num_rows; r++)
	{
		digitalWrite(r, HIGH);

		// Für jede Spalre c...
		for(int c = 8; c < num_cols+8; c++)
		{
			// Prüfen, welche Spalte leuchten soll
			if((display[r] >> c-8) & bitmask)
				digitalWrite(c, HIGH);
		}
		// Spalten wieder ausschalten
		for(int c = 8; c < num_cols+8; c++)
		{
			digitalWrite(c, LOW);
		}
		// Zeile wieder ausschalten
		digitalWrite(r, LOW);
	}
}

Die Pins für die Zeilen sind in diesem Beispiel 0-7, die Spalten sind Pins 8-16. Das Offset für die Spalten Beträgt also 8, daher fangen die For-Schleifen für die Spalten immer bei 8 an, und display[r] wird nicht um c verschoben, sondern c-8, da c um 8 zu hoch ist.
Wie gesagt, seht euch das GitHub-Repo für meine Laufschrift an, falls ihr noch ein konkreteres Beispiel braucht.

Der Artikel zur Laufschrift, in dem ich die Programmierung mehr im Detail erläutere, wird bald online sein und hier verlinkt werden.

MfG
Damon Dransfeld

Dieser Eintrag wurde veröffentlicht in Hardware Hacks
Bookmarken: Permanent-Link Schreibe einen Kommentar oder hinterlasse einen Trackback: Trackback-URL.
Achtung: Wordpress interpretiert bestimmte Zeichenfolgen als Markup und verändert diese. Nutzt für Programmcode lieber Gist oder PasteBin-Services und verlinkt die Code-Schnipsel.

Post a Comment

Ihre E-Mail wird niemals veröffentlicht oder verteilt. Benötigte Felder sind mit * markiert

*
*

Du kannst diese HTML Tags und Attribute verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>