LED Blaulicht auf Atmel Microcontroller-Basis

Ich frage mich schon, wieso Playmobil seine Feuerwehrfahrzeuge alle bis auf eines mit Blaulicht ausgestattet hat. Ich hätte erwartet, dass diese Firma dann wenigstens so geschäftstüchtig ist, das Blaulicht auf seinem Einsatzleitfahrzeug einzeln zum Nachrüsten anzubieten. Dem ist aber leider nicht so. Und da meine Tochter gerne alle Ihre Fahrzeuge schön mit Blaulicht zum Einsatz schicken möchte, hat sie mich gebeten, das nachzurüsten.

Da meine Tochter durchaus weiss, wie man mich um den Finger wickelt, hat sie auch direkt eine Anforderungsliste erdacht. Das Blaulicht soll einen Druckschalter wie die anderen Fahrzeuge haben, einmal drücken aktiviert das Blaulicht, ein weiterer Druck deaktiviert es wieder. Und von alleine muss es nach einer festen Wartezeit auch ausgehen können. Da sich meine Fähigkeiten im elektronischen Bereich hauptsächlich auf Digitaltechnik beschränken, war ich dank dieser Anforderungen gezwungen mich von der klassischen instabilen Kippstufe abzuwenden und mich nach Alternativen umzusehen, zumal ich kaum Platz für die Schaltung habe – das Blaulicht auf dem Fahrzeug ist recht klein, was wohl auch der Grund für das Fehlen des Blaulichts an sich darstellen dürfte. Netterweise hatte ich noch mehrere Atmel Microcontroller herum liegen, von denen ich mir auch direkt einen kleinen 8-pin grossen ATtiny13 in DIP Bauweise geschnappt und als Basis meiner Lösung auserkoren habe.

Auf einem Breadboard wurde schnell die Basisschaltung (Schaltplan ganz unten auf der Seite) aufgebaut:

00_exp

Das Programm für den MC war schnell geschrieben (Download hier). Diese Atmel MCs sind featurereich und flexibel, ich arbeite immer wieder gerne mit ihnen. Meine Tochter war sehr zufrieden mit dem Ergebnis. Sie freute sich darauf schon am nächsten Tag mit dem Blaulicht spielen zu können! Ich zügelte Ihre Vorfreude allerdings etwas, denn ich ahnte schon, dass der Einbau etwas schwierig werden könnte. Mit dem folgenden Foto kann sich der Leser vielleicht vorstellen, vor welchem Problem ich jetzt stand:

01_case

Die Grösse des Gehäuses lässt nicht viel Spielraum für klassische Bauweisen. SMD ist nicht mein Ding, mir fehlt dazu das Equipment. Also musste ich die Schaltung irgendwie anders in das Gehäuse kriegen. Ich bevorzuge für Probleme wie diese die pragmatische Methode: einfach am Rand anfangen, immer knapp arbeiten und mal gucken, ob es passt. Zuerst also die LEDs einbauen, das erwies sich als recht übersichtliche Herausforderung:

02_led1

Da Isolation im engen Raum extrem wichtig ist, wurden die Leitungen gekappt und durch isolierte Kabel ersetzt:

03_led2

Durch die Gehäusedecke wurde ein kleines Loch gebohrt und mithilfe eines abgesägten und sauber abgefeilten Nagels der Schalterkopf improvisiert. Darauf wurde mit Heisskleber (ich liebe das Zeug) der Taster fixiert:

04_sw

Schon war mehr als ein Viertel des Platzes weg. Im Rest wurde jetzt die Schaltung platziert. Das klingt unproblematisch. War es aber nicht. Es wurde viel geflucht und meine Finger haben sehr unter Lötkolbenkontakt gelitten. Aber was macht man nicht alles für seine Tochter. Hier also das (noch nicht ausreichend komprimierte) Ergebnis:

05_fitted

Der nächste Schritt kann nicht durch Fotos dokumentiert werden. Auch ein Film würde hier nicht reichen. Wenn es eine Aufzeichnungsmethodik gäbe, mit der auch Emotionen aufgezeichnet werden könnten, wäre es vielleicht ausreichend. Auf jeden Fall offenbarten sich in der kleinen Veränderung vom oberen zum folgenden Foto sämtlichen kalten Lötstellen und brüchigen Kabel beim Eingiessen in Heisskleber und komprimieren der Schaltung zum endgültigen Aufsetzen des Gehäuses auf das Fahrzeugdach. Ich war mehrfach kurz davor, den ganzen Kram rituell zu verbrennen, im Garten zu verscharren oder zu mindestens in einen permanenten Orbit eines beliebigen Jupitermondes zu pfeffern. Netterweise hat meine (als SysAdmin erwartungsgemäss extrem hohe) Frusttoleranz gehalten und ein Ergebnis zu Tage gefördert.

06_compl

Meine Tochter ist übrigens sehr glücklich damit. Ich habe weiter unten ein Video als Funktionsbeweis eingefügt. Alle Anforderungen der „Kundin“ wurden gemäss Ihrer Wünsche erfüllt. Im folgenden ein Schaltplan zum Nachbauen. Ich empfehle es allerdings nur Menschen mit sehr viel Geduld und natürlich hoher Frusttoleranz. die Batterie ist übrigens unter dem Dach fixiert, ebenso wie ein kleiner Schalter zum Trennen des Stromkreises. Da ich aus drei Knopfzellen ein Batteriepack gebastelt habe, wollte ich so verhindern, ständig neue Packs basteln zu müssen. Auch wenn der Atmel wenig Strom verbraucht, ein wenig frisst er schon und bei der Anwendung haben wir sehr hohe Stillstandzeiten.

07_plan

Hier der Sourcecode für den Atmel, der mit einigen Codebeispielen der Seite mikrocontroller.net (mit bestem Dank an die Truppe) schnell zusammengeschustert wurde. Viel Erfolg beim Nachbauen!

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/sleep.h>

volatile uint8_t pcint_flag;
volatile uint8_t timer_flag;
volatile uint8_t timer_cnt;
volatile uint8_t blinker_cnt;

// aus Codebeispielen mikrocontroller.net - http://www.mikrocontroller.net/articles/Entprellung
void entprellung( volatile uint8_t *port, uint8_t maske ) {
  uint8_t   port_puffer;
  uint8_t   entprellungs_puffer;

  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {
    entprellungs_puffer<<=1;
    port_puffer = *port;
    _delay_us(150);
    if( (*port & maske) == (port_puffer & maske) )
      entprellungs_puffer |= 0x01;
  }
}

ISR(PCINT0_vect)
{
        cli();
        entprellung( &PINB, (1<<PINB2) );
        if( PINB & (1<<PINB2) ) pcint_flag=1;
        sei();
}

ISR( TIM0_OVF_vect ) {
        timer_cnt--;
        if (!timer_cnt) {
                timer_cnt=75;
            timer_flag = 1;
        };
}

int main (void) {

        GIFR ^= (1<<PCIF);
        GIMSK = _BV (PCIE);                                  // Pin Change Interrupt Enable
        PCMSK ^= (1<<PCINT2)|(1<<PCINT3);
        TCCR0B ^= (1<<CS02);                     // Vorteiler 256 -> ~65ms berlaufperiode
        TIMSK0 ^= (1<<TOIE0);            // Timer Overflow Interrupt freischalten

        timer_cnt=244;
        timer_flag=0;
        pcint_flag=0;
        blinker_cnt=0;

        DDRB ^= (1 << DDB0) | (1 << DDB1);
        PORTB = 4;

        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_cpu();

        sei();

        while(1) {

                // input has changed
                if (pcint_flag==1) {
                        pcint_flag=0;
                        // do stuff about it
                        if (blinker_cnt>0) blinker_cnt=0;
                        else {
                                blinker_cnt=120;
                                PORTB=1;
                        }
                };

                // timer has expired
                if (timer_flag==1) {
                        timer_flag=0;
                        // do stuff about it
                        if (blinker_cnt>0) {
                                PORTB ^= (1<<PB0);
                                PORTB ^= (1<<PB1);
                                blinker_cnt--;
                        } else {
                                PORTB = 0;
                                set_sleep_mode(SLEEP_MODE_PWR_DOWN);
                                sleep_cpu();
                        };
                };

        };

        cli();

        /* wird nie erreicht */
        return 0;
}