Der LED-Würfel ist ein kleineres Wochenendprojekt, welches ich vor einigen Jahren gemacht habe.
Die erste Variante, die ich gebaut hatte besitzt zwei Würfel. Diese lassen sich unabhängig voneinander würfeln. Zum Einsatz kam ein Mikrocontroller der Firma Atmel – ein ATmega8. Damit das ganze besser aussieht habe ich eine Auslaufanimation einprogrammiert, welche man im Video ganz gut sieht:
Die zweite Version unterscheidet sich eigentlich nur darin, dass ein anderes Gehäuse zum Einsatz kommt. Dieses wurde mit FreeCAD konstruiert und anschließend auf meinem 3D-Drucker gedruckt. Das Gehäuse hat eine Würfelform, bei dem der Würfel auf der Spitze steht.
Es besteht aus drei Einzelteilen: dem Sockel, dem Würfelgrundgehäuse und dem Würfeldeckel mit den LEDs. Das Würfelgrundgehäuse ist mit dem Sockel verschraubt (wurden nachträglich Löcher gebohrt, da kleben nicht so gut geklappt hat). Der Deckel ist nur gesteckt. Der Vorteil an diesen drei Einzelkomponenten war, dass kein Support benötigt wurde. Als Material wurde PLA genommen.
Die Platine wurde im Würfelgrundgehäuse verstaut. Im Sockel befindet sich nur der Taster und das Verbindungskabel zur Platine.
Bilder
neue Variante
Platine und LEDs
Schaltplan
Der Schaltplan ist eigentlich absolut nichts besonderes. Es ist ein Spannungsregler verbaut, damit das ganze mit einem Steckernetzteil höherer Spannung (z.B. 7,5V) versorgt werden kann. Der Schaltplan ist die Variante mit einem Würfel. Für die Variante mit zwei Würfeln kann man die Pinzuordnung aus der io_label.h erfahren/anpassen.
Source
Der Quelltext wurde in C geschrieben. Als Entwicklungsumgebung diente das Atmel Studio.
Der Code ist mittlerweile über 9 Jahre alt. Heute würde ich auch einiges anders machen. Vieles ist hier nicht wirklich optimal gelöst. Z.B. mit einer sauberen Schrittkette bzw. einem Zustandsautomaten. Aber dafür funktioniert der Code einwandfrei.
#include <avr/io.h>
#include <avr/interrupt.h>
#include "io_label.h"
#include "sbit.h"
#include "led_wuerfel.h"
#include "timer.h"
#define min_prescaler_grenze_verschieben 0
#define max_prescaler_grenze_verschieben 2000
#define auslaufen_prescaler 4
void init_timer0(void) {
TCCR0 |= (1<<CS01); //Prescaler 8
TIMSK |= (1<<TOIE0); //Interrupt bei Timer Overflow
sei(); //Interrupts aktivieren
}
int8_t count_g = -1; //aktueller Zählwert
int8_t key_g_pressed; //Taste wurde seit dem letzten Auslaufen gedrückt
int8_t key_g_last = true; //Wert der Taste im letzten Zyklus; für Flankenerkennung
int8_t key_g_abbild; //Abbild des Tasters, damit wärend des gesamten Interrupt-Codes auf gleichen Datenbestand zugegriffen wird
uint16_t prescaler_pressed_led_grenze_g;
uint16_t prescaler_pressed_led_g;
uint16_t prescaler_grenze_verschieben_g;
int8_t count_r = -1; //aktueller Zählwert
int8_t key_r_pressed; //Taste wurde seit dem letzten Auslaufen gedrückt
int8_t key_r_last = true; //Wert der Taste im letzten Zyklus; für Flankenerkennung
int8_t key_r_abbild; //Abbild des Tasters, damit wärend des gesamten Interrupt-Codes auf gleichen Datenbestand zugegriffen wird
uint16_t prescaler_pressed_led_grenze_r;
uint16_t prescaler_pressed_led_r;
uint16_t prescaler_grenze_verschieben_r;
ISR(TIMER0_OVF_vect) {
key_g_abbild = KEY_G;
if (key_g_abbild != key_g_last && key_g_abbild == true) { //fallende Flanke -> gedrückt, da KEY_G invertiert
key_g_pressed = false; //auf Standartwerte setzen, damit durch dieses Betätigen alles wie immer ist
prescaler_pressed_led_grenze_g = min_prescaler_grenze_verschieben;
prescaler_pressed_led_g = 0;
prescaler_grenze_verschieben_g = 0;
}
if (key_g_abbild == false) { //wenn Taster gedrückt wird, dann
set_wuerfel_g(-1); //alle Segmente zum leuchten bringen
if (count_g == 6) { //von 1-6 zählen
count_g = 1;
} else {
count_g++;
}
}
if (key_g_abbild != key_g_last && key_g_abbild == true) { //steigende Flanke -> losgelassen, da KEY_G invertiert
key_g_pressed = true;
}
if (key_g_pressed == true) { //wenn taste gedrückt, aber inzwischen losgelassen wurde, dann
if(prescaler_pressed_led_g < prescaler_pressed_led_grenze_g) { //prescaler - zählen von 0 bis zur dynamischen prescalerobergrenze (das dynamische wird für das auslaufen benötigt)
prescaler_pressed_led_g++;
} else {
prescaler_pressed_led_g = 0;
}
if (prescaler_grenze_verschieben_g < auslaufen_prescaler) { //prescaler für die geschwindigkeit des auslaufens -> ruft bei 0 die erhöhung des dynamischen prescalers auf
prescaler_grenze_verschieben_g++;
} else {
prescaler_grenze_verschieben_g = 0;
}
if (prescaler_pressed_led_g == 0) { //wenn overflow des dynamischen prescalers, dann
if (count_g == 6) { //von 0 bis 6 zählen
count_g = 1;
} else {
count_g++;
}
set_wuerfel_g(count_g); //und ausgeben
}
if (prescaler_grenze_verschieben_g == 0) { //wenn overflow des prescalers für das verschieben der geschwindigkeit, dann
if (prescaler_pressed_led_grenze_g < max_prescaler_grenze_verschieben) { //wenn langsamste geschwindigkeit noch nicht erreicht, dann
prescaler_pressed_led_grenze_g++; //geschwindigkeit verlangsamen (durch erhöhung des prescalers)
} else {
key_g_pressed = false; //sonst alles auf standartwerte setzen und das auslaufen verlassen (zahl bleib stehen!)
prescaler_pressed_led_grenze_g = min_prescaler_grenze_verschieben;
prescaler_pressed_led_g = 0;
prescaler_grenze_verschieben_g = 0;
}
}
}
key_g_last = key_g_abbild;
key_r_abbild = KEY_R;
if (key_r_abbild != key_r_last && key_r_abbild == true) { //fallende Flanke -> gedrückt, da KEY_G invertiert
key_r_pressed = false; //auf Standartwerte setzen, damit durch dieses Betätigen alles wie immer ist
prescaler_pressed_led_grenze_r = min_prescaler_grenze_verschieben;
prescaler_pressed_led_r = 0;
prescaler_grenze_verschieben_r = 0;
}
if (key_r_abbild == false) { //wenn Taster gedrückt wird, dann
set_wuerfel_r(-1); //alle Segmente zum leuchten bringen
if (count_r == 6) { //von 1-6 zählen
count_r = 1;
} else {
count_r++;
}
}
if (key_r_abbild != key_r_last && key_r_abbild == true) { //steigende Flanke -> losgelassen, da KEY_G invertiert
key_r_pressed = true;
}
if (key_r_pressed == true) { //wenn taste gedrückt, aber inzwischen losgelassen wurde, dann
if(prescaler_pressed_led_r < prescaler_pressed_led_grenze_r) { //prescaler - zählen von 0 bis zur dynamischen prescalerobergrenze (das dynamische wird für das auslaufen benötigt)
prescaler_pressed_led_r++;
} else {
prescaler_pressed_led_r = 0;
}
if (prescaler_grenze_verschieben_r < auslaufen_prescaler) { //prescaler für die geschwindigkeit des auslaufens -> ruft bei 0 die erhöhung des dynamischen prescalers auf
prescaler_grenze_verschieben_r++;
} else {
prescaler_grenze_verschieben_r = 0;
}
if (prescaler_pressed_led_r == 0) { //wenn overflow des dynamischen prescalers, dann
if (count_r == 6) { //von 0 bis 6 zählen
count_r = 1;
} else {
count_r++;
}
set_wuerfel_r(count_r); //und ausgeben
}
if (prescaler_grenze_verschieben_r == 0) { //wenn overflow des prescalers für das verschieben der geschwindigkeit, dann
if (prescaler_pressed_led_grenze_r < max_prescaler_grenze_verschieben) { //wenn langsamste geschwindigkeit noch nicht erreicht, dann
prescaler_pressed_led_grenze_r++; //geschwindigkeit verlangsamen (durch erhöhung des prescalers)
} else {
key_r_pressed = false; //sonst alles auf standartwerte setzen und das auslaufen verlassen (zahl bleib stehen!)
prescaler_pressed_led_grenze_r = min_prescaler_grenze_verschieben;
prescaler_pressed_led_r = 0;
prescaler_grenze_verschieben_r = 0;
}
}
}
key_r_last = key_r_abbild;
}
Hier läuft das Hauptprogramm ab. Sobald der Taster gedrückt wird, leuchten die LEDs des entsprechenden Würfels. Solange der Taster gedrückt wird, zählt der µC die Zahlen 1-6 kontinuierlich hoch. Damit wird ein Zufall simuliert. Lässt man den Taster los, so läuft der Würfel langsam aus.
Das ganze funktioniert mit zwei virtuellen Prescalern:
prescaler_pressed_led_g – wenn dieser überläuft, dann wird der Würfel eins nach oben gezählt. Dieser Prescaler ist variabel und wird durch prescaler_pressed_led_grenze_g angepasst.
prescaler_grenze_verschieben_g – wenn dieser überläuft, dann wird die Geschwindigkeit des überlaufen von prescaler_pressed_led_g verringert (durch Erhöhung des Maximalwertes, bevor Prescaler überläuft – über die Variable prescaler_pressed_led_grenze_g), vorausgesetzt, die langsamste Stufe wurde noch nicht erreicht. Dieser Prescaler ist fix.
Wenn die langsamste Geschwindigkeit erreicht wurde, dann wird alles resettet und die Zahl bleibt stehen.