PIC Binäruhr

Zugegeben eine Binäruhr ist schon etwas schräg, aber zum Üben ist sie optimal. Die Zeit muss nicht für die Anzeige aufbereitet werden, sondern kann direkt ausgegeben werden. Trotzdem bietet so eine Uhr noch genügend Herausforderungen. Für einen guten Sekundentakt wird ein 2,097152 MHz Quarz verwendet. Es wird eine Einstellmöglichkeit für die Uhrzeit benötigt und es darf natürlich auch eine Stromausfallsicherung nicht fehlen. Zusätzlich ist es möglich die Uhr mit dem DCF77 Signal synchron laufen zu lassen.
Doch der Anfang ist die Schaltung.

Schaltung

Schaltplan der Binäruhr mit PIC 16F84A, nur die Kondensatoren fehlen noch.

Die Schaltung ist an sich sehr einfach. Die Transistoren links (NPN) schalten die Kathoden der Leuchtdiodenreihen, die Transistoren unten (PNP) die Anoden der LED-Reihen. Die LED-Vorwiderstände sind zwischen den PNP Transistoren und dem Plus-Anschluss der LEDs, da in diesen Reihen immer nur eine LED zur Zeit an sein wird. Die drei Taster werden auch nach einander abgefragt, sie bekommen ihr positive Versorgung von den drei Ausgängen die die NPN Transistoren steuern und leiten es dann auf den Eingang weiter. Durch geschicktes Schalten der Ausgänge kommt man so mit wenig Pins am IC aus und kann trotzdem die 17 Leuchtdioden und die drei Schalter ansteuern bzw. abfragen.

Für den Fall, dass mal die Stromversorgung ausfallen sollte wird der Goldcap (C3) verwendet, um die Versorgung zu übernehmen. Dazu wird der Transistor Q4 so verwendet, dass er sobald im Kondensator C4 die Spannung unter etwa 2 Volt fällt er den Eingang des PIC auf Vdd wechseln lässt. Der PIC kann dann die LEDs ausschalten. Der Goldcap hat dann genug Energie um den PIC alleine noch etwa 1,5 Stunden zu versorgen.

Der Festspannungsregler ist ein Low-Drop Linearregler, der gleichzeitig einen Verpolungsschutz bietet (bin mir aber nicht 100% sicher). Er ermöglicht es die Uhr mit 3 bis 9 Volt zu versorgen, da er bei kleiner Spannung nur 0,5 Volt Spannungsabfall hat die Spannung in der Schaltung aber nicht größer als 5 Volt werden lässt. Die Diode D23 sorgt beim Stromausfall dafür, dass die Spannugssüberwachung spannungslos wird, und der Goldcap nur den PIC versorgt.

Das DCF77-Empfangsmodul von Conrad ist extern in einem kleinen Gehäuse untergebracht, weil es sonst von der Schaltung zu stark gestört wird. Dazu sind die drei Anschlüsse unten im Schaltplan. Auf der Empfängerplatine selbst ist noch ein Pullup-Widerstand sowie eine Entstörung untergebracht. Verwendet wurde eine 3,5mm Kopfhörerbuchse, damit man den Empfänger auch während des Betriebs an oder abstecken kann wird in die Plusleitung noch vor der Buchse ein 100Ω Widerstand eingebaut und die Signalleitung noch mit einem 22kΩ Pullup-Widerstand ausgestattet. Dies sorgt dafür, dass es keinen Kurzschluss während des Steckvorgangs gibt, der Pullupwiderstand sorgt für einen definierten Pegel auch ohne den Empfänger. Es muss das nicht invertierte Signal (100ms oder 200ms high; 900ms oder 800ms low) eingespeist werden.

Die Lastkondensatoren für den Quarz sowie die Entstörkondensatoren wurden nicht in den Schaltplan eingezeichnet. Es wäre empfehlenswert noch einen ICSP-Anschluss vorzusehen. Zusätzlich wurde der Widerstand, der den MCLR auf Vdd zieht nicht mit eingezeichnet.

Nach dem Aufbau und einem Funktionstest der LEDs und Transistoren beginnt das Programmieren.

Foto der geöffneten Uhr.

Foto der geschlossenen Uhr im Dunkeln, es ist 19:43:18 Uhr.

Fotos der Uhr. Auf dem oberen Bild ist unten der ICSP-Anschluss zu sehen, welcher aus Platzgründen unter der IC-Fassung angelötet wurde. Auf dem unteren Bild sieht man, dass alle Leuchtdioden leicht leuchten, so ist die Zeit im Dunkeln besser abzulesen. Auf dem unteren Foto ist es gerade 19:43:18 .

Programm

Anmerkungen und Änderungen

Auch wenn das Programm bis jetzt fehlerfrei Läuft, bin ich an einer kontinuierlichen Verbesserung interessiert. Wenn du noch Teile erkennst, die man einfacher oder schneller machen kann bin ich sehr

Es stehen zwei Betriebsarten zur Verfügung. Die Uhr kann ohne DCF-Unterstützung nur durch den Quarz getaktet betrieben werden, oder aber mit DCF-Unterstützung. Wenn die DCF Synchronisierung eingeschaltet ist versucht die Uhr so oft wie möglich die Zeit zu empfangen. Fällt in diesem Modus der Empfang länger als 24 Stunden aus, dann wird dies durch ein leichtes Flackern in der Anzeige signalisiert. Es wurde alles in einzelne Schleifen und Unterprogramme ausgelagert, die nur bei bedarf ausgeführt werden. Die einzelnen Module sind:

  • Einem Initialisierungsteil, der bei jedem Neustart ausgeführt wird, er legt fest, wie die Pins verwendet werden und welche Startwerte die Variablen haben.
  • Vor der Anzeigeschleife werden kurzzeitig alle Leuchtdioden angeschaltet. So kann erkannt werden, ob alle Pins richtig in der Fassung stecken, und ob alle Transistoren und LEDs funktionieren. Wenn man von der Zeiteinstellung zurückkehrt erkennt man so dass man wieder im Hauptmodus ist.
  • Die Anzeigeschleife läuft als Endlosschleife, die nacheinander die Sekunden, die Minuten und die Stunden aufleuchten lässt. Alle LED-Reihen sollen dass gleiche ON/OFF Verhältnis aufweisen, bei mir etwa 6% ON-Zeit. Hier wird geprüft, ob und wenn ja wie lange der Taster für die Zeiteinstellung gedrückt wurde um dann eventuell in den Programmteil für die Einstellungen zu springen. Dafür muss der Taster für mindestens 1,14s gedrückt bleiben.
  • Der Timer des 16F84A wird internen mit dem PIC-Takt versorgt, der vorher noch durch 8 geteilt wurde und so genau 256 mal pro Sekunde überläuft [2097152Hz / (4 * 8 * 256) = 256Hz]. Der Zähler wird aus der Warteschleife aus aufgerufen. Da es genug Möglichkeiten gib ihn auszuführen überprüft er zu Beginn, ob die Zeit noch ausreicht. Sonst wird er erst beim Nächsten Aufruf ausgeführt, wo die Warteschleife mindestens 0,5ms warten soll. In dem Zähler wird der Pin des DCF-Empfängers abgefragt und eine Art Schmitt-Trigger angewendet, um ein Sauberes Signal zu bekommen. Dieses wird dann vom entsprechenden Programm verarbeitet. Der Zähler ruft anschließen noch die Unterzähler auf, die die Uhrzeit weiterzählen und eine empfangene Zeit eintragen.
  • Zum Dekodieren und Überprüfen des DCF-Signals werden drei Zähler verwendet. Es gibt einen Zähler, der die Low-Zeit des Signals, einen der die High-Zeit des Signals und einen, der die Bits zählt. Es wird auf eine Stelle gewartet an der mindestens eine Low-Zeit von einer Sekunde ist. Dort beginnt das neue Zeittelegramm. Alle nun empfangenen Impulse werden genau in ihrer Länge vermessen. Und anhand der Länge wird entschieden, ob sie mit einer gewissen Toleranz 100ms (zwischen 70ms und 130ms) oder 200ms (zwischen 170ms und 230ms) lang waren. Wenn so ein Bit als richtig erkannt wurde wird es entsprechend seiner Bedeutung zu den Minuten oder Stunden dazugezählt und der Bitzähler erhöht.
  • Wurde ein Zeittelegramm ohne Bitlängenfehler empfangen wird die Zeit gespeichert und mit dem vorherigen Telegramm verglichen. es dürfen nur bestimmte festgelegte Differenzen auftreten (1Minute, 59Minuten, 0Stunden, 1Stunde, 2Stunden).
  • Der Teiler, der die 1/256 Sekunden zählt wird jede Sekunde wenn möglich mit dem DCF-Signal synchronisiert. So kann bei schlechtem Empfang, wenn die Telegramme nicht fehlerfrei empfangen werden können der Zähler zumindest mit den sauberen Bits (richtige Länge) in der richtigen Geschwindigkeit gehalten werden.
  • Das Übertragen der Empfangenen Zeit in das Uhrwerk findet immer zur vollen Minute statt. Dabei wird auch ein Zähler gesetzt, der Die Stunden seit dem letzten erfolgreichen Empfang zählt.
  • Wenn die Uhr im DCF-Betrieb ist und seit mehr als 24 Stunden keine zwei fehlerfreien Zeittelegramme mehr empfangen wurden, dann wird in der Anzeigeschleife ein leichtes Flackern erzeugt, damit man weiß, dass die Uhr leicht abweichen kann.
  • Wurde Taster 1 im normalen Modus lange genug gedrückt, dann wird in eine Unterschleife gesprungen, die es ermöglicht zwischen guarzgestütztem und DCF-gestütztem Betrieb umzuschalten. Von dort kommt man mit Taster 1 in den Modus für die Sekunden, dann zu den Minuten und von da zu den Stunden. Zurück zur normalen Anzeige kommt man wieder mit Taster 1.
  • Um zu sehen, welche Zeile man nun verstellen kann blitzt diese zu Beginn kurz auf. Zum Abfragen der Taster wir eine kleines Unterprogramm verwendet.
  • Das Unterprogramm zum Abfragen der Taster erkennt einen Tasterdruck immer dann, wenn man den Taster wieder loslässt. Oder aber wenn man ihn länger hält, dann wird in einen Schnellzählmodus geschaltet, damit man die Zeit einstellen kann ohne 30 mal drücken zu müssen.
  • Wenn die Stromsparschleife aufgerufen wird, dann werden in dieser alle Ausgänge so geschaltet, dass kein Strom fließt (aktiv-low nach high und aktiv-high nach low), dann wird geprüft, ob der Teiler überläuft und anschließend, ob der Strom wieder da ist. In diesem Modus verbraucht der PIC den gleichen Strom wie sonst auch, aber alle Ströme durch Ausgänge und LEDs entfallen. Der Verbrauch wird so auf 352µA gesenkt.
  • Die meiste Zeit befindet sich das Programm in der Warteschleife, deshalb wird von dieser aus auch das Pollen des Überlaufbits des Timers überwacht, so dass kein Überlauf unbemerkt bleibt.

Alles weitere zum Programm sollte man den Kommentaren im Programm entnehmen können.

  1. ; Binäruhr mit 6 Bit bei Sekunden und Minuten, 5 Bit bei Stunden
  2. ; LED-Array gemultiplext zwischen RA0 - RA2 und RB0 - RB5
  3.    
  4. ; RA0 out      high-aktiv     Sekunden Zeile
  5. ; RA1 out      high-aktiv     Minuten Zeile
  6. ; RA2 out      high-aktiv     Stunden Zeile
  7.    
  8. ; RA4 in       -              DCF-Signal vom Empfänger (100ms oder 200ms high; 900ms oder 800ms low)
  9.    
 10. ; RA3 in       high-aktiv     Taster Abfrage (gemultiplext mit RA0 - RA2)
 11. ; RB6 in       low-aktiv      Spannungskontrolle
 12.    
 13. ; RB0 out      low-aktiv      2^0 Bit
 14. ; RB1 out      low-aktiv      2^1 Bit
 15. ; RB2 out      low-aktiv      2^2 Bit
 16. ; RB3 out      low-aktiv      2^3 Bit
 17. ; RB4 out      low-aktiv      2^4 Bit
 18. ; RB5 out      low-aktiv      2^5 Bit
 19. ; **************************************************************
 20.    #include <P16f84A.INC>
 21.    __CONFIG       _PWRTE_ON & _WDT_OFF & _XT_OSC
 22. ; **************************************************************
 23. ; Variablennamen vergeben
 24. aloops         Equ            0x22           ; Zähler für Warteschleife
 25. bloops         Equ            0x23           ; Zähler für Warteschleife
 26. Bits1          Equ            0x24           ; Speicherstelle für einzelne Bits
 27. Bits2          Equ            0x25           ; Speicherstelle für einzelne Bits
 28.    
 29. T1_Wert        Equ            0x26           ; Betätigungszeit Taster1 Messen
 30. T2_Wert        Equ            0x27           ; Betätigungszeit Taster2 Messen
 31. T3_Wert        Equ            0x28           ; Betätigungszeit Taster3 Messen
 32.    
 33. DCF_Zeit_L     Equ            0x29           ; Zeit die das DCF_Signal Low ist
 34. DCF_Zeit_H     Equ            0x2a           ; Zeit die das DCF_Signal High ist
 35. DCF_BitN       Equ            0x2b           ; Zählt mit, welches Bit vom DCF nun kommt
 36. DCF_BitWS      Equ            0x2c           ; Gibt die Wertigkeit an, die das DCF-Bit hat, wenn es ein Bit für die Stunden ist
 37. DCF_BitWM      Equ            0x2d           ; Gibt die Wertigkeit an, die das DCF-Bit hat, wenn es ein Bit für die Minuten ist
 38. DCF_WertM      Equ            0x2e           ; Zähler zur Mittelung des DCF-Signals
 39. DCF_ok         Equ            0x2f           ; Stundenwechsel seit dem letzten erfolgreichen DCF-Empfang
 40.    
 41. Teiler         Equ            0x30           ; Teiler für weiteres Herunterteilen des Timers
 42.    
 43. Sekunden       Equ            0x31           ; Speicher für die aktuellen Sekunden
 44. Minuten        Equ            0x32           ; Speicher für die aktuellen Minuten
 45. Stunden        Equ            0x33           ; Speicher für die aktuellen Stunden
 46.    
 47. Min_dcf_zw     Equ            0x34           ; Zwischenspeicher für die empfangenen DCF Minuten
 48. Std_dcf_zw     Equ            0x35           ; Zwischenspeicher für die empfangenen DCF Stunden
 49.    
 50. Sek_dcf        Equ            0x36           ; Speicher für die empfangenen DCF Sekunden
 51. Min_dcf        Equ            0x37           ; Speicher für die empfangenen DCF Minuten
 52. Std_dcf        Equ            0x38           ; Speicher für die empfangenen DCF Stunden
 53.    
 54. ; Konstanten definieren
 55. #define        T1_Set         Bits1,0        
 56. #define        T2_Set         Bits1,1        
 57. #define        T3_Set         Bits1,2        
 58. #define        T1_Fast        Bits1,3        
 59. #define        T2_Fast        Bits1,4        
 60. #define        DCF_Bit_L      Bits1,5        
 61. #define        DCF_Bit_H      Bits1,6        
 62. #define        DCF_Good       Bits2,0        
 63. #define        DCF_PortM      Bits2,1        
 64. #define        DCF_Bit_OK     Bits2,2        
 65. #define        DCF_ON         Bits2,3        
 66.    
 67. #define        Power_Good     PORTB,6        
 68. #define        DCF_Port       PORTA,4        
 69.    
 70. #define        Sec_Zeile      PORTA,0        
 71. #define        Min_Zeile      PORTA,1        
 72. #define        Std_Zeile      PORTA,2        
 73. #define        T_Input        PORTA,3        
 74.    
 75. ; **************************************************************
 76. ; Initialisieren
 77.    
 78. ; Ausgänge und 256 Hz-Timer einstellen
 79.    bsf            STATUS,RP0     ; auf Bank 1 umschalten
 80.    movlw          B'10000010'    
 81.    movwf          OPTION_REG     ; internen Takt zählen, Vorteiler zum Timer0, 8:1
 82.    movlw          B'11111000'       
 83.    movwf          TRISA          ; PortA RA0-RA2 output, RA3,RA4 input
 84.    movlw          B'11000000'    
 85.    movwf          TRISB          ; PortB RB0-RB5 output, RB6 input
 86.    bcf            STATUS,RP0     ; auf Bank 0 zurückschalten
 87.    clrf           TMR0           ; (((2097152Hz : 4 ): 8 ): 256 = 256 Hz)
 88.    
 89. ; Variablen setzen
 90.    clrf           T1_Wert        
 91.    clrf           T2_Wert        
 92.    clrf           T3_Wert        
 93.    clrf           Sekunden       
 94.    clrf           Minuten        
 95.    clrf           Stunden        
 96.    clrf           Bits1          
 97.    clrf           Bits2          
 98.    clrf           Teiler         
 99.    clrf           DCF_WertM      
100.    bsf            DCF_WertM,3    
101.    clrf           Min_dcf_zw     
102.    clrf           Std_dcf_zw     
103.    clrf           Min_dcf        
104.    clrf           Std_dcf        
105.    clrf           Sek_dcf        
106.    clrf           DCF_Zeit_L     
107.    clrf           DCF_Zeit_H        
108.    clrf           DCF_BitN       
109.    clrf           DCF_BitWS      
110.    clrf           DCF_BitWM      
111.    clrf           DCF_ok         
112.    bsf            DCF_ON         
113. ; **************************************************************
114. All_on         
115. ; Aufblitzen
116.    bsf            Sec_Zeile      
117.    bsf            Min_Zeile      
118.    bsf            Std_Zeile      ; Alle Zeilen an
119.    clrf           PORTB          ; Alle LEDs an
120.    movlw          D'250'         ; 25 ms Pause
121.    call           wait           
122.    bcf            Sec_Zeile      
123.    bcf            Min_Zeile      
124.    bcf            Std_Zeile      ; Alle Zeilen aus
125.    
126. Anzeige        
127. ; Sekunden Anzeigen, Taster 1 abfragen
128.    comf           Sekunden,0     ; Sekunden laden und negieren
129.    btfss          DCF_Bit_OK     ; Wenn DCF_Bit_OK 0, dann LEDs aus lassen
130.    movlw          B'11111111'    
131.    movwf          PORTB          ; Sekunden in PORTB schreiben
132.    bsf            Sec_Zeile      ; Sekunden an
133.    movlw          D'3'           ; 0.3 ms Pause
134.    call           wait           
135.    clrz           
136.    btfsc          T_Input        
137.    incf           T1_Wert,1      ; Wenn Taster 1 gedrückt, dann erhöhen
138.    btfsc          STATUS,Z       ; Wenn Taster 1 lange genug gedrückt(Überlauf; 256 Durchläufe; 1,14s), dann zum einstellmod
139.    goto           einstellmod    
140.    btfss          T_Input        
141.    clrf           T1_Wert        ; Wenn Taster 1 nicht gedrückt, dann Zähler löschen
142.    bcf            Sec_Zeile      ; Sekunden aus
143.    movlw          D'12'          ; 1.2 ms Pause
144.    call           wait           
145.    
146. ; Minuten Anzeigen, Taster 1 Zeit prüfen
147.    comf           Minuten,0      ; Minuten laden und negieren
148.    btfss          DCF_Bit_OK     ; Wenn DCF_Bit_OK 0, dann LEDs aus lassen
149.    movlw          B'11111111'    
150.    movwf          PORTB          ; Minuten in PORTB schreiben
151.    bsf            Min_Zeile      ; Minuten an
152.    movlw          D'3'           ; 0.3 ms Pause
153.    call           wait           
154.    bcf            Min_Zeile      ; Minuten aus
155.    movlw          D'12'          ; 1.2 ms Pause
156.    call           wait           
157.    
158. ; Stunden Anzeigen
159.    comf           Stunden,0      ; Stunden laden und negieren
160.    btfss          DCF_Bit_OK     ; Wenn DCF_Bit_OK 0, dann LEDs aus lassen
161.    movlw          B'11111111'    
162.    movwf          PORTB          ; Stunden in PORTB schreiben
163.    bsf            Std_Zeile      ; Stunden an
164.    movlw          D'3'           ; 0.3 ms Pause
165.    call           wait           
166.    bcf            Std_Zeile      ; Stunden aus
167.    clrf           PORTB          ; Alle Transistoren rechtzeitig an , sonst im nachfolgenden der Wert der Stunden erkennbar
168.    movlw          D'12'          ; 1.2 ms Pause
169.    call           wait           
170.    
171. ; Alle Leuchtdioden leicht glimmen lassen, Schleife schließen
172.    bsf            Sec_Zeile      
173.    bsf            Min_Zeile      
174.    bsf            Std_Zeile      ; Alle Zeilen an
175.    nop            
176.    nop            
177.    nop            
178.    nop            
179.    bcf            Sec_Zeile      
180.    bcf            Min_Zeile      
181.    bcf            Std_Zeile      ; Alle Zeilen aus
182.    goto           Anzeige        ; Wiederholen
183. ; **********************************************************
184. ; Schleife wird 256 mal pro Sekunde ausgeführt
185. zaehler        
186. ; Diese Schleife nur ausführen, wenn noch mindestens 0.5 ms gewartet werden soll
187.    movlw          D'251'         
188.    addwf          aloops,0       
189.    btfss          STATUS,C       
190.    return         ; Wenn aloops größer als 5, dann nicht zurück
191.    
192.    bcf            INTCON,T0IF    ; Interruptbit wieder löschen um das nächste Interrupt zu bemerken
193.    
194. ; Tiefpass mit nachgeschaltetem Schmitt-Trigger
195.    btfsc          DCF_Port       
196.    incf           DCF_WertM,1    ; Wenn DCF-Port 1, dann Mittelwert hochzählen
197.    btfss          DCF_Port       
198.    decf           DCF_WertM,1    ; Wenn DCF-Port 0, dann Mittelwert runterzählen
199.    
200.    btfsc          DCF_WertM,7    ; Wenn DCF_WertM,7 = 1, dann Wert größer 128, negativer Überlauf
201.    bcf            DCF_PortM      ; DCF_PortM auf 0
202.    btfsc          DCF_WertM,7    
203.    incf           DCF_WertM,1    ; Wert wieder in den gewünschten Bereich zurückbringen
204.    
205.    btfsc          DCF_WertM,3    ; Wenn DCF_WertM,3 = 1, dann Wert auf 8 gestiegen, Grenze
206.    bsf            DCF_PortM      ; DCF_PortM auf 1
207.    btfsc          DCF_WertM,3    
208.    decf           DCF_WertM,1    ; Wert wieder in den gewünschten Bereich zurückbringen
209.    
210.    call           anzeige_flacker; Erzeugt Flackern falls letzter erfolgreicher DCF-Empfang zulange zurückliegt
211.    
212.    btfss          DCF_ON         
213.    bcf            DCF_PortM      ; Wenn DCF_ON nicht 1, dann DCF_PortM immer wieder auf 0 => kein DCF-Empfang
214.    btfss          DCF_ON         
215.    bsf            DCF_Bit_OK     ; Wenn DCF_ON nicht 1, dann DCF_Bit_OK immer 1, weil im Quarzmodus der Empfang nie zu lang her sein kann
216.    
217.    call           dcf_zeit_fax   ; DCF-Signale Auswerten, wenn eine gültige Zeit in Speicher, dann ist Sek_dcf größer 0
218.    
219.    incf           Teiler,1       ; Teiler um 1 erhöhen
220.    btfss          STATUS,Z       
221.    return         ; Wenn Teiler noch nicht überläuft, dann zurück
222.    
223.    call           zaehler_normal ; Sonst die Zähler bedienen
224.    call           zaehler_dcf    
225.    
226.    movlw          D'3'           
227.    subwf          aloops,1       ; Die Zeit, die die Schleifen etwa brauchten (ca 0.3ms) von der Wartevariable abziehen
228.    
229.    return         
230. ; **********************************************************
231. ; Stellt die Anzeige für 8/256 aus, um zu signalisieren, dass letzter gültiger Empfang zu lange her
232. anzeige_flacker
233.    bsf            DCF_Bit_OK     
234.    
235.    movf           DCF_ok,1       
236.    btfss          STATUS,Z       
237.    return         ; Wenn DCF_ok gleich 0, dann flackern und nicht zurück
238.    
239.    movlw          D'20'          
240.    addwf          Teiler,0       
241.    btfsc          STATUS,C       
242.    bcf            DCF_Bit_OK     ; Wenn Teiler größer als 247 (235 + 20 = Überlauf), dann Anzeige aus
243.    
244.    return         
245. ; **********************************************************
246. ; Normaler Zähler, der mit dem Teiler(Quarz und DCF abhängig) die Zeit zählt
247. zaehler_normal 
248.    incf           Sekunden,1     ; Sekunden um 1 erhöhen
249.    
250.    movlw          D'60'          
251.    xorwf          Sekunden,0     
252.    btfss          STATUS,Z          
253.    return         ; Wenn Sekunden noch nicht 60, dann zurück
254.    
255.    incf           Minuten,1      ; Minuten um 1 erhöhen
256.    clrf           Sekunden       ; Sekunden löschen
257.    
258.    movlw          D'60'          
259.    xorwf          Minuten,0      
260.    btfss          STATUS,Z       
261.    return         ; Wenn Minuten noch nicht 60, dann zurück
262.    
263.    incf           Stunden,1      ; Stunden um 1 erhöhen
264.    clrf           Minuten        ; Minuten löschen
265.    
266.    movlw          D'24'          
267.    xorwf          Stunden,0      
268.    btfsc          STATUS,Z       
269.    clrf           Stunden        ; Wenn Vergleich 0 ergibt, war Stunden vorher 24
270.    
271.    movf           DCF_ok,1       
272.    btfss          STATUS,Z       
273.    decf           DCF_ok,1       ; Wenn DCF_ok geleich 0, dann nicht mehr runterzählen, Empfang zu lange her
274.    
275.    return         
276. ; **********************************************************
277. ; Schreibt bei gültigem Empfang zur vollen Minute die neue Zeit in die normalen Variablen
278. zaehler_dcf    
279.    movf           Sek_dcf,1      ; Wenn Sek_dcf gleich 0, dann zurück, weil kein gültiger Empfang vorliegt
280.    btfsc          STATUS,Z       
281.    return         
282.    
283.    incf           Sek_dcf,1      ; Sek_dcf um 1 erhöhen
284.    
285.    movlw          D'60'          
286.    xorwf          Sek_dcf,0      
287.    btfss          STATUS,Z          
288.    return         ; Wenn Sek_dcf noch nicht 60, dann zurück
289.    
290.    movfw          Min_dcf        ; Min_dcf nach Minuten schreiben
291.    movwf          Minuten        
292.    movfw          Std_dcf        ; Std_dcf nach Stunden schreiben
293.    movwf          Stunden        
294.    clrf           Sek_dcf        ; Sek_dcf löschen
295.    clrf           Sekunden       ; Sekunden löschen
296.    
297.    movlw          D'24'          ; Erfolgreicher Empfang, Countdown wieder auf 24 Stunden setzen
298.    movwf          DCF_ok         
299.    
300.    return         
301. ; **********************************************************
302. dcf_zeit_fax   
303. ; Liest die Daten aus dem DCF Brief, Aufruf 256 mal die Sekunde
304.    
305. ; Wartet auf mindestens 1 Sekunde Pause im DCF um den Anfang zu erkennen
306.    btfsc          DCF_PortM      ; Wenn 1, dann DCF_Zeit_H erhöhen
307.    incf           DCF_Zeit_H,1   
308.    
309.    btfsc          DCF_PortM      ; Wenn 1, dann DCF_Zeit_L löschen
310.    clrf           DCF_Zeit_L     
311.    
312.    clrz           
313.    btfss          DCF_PortM      ; Wenn DCF-Port 0, dann DCF_Zeit_L erhöhen
314.    incf           DCF_Zeit_L,1   
315.    btfsc          STATUS,Z       ; Wenn DCF_Zeit_L überläuft, dann 1 Sekunde Pause im Signal => neues Fax
316.    clrf           DCF_BitN       
317.    
318. ; Länge der Signale messen um zu entscheiden, ob es korrekt als 1 oder 0 erkannt wurde
319.    btfsc          DCF_PortM      ; Wenn DCF-Port 1, dann zurück
320.    return         
321.    
322.    movf           DCF_Zeit_H,1   ; Wenn DCF_Zeit_H gleich 0, dann zurück
323.    btfsc          STATUS,Z       
324.    return         
325.    
326.    bcf            DCF_Bit_L      ; Wenn high dann eine 0
327.    bcf            DCF_Bit_H      ; Wenn high dann eine 1
328.    
329.    movlw          D'238'         
330.    addwf          DCF_Zeit_H,0   
331.    btfsc          STATUS,C       
332.    bsf            DCF_Bit_L      ; Wenn DCF_Zeit_H größer als 18 (238 + 18 = Überlauf), dann war Signal länger als 0,07 Sekunden => lang genug für eine 0
333.    
334.    movlw          D'222'         
335.    addwf          DCF_Zeit_H,0   
336.    btfsc          STATUS,C       
337.    bcf            DCF_Bit_L      ; Wenn DCF_Zeit_H größer als 34, dann war Signal länger als 0,13 Sekunden => zu lang für eine 0
338.    
339.    movlw          D'212'         
340.    addwf          DCF_Zeit_H,0   
341.    btfsc          STATUS,C       
342.    bsf            DCF_Bit_H      ; Wenn DCF_Zeit_H größer als 44, dann war Signal länger als 0,17 Sekunden => lang genug für eine 1
343.    
344.    movlw          D'197'         
345.    addwf          DCF_Zeit_H,0   
346.    btfsc          STATUS,C       
347.    bcf            DCF_Bit_H      ; Wenn DCF_Zeit_H größer als 59, dann war Signal länger als 0,23 Sekunden => zu lang für eine 1
348.    
349.    bcf            DCF_Good       ; Mindestens DCF_Bit_H = 1 oder DCF_Bit_L = 1, dann wird DCF_Good gesetzt
350.    btfsc          DCF_Bit_H      
351.    bsf            DCF_Good       
352.    btfsc          DCF_Bit_L      
353.    bsf            DCF_Good       
354.    
355.    btfss          DCF_Good       ; Wenn ein Error, dann DCF_BitN auf 0 um neu zu beginnen, verzichte auf return
356.    clrf           DCF_BitN       
357.    
358. ; Die Bits nach Wertigkeit zuordnen
359.    movlw          D'20'          ; Min_dcf_zw leeren
360.    xorwf          DCF_BitN,0     
361.    btfsc          STATUS,Z       
362.    clrf           Min_dcf_zw     
363.    
364.    movlw          D'21'          ; Beginnen mit B'00000001'
365.    xorwf          DCF_BitN,0     
366.    btfsc          STATUS,Z       
367.    bsf            DCF_BitWM,0    
368.    
369.    movfw          DCF_BitWM      ; Bitwertigkeit Minuten in W, falls Bit 1 sonst 0 in W (Wertigkeit * 0 = 0)
370.    btfsc          DCF_Bit_L      
371.    clrw           
372.    addwf          Min_dcf_zw,1   
373.       
374.    movlw          B'00001000'    
375.    xorwf          DCF_BitWM,0    
376.    btfss          STATUS,Z       ; Wenn B'00001000' dann kein leftschift...
377.    rlf            DCF_BitWM,1    
378.    btfsc          STATUS,Z       ; ...in dem Fall dann das 1. Bit setzen => B'00001010'
379.    bsf            DCF_BitWM,1    
380.    
381.    movlw          D'27'          ; Letztes Bit drin Ende Minuten, deshalb clr
382.    xorwf          DCF_BitN,0     
383.    btfsc          STATUS,Z       
384.    clrf           DCF_BitWM      
385.    
386.    movlw          D'28'          ; Std_dcf_zw leeren
387.    xorwf          DCF_BitN,0     
388.    btfsc          STATUS,Z       
389.    clrf           Std_dcf_zw     
390.    
391.    movlw          D'29'          ; Beginnen mit B'00000001'
392.    xorwf          DCF_BitN,0     
393.    btfsc          STATUS,Z       
394.    bsf            DCF_BitWS,0    
395.    
396.    movfw          DCF_BitWS      ; Bitwertigkeit Stunden in W, falls Bit 1 sonst 0 in W (Wertigkeit * 0 = 0)
397.    btfsc          DCF_Bit_L      
398.    clrw           
399.    addwf          Std_dcf_zw,1   
400.    
401.    movlw          B'00001000'    
402.    xorwf          DCF_BitWS,0    
403.    btfss          STATUS,Z       ; Wenn B'00001000' dann kein leftschift...
404.    rlf            DCF_BitWS,1    
405.    btfsc          STATUS,Z       ; ...in dem Fall dann das 1. Bit setzen => B'00001010'
406.    bsf            DCF_BitWS,1    
407.    
408.    movlw          D'34'          ; Letztes Bit drin Ende Stunde, deshalb clr
409.    xorwf          DCF_BitN,0     
410.    btfsc          STATUS,Z       
411.    clrf           DCF_BitWS      
412.    
413.    movlw          D'34'          ; Zeit empfangen, jetzt prüfen
414.    xorwf          DCF_BitN,0     
415.    btfsc          STATUS,Z       
416.    call           dcf_zeit_test  ; DCF-Zeit auf Schlüssigkeit prüfen
417.    
418.    movf           DCF_BitN,1     ; Wenn DCF_BitN ungeleich 0, dann Teiler_dcf syncronisieren
419.    btfss          STATUS,Z       
420.    call           teiler_dcf_sync
421.    
422.    clrf           DCF_Zeit_H     ; DCF_Zeit_H ausgewertet, wieder löschen (erst hier, damit in teiler_dcf_sync vorhanden)
423.    
424.    incf           DCF_BitN,1     
425.    return         
426. ; **********************************************************
427. teiler_dcf_sync
428.    movfw          DCF_Zeit_H     ; Teiler_dcf syncronisieren
429.    subwf          Teiler,0       
430.    
431.    btfss          STATUS,C       
432.    incf           Teiler,1       ; Wenn Teiler_dcf kleiner als DCF_Zeit_H => + 1, incf, weil Carry unbeeinflusst
433.    
434.    btfsc          STATUS,C       
435.    decf           Teiler,1       ; Wenn Teiler_dcf größer als DCF_Zeit_H => - 1, decf, weil Carry unbeeinflusst
436.    
437.    return         
438.    
439. dcf_zeit_test  
440.    movlw          D'34'          ; Sek_dcf auf die 34 Sekunde setzen
441.    movwf          Sek_dcf        
442.    
443.    movfw          Min_dcf_zw     
444.    subwf          Min_dcf,1      ; Min_dcf_zw ist das Ältere und sollte 1 kleiner oder 59 größer als aktuelles sein
445.    
446.    bcf            DCF_Good       
447.       
448.    movlw          D'255'         ; Bei 255 (-1) wird DCF_Good gesetzt
449.    xorwf          Min_dcf,0      
450.    btfsc          STATUS,Z       
451.    bsf            DCF_Good       
452.    
453.    movlw          D'59'          ; Bei 59 wird DCF_Good gesetzt
454.    xorwf          Min_dcf,0      
455.    btfsc          STATUS,Z       
456.    bsf            DCF_Good       
457.    
458.    btfss          DCF_Good       ; Wenn DCF_Good 0 war (keine Bedingung wahr), dann wird Sek_dcf gelöscht
459.    clrf           Sek_dcf        
460.       
461.    movfw          Std_dcf_zw     
462.    subwf          Std_dcf,1      ; Std_dcf_zw ist das Ältere und sollte maximal 2 kleiner, 1 kleiner oder gleich wie aktuelles sein
463.    
464.    bcf            DCF_Good       
465.    
466.    movlw          D'254'         ; Bei 254 (-2) wird DCF_Good gesetzt
467.    xorwf          Min_dcf,0      
468.    btfsc          STATUS,Z       
469.    bsf            DCF_Good       
470.    
471.    movlw          D'255'         ; Bei 255 (-1) wird DCF_Good gesetzt
472.    xorwf          Min_dcf,0      
473.    btfsc          STATUS,Z       
474.    bsf            DCF_Good       
475.    
476.    movlw          D'0'           ; Bei 0 wird DCF_Good gesetzt
477.    xorwf          Min_dcf,0      
478.    btfsc          STATUS,Z       
479.    bsf            DCF_Good       
480.    
481.    btfss          DCF_Good       ; Wenn DCF_Good 0 war (keine Bedingung wahr), dann wird Sek_dcf gelöscht
482.    clrf           Sek_dcf        
483.    
484.    movfw          Min_dcf_zw     ; Min_dcf und Std_dcf mit Aktuellem füllen
485.    movwf          Min_dcf        
486.    
487.    movfw          Std_dcf_zw     
488.    movwf          Std_dcf        
489.    
490.    return         
491. ; **********************************************************
492. einstellmod    
493.    movlw          B'11111111'    
494.    movwf          PORTB          ; Alle Transistoren aus
495.    bsf            Sec_Zeile      ; Nur Sekundenzeile an
496.    movlw          D'100'         ; 10 ms Pause
497.    call           wait           
498.    btfsc          T_Input        
499.    goto           einstellmod    ; Warten bis Taster losgelassen
500.    bcf            Sec_Zeile      ; Jetzt Sekundenzeile aus
501.    clrf           T1_Wert        
502.    clrf           T2_Wert        
503.    clrf           T3_Wert        
504.    
505. dcf_test       
506.    movlw          B'11100000'    
507.    movwf          PORTB          
508.    
509.    bsf            Sec_Zeile      
510.    bsf            Min_Zeile      
511.    bsf            Std_Zeile      
512.    
513.    btfss          DCF_Port       
514.    bsf            PORTB,2        
515.    
516.    btfss          DCF_ON         
517.    bsf            PORTB,1        
518.    btfss          DCF_ON         
519.    bsf            PORTB,3        
520.    
521.    call           pol_taster     
522.    btfsc          T1_Set         ; Taster 1 nächster Modus
523.    goto           edit_sek_on    
524.    
525.    btfsc          T2_Set         ; Taster 2 DCF off
526.    bcf            DCF_ON         
527.    
528.    btfsc          T3_Set         ; Taster 3     DCF on
529.    bsf            DCF_ON         
530.    
531.    goto           dcf_test       
532.    
533. edit_sek_on    
534.    bsf            Sec_Zeile      
535.    bcf            Min_Zeile      
536.    bcf            Std_Zeile      ; Nur Sekunden an
537.    clrf           PORTB          
538.    movlw          D'250'         ; 25 ms Pause
539.    call           wait           
540.    bcf            Sec_Zeile      ; Sekunden aus
541.    
542. edit_sec       
543.    call           pol_taster     
544.    
545.    comf           Sekunden,0     ; Sekunden laden und negieren
546.    movwf          PORTB          ; Sekunden in PORTB schreiben
547.    bsf            Sec_Zeile      ; Sekunden an
548.    
549.    btfsc          T1_Set         ; Taster 1 nächster Modus
550.    goto           edit_min_on    
551.    
552.    btfsc          T2_Set         
553.    clrf           Sekunden       
554.    btfsc          T2_Set         
555.    clrf           Teiler         
556.    
557.    btfsc          T3_Set         
558.    clrf           Sekunden       
559.    btfsc          T3_Set         
560.    clrf           Teiler         
561.    
562.    goto           edit_sec       
563.    
564. edit_min_on    
565.    bcf            Sec_Zeile      
566.    bsf            Min_Zeile      
567.    bcf            Std_Zeile      ; Nur Minuten an
568.    clrf           PORTB          
569.    movlw          D'250'         ; 25 ms Pause
570.    call           wait           
571.    bcf            Min_Zeile      ; Minuten aus
572.    
573. edit_min       
574.    call pol_taster
575.    
576.    comf           Minuten,0      ; Minuten laden und negieren
577.    movwf          PORTB          ; Minuten in PORTB schreiben
578.    bsf            Min_Zeile      ; Minuten an
579.    
580.    btfsc          T1_Set         ; Taster 1 nächster Modus
581.    goto           edit_std_on    
582.    
583.    btfsc          T2_Set         
584.    incf           Minuten,1      
585.    movlw          B'00111100'    
586.    xorwf          Minuten,0      
587.    btfsc          STATUS,Z       
588.    clrf           Minuten        
589.    
590.    btfsc          T3_Set         
591.    decf           Minuten,1      
592.    movlw          B'11111111'    
593.    xorwf          Minuten,0      
594.    btfsc          STATUS,Z       
595.    movlw          B'00111011'    
596.    btfsc          STATUS,Z       
597.    movwf          Minuten        ; Wenn -1 (255), dann Minuten auf 59 stellen
598.    
599.    goto           edit_min       
600.    
601. edit_std_on    
602.    bcf            Sec_Zeile      
603.    bcf            Min_Zeile      
604.    bsf            Std_Zeile      ; Nur Stunden an
605.    clrf           PORTB          
606.    movlw          D'250'         ; 25 ms Pause
607.    call           wait           
608.    bcf            Std_Zeile      ; Stunden aus
609.    
610. edit_std       
611.    call pol_taster
612.    
613.    comf           Stunden,0      ; Stunden laden und negieren
614.    movwf          PORTB          ; Stunden in PORTB schreiben
615.    bsf            Std_Zeile      ; Stunden an
616.    
617.    btfsc          T1_Set         ; Taster 1 nächster Modus
618.    goto           All_on         
619.    
620.    btfsc          T2_Set         
621.    incf           Stunden,1      
622.    movlw          B'00011000'    
623.    xorwf          Stunden,0      
624.    btfsc          STATUS,Z       
625.    clrf           Stunden        
626.    
627.    btfsc          T3_Set         
628.    decf           Stunden,1      
629.    movlw          B'11111111'    
630.    xorwf          Stunden,0      
631.    btfsc          STATUS,Z       
632.    movlw          B'00010111'    
633.    btfsc          STATUS,Z       
634.    movwf          Stunden        ; Wenn -1 (255), dann Stunden auf 23 stellen
635.    
636.    goto           edit_std       
637.    
638. pol_taster     
639.    movlw          D'20'          ; 2 ms Pause
640.    call           wait           
641.    
642.    bcf            Sec_Zeile      
643.    bcf            Min_Zeile      
644.    bcf            Std_Zeile      ; Alle Zeilen aus
645.    
646.    movlw          B'11111111'    ; Alle LEDs aus
647.    movwf          PORTB          
648.    
649.    bcf            T1_Set         ; Alle Taster zurücksetzen
650.    bcf            T2_Set         
651.    bcf            T3_Set         
652.    
653. ; Drücken von Taster 1 Überwachen
654.    bsf            Sec_Zeile      ; Taster 1 an
655.    movlw          D'3'           ; 0,3 ms Pause
656.    call           wait           
657.    btfsc          T_Input        
658.    incf           T1_Wert,1      ; Wenn Taster 1 gedrückt, dann erhöhen
659.    movfw          T1_Wert        
660.    btfss          T_Input        
661.    clrf           T1_Wert        ; Wenn nicht gedrückt, dann löschen
662.    xorwf          T1_Wert,0      
663.    btfss          STATUS,Z       
664.    bsf            T1_Set         ; Wenn löschen den Wert verringert hat, dann auch Taster 1 Bit setzen
665.    btfsc          T1_Set         
666.    clrf           T1_Wert        ; Wenn Tasterbit gesetzt, dann Zähler löschen
667.    bcf            Sec_Zeile      ; Taster 1 aus
668.    
669. ; Drücken von Taster 2 Überwachen
670.    bsf            Min_Zeile      ; Taster 2 an
671.    movlw          D'3'           ; 0,3 ms Pause
672.    call           wait           
673.    btfsc          T_Input        
674.    incf           T2_Wert,1      ; Wenn Taster 2 gedrückt, dann erhöhen
675.    
676.    movfw          T2_Wert           
677.    btfss          T_Input        
678.    clrf           T2_Wert        ; Wenn nicht gedrückt, dann löschen
679.    xorwf          T2_Wert,0      
680.    btfss          STATUS,Z       
681.    bsf            T2_Set         ; Wenn löschen den Wert verringert hat, dann auch Taster 2 Bit setzen
682.    
683.    btfsc          T2_Wert,5      
684.    bsf            T1_Fast        ; Taster 2 Schnellbit setzen
685.    
686.    btfsc          T2_Wert,5      
687.    bsf            T2_Set         ; Taster 2 Bit setzen
688.    
689.    movlw          B'0000111'     
690.    btfsc          T1_Fast        
691.    addwf          T2_Wert,1      ; Wenn Schnellbit gesetzt, dann schneller zählen.
692.    
693.    btfss          T_Input        
694.    bcf            T1_Fast        ; Wenn nicht gedrückt, dann Schnellbit löschen
695.    
696.    btfsc          T2_Set         
697.    clrf           T2_Wert        ; Wenn Tasterbit gesetzt, dann Zähler löschen
698.    bcf            Min_Zeile      ; Taster 2 aus
699.    
700. ; Drücken von Taster 3 Überwachen
701.    bsf            Std_Zeile      ; Taster 3 an
702.    movlw          D'3'           ; 0,3 ms Pause
703.    call           wait           
704.    btfsc          T_Input        
705.    incf           T3_Wert,1      ; Wenn Taster 3 gedrückt, dann erhöhen
706.    
707.    movfw          T3_Wert           
708.    btfss          T_Input        
709.    clrf           T3_Wert        ; Wenn nicht gedrückt, dann löschen
710.    xorwf          T3_Wert,0      
711.    btfss          STATUS,Z       
712.    bsf            T3_Set         ; Wenn löschen den Wert verringert hat, dann auch Taster 3 Bit setzen
713.    
714.    btfsc          T3_Wert,5      
715.    bsf            T2_Fast        ; Taster 3 Schnellbit setzen
716.    
717.    btfsc          T3_Wert,5      
718.    bsf            T3_Set         ; Taster 3 Bit setzen
719.    
720.    movlw          B'0001111'     
721.    btfsc          T2_Fast        
722.    addwf          T3_Wert,1      ; Wenn Schnellbit gesetzt, dann schneller zählen.
723.    
724.    btfss          T_Input        
725.    bcf            T2_Fast        ; Wenn nicht gedrückt, dann Schnellbit löschen
726.       
727.    btfsc          T3_Set         
728.    clrf           T3_Wert        ; Wenn Tasterbit gesetzt, dann Zähler löschen
729.    bcf            Std_Zeile      ; Taster 3 aus
730.    
731.    movlw          D'100'         ; 10 ms Pause
732.    call           wait           
733.    
734.    return         
735. ; **********************************************************
736. ; Ohne Anzeige
737. power_save     
738.    movlw          B'11111111'    
739.    movwf          PORTB          
740.    movlw          B'00000000'    
741.    movwf          PORTA          ; Alle Transistoren aus
742.    
743.    movlw          D'5'           ; Zeitkonstante damit Zähler ausgeführt werden kann
744.    movwf          aloops         
745.    btfsc          INTCON,T0IF    ; Wenn Interuptbit gesetzt ist...
746.    call           zaehler        
747.    
748.    btfsc          Power_Good     
749.    goto           power_save     
750.    return         
751. ; **********************************************************
752. ; Warteschleife
753. wait           
754.    movwf          aloops         ; Zeitkonstante für gewünschte Wartezeit in 0.1 ms
755. await          
756.    movlw          D'5'           ; Zeitkonstante für etwa 0.1ms
757.    movwf          bloops         
758.    btfsc          INTCON,T0IF    ; Wenn Interuptbit gesetzt ist...
759.    call           zaehler        
760.    btfsc          Power_Good     
761.    call           power_save     ; Wenn der Power_Good Pin high wird dann Anzeige aus
762. bwait          
763.    nop            
764.    nop            
765.    nop            
766.    nop            
767.    nop            
768.    decfsz         bloops, F      ; 1 ms vorbei?
769.    goto           bwait          ; nein, noch nicht
770.    decfsz         aloops, F      ; Wunschzeit vorbei?
771.    goto           await          ; nein, noch nicht
772.    return         ; das Warten hat ein Ende
773.    
774.    end            

Erstellt im März 09