' --------------------------------------------------------------------------
' Dekodierung des DCF77-Signals auf der Open-Micro, Open-Mini und Open-Midi
' In OCBASIC 1.05 auf der Open-Midi von Dietmar Harlos am 12. Dezember 2006
' erweitert um Datum und Fehlerabfrage Feb. 2007 von Eckhard Raetz
' --------------------------------------------------------------------------

' Es wird der invertierte Ausgang des preiswerten DCF77-Empfangsmoduls mit
' der Conrad-Bestellnummer 641138 verwendet. Sicherheitshalber sollte ein
' Widerstand 10 kOhm zwischen Ausgang und +5 Volt geschaltet werden.

' Die Befehle BEEP, GET, INPUT und SLOWMODE deaktivieren standardmaessig
' die Interrupts und fuehren deshalb zu Empfangsstoerungen.

'Das Zeitprotokoll des DCF Senders der Physikalisch-Technische Bundesanstalt (PTB) in Braunschweig :
'Sekunde:	Bedeutung:
'0.       	Minutenbeginn (immer LOW)
'1. - 14.	Reserviert, keine Bedeutung
'15.	        Reserveantenne aktiv
'16.	        Umstellung von Sommer- auf Winterzeit, oder umgekehrt
'17.	        Sommerzeit aktiv
'18.	        Winterzeit aktiv
'19.	        Ankündigung Schaltsekunde
'20.	        Zeitbeginn, (immer High)
'21. - 27.	Minute plus 1, 2, 4, 8, 10, 20, 40
'28.	        Prüfbit Minute,  Parität gerade 0 oder ungerade 1
'29. - 34.	Stunde plus 1, 2, 4, 8, 10, 20
'35.       	Prüfbit Stunde
'36. - 41.	Tag plus 1, 2, 4, 8, 10, 20
'42. - 44.	Wochentag plus1, 2, 4
'45. - 49.	Monat plus 1, 2, 4, 8, 10
'50. - 57.	Jahr plus 1, 2, 4, 8, 10, 20, 40, 80
'58.	        Prüfbit Datum, Parität 36 bis 57 Sekunde
'59.	        fehlt zur Erkennung des Minutenanfangs

' --------------------------------------------------------------------------

DEFINE dcfport   bPTA3        'DCF77-Empfaenger-Ausgang; entspricht PORT[4]
DEFINE dcfimpuls   BYTE
DEFINE dcfzaehler  BYTE          '8 Byte Zwischenspeicher fuer DCF77-Routine.
DEFINE dcfmin    BYTE
DEFINE dcfhour   BYTE
DEFINE dcfday    BYTE
DEFINE dcfmonth  BYTE
DEFINE dcfyear   BYTE
Define dcfmerk   Byte         'zur Ganggenauigkeit

DEFINE dcfstat   BYTE
define dcf_ok Bit [7] of dcfstat 'wird im Basicprogramm verwendet
DEFINE dcfparity 0            'Paritaet muss auf Bit Nummer 0 liegen!
DEFINE dcfparmsk &b00000001   'Bitmaske fuer Paritaetsbit
DEFINE dcftog    1            'letzter Portzustand (zur Flankendetektion)
DEFINE dcfok     6            '"DCF-Signal okay"
DEFINE dcfset    7            '"Interne Uhr der OM wurde gestellt"

' --------------------------------------------------------------------------

' User-Assemblerroutine im Timer Overflow Interrupt.

' Sie wird in den 20-ms-TIMER-Interrupt der OM eingebunden und deshalb
' 50 mal in der Sekunde aufgerufen. In der folgenden Interruptroutine
' wird der DCF77-Pin, an dem der invertierte Ausgang des DCF77-Empfaengers
' angeschlossen ist, abgefragt.

' Das Schluesselwort INLASM kennzeichnet die folgende PROCEDURE als
' Assemblerroutine ("IAR"). Dadurch, und dass sie unmittelbar am Anfang
' des Programms steht, wird sie zu einer IIAR, also zu einer Assembler-
' Interruptroutine, die bei einem aktivierten User-Interrupt gestartet wird.

PROCEDURE iiar_tofl INLASM

! cbeqa #iTOFL,iiar_tofl_istofl 'falls kein Timer Overflow Interrupt,
! jmp iiar_tofl_ende+2        ' dann zur naechsten IIAR in der Kette

#iiar_tofl_istofl
! lda dcfimpuls                 'Variable zur Messung der DCF-Impulslaenge
! inc dcfimpuls                 'Dcfimpuls = dcfimpuls + 1
! bmi ende1                     'Signal nicht mehr vorhanden (timeout)

' Je nachdem, ob der DCF-Port high oder low ist, werden verschiedene Code-
' teile (Subroutinen) aufgerufen. Es werden LOW- und HIGH-Flanken erkannt.

! brset dcfport,PTA,iiar_tofl_portishigh

' Subroutine fuer LOW-Flanke. Der DCF-Impuls beginnt. Das geschieht am
' Beginn einer Sekunde oder einer Minute.

#iiar_tofl_portislow
! brclr dcftog,dcfstat,ende
! bclr dcftog,dcfstat
! cmp #75                     'seit mehr als 1,5 Sekunden kein Impuls?
! bcs iiar_tofl_keinminsync   'dann wurde der Minutensync empfangen
! brclr dcfok,dcfstat,iiar_tofl_keindcfok
! lda dcfzaehler
! cmp #59                     'die dekodierte DCF-Uhrzeit in die System-
! bne iiar_tofl_keindcfok     ' variablen fuer die RTC der OM uebernehmen,
! jmp uebernahme
#iiar_tofl_keindcfok          'nach dem Minutensync auf jeden Fall den
! jmp kein_ok                 ' "DCF-Signal ist okay" setzen.
#iiar_tofl_keinminsync
! clr dcfimpuls                 'und in jedem Fall den Zaehler fuer die Laenge
#ende
! jmp iiar_tofl_exit          ' des DCF-Impuls auf Null setzen und beenden
#ende1
! jmp iiar_tofl_cleardcfok


' Subroutine fuer HIGH-Flanke. Der DCF-Impuls endete und wird ausgewertet.

#iiar_tofl_portishigh
! brset dcftog,dcfstat,ende
! bset dcftog,dcfstat
! cmp #3                      'falls der DCF-Impuls weniger als 0,06 Sekunden
! bcs iiar_tofl_cleardcfok    ' dauerte ist das Signal nicht okay


! ldx dcfzaehler                 'Zaehler fuer die DCF-Impulse ins Indexregister laden
! inc dcfzaehler                 'und um 1 erhöhen.
! cmpx #20                       'Wenn 20te Sekunde ...
! bcs ende                       ' kleiner 20te Sekunde, interessiert nicht.
! beq iiar_20te_sekunde               ' ....dann beginnt mit dem nächsten Impuls das Minutensignal.
! cbeqx #28,iiar_tofl_checkparity 'Nach Minuten Empfang
! cbeqx #35,iiar_tofl_checkparity 'Nach Stunden Empfang
! cmpx #58                        'nach Datums Empfang
                                  ' Paritaet kontrollieren und wieder loeschen.

! beq iiar_tofl_checkparity   ' Wenn 58 Sekunde empfangen? -> Paritaet kontrollieren

! bcc ende                    'Beenden, Wenn ueber Datum hinaus.Sollte nicht sein.

! cmp #7                      'dcfimpuls (im Accu) wird mit 7 verglichen
                              'typische Werte: 4 & 5 bei 0-Bit, 9 & 10 bei 1
! bcs ende                    'bei >= 0,14 s: 1-Bit, sonst: 0-Bit -> beenden

! lda dcfstat                 'Paritaet berechnen/aktualisieren
! xor #dcfparmsk
! sta dcfstat                  'und wieder speichen

! cmpx #28
! bhi iiar_tofl_addhour       'beim Empfang eines 1-Bits das Bit von BCD nach
! lda dcftable-21,x           ' binaer konvertieren und temporaere Minute und
! add dcfmin                  ' zusammenaddieren
! sta dcfmin
! bra iiar_tofl_exit

#iiar_tofl_addhour
 ! cmpx #35                   'desgleichen bei Stunde
! bhi iiar_tofl_addday
! lda dcftable-29,x
! add dcfhour
! sta dcfhour
! bra iiar_tofl_exit          'und beenden

 #iiar_tofl_addday
! cmpx #41                    'bei Tag
!  bhi wtag
! lda dcftable-36,x
! add dcfday
! sta dcfday
! bra iiar_tofl_exit          'und beenden

#wtag:
!cmpx #44                   'Wochentag, wird nicht ausgewertet
!bhi iiar_tofl_monat
!bra iiar_tofl_exit

#iiar_tofl_monat:
!cmpx #49                   'Monat
!bhi iiar_tofl_jahr
!lda dcftable-45,x
!add dcfmonth
!sta dcfmonth
!bra iiar_tofl_exit

#iiar_tofl_jahr:
!cmpx #58                 'Jahr
!bhi  iiar_tofl_exit
!lda dcftable-50,x
!add dcfyear
!sta dcfyear
!bra iiar_tofl_exit

#iiar_20te_sekunde
!cmp #7                     'die 20te Sekunde muß HIGH sein
!bcs kein_ok                'wenn nicht von vorn
!bra iiar_tofl_clearparity  'wenn ja Parityflag löschen.

#iiar_tofl_checkparity        'empfangene Paritaet mit der berechneten
! cmp #7                      ' Paritaet vergleichen und bei einem Fehler
! clra                        ' das Okay-Flag loeschen
! adc dcfstat
! lsra
! bcs iiar_tofl_clearparity

#iiar_tofl_cleardcfok         'Okay-Flag loeschen
! bclr dcfok,dcfstat

#iiar_tofl_clearparity        'Bit zur Berechnung der Paritaet loeschen
! lda #dcfparmsk
! coma
! and dcfstat
! sta dcfstat

#iiar_tofl_exit               'Ruecksprung in das Betriebssystem
! clc                         'CLC bedeutet: Betriebssystemroutinen fuer
! rts                         '  Tofl-Interrupt werden ausgefuehrt

#uebernahme
! ldx dcfmin
! cmpx #59                  'wenn Minute größer als 59
! bhi kein_ok
! ldx dcfhour
! cmpx #23                  'wenn Stunde größer als 23
! bhi kein_ok
! ldx dcfday
! cmpx #31                  'wenn Tag größer als 31
! bhi kein_ok
! ldx dcfmonth
! cmpx #12                  'wenn Monat größer als 12
! bhi kein_ok
! ldx dcfyear
! cmpx #10                    ' 2011 muß ein Update gemacht werden
! bhi kein_ok
! cmpx #7                     'kann jedes Jahr um 1 erhöht werden
! blo kein_ok
! mov dcfmin,MINUTE           ' Übernahme Funkzeit -- Interne Uhr
! mov dcfmin,dcfmerk          ' Zur Genauigkeit, Minute merken, wann letztmals Funkzeit empfangen wurde
! mov dcfhour,HOUR            '
! mov dcfday,DAY
! mov dcfmonth,MONTH
! mov dcfyear,YEAR
! clr SECOND
! clr TIMER
! bset dcfset,dcfstat
#kein_ok
! clr dcfzaehler                 ' Zaehler fuer die DCF-Impulse und den
! clr dcfmin                  ' temporaeren Speicher zur Berechnung der
! clr dcfhour
! clr dcfday                ' DCF-Uhrzeit loeschen. Ausserdem das Flag
! clr dcfmonth
! clr dcfyear
! clr dcfimpuls
! bset dcfok,dcfstat         'dcfok muß ja irgendwann gesetzt werden
! bra iiar_tofl_exit

#dcftable                     'Tabelle fuer die Konvertierung BCD -> binaer
! db 1,2,4,8,10,20,40,80

END PROCEDURE

#iiar_tofl_ende               'hier hinter beginnt die naechste IIAR
                              '(sofern vorhanden)

' ---------------------------------------------------------------------------