Drucken

LED Cube der Größe 3x3x3 mit Arduino

Einleitung

Dieses Dokument dient als technische Dokumentation für einen LED CUBE der Größe 3 x 3 x 3. Es werden die notwendigen Bauteile, der Zusammenbau und die Software beschrieben. Der LED CUBE soll wie in der folgenden Abbildung aussehen.

Für einen LED CUBE der Größe 3 x 3 x 3 werden 27 LEDs benötigt. Um diese nicht einzeln ansteuern zu müssen wird eine Matrixschaltung verwendet. Dabei reduzieren sich die Steuerleitungen für die LEDs auf nur 12. Eine LED besitzt zwei Anschlüsse, die Anode (langes Beinchen) und die Kathode (kurzes Beinchen). In der folgenden Abbildung werden alle LEDs einer Ebene mit den Verbindungen der Kathoden dargestellt. Alle Kathoden auf einer Ebene sind miteinander verbunden. Bei drei Ebenen werden daher drei Steuerleitungen für die Kathoden benötigt. Der Abstand der LEDs ist hier ca. 20 mm.

Die drei Ebenen sind über die Anoden der jeweils 9 LEDs miteinander verbunden. Es bilden also immer 3 LEDs eine Spalte. Damit ergeben sich 9 Steuerleitungen für die Anoden.

Bauteile

Für den Bau eines LED CUBEs werden die folgenden Bauteile benötigt:

Zusätzlich werden noch Schaltdraht in verschiedenen Farben und Werkzeug zum Verarbeiten und Löten der Bauteile benötigt.

Zusammenbau

Der LED-CUBE besteht aus drei Komponenten:

LED Cube

Der eigentliche LED CUBE besteht aus 27 LEDs. Es befinden sich immer 9 LEDs auf einer Ebene. Um eine Ebene gleichmäßig und gerade erstellen zu können, bedient man sich einem Hilfsmittel. Man nimmt eine Holzplatte und bohrt 9 Löcher (Durchmesser 5mm) mit einem Abstand von 20mm. Bei jeder LED wird die Anode wie im Bild links gebogen. Anschließend werden 9 LEDs auf das Brett gesteckt (siehe Bild Mitte). Die Kathoden werden zur jeweils nächsten LED umgebogen und verlötet (siehe Bild rechts).

  

Dieser Vorgang wird dreimal wiederholt. Das Ergebnis sieht dann so aus.

Jetzt müssen die drei Ebenen miteinander verbunden werden. Die vorher gebogenen Anoden werden verlötet. Abstandshalter sorgen für einen parallelen Verlauf der Ebenen (siehe Bild links).

 

Das Ergebnis – der fertige LED CUBE – sieht dann so aus:

Sockel

Der Sockel besteht aus einem schwarzen Kunststoffgehäuse mit einer Klappe für einen 9V-Block. Um den LED CUBE auf dem Sockel zu befestigen werden entsprechende Bohrlöcher für die 12 Steuerleitungen benötigt. Dazu erstellt man eine Schablone (siehe Bild rechts) und befestigt sie mit Kleber auf dem Gehäuse. Mit einem 1mm Bohrer werden die Löcher gebohrt (siehe Bild links).

Um den LED CUBE später ein- und ausschalten zu können, benötigt man noch eine Öffnung für den Schalter.

Platine

Bevor man die Bauteile auf die Platine löten kann, muss man sich zunächst über die Schaltung klar werden. Mit der Software EAGLE (Einfach Anzuwendender Grafischer Layout Editor) wurde der folgende Schaltplan erstellt.

Links unten befindet sich der Spannungsregler, der die Spannung auf 5V begrenzt. Oben wird der eigentliche Controller, ein Amtel ATmega 328 mit einem 16 MHz Quarz und den 12 Steuerleitungen (Ebenen 0 - 3, Spalten 1 - 9) dargestellt. Zusätzlich wurde ein Anschluss („USB to serial communcation“) vorgesehen, um den Controller direkt über FTDI (siehe Bild unten) programmieren zu können. Das FTDI-Board (siehe Bild unten) ist nicht Bestandteil des LED CUBEs!

Vom Schaltplan muss man nun irgendwie zum eigentlichen Löten auf einer Platine kommen. Dazu ist das folgende Mapping hilfreich. Es beschreibt die Anschlüsse des Controllers.

Der eigentliche Controller (Atmel ATmega 328) sieht so aus.

Mit Hilfe des obigen Mappings und der Software „Lochmaster“ kann man die Bauteile für das Löten planen. Es wurde eine Streifenrasterplatine verwendet der Größe 55 x 64 mm. Das linke Bild zeigt die Platine von oben, das rechte Bild die Platine von unten.

 

In echt sieht das dann so aus (vor dem Löten):

 

Die schwarzen Punkte auf dem rechten Bild zeigen die Stellen, an denen die Streifenrasterplatine durchtrennt werden muss. Nach dem Löten sieht es dann so aus:

 

Die roten Drähte stehen für 5V, die blauen Drähte für GND.

Endmontage

Die fertige Platine kommt in die untere Schale des Gehäuses. Jetzt muss noch der LED CUBE mit der Platine verbunden werden (Hochzeit).

Die orangen Drähte sind die Kathoden, die für die Steuerung der drei Ebenen zuständig sind. Die lila Drähte hängen an den Anoden, die für die neun Spalten zuständig sind. Auf dem Bild rechts unten ist über 5 bunte Kabel das FTDI-Board angeschlossen.

   

Software

Die in Kapitel 1 beschriebene Matrixsteuerung hat einen Nachteil. Man kann nicht alle LEDs einzeln leuchten lassen. Warum?
Möchte man zum Beispiel die beiden blau markierten LEDs im Bild unten leuchten lassen, muss man die beiden unteren Ebenen (rot markiert) und die zwei Spalten (rot markiert) aktivieren.
Damit leuchten aber nicht nur diese beiden LEDs, sondern auch die diagonalen LEDs zusätzlich! Also vier statt nur zwei LEDs. Damit wären einige Animationen nicht möglich.

Hier kommt nun die Software zum Einsatz. Sie nutzt den Effekt aus, dass das menschliche Auge träge ist. Die Software aktiviert immer nur eine Ebene zur gleichen Zeit. Zunächst werden die LEDs auf der unteren Ebene eingeschalten, dann wieder ausgeschalten. Dann werden die LEDs auf der mittleren Ebene eingeschalten, dann wieder ausgeschalten. Dann werden die LEDs auf der oberen Ebene eingeschalten und dann wieder ausgeschalten... und wieder von vorne. Das Ganze läuft so schnell, dass das menschliche Gehirn der Meinung ist, dass mehrere LEDs auf den drei Ebenen gleichzeitig leuchten. Damit ist man dann auch in der Lage, nur die zwei blauen LEDs in dem Bild oben gleichzeitig leuchten zu lassen.
Der Sourcecode ist so gestaltet, dass es einen Teil für die Definition der Animationen und einen Teil für das Abspielen der Animationen gibt. Dadurch kann man recht leicht die Animationen ändern, ohne den Sourcecode anfassen zu müssen.

Die Animationen werden über Bitmuster definiert. Eine Zeile des Bitmusters bildet ein Bild auf dem LED CUBE ab. Sie besteht aus 9 dreistelligen Binärzahlen.

B101, B010, B111, B111, B111, B111, B111, B111, B111, 5

Die erste Binärzahl bezieht sich auf die ersten drei LEDs unten vorne, die zweite Binärzahl auf eine Reihe weiter hinten... die vierte Binärzahl bezieht sich auf die mittlere Zeile vorne usw. (siehe Bild rechts). Der Inhalt der Binärzahl steuert die linke, mittlere und rechte LED einer Zeile. Die Zahl am Ende einer Zeile gibt die Zeit an, wie lange das Bild angezeigt werden soll.

Hier nun der Sourcecode der Anwendung. Die Animationen sind in den Anhang verlagert, weil sie recht lang sind (ca. 600 Zeilen).

#include <avr/pgmspace.h> // allows use of PROGMEM to store patterns in flash

#define CUBESIZE 3
#define PLANESIZE CUBESIZE*CUBESIZE
#define PLANETIME 3333 // time each plane is displayed -> 100 Hz refresh
#define TIMECONST 20 // multiplies DisplayTime to get ms

// LED Pattern Table in PROGMEM - last column is display time in 100ms units
prog_uchar PROGMEM PatternTable[] = {
// blink on and off
B111, B111, B111, B111, B111, B111, B111, B111, B111, 5,
B000, B000, B000, B000, B000, B000, B000, B000, B000, 1,
B111, B111, B111, B111, B111, B111, B111, B111, B111, 5,
B000, B000, B000, B000, B000, B000, B000, B000, B000, 10,
/*
** further animation can be found in the appendix
*/
// this is a dummy element for end of table (duration=0)
B000, B000, B000, B000, B000, B000, B000, B000, B000, 0 
};

/*
** Defining pins in array makes it easier to rearrange how cube is wired
** Analog inputs 0-5 are also digital outputs 14-19
*/

int LEDPin[] = {8, 7, 6, 9, 12, 5, 10, 11, 13};
int PlanePin[] = {18, 17 , 19};

// initialization
void setup()
{
	int pin; // loop counter
	// set up LED pins as output (active HIGH)
	for (pin=0; pin<PLANESIZE; pin++) {
		pinMode( LEDPin[pin], OUTPUT );
	}
	// set up plane pins as outputs (active LOW) 
	for (pin=0; pin<CUBESIZE; pin++) {
		pinMode( PlanePin[pin], OUTPUT );
	}
}

// display pattern in table until DisplayTime is zero (then repeat)
void loop()
{
	// declare variables
	byte PatternBuf[PLANESIZE]; // saves current pattern from PatternTable
	int PatternIdx;
	byte DisplayTime; // time*20ms to display pattern
	unsigned long EndTime;
	int plane; // loop counter for cube refresh
	int patbufidx; // indexes which byte from pattern buffer
	int ledrow; // counts LEDs in refresh loop
	int ledcol; // counts LEDs in refresh loop
	int ledpin; // counts LEDs in refresh loop
	
	// Initialize PatternIdx to beginning of pattern table
	PatternIdx = 0;
	// loop over entries in pattern table - while DisplayTime>0
	do {
		// read pattern from PROGMEM and save in array
		memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE );
		PatternIdx += PLANESIZE;
		// read DisplayTime from PROGMEM and increment index
		DisplayTime = pgm_read_byte_near( PatternTable + PatternIdx++ ); 
		// compute EndTime from current time (ms) and DisplayTime
		EndTime = millis() + ((unsigned long) DisplayTime) * TIMECONST;
		
		// loop while DisplayTime>0 and current time < EndTime
		while ( millis() < EndTime ) {
			patbufidx = 0; // reset index counter to beginning of buffer
			// loop over planes
			for (plane=0; plane<CUBESIZE; plane++) {
				// turn previous plane off
				if (plane==0) {
				digitalWrite( PlanePin[CUBESIZE-1], HIGH );
				} else {
				digitalWrite( PlanePin[plane-1], HIGH );
				}
		
				// load current plane pattern data into ports
				ledpin = 0;
				for (ledrow=0; ledrow<CUBESIZE; ledrow++) {
					for (ledcol=0; ledcol<CUBESIZE; ledcol++) {
						digitalWrite( LEDPin[ledpin++], 
								 PatternBuf[patbufidx] & 
								 (1 << ledcol) );
					}
					patbufidx++;
				} 
		
				// turn current plane on
				digitalWrite( PlanePin[plane], LOW );
				// delay PLANETIME
				delayMicroseconds( PLANETIME );
			} // for plane
		} // while <EndTime
	} while (DisplayTime > 0); // read patterns until time=0 which signals 
				 	// end
}

Der Controller ATmega 328 ist mit einem Arduino Bootloader ausgerüstet. Damit ist man in der Lage Software direkt auf den Controller zu laden. Dazu gibt es eine eigene Entwicklungsumgebung.

Video