- LiTime Speicher und Akkus         
Ergebnis 1 bis 5 von 5

Thema: DCF77 Signalauswertung - komme nicht weiter

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    05.04.2008
    Beiträge
    40

    DCF77 Signalauswertung - komme nicht weiter

    Anzeige

    LiFePo4 Akku selber bauen - Video
    Hallo liebe Community,

    wie der Name schon sagt möchte ich ein DCF77 Signal auswerten. Das ganze mache ich mit einem Atmega 16 bei 2.048 MHz. (2.048MHz/1024 =2000 Hz)
    DCF77:
    -ein Zeitsignal
    -in einer Sekunde ist entweder 0,1 (0) oder 0,2 (1) s ein Low-Pegel
    -je nach dem ist das Bit der Sekunde High oder Low (0 oder 1)

    Was der Code eigentlich machen soll:
    -er soll messen ob ein High-Pegel länger als eine Sekunde ist und so den Beginn einer neuen Minute anzeigt und eine 2 schreiben
    -er soll die Low-Pegel messen und bei 0,1s eine 0 schreiben und bei 0,2s eine 1 schreiben
    Was der Code macht:
    -den Beginn einer neuen Minute erkennt er und schreibt eine 2
    -das Low-Pegel messen geht nicht, es sind alle länger als 0,2 Sekunden und er schreibt immer eine 1

    Code:
    /*
     * main.c
     *
     *  Created on: 21.09.2010
     *    
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define F_CPU       2048000UL
    
    #define BAUD        9600UL
    #define UBRR_BAUD   ((F_CPU/(16UL*BAUD))-1)
    
    #define LED_DDR		DDRD
    #define LED_PORT	PORTD
    #define LED_PORTPIN	PD7
    
    char InterruptFlag; 									// =0 -> nächster Interrupt bei fallender Flanke / =1 -> nächster Interrupt bei steigender Flanke
    
    unsigned int Zaehlerstand;								//nimmt den Zählerstand aus IRC1 auf
    
    unsigned char Sekundenzaehler;							//zählt die Sekunden / wird resetet bei der 59 S
    
    char Feld [60];											//das Feld enthält die binären Werte der jeweiligen Sekunden
    
    void uart_init(void)
    {
        // Baudrate einstellen (Normaler Modus)
        UBRRH = (uint8_t) (UBRR_BAUD>>8);
        UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);
    
        // Aktivieren von receiver und transmitter
        UCSRB = (1<<RXEN)|(1<<TXEN);
    
        // Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
        UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
    }
    
    ISR(TIMER1_CAPT_vect)
    	{
    	Zaehlerstand = ICR1;								//Zählerstand wird aus IRC1 ausgelsen
    
    	TCNT1 = 0;											//16-Bit-Timer Zählerstand wird zurückgesetzt
    
    	switch(InterruptFlag)
    		{
    		case 0:											//der Interrupt hat bei fallender Flanke ausgelöst -> Pausenzeit wird gemessen
    
    			InterruptFlag = 1;							//zeigt an: nächster Interrupt bei steigender Flanke
    
    			TCCR1B |= (1<<ICES1);						//Capture Interrupt nun bei steigender Flanke
    
    			if (Zaehlerstand >= 3600)					//Pause von 1,8s -> Zeigt an der nächste Low-Pegel gehört zur neuen Minute
    				{
    				Sekundenzaehler = 0;					//der Sekundenzaehler wird resetet
    
    				// Warten bis der Sendepuffer frei ist
    				while ( !( UCSRA & (1<<UDRE)) );
    				// Daten in den Puffer schreiben und damit senden
    				UDR = '2';
    
    				}
    			break;
    
    		case 1:											//der Interrupt hat bei steigender Flanke ausgelöst -> Low-Pegel-Zeit wird gemessen
    
    			InterruptFlag = 0;							//zeigt an; nächster Interrupt bei fallender Flanke
    
    			TCCR1B |= (0<<ICES1);						//Capture Interrupt nun bei fallender Flanke
    
    			if (Zaehlerstand < 300)						//Low-Pegel-Zeit unter 100ms -> 0
    				{
    				Feld [Sekundenzaehler] = 0;				//schreibt eine null an die aktuelle Stelle des Feldes
    
    				Sekundenzaehler = Sekundenzaehler+1;	//der Sekundenzähler wird um 1 erhöht für die nächste Sekunde
    
    				// Warten bis der Sendepuffer frei ist
    				while ( !( UCSRA & (1<<UDRE)) );
    				// Daten in den Puffer schreiben und damit senden
    				UDR = '0';
    				}
    			if (Zaehlerstand > 300)						//Low-Pegel-Zeit über 100ms -> 1
    				{
    				Feld [Sekundenzaehler] = 1;				//schreibt eine eins an die aktuelle Stelle des Feldes
    
    				Sekundenzaehler = Sekundenzaehler+1;	//der Sekundenzähler wird um 1 erhöht für die nächste Sekunde
    
    				// Warten bis der Sendepuffer frei ist
    				while ( !( UCSRA & (1<<UDRE)) );
    				// Daten in den Puffer schreiben und damit senden
    				UDR = '1';
    				}
    			break;
    		}
    
    	}
    
    void main(void)
    	{
    	TIMSK |= (1<<TICIE1);								//gibt den Capture Interrupt frei
    
    	TCCR1B |= (0<<ICES1) | (1<<CS12) | (1<<CS10);		//erster Interrupt bei einer fallenden Flanke | Prescaler auf 1024 -> 2.048MHz/1024 -> 2000Hz
    
    	InterruptFlag = 0;									//zeigt an erster Interrupt bei fallender Flanke
    
    	DDRD &=~ (1<<PD6);									//PD6 als Eingang
    
    	PORTD |= (1<<PD6);									//...
    
    	sei();
    
    	uart_init();
    
    	LED_DDR |= (1<<LED_PORTPIN);						//LED Port als Ausgang
    
    	LED_PORT |= (1<<LED_PORTPIN);						//LED Port auf High -> LED leuchtet
    
    	while(1);
    		{
    		if (Sekundenzaehler == 59)						//Ende einer Minute steht an -> Auswertung kann erfolgen
    			{
    														//Auswertungsfunktion
    			}
    		}
    	}
    Ich weiß nicht was ich noch machen soll. Das Signal von dem DCF77-Empfänger kommt auch an. Hab mal mein analoges Voltmeter an den PIN gehalten und man erkennt anhand der unterschiedlichen Tiefe der Ausschläge die 0 und 1... bloß der mC macht das nicht.

    Vielleicht fällt ja irgendwem etwas an meinem Code auf. Danke schonmal.

  2. #2
    Erfahrener Benutzer Robotik Einstein Avatar von Jaecko
    Registriert seit
    16.10.2006
    Ort
    Lkr. Rottal/Inn
    Alter
    41
    Beiträge
    2.009
    Einen direkten Fehler seh ich grad nicht, aber einige Punkte, die man verbessern könnte:

    #1: InterruptFlag ist nicht volatile, d.h. bei durch Interrupts geänderten Variablen sollte das schon sein.

    #2: Senden und v.a. While in einer ISR: Eine While in einer ISR kann "lustige" Effekte erzeugen, wenn das Warten mal länger dauert, als der Abstand zwischen 2 ISRs ist.
    Hier einfach eine weitere Volatile-Variable als Flag verwenden, in der ISR auf 1 setzen und in der while(1) der Main-Loop schauen, ob das gesetzt ist. Wenn ja: Da senden und wieder auf 0 setzen.

    #3: Kleinigkeit: Den Vergleich in der Abfrage "if (Sekundenzaehler == 59)" auf ">= 59" ändern. Sollte - aus irgend einem Grund - der Wert doch mal 61 o.ä. sein, wird dieser Fehler damit auch abgebrochen.
    Ansonsten würde der einfach bis 255 durchlaufen.


    Ich hab die DCF77-Auswertung damals anders gemacht; Der Interrupt reagiert auf beide Flanken und der Timer läuft als ganz normaler Timer (also ohne Input Capture). Bei jedem Flankenwechsel wird der aktuelle Zählerstand gespeichert und mit dem des vorherigen Wechsels verglichen.
    #ifndef MfG
    #define MfG

  3. #3
    Benutzer Stammmitglied
    Registriert seit
    05.04.2008
    Beiträge
    40
    Hallo Jaecko,

    danke für deine Antwort!

    Punkt eins und drei auf deiner Liste hab ich erstmal geändert, bringt aber nix...

    Punkt 2 ist vielleicht nicht ideal aber das hab ich erstmal nur zu Textzwecken so gemacht und da ich weiß, dass der UART frei ist, sollte das auch klappen.

    Zu deiner Lösung des Problems:

    Wie kann man denn einen Interrupt auf einen generellen Flankenwechsel reagieren lassen?

    Schönen Tag noch,

    Knipser

  4. #4
    Erfahrener Benutzer Robotik Einstein Avatar von Jaecko
    Registriert seit
    16.10.2006
    Ort
    Lkr. Rottal/Inn
    Alter
    41
    Beiträge
    2.009
    Der Flankenwechsel geht an den INTx-Pins (D2, D3) mit ner bestimmten Kombination der ISCx0 und ISCx1-Bits.
    Ist dann ein normaler "external interrupt".


    Ansonsten kann jeder PCINT-Pin dafür verwendet werden (gibts beim ATMega16 aber keine; der Mega644 hat dafür gleich über 30 davon)
    #ifndef MfG
    #define MfG

  5. #5
    Benutzer Stammmitglied
    Registriert seit
    05.04.2008
    Beiträge
    40
    Großes Dank nochmal an Jaecko!

    Hab den Code jetzt nach deiner Empfehlung mit den INT-Pins nochmal neu gestrickt.
    Code:
    /*
     * main.c
     *
     *  Created on: 23.09.2010
     *   
     */
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define F_CPU       2048000UL
    
    #define BAUD        9600UL
    #define UBRR_BAUD   ((F_CPU/(16UL*BAUD))-1)
    
    #define LED_DDR        DDRD
    #define LED_PORT    PORTD
    #define LED_PORTPIN    PD7
    
    volatile unsigned int Zaehlerstand;
    
    
    
    void uart_init(void)
    {
        // Baudrate einstellen (Normaler Modus)
        UBRRH = (uint8_t) (UBRR_BAUD>>8);
        UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);
    
        // Aktivieren von receiver und transmitter
        UCSRB = (1<<RXEN)|(1<<TXEN);
    
        // Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
        UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
    }
    
    ISR (INT1_vect)
    {
        LED_PORT |= (1<<LED_PORTPIN);
    
        Zaehlerstand = TCNT1;                                //den aktuellen Zaehlerstand auslesen
    
        TCNT1 = 0;                                            //den Zaehlerstand zuruecksetzen
    
        if (Zaehlerstand >= 1000)                            //die Zeit zwischen zwei Low-Pegeln wurde gemessen
            {
                if (Zaehlerstand >= 3400)                    //die Pause vor der nächsten Minute wurde gemessen
                    {
                    while ( !( UCSRA & (1<<UDRE)) );        // Warten bis der Sendepuffer frei ist
    
                    UDR = '2';                                // Daten in den Puffer schreiben und damit senden
                    }
            }
        else                                                //die Zeit eines Low-Pegel wurde gemessen
            {
                if (Zaehlerstand >= 300)                    //Low-Pegel > 150ms -> 1
                    {
                        while ( !( UCSRA & (1<<UDRE)) );    // Warten bis der Sendepuffer frei ist
    
                        UDR = '1';                            // Daten in den Puffer schreiben und damit senden
                    }
                else                                        //Low-Pegel < 150ms -> 0
                    {
                        while ( !( UCSRA & (1<<UDRE)) );    // Warten bis der Sendepuffer frei ist
    
                        UDR = '0';                            // Daten in den Puffer schreiben und damit senden
                    }
            }
    }
    
    
    
    void main(void)
        {
        uart_init();                                        //UART initialisieren
    
        GICR |= (1<<INT1);                                    //externer Interrupt an INT1 (PD3) aktiviert
    
        MCUCR |= (0<<ISC11) | (1<<ISC10);                    //Interupt bei jeder logische Veränderung an INT1 (PD3)
    
        TCCR1B |= (1<<CS12) | (1<<CS10);                    // Prescaler auf 1024 -> 2.048MHz/1024 -> 2000Hz
    
        sei();                                                //Interrupts freischalten
    
        LED_DDR |= (1<<LED_PORTPIN);                        //LED Port als Ausgang
    
        LED_PORT &= ~ (1<<LED_PORTPIN);                        //LED Port auf High -> LED leuchtet
    
    
        while(1);
            {
    
            }
        }
    Jetzt klappt alles einwandfrei!

    Einen kleinen Wermutstropfen gibts aber doch: Ich weiß immer noch nicht warum mein erster Code nich funktioniert hat, aber jetzt bin ich endlich erstmal weiter :-D

    Schönen Abend noch,

    Knipser

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

LiTime Speicher und Akkus