Sonntag, 28. Mai 2017

Plexiclock - Fertigstellung Teil 1

Hallo,
mittlerweile sind drei Wochen seit meinem letzten Eintrag zur Plexiclock vergangen. Seit dem hat sich viel getan, die Uhr ist nahezu fertig.

Zunächst hatte ich mich die Verkabelung der verbleibenden drei Segmente gemacht, über welche ich in meinem letzten Eintrag berichtet hatte. Daran angeschlossen hat sich die Verbindung der Low-Side Arrays (R,G,B). Dabei habe ich auch gleich noch die mittlere LED für den Doppelpunkt eingearbeitet.


Die High-Side der LED ist dabei mit an einem der mittleren Segmente angeschlossen. Diese benötigen ansonsten nur sieben der acht Anschlüsse.

Als nächstes ging es an die Verkabelung der Schieberegister/Treiber-Platinen. Diese besitzen jeweils sieben Anschlusspins: Masse, Versorgungsspannung, Output-Enable, Data-In, Data-Out, Register-Clock & Storage-Clock. Die meisten davon sind einfach parallel von Platine zu Platine verbunden. Eine Ausnahme davon stellen Data-In/-Out dar, da darüber die Schieberegister verbunden sind. Daher ist jeweils Data-In einer Platine mit Data-Out der vorhergehenden Platine verbunden. Außerdem besitzt jede Platine eine eigene Ader für die Versorgungsspannung. Dies ist darin begründet, dass dort ein relativ hoher Strom fließt, welcher über die Treiber-ICs die LEDs antreibt. Der Strom fließt anschließend über die R,G&B Low-Side der LEDs über jeweils einen Widerstand ab. Die Verkabelung einer einzelnen Platine ist in folgendem Bild dargestellt.


Die Leitungen enden auf einer Seite der Uhr an der Steuerplatine. Diese habe ich natürlich auch erst entworfen und zusammengelötet. Als Intelligenz der Uhr ist ein ESP8266 eingebaut. Zu diesem komme ich später noch einmal. Er übergibt jedenfalls über zwei Signalleitungen Steuerdaten an einen AtTiny85, welcher die PWM-Signale für die Farbkanäle erzeugt. Diese werden anschließend über drei n-Kanal Mosfets verstärkt. Die Ausgänge der Mosfets erzeugen direkt die Spannung, welche über die Widerstände an die LEDs geht. Neben der Farbsteuerung befinden sich auf der Steuerplatine Elemente zur Bedienung, Spannungsstabilisierung, etc. Der Schaltplan und das finale Aussehen sind unterhalb abgebildet.



Da die Steuerplatine den letzten elektrischen Part darstellt, konnte mit ihrer Fertigstellung die Programmierung beginnen. Zunächst habe ich den AtTiny85 programmiert, welcher die Farben steuert. Als Programmiersprache habe ich AVR-C innerhalb von Atmel Studio verwendet. Wer sich für den Code interessiert, kann ihn sich durch drücken des Knopfes unten ansehen. Das Programm nimmt über zwei Eingänge (Data, Clock) Befehle entgegen und übernimmt die empfangenen Daten nach Beendigung der Übertragung in die integrierten PWM-Controller. Die Übertragung findet über eine eigens erdachte Schnittstelle statt, welche aber einfach zu verstehen ist. Für die Übertragung eines Bits wird zunächst Clock auf high gesetzt. Data muss nun einen Sprung von Low auf High (Bit=1) oder High auf Low (Bit=0) durchführen. Zum Abschluss muss nun Clock wieder auf Low gezogen werden. Der Vergleich von Data bei den beiden Änderungen von Clock ergibt also den Wert des Bits. Was mit Data passiert, während Clock auf Low ist, wird nicht beachtet. Dadurch können die benötigten Ausgangszustände auf Data erreicht werden. Findet in einem Clock-Intervall keine Änderung von Data statt, so wird die Übertragung als beendet angenommen.



/*
 * RGBController.c
 *
 * Created: 09.05.2017 11:28:11
 *  Author: Markus

 * PINS:
 * 1-> Reset
 * 2-> Data Connection PB3 / PCINT3
 * 3-> PB4, Color 3
 * 4-> GND
 * 5-> PB0, Color 1
 * 6-> PB1, Color 2
 * 7-> Data Connection PB2 / INT0 / PCINT2
 * 8-> VCC
 */

#ifndef F_CPU
#define F_CPU 1000000UL     /* Quarz mit 1.000.000 hz */
#endif

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

void SetValue(int Colour, char Value);

//DutyRegisters
volatile uint8_t* Port[] = {&OCR0A, &OCR0B, &OCR1B};

long int values=0;

//used in both regular programm and interrupts
volatile char SomethingHappened=0;

int main(void)
{
    //Set Output
    DDRB = 0x13;    //B0,B1,B4

    PRR|= _BV(PRADC)||_BV(PRUSI); //Disable ADC and USI (not needed, less power)

    // Timer 0, A side
    TCCR0A = _BV (WGM00) | _BV (WGM01) | _BV (COM0A1); // fast PWM, clear OC0A on compare
    TCCR0B = _BV (CS00);           // fast PWM, top at 0xFF, no prescaler
   
    // Timer 0, B side
    TCCR0A |= _BV (COM0B1);        // clear OC0B on compare
   
    // Timer 1
    TCCR1 = _BV (CS10);           // no prescaler
    GTCCR = _BV (COM1B1) | _BV (PWM1B);  //  clear OC1B on compare
    OCR1C = 255;                  // count to 255

    SetValue(0,128);
    SetValue(1,128);
    SetValue(2,128);
   
    MCUCR |= _BV(ISC00);    //Any logical change on INT0 generates an interrupt
    GIMSK |= _BV(INT0);   

    sei();                    //Enable interrupts

    set_sleep_mode(SLEEP_MODE_IDLE);
    while(1)
    {
        sleep_mode();        // in den Schlafmodus wechseln
        SomethingHappened=1;
        while(SomethingHappened){
            //while something happens, stay active
            _delay_ms(100);
            SomethingHappened=0;
        }
    }
}

void SetValue(int Colour, char Value){
    *Port[Colour] = Value;
}


char StartValue=0;
ISR(INT0_vect){
    SomethingHappened=1;
    char EdgeType = (PINB&_BV(PINB2))== _BV(PINB2);
    char CurrentValue = (PINB&_BV(PINB3))== _BV(PINB3);
    if(EdgeType){
        //Rising Edge
        StartValue = CurrentValue;
    }else{
        //Falling Edge
        if(CurrentValue==StartValue){
            //End of Data --> Apply values
            //B-Value was sent last, is most right
            char applyValue = values&255;
            SetValue(2,applyValue);
            applyValue = (values>>8)&255;
            SetValue(1,applyValue);
            //R-Value was sent first, is most left
            applyValue = (values>>16)&255;
            SetValue(0,applyValue);
            //Reset received values    (not needed?)
            values = 0;
        }else{
            //Bit received --> Add to existing values
            values=values<<1;
            if(CurrentValue){
                values|=1;
            }
        }
    }
}

Nun zur Programmierung des ESP8266. Ich möchte auf diese nicht so detailliert eingehen, wie auf die des Attiny, da hier eine ganze Reihe an Aufgaben durchgeführt wird. Diese möchte ich jedoch zumindest anführen:
  • Aufbau und Aufrechterhaltung der WLAN-Verbindung
  • Kommunikation mit dem lokalen Homeautomation-System
  • Abrufen der Uhrzeit über externe NTP-Server (alle 15 Minuten)
  • Mitzählen der Uhrzeit und ständiges Aktualisieren der Anzeige
  • Kommunikation mit dem RGB-Controller
  • Beachtung von Eingabe über die integrierten Taster
Nach, beziehungsweise während der Programmierung habe ich natürlich auch schon Anzeigetests durchgeführt. Ich muss zugeben, dass ich währenddessen nicht sonderlich zufrieden mit dem angezeitden Resultat war. Die folgenden Bilder zeigen, warum...



Weder die 12:24, noch die 12:32 sind intuitiv ablesbar. Auch im Dunkeln wurde das Ganze nicht sonderlich besser...



Die obere (rote) Anzeige sollte hier 09:26 und die untere 88:88 darstellen. Auch wenn 88:88 keine sinnvolle Uhrzeit ist, so zeigt es zumindest, welche Elemente gut und welche schlecht bis garnicht funktionieren.

In ein paar Tagen geht es [hier] zu Teil 2...

Keine Kommentare:

Kommentar veröffentlichen