|
Forum Index : Microcontroller and PC projects : [MM2]DCF77 Decoder
| Author | Message | ||||
| twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1772 |
This is yet another DCF77 Decoder. I fear it will only interest Europeans. Because of DCF77 on Wikipedia The code is well commented and tested. I used a very old (~30 years?) DCF77 module from Conrad Electronics in germany. The red LED is pulsing at 1 Hz and indicates the DCF77 signal. My module is connected to the MM2 with a 2m telephone cable. ![]() '**************************************************** '* '* Yet another DCF77-Decoder for MicroMites, Version 1.0 '* '* System: MMBasic 5.0 '* Micromite 2 (PIC32MX170/28, Micks BP170 + 2.8" TFT) '* by twofingers 1-2019 at TBS '* -------------------------- '* f. Conrad-Modul '* Pin1=GND Pin2=5V or 3.3V '* Out=inverting output=Pin3 (connected to the MM with a 2m cable) '* '* LED (+2.7k) from Pin3 to Pin2 '* to make the 1 Hz pulses visible '* -------------------------- '* MM Input = DCFPIN '* -------------------------- '* Note: '* In order to have a reliable result, at least '* two successive times should be determined '* '* Ignored: '* Summer time announcement (bit 16) '* Leap second announcement (bit 19) '* '* https://en.wikipedia.org/wiki/DCF77 '*---------------------------------------------------- ' This code may be freely distributed and changed. ' Provided AS IS without any warranty. ' Use it at your own risk. All information is provided for ' educational purposes only! '------------------------------------------------------ ' ++++++ Credit to Geoff for his great MMBasic ++++++ '****************************************************** Option EXPLICIT Dim As integer pass=0 Dim As integer tGap(60) 'tGap() = Gap_length Dim As integer DCF(60)'DCF-Telegramm in Bitform ' reverse bit order = least significant bit goes first. Dim WT$(7) 'Weekdays WT$(1)="Monday" WT$(2)="Tuesday" WT$(3)="Wednesday" WT$(4)="Thursday" WT$(5)="Friday" WT$(6)="Saturday" WT$(7)="Sunday" Dim DST$(2) = ("","CET","CEST") 'german: MEZ,MESZ=Sommerzeit Dim DCF_date$, DCF_time$ Dim VT100_Cup$=Chr$(27)+"[1A" 'VT100 Cursor up Dim VT100_Del$=Chr$(27)+"[2K" 'VT100 erase cursorline Const DCFPIN=15 Const TRUE=1, FALSE=0 Const THRESHOLD=150 'gap length threshold, between 100ms and 200ms Const TOLERANCE=50 Const min_pw=1000-TOLERANCE Const max_pw=1000+TOLERANCE 'limits of period duration of sec cycle Const min_mmark=2000-TOLERANCE Const max_mmark=2000+TOLERANCE 'limits of period duration of minute mark Dim As integer BitN 'Sec_index Dim As integer tSec 'tSec=Period length, rising edge to rising edge Dim As integer SyncOK 'Minute mark found! Dim As integer DCF_Time_OK=FALSE Dim As integer DCF_error =FALSE Do '***** Begin Main Loop ****** pass=pass+1 BitN=0 tSec=0 SyncOK=FALSE SetPin DCFPIN,INTH,HiLevel Print "MM DCF77 Decoder V1.0 "Time$, "Pass: "pass Timer=0 ' Let's see if we have a 1000ms pulse (1 Hz) signal ' The interrupt does the job. tSec=time of one period of the 1 Hz DCF signall Do Print "Waiting for 1 Hz Signal (1 cycle/1000ms) ",Str$(tSec,4)"ms";VT100_Cup$ Loop Until InRange(tSec,min_pw,max_pw) ' now we have probably a DCF77 signal ' Note: The better it matches the 1000 ms, the better the signal quality Print VT100_Del$+"DCF77 signal available ... " DCF_error=FALSE Do '**************** If InRange(tSec,min_mmark,max_mmark) Then ' we found a 2000ms period BitN=0:SyncOK=1:tSec=0 Print "Minute mark found! Collecting DCF77 data ..." Print "0 1 2 3 4 5 6 x10" Print; ' Sub LoLevel does the job EndIf If BitN=59 And SyncOK=1 Then ' seems we are complete DCF_error = Not DCF77_readout() Exit Do EndIf If BitN >59 Then ' uff! No sync found. Let's start again ... Print "Error: Minute mark not found " Exit Do EndIf If DCF_error Then Print "Error: DCF_error (Signal)" Exit Do EndIf Loop Loop While Not DCF_Time_OK'***** End Main Loop ****** ' copy the DCF time into the MM if you like 'time$=DCF_time$ 'date$=DCF_date$ End'-------------------------------------------------------- '* TWO interrupt routines '* Setpin interrupt sub to detect the rising edge, '* the time of one cycle, tSec = ~1000ms or ~2000ms Sub HiLevel SetPin DCFPIN,INTL,LoLevel tSec=Timer:Timer=0 If tSec<min_pw Then DCF_error=TRUE 'we lost the DCF signal? End Sub '* Setpin interrupt sub to detect the falling edge '* we get the gap length - tGap() = ~100 or ~200ms Sub LoLevel ' Setpin interrupt sub, high to low SetPin DCFPIN,INTH,HiLevel tGap(BitN)=Timer If SyncOK Then Print Str$(tGap(BitN)>threshold,1); Else Print "Searching for minute mark" BitN, Str$(tSec,4)"ms"; VT100_Cup$ EndIf If BitN<60 Then BitN=BitN+1 'just in case to prevent overflow End Sub ' let's see what the DCF telegram says ' ****** readout, takes about 46ms ****** Function DCF77_readout() Local min, hour, day, mon, year, i, tAs integer Local weekday$ SetPin DCFPIN,OFF 'interrupt disable ' convert pulse widths (i.e. gaps) to bits For i= 0 To 58 DCF(i)= tGap(i) >threshold Next DCF77_readout = TRUE ' Some error checks: ' Bit 0 must be 0 ' Bit20 must be 1 ' Bit17 = not Bit18 If DCF(0)<>0 Then DCF77_readout = FALSE:Print "E1":Exit Function If DCF(20)<>1 Then DCF77_readout = FALSE:Print "E2":Exit Function If DCF(17)<>Not DCF(18) Then DCF77_readout = FALSE:Print "E3":Exit Function ' Parity check If Parity(21,28) Or Parity(29,35) Or Parity(36,58) Then DCF77_readout = FALSE Print "E4" Exit Function EndIf min = BCD_Sum(21,27) hour = BCD_Sum(29,34) day = BCD_Sum(36,41) weekday$=wt$(BCD_Sum(42,44)) mon = BCD_Sum(45,49) year = BCD_Sum(50,57) DCF_date$=Str$(day,2,0,"0")+"-"+Str$(mon,2,0,"0")+"-"+Str$(2000+year,4,0,"0") DCF_time$=Str$(hour,2,0,"0")+":"+Str$(min,2,0,"0")+":"+"00" If ValidTime(DCF_time$) Then Print "Success!" Print DCF_date$,DCF_time$, weekday$, DST$(DCF(17)+1) DCF_Time_OK=TRUE Else Print "DCFtime INVALID!" DCF_Time_OK=FALSE EndIf Print String$( 80,"_") ' Print:Print "Press <Space> to continue" ' Do: Loop Until Inkey$ = " " End Function ' check the parity Function Parity(a,b) As integer Local p,i p=0 For i = a To b p = p + DCF(i) Next Parity = p Mod 2 End Function ' Converts DCF() bits from BCD to decimal (max 9bit) Function BCD_Sum(a,b) As integer Local As integer bcd(9) = (0,1,2,4,8,10,20,40,80,100), i For i = a To b BCD_Sum = BCD_Sum + DCF(i)*bcd(i-a+1) Next End Function Function ValidTime(tm$) As integer Local legal(3)=(0,23,59,59), i As integer ValidTime=TRUE For i = 1 To 7 Step 3 '1,4,7 If Val(Mid$(tm$,i,2))>legal(i\3+1) Then ValidTime=FALSE: Exit For Next i End Function Function InRange(x,min,max) As integer InRange = x>=min And x<= max End Function ' rBCD2Dec() converts a part of an array of bit numbers BCD to decimal, ' reverse bit order = least significant bit goes first. ' e.g. dcf(7)=(1,1,1,0,0,0,1,0) = 47 ' // not used in this program // Function rBCD2Dec(a,b) As integer Local As integer i, d=1 Do For i = 0 To 3 rBCD2Dec = rBCD2Dec + DCF(a)*2^i*d If a=b Then Exit Do a=a+1 Next d=d*10 Loop End Function 2019-02-10_233412_mm_dcf77.zip Any comments welcome. Regards Michael ‚My name is Ozymandias, king of kings Look on my works, ye Mighty, and despair!‘ Nothing beside remains. Round the decay Of that colossal wreck, boundless and bare The lone and ... |
||||
| Frank N. Furter Guru Joined: 28/05/2012 Location: GermanyPosts: 1153 |
Hi Michael, thanks a lot!!! My last DCF77 receiver I programmed under PBP over 10 years ago (with the same Conrad module) and it works since then in a roller blind control of my parents. I had long intended to realize a DCF77 clock with a Micromite - you came before me! THANKS! Frank |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |