PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : DCF77 Signalauswertung - komme nicht weiter



Knipser-14
22.09.2010, 11:34
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



/*
* 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.

Jaecko
22.09.2010, 11:48
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.

Knipser-14
22.09.2010, 12:34
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

Jaecko
22.09.2010, 12:46
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)

Knipser-14
24.09.2010, 00:24
Großes Dank nochmal an Jaecko!

Hab den Code jetzt nach deiner Empfehlung mit den INT-Pins nochmal neu gestrickt.

/*
* 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