{{ :elektronik:led_wuerfel:header.png?nolink&3000 |}}
====== LED Würfel ======
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:
{{:elektronik:led_wuerfel:led_wuerfel.mp4|}}
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 =====
{{:elektronik:led_wuerfel:wuerfel_3d_druck_low.jpg?direct&300|}}
\\ neue Variante
{{:elektronik:led_wuerfel:wuerfel_3d_druck_platine_low.jpg?direct&300|}}
\\ 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.
{{pdfjs 500px>:elektronik:led_wuerfel:wuerfel.pdf}}
===== 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.
++++ main.c |
#define F_CPU 8000000
#include
#include
#include "sbit.h"
#include "io_label.h"
#include "led_wuerfel.h"
#include "timer.h"
int main(void)
{
DDRB = 0b11000111; //alles auf Ausgang, außer SPI
DDRC = 0b00111111; //und Reset
DDRD = 0b11111111;
KEY_G_PORT = true; //bei beiden Tastern Pullup aktivieren
KEY_R_PORT = true;
init_timer0();
while(1)
{
//Hauptprogramm läuft in Timer Interrupts
}
}
++++
++++ timer.h |
void init_timer0(void);
++++
Hier wird letztendlich nur die Hardware initialisiert.
++++ timer.c |
#include
#include
#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< 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.
#include
struct bits {
uint8_t b0:1;
uint8_t b1:1;
uint8_t b2:1;
uint8_t b3:1;
uint8_t b4:1;
uint8_t b5:1;
uint8_t b6:1;
uint8_t b7:1;
} __attribute__((__packed__));
#define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
#define true 1
#define false 0
Hilfskonstrukt, damit den einzelnen I/Os Namen zugeordnet werden können (siehe io_label.h).
++++ wuerfel.h |
#include <avr/io.h>
void set_wuerfel_g(int8_t nr);
void set_wuerfel_r(int8_t nr);
++++
++++ wuerfel.c |
#include
void set_wuerfel_g(int8_t nr);
void set_wuerfel_r(int8_t nr);
#include
#include "led_wuerfel.h"
#include "sbit.h"
#include "io_label.h"
void set_wuerfel_g(int8_t nr) {
if(nr == 0) {
LED_G_lo = false;
LED_G_lm = false;
LED_G_lu = false;
LED_G_ro = false;
LED_G_rm = false;
LED_G_ru = false;
LED_G_m = false;
} else if(nr == -1) {
LED_G_lo = true;
LED_G_lm = true;
LED_G_lu = true;
LED_G_ro = true;
LED_G_rm = true;
LED_G_ru = true;
LED_G_m = true;
} else if(nr == 1) {
LED_G_lo = false;
LED_G_lm = false;
LED_G_lu = false;
LED_G_ro = false;
LED_G_rm = false;
LED_G_ru = false;
LED_G_m = true;
} else if(nr == 2) {
LED_G_lo = true;
LED_G_lm = false;
LED_G_lu = false;
LED_G_ro = false;
LED_G_rm = false;
LED_G_ru = true;
LED_G_m = false;
} else if(nr == 3) {
LED_G_lo = true;
LED_G_lm = false;
LED_G_lu = false;
LED_G_ro = false;
LED_G_rm = false;
LED_G_ru = true;
LED_G_m = true;
} else if(nr == 4) {
LED_G_lo = true;
LED_G_lm = false;
LED_G_lu = true;
LED_G_ro = true;
LED_G_rm = false;
LED_G_ru = true;
LED_G_m = false;
} else if(nr == 5) {
LED_G_lo = true;
LED_G_lm = false;
LED_G_lu = true;
LED_G_ro = true;
LED_G_rm = false;
LED_G_ru = true;
LED_G_m = true;
} else if(nr == 6) {
LED_G_lo = true;
LED_G_lm = true;
LED_G_lu = true;
LED_G_ro = true;
LED_G_rm = true;
LED_G_ru = true;
LED_G_m = false;
}
return;
}
void set_wuerfel_r(int8_t nr) {
if(nr == 0) {
LED_R_lo = false;
LED_R_lm = false;
LED_R_lu = false;
LED_R_ro = false;
LED_R_rm = false;
LED_R_ru = false;
LED_R_m = false;
} else if(nr == -1) {
LED_R_lo = true;
LED_R_lm = true;
LED_R_lu = true;
LED_R_ro = true;
LED_R_rm = true;
LED_R_ru = true;
LED_R_m = true;
} else if(nr == 1) {
LED_R_lo = false;
LED_R_lm = false;
LED_R_lu = false;
LED_R_ro = false;
LED_R_rm = false;
LED_R_ru = false;
LED_R_m = true;
} else if(nr == 2) {
LED_R_lo = true;
LED_R_lm = false;
LED_R_lu = false;
LED_R_ro = false;
LED_R_rm = false;
LED_R_ru = true;
LED_R_m = false;
} else if(nr == 3) {
LED_R_lo = true;
LED_R_lm = false;
LED_R_lu = false;
LED_R_ro = false;
LED_R_rm = false;
LED_R_ru = true;
LED_R_m = true;
} else if(nr == 4) {
LED_R_lo = true;
LED_R_lm = false;
LED_R_lu = true;
LED_R_ro = true;
LED_R_rm = false;
LED_R_ru = true;
LED_R_m = false;
} else if(nr == 5) {
LED_R_lo = true;
LED_R_lm = false;
LED_R_lu = true;
LED_R_ro = true;
LED_R_rm = false;
LED_R_ru = true;
LED_R_m = true;
} else if(nr == 6) {
LED_R_lo = true;
LED_R_lm = true;
LED_R_lu = true;
LED_R_ro = true;
LED_R_rm = true;
LED_R_ru = true;
LED_R_m = false;
}
return;
}
++++
Mit diesen Funktionen werden die I/Os der beiden Würfel den entsprechenden Zahlen zugeordnet. “-1” dient dazu alle LEDs anzuschalten.
++++ io_label.h |
#define KEY_R SBIT(PINB, 2)
#define KEY_G SBIT(PINB, 1)
#define KEY_R_PORT SBIT(PORTB, 2)
#define KEY_G_PORT SBIT(PORTB, 1)
#define LED_R_lu SBIT(PORTC, 5)
#define LED_R_lm SBIT(PORTD, 0)
#define LED_R_lo SBIT(PORTC, 4)
#define LED_R_m SBIT(PORTC, 3)
#define LED_R_ro SBIT(PORTC, 2)
#define LED_R_rm SBIT(PORTC, 1)
#define LED_R_ru SBIT(PORTC, 0)
#define LED_G_ru SBIT(PORTD, 1)
#define LED_G_rm SBIT(PORTD, 2)
#define LED_G_ro SBIT(PORTD, 3)
#define LED_G_m SBIT(PORTD, 4)
#define LED_G_lo SBIT(PORTB, 6)
#define LED_G_lm SBIT(PORTB, 7)
#define LED_G_lu SBIT(PORTD, 5)
++++
Die Namens-Zuordnungen zu den einzelnen I/Os.
===== 3D-Modelle =====
{{:elektronik:led_wuerfel:wuerfel_spiel_new19_deckel.stl?s=300&bgcolor=#ffffff|Deckel}}
{{:elektronik:led_wuerfel:wuerfel_spiel_new19_grund01.stl?s=300&bgcolor=#fffff|Gehäuseteil 1}}
{{:elektronik:led_wuerfel:wuerfel_spiel_new19_grund02.stl?s=300&bgcolor=#fffff|Gehäuseteil 2}}
===== Download =====
{{ :elektronik:led_wuerfel:wuerfel_atmel_studio62.zip |}}
{{tag>[avr atmega wuerfel]}}
\\ ~~DISQUS~~