Vor Kurzem habe ich im Internet ein Angebot für eine Binäruhr gesehen. Allerdings waren 20$ (zzgl. Versand) meiner Meinung nach doch etwas zu viel. So bin ich auf die Idee gekommen, selbst eine Binäruhr zu bauen. Die nötigen Komponenten hatte ich zum Glück alle zu Hause. Hier folg also nun meine Anleitung zum Bau einer Binäruhr:
Ihr benötigt folgende Komponenten:
-
20 blaue LEDs
-
20 12 Ohm Widerstände
-
2 10k Ohm Widerstände
-
1 Streifenrasterplatine (10cmx10cm)
-
8adriges Flachbandkabel
-
2 Kurzhubtaster
-
Anschlussklemme für DC-Netzteil
-
40-Pin Sockel für Microcontroller
-
Microcontroller (z.B. ATMega32)
Ich habe einen Schaltplan erstellt nach dem ihr die Schaltung aufbauen könnt.
Als erstes habe ich alle Bauteile auf die Platine gesetzt, um zu sehen, wo ich die Leiterbahnen der Streifenrasterplatine durchtrennen muss, um die nötigen "Isolationen" zwischen den Bauteilen zu erzeugen. Wichtig ist hierbei besonders, die einzelnen Pins des Sockels für den Microcontroller voneinander zu isolieren. Auch die Anoden der LEDs müssen voneinander getrennt sein, damit es möglich ist, sie einzeln anzusteuern. Die Kathoden habe ich zeilenweise auf gleiches Potential gelegt. Auch die Schalter müssen voneinander isoliert sein, um einen eindeutigen Schaltvorgang zu erhalten.
Dann habe ich die 20 12 Ohm Widerstände auf die Platine gesteckt und angelötet. Im Anschluss habe ich die 2 Schalter, die 10k Ohm Widerstände und den Sockel an ihre vorgesehenen Positionen gelötet. Das Ganze sah dann in etwa so aus:
Nun geht es daran, die LEDs auf der Platine zu befestigen. Hierbei ist es wichtig, die Polarität der Dioden zu beachten. Ich habe die Kathoden auf die gemeinsame Leiterbahn gesetzt, so dass nur ein Masseanschluss pro Zeile nötig ist.
Jetzt kommt nochmal ein etwas schwierigerer Teil: Da unsere Platine nur einseitig bedruckt ist, muss die restliche Verdrahtung zwischen Microcontroller und den LEDs "fliegend" erfolgen. Dazu habe ich das achtadrige Flachbandkabel verwendet und in je 2 vieradrige Teile geteilt, so dass immer eine Zeile verkabelt wird. Wenn man die Verkabelung hier sinnvoll macht, spart man sich hinterher eine Menge Arbeit beim Programmieren des Microcontrollers Tipp: Es ist sinnvoll die LEDs entsprechend ihrer Wertigkeit mit den Ausgängen des Microcontrollers zu verbinden.
Außerdem muss natürlich auch für die Spannungsversorung des AVR gesorgt sein, die Schalter müssen angeschlossen werden und die Masseanschlüsse der LEDs müssen verbunden seinâ -- so sieht's bei mir aus:
Als Letztes fehlt noch die Anschlussklemme für die Spannungsversorgung, die auf die dafür vorgesehenen Leiterbahnen gelötet wird. Bei mir gab es ein kleines Problem mit der Spannungsversorgung (Wackelkontakt), so dass ich noch eine Kontroll-LED mit Vorwiderstand (12k Ohm) eingebaut habe, um zu sehen, ob die Schaltung mit Strom versorgt wird. Und auch hiervon gibt es wieder ein nettes Bildchen:
Auf dem Bild fehlt noch der Microcontroller und das hat auch seinen Sinn, denn bisher fehlt ja noch die nötige Software für unsere Uhr:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
typedef unsigned char u8;
typedef signed short s16;
#define XTAL 1e6 // 1MHz = 1 * 10^6
#define KEY_PIN PINC
#define HOURS PORTD
#define MINS PORTB
#define SECS PORTA
#define BUT_PORT PORTC
#define BUT_HOUR PINC0
#define BUT_MIN PINC1
#define REPEAT_MASK (1<<BUT_HOUR^1<<BUT_MIN)
#define REPEAT_START 50 // after 500ms
#define REPEAT_NEXT 20 // every 200ms
u8 key_state; // debounced and inverted key state:
// bit = 1: key pressed
u8 key_press; // key press detect
u8 key_rpt; // key long press and repeat
static unsigned int count = 0;
static u8 hours_1st = 0, hours_2nd = 0;
static u8 minutes_1st = 0, minutes_2nd = 0;
static u8 seconds_1st = 0, seconds_2nd = 0;
SIGNAL (SIG_OVERFLOW0) // every 10ms
{
static u8 ct0, ct1, rpt/*, hours, minutes, seconds */;
/* static unsigned int count; */
u8 i;
TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5); // preload for 10ms
i = key_state ^ ~KEY_PIN; // key changed ?
ct0 = ~( ct0 & i ); // reset or count ct0
ct1 = ct0 ^ (ct1 & i); // reset or count ct1
i &= ct0 & ct1; // count until roll over ?
key_state ^= i; // then toggle debounced state
key_press |= key_state & i; // 0->1: key press detect
if( (key_state & REPEAT_MASK) == 0 ) // check repeat function
rpt = REPEAT_START; // start delay
if( --rpt == 0 ){
rpt = REPEAT_NEXT; // repeat delay
key_rpt |= key_state & REPEAT_MASK;
}
> count++;
if (count >= 100){
seconds_2nd++;
count = 0;
if (seconds_2nd >= 10){
seconds_1st++;
seconds_2nd = 0;
if (seconds_1st >= 6){
minutes_2nd++;
seconds_1st = 0;
if (minutes_2nd >= 10){
minutes_1st++;
minutes_2nd = 0;
if (minutes_1st >= 6){
hours_2nd++;
minutes_1st = 0;
if (hours_2nd >= 10){
hours_1st++;
hours_2nd = 0;
if (hours_1st >= 2 && hours_2nd >= 4){
hours_1st = 0;
hours_2nd = 0;
}
}
}
}
}
}
}
}
u8 get_key_press( u8 key_mask )
{
cli(); // read and clear atomic !
key_mask &= key_press; // read key(s)
key_press ^= key_mask; // clear key(s)
sei();
return key_mask;
}
u8 get_key_short( u8 key_mask )
{
cli(); // read key state and key press atomic !
return get_key_press( ~key_state & key_mask );
}
u8 get_key_long( u8 key_mask )
{
return get_key_press( get_key_rpt( key_mask ));
}
int main( void )
{
u8 x;
unsigned int flicker = 0;
DDRA = 0xff;
DDRB = 0xff;
DDRC = 0x00;
DDRD = 0xff;
PORTA = 0x00;
PORTB = 0x00;
PORTD = 0x00;
for (x=0; x<7; x++){
SECS = (1< _delay_ms(500);
}
SECS = 0x00;
for (x=0; x<7; x++){
MINS = (1<
_delay_ms(500);
}
MINS = 0x00;
for (x=0; x<6; x++){
HOURS = (1<
_delay_ms(500);
}
HOURS = 0x00;
TCCR0 = 1<<
TIMSK = 1<
sei();
for(;;){ // main loop
flicker++;
if (flicker < 50){
HOURS = (hours_1st<<4)|(hours_2nd);
MINS = (minutes_1st<<4)|(minutes_2nd);
SECS = (seconds_1st<<4)|(seconds_2nd);
} else if (flicker > 50 && flicker < 100){
HOURS = 0x00;
MINS = 0x00;
SECS = 0x00;
flicker = 0;
}
// short press
if( get_key_short( 1<
hours_2nd++;
seconds_1st = 0;
seconds_2nd = 0;
if (hours_2nd >= 10){
hours_1st++;
hours_2nd = 0;
if (hours_1st >= 2 && hours_2nd >= 4){
hours_1st = 0;
hours_2nd = 0;
}
}
}
// long press
if( get_key_long( 1< hours_2nd += 2;
seconds_1st = 0;
seconds_2nd = 0;
if (hours_2nd >= 10){
hours_1st++;
hours_2nd = 0;
if (hours_1st >= 2 && hours_2nd >= 4){
hours_1st = 0;
hours_2nd = 0;
}
}
}
// short press
if( get_key_short( 1< minutes_2nd++;
seconds_1st = 0;
seconds_2nd = 0;
if (minutes_2nd >= 10){
minutes_1st++;
minutes_2nd = 0;
if (minutes_1st >= 6){
hours_2nd++;
minutes_1st = 0;
if (hours_2nd >= 10){
hours_1st++;
hours_2nd = 0;
if (hours_1st >= 2 && hours_2nd >= 4){
hours_1st = 0;
hours_2nd = 0;
}
}
}
}
}
// long press
if( get_key_long( 1< minutes_2nd+=5;
seconds_1st = 0;
seconds_2nd = 0;
if (minutes_2nd >= 10){
minutes_1st++;
minutes_2nd = 0;
if (minutes_1st >= 6){
hours_2nd++;
minutes_1st = 0;
if (hours_2nd >= 10){
hours_1st++;
hours_2nd = 0;
if (hours_1st >= 2 && hours_2nd >= 4){
hours_1st = 0;
hours_2nd = 0;
}
}
}
}
}
}
}
Den Code für die Binäruhr gibt es auch hier nochmal etwas ordentlicher formatiert zum Download.
Die Software funktioniert soweit vollständig. Bei mir zeigt sie allerdings zu Beginn ein merkwürdiges Verhalten: Nachdem die Anzeige drei Sekunden anzeigt, springt die Zeit plötzlich auf 2 Stunden und 5 Minuten vor... da ist also noch irgendwo ein Bug ;)
Schön finde ich das Feature, bei einem längeren Tastendruck einen "schnellen Vorlauf" zu haben. Das spart etwas Zeit, wenn man eine etwas spätere Stunde (oder besonders Minute) einstellen will. Allerdings wäre es hier noch sinnvoll, die Zeit für "lang" auf ein paar Sekunden zu erhöhen, da sonst nur sehr selten ein kurzer Druck erkannt wird. Der Code hierfür stammt größtenteils von hier. Interessant ist vielleicht auch noch, dass die LEDs eigentlich nur die Hälfte der Zeit überhaupt an sind. Das spart eine Menge Strom und besonder Abwärme ;) Und für das Auge ist aufgrund der hohen Frequenz nicht mal ein Flackern wahrnehmbar.
Als nächstes möchte ich evtl. noch ein passendes Gehäuse für die Uhr finden - dann sieht das ganze gleich nach viel mehr aus :) Außerdem stört dann die Verkabelung hinten nicht mehr so sehr.