/*#########################################################################################

Auswertung des Daewoo-Fernbedienungscodes

Achtung! Dies ist nur eine Studienversion, die noch keine weitere Funktionalität hat.

Das Programm wurde erstellt von Guido S.

ATMega8

#########################################################################################*/

#include <avr/io.h>
#include <avr/interrupt.h>

// die nächsten Zeilen sind nur zur Veranschaulichung meiner Frage
//#define Test_OCIE1A    // Werte können sein: CTC, CLI oder OCIE1A
#define Test_CTC        // Werte können sein: CTC, CLI oder OCIE1A
//#define Test_CLI        // Werte können sein: CTC, CLI oder OCIE1A


uint8_t FlankenZaehler;
uint16_t Adr_OPCode;
uint16_t VergZeit;


void Ausgabe(uint8_t Wert)
{
    // an PortB2 bis B6 und PortC0 bis C2 sind LEDs angeschlossen
    // somit kann ein Word angezeigt werden

    // Aufbereitung der Daten und Ausgabe an die richtigen Pins
    // die Bits werden negiert, damit man auf eine richtige Darstellung kommt
    // => Bit von Wert = "1" - LED an; Bit von Wert = "0" - LED aus
    PORTB = ~((Wert <<  1) & 0b00111110);
    PORTC = ~(Wert >> 5);
}


ISR(INT1_vect)
{
    /* Interrupt Code */

    // hier findet die Zeitmessung statt
    // Messung beginnt beim Eintreffen des ersten EXT1-Interrupts

    void Fehlerbehandlung(void)
    {
        // Flankenzähler wird auf 100 gestellt und dient der weiteren Bearbeitung
    //    FlankenZaehler = 100;

        #if defined (Test_OCIE1A)    // Werte können sein: CTC, CLI oder OCIE1A
        TIMSK &= ~(1 << OCIE1A);    // Timer-Interrupt sperren
        #endif
        
        #if defined (Test_CTC)        // Werte können sein: CTC, CLI oder OCIE1A
        TCCR1A = 0;                    // CTC abschalten 
        TCCR1B = 0;                    // CTC abschalten
        #endif
        
        #if defined (Test_CLI)        // Werte können sein: CTC, CLI oder OCIE1A
        cli();                        // Disable interrupts
        #endif

        FlankenZaehler = 0;
    }

    // Timer auslesen
    cli();                // Disable interrupts
    VergZeit = TCNT1;    // Read TCNT1   // seit dem Int ist TCNT1 um 43 erhöht
    TCNT1 = 0;            // Timer soll von 0 beginnen; für diesen Befehl werden weitere 6 Takte benötigt
    sei();                 // Enable interrupts

#define Tolleranz 8 // Werte zwischen 5 und 15 haben sich als sinnvoll gezeigt

#if (F_CPU <= 6553500)
    // Diese Berechnung geht nur bis 6,5535 MHz
    #define T10ms (F_CPU/100)
    #define T8msh (((F_CPU/1000)*8)+((F_CPU/10000)*Tolleranz))
    #define T8msl (((F_CPU/1000)*8)-((F_CPU/10000)*Tolleranz))
    #define T4msh (((F_CPU/1000)*4)+((F_CPU/10000)*Tolleranz))
    #define T4msl (((F_CPU/1000)*4)-((F_CPU/10000)*Tolleranz))
    #define T055msh (((F_CPU/100000)*55)+((F_CPU/100000)*3*Tolleranz))
    #define T055msl (((F_CPU/100000)*55)-((F_CPU/100000)*3*Tolleranz))
    #define T045msh (((F_CPU/100000)*45)+((F_CPU/100000)*3*Tolleranz))
    #define T045msl (((F_CPU/100000)*45)-((F_CPU/100000)*3*Tolleranz))
    #define T145msh (((F_CPU/100000)*145)+((F_CPU/100000)*3*Tolleranz))
    #define T145msl (((F_CPU/100000)*145)-((F_CPU/100000)*3*Tolleranz))
#else
    // Bei F_CPU > 6,5535 MHz wird der Vorteiler auf 8 gestellt und die Zeiten berechnen sich so:
    #define T10ms (F_CPU/800)
    #define T8msh (((F_CPU/8000)*8)+((F_CPU/80000)*Tolleranz))
    #define T8msl (((F_CPU/8000)*8)-((F_CPU/80000)*Tolleranz))
    #define T4msh (((F_CPU/8000)*4)+((F_CPU/80000)*Tolleranz))
    #define T4msl (((F_CPU/8000)*4)-((F_CPU/80000)*Tolleranz))
    #define T055msh (((F_CPU/800000)*55)+((F_CPU/800000)*3*Tolleranz))
    #define T055msl (((F_CPU/800000)*55)-((F_CPU/800000)*3*Tolleranz))
    #define T045msh (((F_CPU/800000)*45)+((F_CPU/800000)*3*Tolleranz))
    #define T045msl (((F_CPU/800000)*45)-((F_CPU/800000)*3*Tolleranz))
    #define T145msh (((F_CPU/800000)*145)+((F_CPU/800000)*3*Tolleranz))
    #define T145msl (((F_CPU/800000)*145)-((F_CPU/800000)*3*Tolleranz))
#endif

    // hier beginnt die Abfrage des Flankenzählers
    if (FlankenZaehler == 0)
    {
        // Timer 1 einstellen - Timer wird als CTC genutzt
            // Mode 4
            // CLKi/o / 1   oder   CLKi/o / 8
        OCR1A = T10ms;            // entspricht 10 ms
        TCCR1A = 0; 
        #if (F_CPU <= 6553500)
        TCCR1B = (1 << WGM12) | (1 << CS10); 
        #else
        TCCR1B = (1 << WGM12) | (1 << CS11); 
        #endif
        TIMSK |= (1 << OCIE1A); // Timer-Interrupt erlauben
        FlankenZaehler++;        // Flankenzähler erhöhen und auf neue Flanke warten
    }
    else if (FlankenZaehler == 1) 
    {    // die erste Periode müssen 8 ms lang sein
        if ((VergZeit > T8msl+43) & (VergZeit < T8msh+43))
        {
            FlankenZaehler++;    // Flankenzähler erhöhen und auf neue Flanke warten
        }
        else
        {
            // Flanke ist nicht im erwarteten Zeitraum gekommen
            Fehlerbehandlung();
        }
    }
    else if ((FlankenZaehler == 2) | (FlankenZaehler == 20))
    {    // diese Perioden müssen 4 ms lang sein
        if ((VergZeit > T4msl) & (VergZeit < T4msh))
        {
            FlankenZaehler++;    // Flankenzähler erhöhen und auf neue Flanke warten
        }
        else
        {
            // Flanke ist nicht im erwarteten Zeitraum gekommen
            Fehlerbehandlung();
        }
    }
    else if ((FlankenZaehler & 1) == 1)
    {    // wenn Flankenzähler ungerade, dann muss die Zeit 0,55 ms sein
        if ((VergZeit > T055msl) & (VergZeit < T055msh))
        {
            if (FlankenZaehler == 37)
            {    // wenn Flankenzähler 37 ist, dann ist das Stopp-Bit erreicht
                // hier wird nicht mehr auf die nächste Flanke gewartet, 
                // da das Stopp-Bit nur eine halbe Welle ist
                // an dieser Stelle wurde der Code komplett empfangen
                // JUCHU!!!

                #if defined (Test_OCIE1A)    // Werte können sein: CTC, CLI oder OCIE1A
                TIMSK &= ~(1 << OCIE1A);    // Timer-Interrupt sperren
                #endif
                
                #if defined (Test_CTC)        // Werte können sein: CTC, CLI oder OCIE1A
                TCCR1A = 0;                    // CTC abschalten 
                TCCR1B = 0;                    // CTC abschalten
                #endif
                
                #if defined (Test_CLI)        // Werte können sein: CTC, CLI oder OCIE1A
                cli();                        // Disable interrupts
                #endif

                Ausgabe((uint8_t)(Adr_OPCode >> 8));     // Ausgabe des OP-Codes
                //Ausgabe((uint8_t)(Adr_OPCode));        // das ist die Geräteadresse
                FlankenZaehler = 0;
            }
            else
            {    // bei allen anderen Flanken ist alles OK und der Flankenzähler wird erhöht
                FlankenZaehler++;    // Flankenzähler erhöhen und auf neue Flanke warten
            }
        }
        else
        {
            // Flanke ist nicht im erwarteten Zeitraum gekommen
            Fehlerbehandlung();
        }
    }
    else if ((FlankenZaehler & 1) == 0)
    {    // hier kommt die Auswertung der 2. Halbwelle, die die Information beinhaltet,
        // ob es eine eine "0" oder "1" ist
        if ((VergZeit > T045msl) & (VergZeit < T145msh))
        {
            FlankenZaehler++;    // Flankenzähler erhöhen und auf neue Flanke warten
            if ((VergZeit > T045msl) & (VergZeit < T045msh))
            // das erkannte Bit ist eine "0"
            {
                Adr_OPCode = (Adr_OPCode >> 1);
            }
            else if ((VergZeit > T145msl) & (VergZeit < T145msh))
            // das erkannte Bit ist eine "1"
            {
                Adr_OPCode = (Adr_OPCode >> 1) | (1 << 15);
            }
            else
            {
                // Flanke ist nicht im erwarteten Zeitraum gekommen
                Fehlerbehandlung();
            }
        }
        else
        {
            // Flanke ist nicht im erwarteten Zeitraum gekommen
            Fehlerbehandlung();
        }
    }
}


ISR(TIMER1_COMPA_vect)
{    // Der Timer ist auf 10ms eingestellt
    // Wenn der Interrupt ausgelöst wird, ist die max. Zeit weit überschritten
    // Die Zeit wird gemessen, indem der Zählerstand des Timers ausgelesen wird
    
    // Flankenzähler zurückstellen, um auf eine neue Sequenz zu warten
    FlankenZaehler = 0;

    // diese Ausgabe soll mir signalisieren, dass ich in dieser Interruptroutine war
    Ausgabe(0b11111111);
    
    // EXT1 Interrupt erlauben 
    GICR |= (1 << INT1);    
}




int main(void)
{
    // Variablen initialisieren
    FlankenZaehler = 0;

    // Interrupts initialisieren 
    MCUCR |= (1 << ISC10); MCUCR &= ~(1 << ISC11);    //EXT1 soll bei jeder Flanke ausgelöst werden
    GICR |= (1 << INT1);      // EXT1 Interrupt erlauben        
    sei();                     // global Interrupt erlauben

    // Ports initialisieren
    DDRB |= (1 << PB1); // PB1 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen
    DDRB |= (1 << PB2); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen
    DDRB |= (1 << PB3); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen
    DDRB |= (1 << PB4); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen
    DDRB |= (1 << PB5); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen

    DDRC |= (1 << PC0); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen
    DDRC |= (1 << PC1); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen
    DDRC |= (1 << PC2); // PB2 als Ausgang; an diesem Port ist eine LED mit Vorwiderstand gegen +5V angeschlossen

    // diese Ausgabe ist nur zum Test und soll mir zeigen, dass die CPU bereit ist
    Ausgabe(0b10100101);

    // Das Hauptprogramm ist eine Endlosschleife, es wird durch Interrupts unterbrochen
    while(1) 
    {
    }
}