|
Forum Index : Microcontroller and PC projects : [MM2]DCF77 Decoder
| Author | Message | ||||
| twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1671 |
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 causality ≠ correlation ≠ coincidence |
||||
| Frank N. Furter Guru Joined: 28/05/2012 Location: GermanyPosts: 983 |
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 2025 |