Forum: Mikrocontroller und Digitale Elektronik DCF77 Signal - Probleme bei der Auswertung. Timerprobleme?


von Andreas D. (galdo)


Lesenswert?

Hallo zusammen,

bezugnehmend auf diesen Thread ATMega 1284p - PinChange Interrupt, 
habe ich gleich noch eine Frage hinterher. Wie ich ja beschrieben habe 
möchte ich ein DCF77 Signal auswerten, allerdings verwende ich nicht den 
externen Interrupt, sondern einen Pin Change Interrupt am PIN PCINT6 
(PA6) meines ATMega1284p.

Hier nochmal die Configuration der Interrupts:
1
   //Interrupt PCINT6 einschalten
2
   PCICR  |= (1<<PCIE0);
3
   PCMSK0 |= (1<<PCINT6);
4
   
5
   //Interrupt Overfolw enable
6
   TIMSK1 |= (1 << TOIE1);
7
   //Setzen des Prescaler auf 1024 
8
   TCCR1B |= (1<<CS10 | 0<<CS11 | 1<<CS12); 
9
  
10
  //F_CPU defined in config.h
11
   TCNT1 = 65535 - (F_CPU / 1024);

F_CPU ist definiert in einer Datei /config.h/:
1
#define F_CPU 16000000UL
Das sollte für mein AVR Net-IO von Pollin, auf dem das Ganze läuft 
hinhauen - zumindest passt die serielle Ausgabe. Ich sehe das mal als 
Indikator für die Korrektheit der Einstellung an :)

Grundsätzlich verwende ich den DCF77 Code von Ulrich Radig, und habe 
daher - nach Anpassung an meinen Chip folgende ISRs:
1
//############################################################################
2
//Overflow Interrupt wird ausgelöst bei 59Sekunde oder fehlenden DCF77 Signal 
3
//############################################################################
4
ISR (TIMER1_OVF_vect)
5
{  
6
   struct  DCF77_Bits *rx_buffer;
7
   rx_buffer = (struct DCF77_Bits*)(char*)&dcf_rx_buffer;
8
        
9
   //Zur¸cksetzen des Timers
10
   TCNT1 = 65535 - (F_CPU / 1024);
11
12
   //wurden alle 59 Bits empfangen und sind die Paritys richtig?
13
   if (rx_bit_counter == 59 && 
14
    flags.parity_P1 == rx_buffer->P1 && 
15
    flags.parity_P2 == rx_buffer->P2 &&
16
    flags.parity_P3 == rx_buffer->P3)
17
    //Alle 59Bits empfangen stellen der Uhr nach DCF77 Buffer
18
    {
19
        //Berechnung der Minuten BCD to HEX
20
        mm = rx_buffer->Min-((rx_buffer->Min/16)*6);
21
        if (mm != 0){mm--;}else{mm = 59; h_hh = 1;};
22
    
23
        //Berechnung der Stunden BCD to HEX
24
        hh = rx_buffer->Hour-((rx_buffer->Hour/16)*6);
25
        if (h_hh) {hh--;h_hh = 0;};
26
27
        //Berechnung des Tages BCD to HEX   
28
        day= rx_buffer->Day-((rx_buffer->Day/16)*6); 
29
    
30
        //Berechnung des Monats BCD to HEX
31
        mon= rx_buffer->Month-((rx_buffer->Month/16)*6);
32
     
33
        //Berechnung des Jahres BCD to HEX
34
        year= 2000 + rx_buffer->Year-((rx_buffer->Year/16)*6);
35
    
36
        //Sekunden werden auf 0 zur¸ckgesetzt
37
        ss = 59;
38
        flags.dcf_sync = 1;
39
    }
40
   else
41
    //nicht alle 59Bits empfangen bzw kein DCF77 Signal Uhr l‰uft 
42
    //manuell weiter
43
    {
44
    Add_one_Second();
45
    flags.dcf_sync = 0;
46
    }
47
   //zur¸cksetzen des RX Bit Counters
48
   rx_bit_counter = 0;
49
   //Lˆschen des Rx Buffers
50
   dcf_rx_buffer = 0;
51
};
1
//############################################################################
2
//DCF77 Modul empfängt Träger 
3
//############################################################################   
4
ISR (PCINT0_vect) {  
5
    if ((PINA & (1<<PINA6)) && (oldPinLevel == 0)) {
6
        //check for high level
7
        INT0_CONTROL = INT0_RISING_EDGE;
8
        oldPinLevel  = 1;
9
        PORTC ^= _BV(PC6);
10
            
11
    } else if ((!(PINA & (1<<PINA6))) && (oldPinLevel == 1)) {
12
        //check for low level
13
        INT0_CONTROL = INT0_FALLING_EDGE;
14
        oldPinLevel  = 0;
15
        PORTC ^= _BV(PC6);
16
    }
17
    
18
    
19
   //Auswertung der Pulseweite 
20
   if (INT0_CONTROL == INT0_RISING_EDGE) {
21
    flags.dcf_rx ^= 1;
22
    
23
    //Secunden Hilfs Counter berechnen // F_CPU defined in USART.H
24
    h_ss = h_ss + TCNT1 - (65535 - (F_CPU / 1024));
25
    //Zur¸cksetzen des Timers
26
    TCNT1 = 65535 - (F_CPU / 1024);
27
    
28
     //ist eine Secunde verstrichen // F_CPU defined in USART.H
29
     if (h_ss > (F_CPU / 1024 / 100 * 90)) {
30
      //Addiere +1 zu Sekunden
31
      Add_one_Second();
32
      //Zur¸cksetzen des Hilfs Counters
33
      h_ss = 0;
34
        };
35
    } else {    
36
    //Auslesen der Pulsweite von ansteigender Flanke zu abfallender Flanke
37
    unsigned int pulse_wide = TCNT1;
38
    
39
    //Zur¸cksetzen des Timers
40
    TCNT1 = 65535 - (F_CPU / 1024);
41
    
42
    //Secunden Hilfs Counter berechnen
43
    h_ss = h_ss + pulse_wide - (65535 - (F_CPU / 1024));
44
    
45
    //Parity speichern
46
    //beginn von Bereich P1/P2/P3
47
    if (rx_bit_counter ==   21 || rx_bit_counter ==  29 || rx_bit_counter ==  36) {
48
     flags.parity_err = 0;
49
    };
50
51
    //Speichern von P1
52
    if (rx_bit_counter ==   28) {flags.parity_P1 = flags.parity_err;};
53
54
    //Speichern von P2
55
    if (rx_bit_counter ==   35) {flags.parity_P2 = flags.parity_err;};
56
57
    //Speichern von P3
58
    if (rx_bit_counter ==   58) {flags.parity_P3 = flags.parity_err;};
59
60
    //‹berpr¸fen ob eine 0 oder eine 1 empfangen wurde
61
    //0 = 100ms
62
    //1 = 200ms
63
    //Abfrage grˆfler als 150ms (15% von 1Sekund also 150ms)  
64
    if (pulse_wide > (65535 - (F_CPU / 1024)/100*85))
65
     {
66
     //Schreiben einer 1 im dcf_rx_buffer an der Bitstelle rx_bit_counter
67
     dcf_rx_buffer = dcf_rx_buffer | ((unsigned long long) 1 << rx_bit_counter);
68
     //Toggel Hilfs Parity
69
     flags.parity_err = flags.parity_err ^ 1;
70
     }
71
72
    //RX Bit Counter wird um 1 incrementiert
73
    rx_bit_counter++;
74
    }
75
    
76
}

Dem Blinken der LED nach, sieht die Erkennung der BITs soweit auch ganz 
gut aus - ich hab leider kein OSZI um das genauer nachzumessen. Ich sehe 
aber mindestens einen Unterschied zwischen den 100ms und 200ms 
High-Pegeln und BIT59.

Leider bekomm ich trotzdem keinen Sync hin.

Könnt ihr mir ein paar Tipps geben, wie ich bei der Fehlersuche evtl. 
noch auf Hinweise zur Ursache kommen kann, oder seht ihr vielleicht 
sogar einen Fehler im obigen Code?

Danke und Grüße
Galdo

: Verschoben durch User
von Martin K. (maart)


Lesenswert?

Ich habe mir die Frage jetzt wirklich nicht komplett durchgelesen.

Aus eigenen Erfahrungen weiß ich aber: Die DCF-Module sind sehr zickig, 
sobald sie nicht aus einer Batterie absolut störungsfrei versorgt 
werden.

von Andreas D. (galdo)


Lesenswert?

Hi,

erstmal danke für die schnelle Antwort. Im Moment hängt das Teil 
spannungsmäßig direkt an nem VCC / Ground Output - also direkt am DC/DC 
Wandler. Der DCF77 wird derzeit (noch) nicht über einen Output-Pin mit 
Spannung versorgt.

Vielen Dank
Galdo

von holger (Gast)


Lesenswert?

>allerdings verwende ich nicht den
>externen Interrupt, sondern einen Pin Change Interrupt am PIN PCINT6
>(PA6) meines ATMega1284p.

Nimm INT0. Kein Mensch kaut deinen gesamten Code durch und
sagt dir dann die Codezeile wo dein Fehler im von dir geänderten
Programm ist.

Ulrichs Code hat bei mir recht gut funktioniert mit INT0.
Er hat aber einige Problemstellen. DCF per externem Interrupt
zu vermessen ist Unsinn. Jeder kleine Störer macht den Empfang
unmöglich weil die Werte nicht mehr stimmen. Bei Ulrichs Code
kann die Uhr sogar falsch laufen wenn Störungen da sind,
weil er viel am Timer manipuliert.

Evtl. empfängst du nichts weil immer wieder kleine
Störungen auf dem DCF Signal sind. Richte das DCF Modul
exakt zum DCF Sender aus und halte mindestens 1m Abstand
zu Monitoren (auch TFT) und Schaltnetzteilen.

von Andreas D. (galdo)


Lesenswert?

holger schrieb:
> Nimm INT0. Kein Mensch kaut deinen gesamten Code durch und
> sagt dir dann die Codezeile wo dein Fehler im von dir geänderten
> Programm ist.

Der ist halt leider bereits belegt. Alle anderen Externen Interrupts 
auch. Ich hab also garkeine andere Chance als eine Lösung über den PCINT 
zu finden.

von holger (Gast)


Lesenswert?

>Der ist halt leider bereits belegt.

Dann ändere das.

>Ich hab also gar keine andere Chance als eine Lösung über den PCINT
>zu finden.

Doch, hast du. Du willst nur nicht.

von Bernie (Gast)


Lesenswert?

Das funktioniert mit Ext-Int-0, Pin-Change-Interrupt und
auch mit Abfrage (alle 10 ms) des DCF-Signals am PortIn,
gesteuert von einem frei laufenden Timer-Interrupt, den
du sowieso brauchst, um Tasten zu bearbeiten und LCDs
anzusteuern.

Habe ich alles schon mit verschiedenen AVRs getestet.
(Nur bei 2313/4313 hatte ich echte Probleme, da hat Atmel
 seit Jahren versäumt, die *.inc  Dateien mit den real
 existierenden µCs in Übereinstimmung zu bringen).

Allerdings habe ich auch keine Lust deinen Code
zu durchforsten, bevor nicht sichergestellt ist, dass
dein DCF-Modul ordentlich angeschlossen ist.

Vom µC-Board aus habe ich es immer über 220 Ohm
und 1..10 µF nach Masse versorgt. Am Ende der Leitung
zum DCF-Modul auch noch mal 100 nF parallel zur Versorgung
und 47..100 pF parallel zum Ausgang des DCF-Moduls.

Beim Selbst-Entwickeln des Programms hat man natürlich
den Vorteil, es schrittweise testen zu können.

Beim Umstricken fremder Routinen auf einen anderen µC
gibt es VIELE Möglichkeiten Fehler einzubauen...

von Anja (Gast)


Lesenswert?

holger schrieb:
> Jeder kleine Störer macht den Empfang
> unmöglich weil die Werte nicht mehr stimmen.

Genau.

Deshalb würde ein Profi auch niemals einen Interrupt über das 
Eingangssignal auslösen. Weder Pin-Change noch PCINT.

Zur Auswertung des Signals bietet sich ein 5ms Timer-Interrupt an.
Dort kann über ein digitales Filter (Entprellung) auch ein Störimpuls 
ausgeblendet, und durch Mehrfachabtastung die Auswertung verbessert 
werden.

Gruß Anja

von Andreas D. (galdo)


Lesenswert?

Ich hab den Fehler gefunden - ich denk das ist eine Besonderheit des 
ELV-DCF77 Empfängers:
1
    if ((PINA & (1<<PINA6)) && (oldPinLevel == 0)) {
2
        //check for high level
3
        INT0_CONTROL = INT0_FALLING_EDGE;
4
        oldPinLevel  = 1;
5
        PORTC ^= _BV(PC6);
6
            
7
    } else if ((!(PINA & (1<<PINA6))) && (oldPinLevel == 1)) {
8
        //check for low level
9
        INT0_CONTROL = INT0_RISING_EDGE;
10
        oldPinLevel  = 0;
11
        PORTC ^= _BV(PC6);
12
    }
Lösung: Einfach nur die steigende und fallende Flanke umdrehen - also 
scheinbar ist das Signal invertiert, wie es leider öfter vorkommt.

Danke für die Hinweise :)

von Bernie (Gast)


Lesenswert?

Freut mich für dich!

Und wieder kann man den Interrupt-Unken klammheimlich
den Mittelfinger zeigen! ;-)

Egal, ob Interrupt, oder Polling: Man darf natürlich
nicht jeden Glitch am Eingang für einen Sekundentakt
halten. Ein Test, ob seit dem letzten Takt 1 s (oder
2 s) +/-0,05 s vergangen ist, kostet nur ein paar
µC-Takte!

von Andreas D. (galdo)


Lesenswert?

Bernie schrieb:
> Und wieder kann man den Interrupt-Unken klammheimlich
> den Mittelfinger zeigen! ;-)

Naja - gut ist zu sehen, dass es sch***egal ist, ob man nun nen PCINT 
oder nen externen INT nimmt...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.