1 | /*
|
2 | Darstellung einer Wort-Uhr auf der Hardware des Retro-PONG-Spiels
|
3 |
|
4 | Version vom 4.11.2011
|
5 |
|
6 |
|
7 | Realisiert mit ATmega8 an 4.096MHz Quarz
|
8 |
|
9 | Keine Bedienelemente - die Uhr empfängt DCF-77 Signale und stellt sich selbst.
|
10 |
|
11 |
|
12 | Angezeigt wird eine interne Uhr, die vom DCF-Empfang bei korrektem Empfang minütlich gestellt wird.
|
13 | Die Flanke jedes Sekundensignals wird zur Feinjustierung der hunderstel Sekunden benutzt.
|
14 | Ohne Empfang läuft die interne Uhr mit der Zeit weg (Quarz zu ungenau). Das können schon mal 2 Minuten pro Tag sein.
|
15 |
|
16 | Der DCF-77 Empfänger startet erst nach einer fallenden Flanke an seinem PON Eingang.
|
17 |
|
18 |
|
19 | Da der Compiler/Optimizer leider ignoriert, dass eine Variable als volatile gekennzeichnet ist
|
20 | und dann den Wert bei einem Vergleich nicht neu aus dem Speicher liest, benutze ich zur Übergabe eines Bits aus einer
|
21 | Interrupt-Routine einen Portpin. Der ist definitiv volatile - und das berücksichtigt der Compiler bisher immer.
|
22 | Man kann daher an PD3 den 200Hz Takt abgreifen.
|
23 |
|
24 | --- Anschluss Quarz ---
|
25 | B6 und B7: 4096KHz Quarz (Prozessorpin 7+8)
|
26 |
|
27 | --- Anschluss DCF-77-Empfänger ---
|
28 | K1: GND
|
29 | K2: +5V (PD2)
|
30 | C4: output PowerOn DCF77 (PC4)
|
31 | C5: input DCF77-Empfänger (PC5)
|
32 | */
|
33 | #define F_CPU 4096000UL /* CPU clock in Hertz */
|
34 |
|
35 | #include <avr/io.h>
|
36 | #include <avr/interrupt.h>
|
37 | #include <util/delay.h>
|
38 |
|
39 |
|
40 | //Potenzen von 2
|
41 | #define BIT0 1
|
42 | #define BIT1 2
|
43 | #define BIT2 4
|
44 | #define BIT3 8
|
45 | #define BIT4 16
|
46 | #define BIT5 32
|
47 | #define BIT6 64
|
48 | #define BIT7 128
|
49 | #define BIT8 256
|
50 | #define BIT9 512
|
51 | #define BIT10 1024
|
52 | #define BIT11 2048
|
53 |
|
54 | //Standard Datentypen
|
55 | #define U8 unsigned char
|
56 | #define U16 unsigned int
|
57 | #define U32 unsigned long
|
58 | #define I16 int
|
59 |
|
60 | //I/O Makros DCF77-Empfänger
|
61 | #define DI_DCF77_BIT (PINC & BIT5)
|
62 | #define DO_DCF_ENABLE (PORTC &=~ BIT4)
|
63 | #define DO_DCF_DISABLE (PORTC |= BIT4)
|
64 | #define DO_DCF_POWERSUPPLY (PORTD |= BIT2)
|
65 |
|
66 | //I/O Makros Spaltentreiber (Shift&Store Schieberegister CD4094)
|
67 | #define DO_DATA_H (PORTB |= BIT4)
|
68 | #define DO_DATA_L (PORTB &= ~BIT4)
|
69 | #define DO_CLK_H (PORTB |= BIT3)
|
70 | #define DO_CLK_L (PORTB &= ~BIT3)
|
71 | #define DO_STROBE_H (PORTB |= BIT2)
|
72 | #define DO_STROBE_L (PORTB &= ~BIT2)
|
73 |
|
74 |
|
75 |
|
76 | /* DCF-77 Decoder */
|
77 | /*
|
78 | Während jeder Minute werden die Nummern von Minute, Stunde, Tag, Wochentag,
|
79 | Monat und Jahr BCD-kodiert durch Impulsmodulation der Sekundenmarken übertragen.
|
80 | Dieses "Telegramm" gilt jeweils für die folgende Minute.
|
81 | Dabei entsprechen Sekundenmarken mit einer Dauer von 0,1 s der binären Null
|
82 | und solche mit einer Dauer von 0,2 s der binären Eins. Die Zuordnung der einzelnen
|
83 | Sekundenmarken auf die übertragene Zeitinformation zeigt das Kodierschema
|
84 |
|
85 |
|
86 | 0. Minutenbeginn (immer LOW)
|
87 | 1. - 14. Reserviert, keine Bedeutung (neuedings: Verschlüselte Wetterdaten)
|
88 | 15. Reserveantenne aktiv
|
89 | 16. Umstellung von Sommer- auf Winterzeit, oder umgekehrt
|
90 | 17. Sommerzeit aktiv
|
91 | 18. Winterzeit aktiv
|
92 | 19. Ankündigung Schaltsekunde
|
93 | 20. Zeitbeginn (Immer High)
|
94 | 21. - 27. Minute 1, 2, 4, 8, 10, 20, 40
|
95 | 28. Prüfbit Minute
|
96 | 29. - 34. Stunde 1, 2, 4, 8, 10, 20
|
97 | 35. Prüfbit Stunde
|
98 | 36. - 41. Tag 1, 2, 4, 8, 10, 20
|
99 | 42. - 44. Wochentag 1, 2, 4
|
100 | 45. - 49. Monat 1, 2, 4, 8, 10
|
101 | 50. - 57. Jahr 1, 2, 4, 8, 10, 20, 40, 80
|
102 | 58. Prüfbit Datum
|
103 | 59. fehlt zur Erkennung des Minutenanfangs
|
104 |
|
105 | Die drei __Prüfbits__ P1, P2 und P3 ergänzen jeweils die vorhergehenden Informationswörter
|
106 | (7 Bits für die Minute, 6 Bits für die Stunden und 22 Bits für das Datum, einschließlich
|
107 | der Nummer des Wochentages) auf eine __gerade__ Anzahl von Einsen.
|
108 |
|
109 | Die Zeitmarken Nr. 17 und 18 zeigen an, auf welches Zeitsystem sich die ausgesandte
|
110 | Zeitinformation bezieht. Bei Aussendung der MEZ wird die Sekundenmarke Nr. 18 auf 0,2 s
|
111 | verlängert; die Sekundenmarke Nr. 17 hat eine Dauer von 0,1 s. Bei der Aussendung der
|
112 | MESZ ist es umgekehrt.
|
113 |
|
114 | Vor einem Übergang von MEZ nach MESZ oder zurück wird außerdem jeweils eine Stunde
|
115 | lang die Sekundenmarke Nr. 16 als verlängerte Marke (0,2 s) ausgesendet. Diese
|
116 | Verlängerung beginnt beim Übergang von MEZ auf MESZ (von MESZ nach MEZ) um
|
117 | 01.00.16 Uhr MEZ (2.00.16 Uhr MESZ) und endet um 01.59.16 Uhr MEZ (02.59.16 Uhr MESZ).
|
118 |
|
119 | Die Sekundenmarke Nr. 19 kündigt eine Schaltsekunde an. Sie wird dann ebenfalls
|
120 | eine Stunde lang vor Einführung der Schaltsekunde als verlängerte Marke (0,2 s)
|
121 | ausgesendet. Schaltsekunden werden weltweit zum gleichen Zeitpunkt in die koordinierte
|
122 | Weltzeitskala UTC eingeführt, vorzugsweise am Ende der letzen Stunde des 31. Dezember
|
123 | oder des 30. Juni. Dies bedeutet, daß Schaltsekunden in der gesetzlichen Zeit der
|
124 | Bundesrepublik Deutschland eine Sekunde vor 1 Uhr MEZ am 1. Januar oder vor
|
125 | 2 Uhr MESZ am 1. Juli eingeschoben werden. Bei einer Schaltsekunde am 1. Januar (1. Juli)
|
126 | beginnt daher die Verlängerung der Sekundenmarke Nr. 19 um 00.00.19 Uhr MEZ (01.00.19 Uhr MESZ)
|
127 | und endet um 00.59.19 Uhr MEZ (01.59.19 Uhr MESZ).
|
128 |
|
129 | Beim Einfügen einer Schaltsekunde hat die zugehörige Minute eine Dauer von 61 Sekunden,
|
130 | und die der Marke 01.00.00 Uhr MEZ (02.00.00 Uhr MESZ) vorhergehende 59. Sekundenmarke
|
131 | wird mit einer Dauer von 0,1 s ausgesendet. Die zur eingefügten 60. Sekunde gehörige
|
132 | Marke wird weggelassen (keine Trägerabsenkung).
|
133 |
|
134 | */
|
135 |
|
136 | //------------------------------------------------------------------------------
|
137 |
|
138 | //Globale Variable
|
139 | typedef struct {
|
140 | U8 zeitUebernehmen;
|
141 | U8 bBit[60]; //die empfangenen Bits
|
142 | U8 erfolgreichdecodiert;
|
143 | U8 stoerung; //Unplausibler Empfang
|
144 | U8 zeitEmpfangen; //Schaltet auf Zeitanzeige statt Sekundenanzeige um
|
145 |
|
146 |
|
147 | U8 uhrHH; //Die interne Uhr (wird von DCF-Uhr gestellt)
|
148 | U8 uhrMM;
|
149 | U8 uhrSS;
|
150 | U8 dcfSekunde; //0..59 - wird bei ausbleibendem Signal (59. Sekunde) auf 0 gesetzt
|
151 |
|
152 | U8 vorHH; //Die empfangene Zeit in der letzten Minute
|
153 | U8 vorMM;
|
154 | U8 vorJJ;
|
155 | }Tglob;
|
156 |
|
157 | Tglob volatile g;
|
158 | U16 spalte[12];
|
159 |
|
160 | //------------------------------------------------------------------------------
|
161 |
|
162 | // Texte im Display
|
163 | #define Z1 1
|
164 | #define Z2 2
|
165 | #define Z3 4
|
166 | #define Z4 8
|
167 | #define Z5 16
|
168 | #define Z6 32
|
169 | #define Z7 64
|
170 | #define Z8 128
|
171 | #define Z9 256
|
172 | #define ZA 512
|
173 |
|
174 | #define TEXT_ES_IST spalte[0]|=Z1; spalte[1]|=Z1; spalte[3]|=Z1; spalte[4]|=Z1; spalte[5]|=Z1
|
175 | #define TEXT_FUENF1 spalte[7]|=Z1; spalte[8]|=Z1; spalte[9]|=Z1; spalte[10]|=Z1
|
176 |
|
177 | #define TEXT_ZEHN1 spalte[0]|=Z2; spalte[1]|=Z2; spalte[2]|=Z2; spalte[3]|=Z2
|
178 | #define TEXT_ZWANZIG spalte[4]|=Z2; spalte[5]|=Z2; spalte[6]|=Z2; spalte[7]|=Z2; spalte[8]|=Z2; spalte[9]|=Z2; spalte[10]|=Z2
|
179 |
|
180 | #define TEXT_DREI1 spalte[0]|=Z3; spalte[1]|=Z3; spalte[2]|=Z3; spalte[3]|=Z3
|
181 | #define TEXT_VIERTEL spalte[4]|=Z3; spalte[5]|=Z3; spalte[6]|=Z3; spalte[7]|=Z3; spalte[8]|=Z3; spalte[9]|=Z3; spalte[10]|=Z3
|
182 |
|
183 | #define TEXT_NACH spalte[2]|=Z4; spalte[3]|=Z4; spalte[4]|=Z4; spalte[5]|=Z4
|
184 | #define TEXT_VOR spalte[6]|=Z4; spalte[7]|=Z4; spalte[8]|=Z4
|
185 |
|
186 | #define TEXT_HALB spalte[0]|=Z5; spalte[1]|=Z5; spalte[2]|=Z5; spalte[3]|=Z5
|
187 | #define TEXT_ZWOELF spalte[5]|=Z5; spalte[6]|=Z5; spalte[7]|=Z5; spalte[8]|=Z5; spalte[9]|=Z5
|
188 |
|
189 | #define TEXT_ZWEI spalte[0]|=Z6; spalte[1]|=Z6; spalte[2]|=Z6; spalte[3]|=Z6
|
190 | #define TEXT_EIN spalte[2]|=Z6; spalte[3]|=Z6; spalte[4]|=Z6
|
191 | #define TEXT_EINS spalte[2]|=Z6; spalte[3]|=Z6; spalte[4]|=Z6; spalte[5]|=Z6;
|
192 | #define TEXT_SIEBEN spalte[5]|=Z6; spalte[6]|=Z6; spalte[7]|=Z6; spalte[8]|=Z6; spalte[9]|=Z6; spalte[10]|=Z6
|
193 |
|
194 | #define TEXT_DREI spalte[1]|=Z7; spalte[2]|=Z7; spalte[3]|=Z7; spalte[4]|=Z7
|
195 | #define TEXT_FUENF spalte[7]|=Z7; spalte[8]|=Z7; spalte[9]|=Z7; spalte[10]|=Z7
|
196 |
|
197 | #define TEXT_ELF spalte[0]|=Z8; spalte[1]|=Z8; spalte[2]|=Z8
|
198 | #define TEXT_NEUN spalte[3]|=Z8; spalte[4]|=Z8; spalte[5]|=Z8; spalte[6]|=Z8
|
199 | #define TEXT_VIER spalte[7]|=Z8; spalte[8]|=Z8; spalte[9]|=Z8; spalte[10]|=Z8
|
200 |
|
201 | #define TEXT_ACHT spalte[1]|=Z9; spalte[2]|=Z9; spalte[3]|=Z9; spalte[4]|=Z9
|
202 | #define TEXT_ZEHN spalte[5]|=Z9; spalte[6]|=Z9; spalte[7]|=Z9; spalte[8]|=Z9;
|
203 |
|
204 | #define TEXT_SECHS spalte[1]|=ZA; spalte[2]|=ZA; spalte[3]|=ZA; spalte[4]|=ZA; spalte[5]|=ZA
|
205 | #define TEXT_UHR spalte[8]|=ZA; spalte[9]|=ZA; spalte[10]|=ZA
|
206 |
|
207 |
|
208 | // 12345 12345 12345 12345 12345 12345 12345 12345 12345 12345
|
209 | //1 § §§§ §§§ § §§§§§ §§ §§§§§ §§§ §§§ §§§
|
210 | //2 §§ § § § § §§ § § § § § § § § §
|
211 | //3 § § § § § §§§§ § § § § § § § §
|
212 | //4 § § §§ § § § §§§§ § §§§ §§§§ § §
|
213 | //5 § § § §§§§§ § § § § § § § § §
|
214 | //6 § § § § § § § § § § § § § § §
|
215 | //7 §§§ §§§§§ §§§ § §§§ §§§ § §§§ §§§ §§§
|
216 | //
|
217 |
|
218 |
|
219 | const U8 ziffern[10][5] = // Die 10 Ziffern in 5 Spalten und 7 Zeilen darstellen
|
220 | {// 1 2 3 4 5
|
221 | {Z2|Z3|Z4|Z5|Z6, Z1|Z7, Z1|Z7, Z1|Z7, Z2|Z3|Z4|Z5|Z6 }, // 0
|
222 | {0, Z2|Z7, Z1|Z2|Z3|Z4|Z5|Z6|Z7, Z7, 0 }, // 1
|
223 | {Z2|Z7, Z1|Z6|Z7, Z1|Z5|Z7, Z1|Z4|Z7, Z2|Z3|Z7 }, // 2
|
224 | {Z2|Z6, Z1|Z7, Z1|Z4|Z7, Z1|Z4|Z7, Z2|Z3|Z5|Z6 }, // 3
|
225 | {Z4|Z5, Z3|Z5, Z2|Z5, Z1|Z2|Z3|Z4|Z5|Z6|Z7, Z5 }, // 4
|
226 | {Z1|Z2|Z3|Z6, Z1|Z3|Z7, Z1|Z3|Z7, Z1|Z3|Z7, Z1|Z4|Z5|Z6 }, // 5
|
227 | {Z3|Z4|Z5|Z6, Z2|Z5|Z7, Z1|Z4|Z7, Z1|Z4|Z7, Z5|Z6 }, // 6
|
228 | {Z1, Z1|Z5|Z6|Z7, Z1|Z4, Z1|Z3, Z1|Z2 }, // 7
|
229 | {Z2|Z3|Z5|Z6, Z1|Z4|Z7, Z1|Z4|Z7, Z1|Z4|Z7, Z2|Z3|Z5|Z6 }, // 8
|
230 | {Z2|Z3, Z1|Z4|Z7, Z1|Z4|Z7, Z1|Z4|Z7, Z2|Z3|Z4|Z5|Z6 } // 9
|
231 | };
|
232 |
|
233 |
|
234 | //------------------------------------------------------------------------------
|
235 | void anzeigen(void){ // Zeit auf spaltes ausgeben (wird jede Sekunde aufgerufen)
|
236 | //------------------------------------------------------------------------------
|
237 | U8 i,h,z,e;
|
238 |
|
239 | //Bildschirm löschen
|
240 | for (i=0; i<12; spalte[i++]=0);
|
241 |
|
242 | if(g.zeitEmpfangen){
|
243 | TEXT_ES_IST;
|
244 | if( g.uhrMM < 5){TEXT_UHR; }
|
245 | else if(g.uhrMM < 10){TEXT_FUENF1; TEXT_NACH; }
|
246 | else if(g.uhrMM < 15){TEXT_ZEHN1; TEXT_NACH; }
|
247 | else if(g.uhrMM < 20){TEXT_VIERTEL; TEXT_NACH; }
|
248 | else if(g.uhrMM < 25){TEXT_ZWANZIG; TEXT_NACH; }
|
249 | else if(g.uhrMM < 30){TEXT_FUENF1; TEXT_VOR; TEXT_HALB; }
|
250 | else if(g.uhrMM < 35){TEXT_HALB; }
|
251 | else if(g.uhrMM < 40){TEXT_FUENF1; TEXT_NACH; TEXT_HALB; }
|
252 | else if(g.uhrMM < 45){TEXT_ZWANZIG; TEXT_VOR; }
|
253 | else if(g.uhrMM < 50){TEXT_VIERTEL; TEXT_VOR; }
|
254 | else if(g.uhrMM < 55){TEXT_ZEHN1; TEXT_VOR; }
|
255 | else if(g.uhrMM < 60){TEXT_FUENF1; TEXT_VOR; }
|
256 |
|
257 | h = g.uhrHH;
|
258 | if(g.uhrMM >=25) h++;
|
259 |
|
260 | switch(h%12){
|
261 | case 0: TEXT_ZWOELF; break;
|
262 | case 1: if (g.uhrMM < 5) {
|
263 | TEXT_EIN;
|
264 | }else{
|
265 | TEXT_EINS;
|
266 | } break;
|
267 | case 2: TEXT_ZWEI; break;
|
268 | case 3: TEXT_DREI; break;
|
269 | case 4: TEXT_VIER; break;
|
270 | case 5: TEXT_FUENF; break;
|
271 | case 6: TEXT_SECHS; break;
|
272 | case 7: TEXT_SIEBEN; break;
|
273 | case 8: TEXT_ACHT; break;
|
274 | case 9: TEXT_NEUN; break;
|
275 | case 10: TEXT_ZEHN; break;
|
276 | case 11: TEXT_ELF; break;
|
277 | }
|
278 | ;
|
279 | //Minuten mit 4 Pixeln am rechten Rand
|
280 | switch(g.uhrMM % 5){
|
281 | case 4: spalte[11]|=BIT6; //absichtlich kein BREAK!
|
282 | case 3: spalte[11]|=BIT5;
|
283 | case 2: spalte[11]|=BIT4;
|
284 | case 1: spalte[11]|=BIT3;
|
285 | }
|
286 |
|
287 |
|
288 | }else{ //Noch keine DCF-Zeit vorhanden. Zeige Sekunden.
|
289 | z = (g.dcfSekunde / 10);
|
290 | e = (g.dcfSekunde % 10);
|
291 | for (i=0; i<5; i++){
|
292 | spalte[i] = ziffern[z][i];
|
293 | spalte[i+6] = ziffern[e][i];
|
294 | }
|
295 | }
|
296 | }
|
297 |
|
298 |
|
299 | //------------------------------------------------------------------------------
|
300 | ISR(TIMER1_COMPA_vect) { // spalte-Multiplexing (1882Hz)
|
301 | //------------------------------------------------------------------------------
|
302 | static U8 x;
|
303 | static U16 scope=0; //12 Bit Speicher für Mini-Oszilloskop
|
304 | static U8 us531=0; //Zählt alle 531µs hoch
|
305 | U16 y;
|
306 |
|
307 | //Low side: Eine einzelne 0 durchschieben (mit CLK) und jeweils mit STROBE aktivieren
|
308 | x++;
|
309 | if(x > 11){
|
310 | x=0;
|
311 | DO_DATA_L;
|
312 | }else{
|
313 | DO_DATA_H;
|
314 | }
|
315 | //Delays, um den CD4094 nicht an seiner Grenze zu betreiben
|
316 | _delay_us(2); //Warte, bis Daten stabil anliegen
|
317 | DO_CLK_H; //Einen weiter schieben
|
318 | _delay_us(2);
|
319 | DO_CLK_L;
|
320 |
|
321 | //Zeilentreiber löschen
|
322 | PORTC &= ~0x0F;
|
323 | PORTD &= ~0xF0;
|
324 | PORTB &= ~0x03;
|
325 |
|
326 | //Spalte aktivieren (Low side)
|
327 | DO_STROBE_H;
|
328 | DO_STROBE_L;
|
329 |
|
330 | //Spalte ausgeben (High side)
|
331 | y = spalte[x]; //10 Zeilentreiber, 12 Spalten
|
332 | //Solange noch keine Zeit empfangen: DCF-Signal in unteren beiden Zeilen anzeigen
|
333 | if (!g.zeitEmpfangen){ //Mini-Oszilloskop (simpler t-Y Schreiber ohne Trigger)
|
334 | us531++;
|
335 | if (us531 > 46){ //25ms sind rum
|
336 | us531=0;
|
337 | scope >>=1;//Pixel weiterschieben
|
338 | }
|
339 | if (DI_DCF77_BIT){
|
340 | scope|=BIT11;
|
341 | }else{
|
342 | scope&=~BIT11;
|
343 | }
|
344 | if (scope & (1<<x)){
|
345 | PORTB |=1;
|
346 | }else{
|
347 | PORTB |=2;
|
348 | }
|
349 | }else{
|
350 | PORTB |= (y >> 8) & 0x03; //2Bit
|
351 | }
|
352 |
|
353 | PORTC |= y & 0x0F; //4Bit
|
354 | PORTD |= y & 0xF0; //4Bit
|
355 | }
|
356 |
|
357 |
|
358 | #if 0
|
359 | //------------------------------------------------------------------------------
|
360 | ISR(TIMER2_COMP_vect){ // 5ms Takt
|
361 | //------------------------------------------------------------------------------
|
362 | PORTD|=BIT3; //volatile Übergabe an main()
|
363 | }
|
364 |
|
365 | #else //Das Ganze handoptimiert in Assembler, da Verzögerungen das Multiplexing stören könnten.
|
366 | void __attribute__ ((naked)) TIMER2_COMP_vect(void){
|
367 | asm("sbi 0x12, 3"); // High auf PD3 ausgeben
|
368 | asm("reti");
|
369 | }
|
370 | #endif
|
371 |
|
372 |
|
373 |
|
374 |
|
375 | //------------------------------------------------------------------------------
|
376 | // MAIN
|
377 | //------------------------------------------------------------------------------
|
378 | int main(void)
|
379 | {
|
380 | static I16 iTic; //ms seit letztem Absenkungsbeginn
|
381 |
|
382 | static U8 bSteigend; //TRUE: Tiefpass bewegt sich von 0 aufwärts (Nahe Sekundenanfang)
|
383 | static I16 iTiefpass;
|
384 | static U8 startphase=1;
|
385 | static U8 toggle;
|
386 | static U8 hh,mm,ss;
|
387 | static I16 hs;
|
388 |
|
389 | static U8 tag, wtag, monat, jahr;
|
390 | static U8 neuesDatum=0;
|
391 | static U8 keinEmpfang; //Zeit ohne korrekten Empfang in Minuten
|
392 | static U8 zeitumstellung; //Wird bei gesetztem DCF-Bit 16 hochgezählt
|
393 | static U8 sommerzeit; //Wird bei gesetztem DCF-Bit 17 hochgezählt
|
394 | static U8 winterzeit; //Wird bei gesetztem DCF-Bit 18 hochgezählt
|
395 | U8 pulsdauer;
|
396 |
|
397 | U8 bChecksum;
|
398 | U8 i;
|
399 | U8 *ram;
|
400 |
|
401 | cli();
|
402 | DDRC = BIT0|BIT1|BIT2|BIT3|BIT4;
|
403 | DDRD = BIT0| BIT2|BIT3|BIT4|BIT5|BIT6|BIT7;
|
404 | DDRB = BIT0|BIT1|BIT2|BIT3|BIT4;
|
405 | SFIOR &= (1<<PUD); //no Pullups
|
406 |
|
407 | PORTB=0;
|
408 | PORTC=0;
|
409 | PORTD=0;
|
410 |
|
411 | //Watchdog
|
412 | asm("wdr");
|
413 | WDTCR |= (1<<WDCE) | (1<<WDE);
|
414 | WDTCR=(1<<WDE) | (1<<WDP2) | (1<<WDP1)| (1<<WDP0); //Watchdog auf 2
|
415 | asm("wdr");
|
416 |
|
417 |
|
418 | DO_DCF_POWERSUPPLY; //Saft auf DCF-Empfänger
|
419 | DO_DCF_DISABLE;
|
420 | //Takt: 4.096MHz
|
421 |
|
422 | // Timer 0 unbenutzt
|
423 |
|
424 | // Timer 1 für spalte-Multiplexing
|
425 | TCCR1A = 0;
|
426 | TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10); //Teiler 64 -> 64000Hz
|
427 | OCR1A = 33; //Gibt 1882Hz Interruptfrequenz. Das erzeugt 156 Bilder/s
|
428 | //DCF77 sendet bei 77,5KHz. Die Multiplex-Frequenz sollte möglichst kein ganzzahliger Teiler davon sein
|
429 | // 77500/1882=41,17 - krumm genug
|
430 |
|
431 |
|
432 | // Timer 2: 5ms DCF-77-Empfang Takt
|
433 | TCCR2 = (1<<WGM21)|(1<<CS22)|(1<<CS21) |(1<<CS20); //CTC Mode, Teiler 1024 -> 4KHz
|
434 | OCR2 = 19; //Gibt 200Hz Interruptfrequenz
|
435 |
|
436 | // Timer Interrupts
|
437 | TIMSK = (1<<OCIE1A) | (1<<OCIE2);
|
438 |
|
439 |
|
440 | //Globale Variablen mit Null initialisieren
|
441 | ram=(U8 *)&g;
|
442 | for (i=0; i< sizeof(Tglob); ram[i++]=0);
|
443 |
|
444 | //Warte, bis Spannungsversorgung des DCF-77-Empfänger stabil
|
445 | asm("wdr");
|
446 | _delay_ms(1000);
|
447 | asm("wdr");
|
448 | DO_DCF_ENABLE; //Fallende Flanke auf PON startet den Empfänger
|
449 |
|
450 | spalte[0]=Z1|ZA;
|
451 | spalte[11]=Z1|ZA;
|
452 | sei(); // Interrupt ein - die Show beginnt
|
453 |
|
454 | while(1){
|
455 | asm("wdr");
|
456 |
|
457 | if (!(PORTD & BIT3)) continue; //Sync auf 5ms Takt
|
458 | PORTD &= ~BIT3;
|
459 |
|
460 |
|
461 | iTic+=5; //5ms sind rum
|
462 | if (iTic>3000){ //Kein Signal
|
463 | iTic=3000;
|
464 | }
|
465 | //Eingang abfragen und filtern
|
466 | if (DI_DCF77_BIT){ //Signalabsenkung liefert High für 100ms oder 200ms
|
467 | iTiefpass+=5;
|
468 | if (iTiefpass>200) iTiefpass=200; //auf 200ms begrenzen
|
469 | }else{
|
470 | iTiefpass-=5;
|
471 | if (iTiefpass<0){
|
472 | iTiefpass=0;
|
473 | bSteigend=1;
|
474 | }
|
475 | }
|
476 |
|
477 |
|
478 | //Minutenanfang erkennen
|
479 | if (iTic > 1500){ //Minutensignal: mehr als 1s keine Absenkung
|
480 | g.dcfSekunde=59;
|
481 | }
|
482 | else
|
483 | //Signallänge bestimmen und im Bitfeld eintragen
|
484 | if (160==iTic){ //160ms Tiefpass ist entweder +100-60=40 oder +100+60=160
|
485 | pulsdauer=iTiefpass;
|
486 | if (iTiefpass > 90){
|
487 | g.bBit[g.dcfSekunde]=1;
|
488 | }else{
|
489 | g.bBit[g.dcfSekunde]=0;
|
490 | }
|
491 |
|
492 | if ((iTiefpass < 5) || ((iTiefpass > 60) && (iTiefpass <100))){ //Unplausible Signaldauer
|
493 | if (g.stoerung <4) g.stoerung++;
|
494 | }else{
|
495 | if (g.stoerung >0) g.stoerung--;
|
496 | }
|
497 | }
|
498 | //Sekundenanfang erkennen
|
499 | if ((60==iTiefpass) && bSteigend){ //60ms seit Pulsbeginn
|
500 | bSteigend=0;
|
501 | iTic=60;
|
502 | g.dcfSekunde++;
|
503 | if (g.dcfSekunde>59){ //Jetzt ist Minutenanfang
|
504 | g.dcfSekunde=0;
|
505 | if (0==startphase){ //beim 1. Minutenanfang nach Einschalten ist die Zeit noch nicht übertragen
|
506 | g.zeitUebernehmen = 1;
|
507 | }
|
508 |
|
509 | //Auswertung zum Minutenanfang
|
510 | if (g.zeitUebernehmen){
|
511 | if (g.bBit[16]){
|
512 | zeitumstellung++;
|
513 | }else{
|
514 | if (zeitumstellung) zeitumstellung--;
|
515 | }
|
516 | if (g.bBit[17]){
|
517 | if (sommerzeit < 60) sommerzeit++;
|
518 | }else{
|
519 | if (sommerzeit) sommerzeit--;
|
520 | }
|
521 | if (g.bBit[18]){
|
522 | if (winterzeit < 60) winterzeit++;
|
523 | }else{
|
524 | if (winterzeit) winterzeit--;
|
525 | }
|
526 | bChecksum=g.bBit[21]+g.bBit[22]+g.bBit[23]+g.bBit[24]+g.bBit[25]+g.bBit[26]+g.bBit[27]+g.bBit[28]; //Minute
|
527 | if (0==(bChecksum % 2)){ //P1
|
528 | bChecksum=g.bBit[29]+g.bBit[30]+g.bBit[31]+g.bBit[32]+g.bBit[33]+g.bBit[34]+g.bBit[35]; //Stunde
|
529 | if (0==(bChecksum % 2)){ //P2
|
530 | if (g.bBit[20]){ //Startbit; immer gesetzt
|
531 | bChecksum=0;
|
532 | for (i=36; i<59; i++){ //Checksumme über Datum
|
533 | bChecksum+=g.bBit[i];
|
534 | }
|
535 | if (0==(bChecksum % 2)){ //P3
|
536 | neuesDatum=0;
|
537 | keinEmpfang=0;
|
538 | tag = g.bBit[36]+g.bBit[37]*2+g.bBit[38]*4+g.bBit[39]*8+g.bBit[40]*10+g.bBit[41]*20;
|
539 | wtag = g.bBit[42]+g.bBit[43]*2+g.bBit[44]*4;
|
540 | monat = g.bBit[45]+g.bBit[46]*2+g.bBit[47]*4+g.bBit[48]*8;
|
541 | jahr = g.bBit[50]+g.bBit[51]*2+g.bBit[52]*4+g.bBit[53]*8+g.bBit[54]*10+g.bBit[55]*20+g.bBit[56]*40+g.bBit[57]*80;
|
542 | if (g.zeitUebernehmen){
|
543 | //Übernehme Zeit
|
544 | g.zeitUebernehmen = 0;
|
545 | hh = g.bBit[29]+g.bBit[30]*2+g.bBit[31]*4+g.bBit[32]*8+g.bBit[33]*10+g.bBit[34]*20;
|
546 | mm = g.bBit[21]+g.bBit[22]*2+g.bBit[23]*4+g.bBit[24]*8+g.bBit[25]*10+g.bBit[26]*20+g.bBit[27]*40;
|
547 | ss = 0;
|
548 | //Ist diese Zeit genau eine Minute später als vor einer Minute? (zusätzliche Plausibilitätscheck)
|
549 | g.vorMM++;
|
550 | if (g.vorMM>59){
|
551 | g.vorMM=0;
|
552 | g.vorHH++;
|
553 | if (g.vorHH >23) g.vorHH=0;
|
554 | }
|
555 | if ((g.vorHH == hh)
|
556 | && (g.vorMM == mm)
|
557 | && (g.vorJJ == jahr) //Beim Jahreswechsel muss die Uhr ja nicht um Mitternacht verdreht werden
|
558 | && (jahr>10)){ //Wir haben 2011 - und mehr als 89 Jahre soll die Uhr gar nicht laufen
|
559 | g.erfolgreichdecodiert=1;
|
560 | }
|
561 | g.vorHH=hh;
|
562 | g.vorMM=mm;
|
563 | g.vorJJ=jahr;
|
564 | }
|
565 | }//P3
|
566 | }//Bit20
|
567 | }//P2
|
568 | }//P1
|
569 | }//g.zeitUebernehmen
|
570 | if (startphase) startphase--;
|
571 | for (i=0; i<60; i++){
|
572 | g.bBit[i]=0;
|
573 | }
|
574 | }//(g.dcfSekunde>59)
|
575 |
|
576 | //Interne Uhr feintunen
|
577 | //Hundertstel Sekunden g.hs sollte auf 6 stehen , wenn die Uhr richtig läuft (60ms seit steigender Flanke)
|
578 | //Nur machen, wenn Empfang OK
|
579 | if ((keinEmpfang < 2) && (0==g.stoerung)){
|
580 | if ((hs > 6) && (hs < 56)) {
|
581 | hs --; //Uhr etwas zurückdrehen
|
582 | }else{
|
583 | if (hs != 6){
|
584 | hs++; //Uhr etwas vor drehen. Überlauf wird weiter unten automatisch behandelt.
|
585 | }
|
586 | }
|
587 | }
|
588 | }//Sekundenanfang (60ms)
|
589 |
|
590 | //Intere Uhr weiterdrehen
|
591 | toggle = !toggle;
|
592 | if (toggle){ //10ms sind rum
|
593 | hs++; //Hundertstel Sekunden
|
594 | if (hs>99){ //1s ist rum
|
595 | hs-=100; //Nicht auf 0 setzen, um Überlauf bei Uhr vordrehen (Feintuning) zu berücksichtigen
|
596 | g.uhrSS++;
|
597 | anzeigen();
|
598 | if (g.uhrSS > 59){
|
599 | g.uhrSS=0;
|
600 | g.uhrMM++;
|
601 | if (keinEmpfang < 100){ //Wird bei Empfang mit korreten Prüfsummen auf 0 gesetzt
|
602 | keinEmpfang++;
|
603 | }
|
604 | if (g.uhrMM > 59){
|
605 | g.uhrMM=0;
|
606 | g.uhrHH++;
|
607 | if (g.uhrHH > 23){
|
608 | g.uhrHH=0;
|
609 | neuesDatum=1; //Datum wird ja nicht angezeigt. Aber wenn ich das mal einbauen will, habe ich hiermit den Tagesbeginn.
|
610 | }
|
611 | //Zeitumstellung?
|
612 | if ((zeitumstellung > 45) && (g.uhrHH >=2) && (g.uhrHH <=3)){ //"zeitumstellung" wurde in der Stunde vor der Umstellung jede Minute hochgezählt
|
613 | if (sommerzeit > winterzeit){ //MESZ->MEZ
|
614 | g.uhrHH--;
|
615 | }else{ //MEZ->MESZ
|
616 | g.uhrHH++;
|
617 | }
|
618 | }
|
619 | }//g.uhrMM>59
|
620 | }//g.uhrSS>59
|
621 | if (g.erfolgreichdecodiert){ //Zeit übernehmen (wenn vorhanden)
|
622 | g.erfolgreichdecodiert=0;
|
623 | g.zeitEmpfangen=1;
|
624 | g.uhrHH=hh;
|
625 | g.uhrMM=mm;
|
626 | g.uhrSS=0;
|
627 | }
|
628 |
|
629 | }//hs>99
|
630 | }//Toggle
|
631 | }//while 1
|
632 | }
|
633 | //EOF
|