Forum: Mikrocontroller und Digitale Elektronik DCF77 Code Peter Dannegger geht nur bis Sekunde 29?


von Gregor B. (gregor54321)


Lesenswert?

Hallo Zusammen!

Ich habe auf Basis Peter Danneggers Code 
Beitrag "DCF77 Uhr in C mit ATtiny26" etwas zusammen gebaut.

Die Telegramme werden alle fehlerfrei erkannt (siehe ganz unten im 
Post). Aber newtime.second wird immer zu früh zurück gesetzt.

Meine kleine Funktion debug_num2port(uint8_t) schreibt mir einen Wert 
für den Logiktester auf einen Port. Nach dem Wert "33" im 
dcf77_signal_auswerten()-else-Zweig wird der Wert der newtime.second 
ausgegeben. Nachdem hier newtime.second==29 war, ist sie im nächsten 
Durchlauf plötzlich "1". Warum?

Danke für eure Hilfe!

1
int main (void) {
2
  sei();
3
  { // UART Initialisieren
4
         uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
5
    uart_puts("\r\n\n\nUART-Initialized\n\r");
6
    delay_ms(500);
7
  }
8
9
  dcf77_init();
10
11
  while (1) {
12
    if ( synchronize == 0xFF) {
13
      // übernehme die Zeit
14
      synchronize = 0;
15
    }
16
  }
17
}
18
19
ISR (TIMER2_COMP_vect) {  
20
  TimingDelay = (TimingDelay==0) ? 0 : TimingDelay-1;
21
22
  static uint8_t a = 0;
23
  static uint8_t b = 0;
24
  
25
  dcf77_isr_modul_auslesen();  // alle 10ms ausführen
26
27
  if ( ++a == 50)  { // alle 500ms ausführen
28
    dcf77_signal_auswerten();
29
    a=0;
30
  }
31
  if ( ++b == 100) {  // alle 1000ms ausführen
32
    clock();
33
    b=0;
34
  }
35
}
36
static void timer2_init() {
37
  TimingDelay = 0;    // initialisierung der zaehl variable  
38
// Timer 2 CTC, OCIE, 1kHz
39
  sei();          // interrupts anschalten, wegen compare match
40
}

----------------------------

modifizierte dcf77.c
1
/************************************************************************/
2
/*                                                                      */
3
/*             Decode DCF Time Information                              */
4
/*                                                                      */
5
/*              Author: Peter Dannegger                                 */
6
/*                      danni@specs.de                                  */
7
/*                                                                      */
8
/************************************************************************/
9
#include <avr/pgmspace.h>
10
#define LPM(i) (__LPM_classic__(i))
11
12
#include <util/delay.h>  // only while debugging gboek
13
#include <stdio.h> // only while debugging gboek
14
#include "clock.h"
15
#include "dcf77.h"
16
17
uint8_t PROGMEM BITNO[] = {
18
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,        // minute
19
  0xFF,                          // parity
20
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15,            // hour
21
  0xFF,                          // parity
22
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25,            // day
23
  0x30, 0x31, 0x32,                    // weekday
24
  0x40, 0x41, 0x42, 0x43, 0x44,              // month
25
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,      // year
26
  0xFF                          // parity
27
};
28
uint8_t PROGMEM BMASK[] = { 1, 2, 4, 8, 10, 20, 40, 80 };         
29
30
struct time newtime;                                        
31
32
uint8_t dcf77error  = 0;                                          
33
uint8_t synchronize = 0;                      // successful recieved
34
35
uint8_t timeflags;
36
uint8_t dcf77_period;
37
uint8_t dcf77_pulse;
38
uint8_t dcf_timer_tick_counter;                  
39
40
41
void dcf77_init ( void ) {
42
  DCF_DDR   &= ~(1<<DCF) ; // Port auf Input
43
  DCF_PORT  |=  (1<<DCF) ; // PullUp setzen
44
#if ( DEBUG_DCF77 == TRUE )
45
  DCF_DEBUG_DDR |= (1<<DCF_DEBUG) ; // Port auf Output
46
#endif
47
}
48
void dcf77_decode( uint8_t pulse ) {
49
  static uint8_t parity = 0;
50
  uint8_t i;
51
  uint8_t *d;
52
53
  i = newtime.second - 21;
54
  if( i >= sizeof( BITNO )) {
55
    return; // only bit 21 ... 58
56
  }
57
  parity ^= pulse;        // calculate parity
58
  i = LPM(&BITNO[i]);
59
  if( i == 0xFF ) {        // test parity
60
    if( parity ) {
61
      dcf77error = 1;
62
      debug_num2port(50); // Parityfehler, dcf77error=1
63
    }
64
    parity = 0;
65
    return;
66
  }
67
  d = (uint8_t *)&newtime.minute + (i >> 4);    // byte address
68
  i &= 0x0F;          // bit number
69
  if( i == 0 ) {
70
    *d = 0;          // clear all, if lsb
71
  }
72
  if( pulse ) {
73
    *d += LPM(&BMASK[i]);      // set bit
74
  }
75
}
76
void dcf77_signal_auswerten( void ) {
77
  if( dcf77_pulse ) {
78
    if( dcf77_pulse >= 7 && dcf77_pulse <= 15 ) {  // eine logische null von 100-150ms
79
      dcf77_decode( 0 );
80
      uart_putc('0');
81
      debug_num2port(10); // eine logische 0 wurde erkannt
82
    }
83
    else {
84
      if( dcf77_pulse >= 18 && dcf77_pulse <= 25 ) { // eine logische eins von 200-250ms
85
        dcf77_decode( 1 );
86
        uart_putc('1');
87
        debug_num2port(11); // eine logische 1 wurde erkannt
88
      }
89
      else {
90
        dcf77error = 1;
91
        debug_num2port(12); // weder 0 noch 1 wurden erkannt, dcf77error=1
92
        debug_num2port(51); // weder 0 noch 1 wurden erkannt, dcf77error=1
93
        {
94
          char buffer[30];
95
          sprintf(buffer, "Error. PulseLenght: %d\n\r",dcf77_pulse);
96
          uart_puts(buffer);
97
        }
98
      }
99
    }
100
    dcf77_pulse = 0;
101
  }
102
  if( dcf77_period ) {
103
    if( newtime.second < 60 ) {
104
      newtime.second++;
105
    } 
106
    if( dcf77_period > 180 && dcf77_period < 220 ) {
107
      uart_puts("newSek erkannt\n\r");
108
      debug_num2port(30); // Sekunde 59 erkannt, nächster Wert am LogicPort --> (newtime.second), danach (dcf77error + 128)
109
      debug_num2port(newtime.second);      // = 3
110
      debug_num2port(dcf77error + 128);    // = 129
111
//      {
112
//        char buffer[30];
113
//        sprintf(buffer, "vorher newtime.sec = %d ",newtime.second);
114
//        uart_puts(buffer);
115
//        sprintf(buffer, "dcf77error = %d\n\r", dcf77error);
116
//        uart_puts(buffer);
117
//      }
118
119
      if( dcf77error == 0 && newtime.second == 59) {
120
        debug_num2port(31); // bisher dcf77error == 0 und newtime.second == 59
121
        synchronize = 0xFF;
122
        time = newtime;
123
        time.second = 0;
124
        sync_sec();
125
        uart_puts("set time\n\r");
126
        // time.minute = newtime.minute;
127
        // time.hour   = newtime.hour;
128
        // time.wday   = newtime.wday;
129
        // time.day    = newtime.day;
130
        // time.month  = newtime.month;
131
        // time.year   = newtime.year;
132
      }
133
      newtime.second = 0;
134
      dcf77error = 0;
135
//      {
136
//        char buffer[30];
137
//        sprintf(buffer, "nachher newtime.sec = %d ",newtime.second);
138
//        uart_puts(buffer);
139
//        sprintf(buffer, "dcf77error = %d\n\r", dcf77error);
140
//        uart_puts(buffer);
141
//      }
142
      
143
    }
144
    else {
145
      debug_num2port(33); // normale Sekunde, nicht die 59ste
146
      debug_num2port(newtime.second);      // = 3
147
      if( dcf77_period < 90 || dcf77_period > 110) {
148
        dcf77error = 1; 
149
        debug_num2port(34); // Sekunde war <900 oder >1100ms, dcf77error=1
150
        debug_num2port(52); // Sekunde war <900 oder >1100ms, dcf77error=1
151
        uart_puts("Err: Period <0.9 && >1.1sec\n\r");
152
      }
153
    }
154
    dcf77_period = 0;
155
    debug_num2port(35); //
156
  }
157
}
158
159
void dcf77_isr_modul_auslesen ( void ) {
160
// diese Funktion wird von einem Timer Interrupt aufgerufen
161
// hier jetzt alle 10ms
162
163
#if ( DEBUG_DCF77 == TRUE )
164
  #if ( DCF_MODUL_MIT_ACTIVE_LOW == TRUE )
165
  if( ~DCF_PIN & (1<<DCF) )
166
  #else
167
  if(  DCF_PIN & (1<<DCF) )
168
  #endif
169
  {
170
    DCF_DEBUG_PORT |= (1<< DCF_DEBUG);
171
  }
172
  else
173
  {
174
    DCF_DEBUG_PORT &= ~(1<<DCF_DEBUG);
175
  }
176
//  DCF_DEBUG_DDR  |= (1<<DCF_DEBUG) ; // Port auf Output
177
//  DCF_DEBUG_PORT ^= (1<<DCF_DEBUG) ; // Ausgang togglen
178
#endif
179
180
181
  static uint8_t dcf77_time, old_dcf77;
182
                          // DCF77 receive
183
  if( dcf77_time != 0xFF ) {              // stop on 0xFF
184
    dcf77_time++;                  // count ticks
185
  }
186
#if ( DCF_MODUL_MIT_ACTIVE_LOW == TRUE )
187
  if( ( ~DCF_PIN ^ old_dcf77) & (1<<DCF) )      // pin changed ? (DCF input Port inverted)
188
#else
189
  if( (  DCF_PIN ^ old_dcf77) & (1<<DCF) )      // pin changed ?
190
#endif
191
  {
192
    old_dcf77 ^= 0xFF;                // change old flag
193
    if( old_dcf77 & (1<<DCF) ) {                       
194
      dcf77_period = dcf77_time;          // store ticks of period
195
    debug_num2port(40); // folgender Wert --> Periodenlänge der erkannten Sekunde
196
    debug_num2port(dcf77_period);
197
      dcf77_time = 0;                // count next period
198
    } else {                                          
199
      dcf77_pulse = dcf77_time;          // store ticks of pulse
200
    debug_num2port(41); // folgender Wert --> Pulslänge der erkannten Sekundenbits
201
    debug_num2port(dcf77_pulse);
202
    }                                               
203
  }
204
205
/*
206
  if( (  DCF_PIN ^ old_dcf77) & (1<<DCF) ) {      // pin changed ?
207
    old_dcf77 ^= 0xFF;                // change old flag
208
    if( old_dcf77 & (1<<DCF) ) {                       
209
      dcf77_period = dcf77_time;          // store ticks of period
210
      dcf77_time = 0;                // count next period
211
    } else {                                          
212
      dcf77_pulse = dcf77_time;          // store ticks of pulse
213
    }                                               
214
  }
215
*/                          // time base 1 second
216
//  TCNT0 = (uint16_t)(256 - T0COUNT);          // reload per tick: -183
217
  if( ++dcf_timer_tick_counter & 100 ){        // 100 ticks = one second
218
    timeflags = 1<<ONE_TICK;            // one tick over
219
    return;
220
  }
221
//  TCNT0 = (uint16_t)(256 - T0COUNTSEC);        // reload per second: -189
222
//  if( timeflags & (1<< ONE_MINUTE ))
223
//    TCNT0 = (uint16_t)(256 - T0COUNTMIN);      // reload per minute: -234
224
  timeflags = 1<<ONE_SECOND^1<<ONE_TICK;        // one tick, one second over
225
}
226
227
void sync_sec( void ) {                  // synchronize by DCF77
228
// Register so einstellen, dass wieder bei 0 zu zählen begonnen wird
229
//  TCNT0 = (uint16_t)(256 - T0COUNTMIN);
230
  dcf_timer_tick_counter = 0;
231
  timeflags = 0;
232
}

1
1Err: Period <0.9 && >1.1sec
2
0newSek erkannt
3
00100101000101000101000011001000010100110010010001100100000newSek erkannt
4
00100100011001000101100011011000010100110010010001100100000newSek erkannt
5
11001101010001000101010011011000010100110010010001100100000newSek erkannt
6
00100100010000000101110011001000010100110010010001100100000newSek erkannt

von Knarf (Gast)


Lesenswert?

Findest du nicht, dass dein Betreff etwas despektierlich gewählt ist? 
Peter Dannegger ist sicher kein Allwissender, aber du darfst ihm schon 
zutrauen dass seine Routinen erfolgreich angewendet werden können.

von holger (Gast)


Lesenswert?

>Peter Dannegger ist sicher kein Allwissender, aber du darfst ihm schon
>zutrauen dass seine Routinen erfolgreich angewendet werden können.

Genau. Wenn man den ganzen debug und uart_puts Schrott
aus den Interrupts rausnimmt könnte das helfen.

von Gregor B. (gregor54321)


Lesenswert?

Knarf schrieb:
> Findest du nicht, dass dein Betreff etwas despektierlich gewählt ist?

Entschuldigung. Ich wollte lediglich, dass der Ursprung des Codes klar 
ist, und jemand, der vielleicht schon damit gearbeitet hat, auf den 
Thread aufmerksam wird. Mein Respekt sollte allein dadurch ausgedrückt 
werden, das ich seinen Code aus den vielen Beispielen der Codesammlung 
gewählt habe. Jedoch habe ich Modifikationen vorgenommen und nun läuft 
er nicht mehr "rund". Ich hoffe, ich bekomme trotzdem etwas 
Unterstützung.

holger schrieb:
> Wenn man den ganzen debug und uart_puts Schrott
> aus den Interrupts rausnimmt könnte das helfen.

Da alle Bits erfolgreich erkannt werden, kann es daran nicht liegen.
Den ganzen Schrott habe ich übrigens nachträglich eingefügt, um das 
Problem bis dahin einzukreisen...

von holger (Gast)


Lesenswert?

>Mein Respekt sollte allein dadurch ausgedrückt
>werden, das ich seinen Code aus den vielen Beispielen der Codesammlung
>gewählt habe. Jedoch habe ich Modifikationen vorgenommen und nun läuft
>er nicht mehr "rund". Ich hoffe, ich bekomme trotzdem etwas
>Unterstützung.

Geht es ohne Modifikationen?
Wenn ja such den Fehler in deinen Modifikationen.
Oder bau deine Modifikationen einfach mal langsam zurück.
Irgendwann findest du die Stelle wo du Mist gebaut hast.

von Karl H. (kbuchegg)


Lesenswert?

Mach da bitte in deinem Code noch eine Modifikation rein
1
void dcf77_signal_auswerten( void ) {
2
  if( dcf77_pulse ) {
3
4
    uart_putc( 'A' + newtime.second );    // <-------
5
6
    if( dcf77_pulse >= 7 && dcf77_pulse <= 15 ) {  // eine logische null von 100-150ms
7
      dcf77_decode( 0 );
8
      uart_putc('0');
9
    ....

und poste noch mal den sich dadurch ergebenden UART Output.
Ich hab keine Erklärung dafür, warum newtime.second plötzlich von 29 auf 
1 gehen sollte. Ich denke deine Ausgabe bzw. die Ablesung ist falsch.

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
  if( ++dcf_timer_tick_counter & 100 ){        // 100 ticks = one second

ist falsch, hat jetzt aber erst mal nichts mit deinem Problem zu tun.

von Gregor B. (gregor54321)


Lesenswert?

Ich versuche gerade die auch geposteten Telegramme laut wikipedia(DCF77) 
von Hand zu entschlüsseln. Irgendwo ist da der Wurm drin.
Bit0 ist wie erwartet 0, aber Bit20 (Beginn der Zeitinformation) ist 
nicht 1.

Karl Heinz Buchegger schrieb:
> und poste noch mal den sich dadurch ergebenden UART Output.

AError. PulseLenght: 2
Err: Period <0.9 && >1.1sec
B1Err: Period <0.9 && >1.1sec
C0D1E1F1G0H0I1J0K0L1M0N0O0P0Q0R0S0T0U0V0W0X0Y1Z1[1\0]0^1C0D0E0F1G1H0I0J1 
K0L0M0N1O0newSek  erkannt
A1B0C1D1E1F1G1H0I1J1K0L0M1N0O0P0Q0R1S0T1U0V0W1X0Y1Z0[0\0]0^0B0C0D0E0F0G0 
H0I0J0K0L1M1N1O0P0Q1R0S0T0U1V1W0X0Y1Z0[0\0]1^0newSek  erkannt
A0B1C0D1E0F0G1H0I0J1K1L0M1N0O0P0Q0R1S0T1U1V0W1X0Y1Z0[0\1]0^0B0C0D0E0F0G0 
H0I0J0K0L1M1N1O0P0Q1R0S0T0U1V1W0X0Y1Z0[0\0]1^0newSek  erkannt
A0B1C1D0

Es hat mit der Parity-Prüfung zu tun, ohne den entsprechenden Abschnitt 
läuft der Code (ohne das erwartete Ergebnis) durch.

von Gregor B. (gregor54321)


Angehängte Dateien:

Lesenswert?

Nachtrag: Die Telegramme sind doch korrekt!
Ich gebe mal den Quellcode als Anlage mit...
Für die Nachwelt: Diese Sourcen funktionieren noch nicht!

oh...  die debug_num2port muss noch global mit // ersetzt werden

von Karl H. (kbuchegg)


Lesenswert?

Gregor B. schrieb:

> Karl Heinz Buchegger schrieb:
>> und poste noch mal den sich dadurch ergebenden UART Output.
>
> AError. PulseLenght: 2
> Err: Period <0.9 && >1.1sec
> B1Err: Period <0.9 && >1.1sec
> C0D1E1F1G0H0I1J0K0L1M0N0O0P0Q0R0S0T0U0V0W0X0Y1Z1[1\0]0^1C0D0E0F1G1H0I0J1 
K0L0M0N1O0newSek
> erkannt


Hmm.
Tatsache. Da wird der Zähler mitten drinn resettet.

> Es hat mit der Parity-Prüfung zu tun, ohne den entsprechenden Abschnitt
> läuft der Code (ohne das erwartete Ergebnis) durch.

Ergibt aber keinen Sinn. Das erklärt den Zähler Reset nicht.

von Gregor B. (gregor54321)


Lesenswert?

Fehler gefunden! Pointer-Arithmetik == evil
Ich hatte die Definition des (extern deklarierten) time-stuct geändert, 
ohne an die Arithmetik zu denken. Also wieder ein Fehler, der nur hätte 
mit Glaskugel gelöst werden können... Sorry.

von Karl H. (kbuchegg)


Lesenswert?

Gregor B. schrieb:
> Fehler gefunden! Pointer-Arithmetik == evil
> Ich hatte die Definition des (extern deklarierten) time-stuct geändert,
> ohne an die Arithmetik zu denken. Also wieder ein Fehler, der nur hätte
> mit Glaskugel gelöst werden können... Sorry.

Gut. Da sag ich jetzt mal nichts dazu, kannst es dir sowieso denken :-)

Trotzdem Danke für die ehrliche Rückmeldung. Ansonsten hängen solche 
Threads immer so offen in der Luft rum.

von VATA (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Danke für die ehrliche Rückmeldung

Da schließe ich mich an.

Gregor B. schrieb:
> Ich hatte die Definition des (extern deklarierten) time-stuct geändert,
> ohne an die Arithmetik zu denken.

Ich vermute Deklaration anstatt Definition. Aber egal, das hört sich 
nach schwer wartbaren Code an. Darf man das einmal sehen, aus eigenem 
Interesse?

von Karl H. (kbuchegg)


Lesenswert?

VATA schrieb:

> Ich vermute Deklaration anstatt Definition. Aber egal, das hört sich
> nach schwer wartbaren Code an. Darf man das einmal sehen, aus eigenem
> Interesse?

Da brauchts gar nicht viel.

Aus einem

struct time
{
  uint8_t second;
  uint8_t minute;
  uint8_t hour;
};

ein

struct time
{
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
};

machen (weil man eine persönliche Vorliebe für die Reihenfolge vom 
größeren zum kleineren hat), und mit der Art und Weise, wie der Code aus 
den Bits die Einzelfelder füllt, hast du schon das beschriebene 
Verhalten.


In dem Code
1
uint8_t PROGMEM BITNO[] = {
2
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,        // minute
3
  0xFF,                          // parity
4
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15,            // hour
5
  0xFF,                          // parity
6
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25,            // day
7
  0x30, 0x31, 0x32,                    // weekday
8
  0x40, 0x41, 0x42, 0x43, 0x44,              // month
9
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,      // year
10
  0xFF                          // parity
11
};
12
13
void dcf77_decode( uint8_t pulse ) {
14
  static uint8_t parity = 0;
15
  uint8_t i;
16
  uint8_t *d;
17
18
  i = newtime.second - 21;
19
  if( i >= sizeof( BITNO )) {
20
    return; // only bit 21 ... 58
21
  }
22
  parity ^= pulse;        // calculate parity
23
  i = LPM(&BITNO[i]);
24
  if( i == 0xFF ) {        // test parity
25
    if( parity ) {
26
      dcf77error = 1;
27
      debug_num2port(50); // Parityfehler, dcf77error=1
28
    }
29
    parity = 0;
30
    return;
31
  }
32
  d = (uint8_t *)&newtime.minute + (i >> 4);    // byte address
33
  i &= 0x0F;          // bit number
34
  if( i == 0 ) {
35
    *d = 0;          // clear all, if lsb
36
  }
37
  if( pulse ) {
38
    *d += LPM(&BMASK[i]);      // set bit
39
  }
40
}

steckt nun mal ein Wissen über den Strukturaufbau (codiert im Array und 
in der Ausgangsbasis von (uint8_t *)&newtime.minute + (i >> 4)) drinnen. 
Das lässt sich anpassen, keine Frage. Nur wenn man drauf vergisst, dann 
gibts Ärger :-)

Nachdem ich in solche Dinge auch schon mal des öfteren reingefallen bin, 
habe ich es mir zur Regel gemacht, Code den ich von irgendwo herkriege 
erst mal so wie er ist zu verwenden und nichts zu ändern. Wenn das dann 
läuft, erst dann fange ich an, meine Änderungen einzubringen, wobei ich 
mich möglichst schnell wieder in einen testbaren Zustand manövriere. 
Aber mit Code downloaden, gleich mal ein paar Dinge ändern die mir 
auffallen bzw. nicht gefallen, bin ich schon zu oft auf die Schnauze 
gefallen. Denn nur durch Code lesen sieht man nicht alle Feinheiten.

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.