;V-2 ;Compile with Crownhill Proton+ PIC Basic compiler. ;Receives DCF77 data from a standard DCF module and shows date/time on a LCD. ;It is important that the DCF77 reception is optimal. ;For DCF77 info, schematic diagram and control: www.picbasic.nl ; PIC16F628A: +--v--+ ; Pulse for gong E1 <[ ]> One pulse per second ; Pulse for gong E2 <[ ]> Daylight saving, high when it's summer ; Volume level gong <[ ]< Connect LDR to +5V, 1M potmeter to GND ; [ ]< 12:00 (AM/PM) or 24:00 notation ; GND [ ] +5V ; DCF77 module signal >[ ]> DB7 LCD ; Normaly open, for test to GND >[ ]> DB6 LCD ; LCD EN <[ ]> DB5 LCD ; LCD RS <[ ]> DB4 LCD ; +-----+ ;Er is ook een Nederlandse versie / There is a Dutch version too ;www.picbasic.nl / Frits Kieftenbelt, Raalte, Netherlands (Frizie) DEVICE = 16F628A CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ;On-chip RC-oscillator ALL_DIGITAL TRUE ;Normal aliases (constants) SYMBOL CharType = 255 ;Maximal 255: 255 is the ASCII code for a block in the LCD time bar SYMBOL LCD_Char = 16 ;Minimal 16: Number of characters of each line (dependence on LCD device) SYMBOL MaxFindTime = 65 ;Minimal 65: (Sec) Time to find the startsequence (second 00) SYMBOL PulseLength = 740 ;Minimal 500! Maximal 750: (mSec) SYMBOL PulseLengthX= PulseLength / 10 SYMBOL StartUpTime = 45000 ;Maximal 65535: (mSec) 45 seconds startup time for the DCF77 receiver after power-on ;Logical aliases (constants) SYMBOL AAN = 0 ;Reversed ON SYMBOL FALSE = 0 SYMBOL OFF = 0 SYMBOL ON = 1 SYMBOL TRUE = 1 SYMBOL UIT = 1 ;Reversed OFF ;Port aliases SYMBOL DaylightSav = PORTA.0 ;High when it's summer SYMBOL SecondPulse = PORTA.1 ;Short pulse every second (Pulse length depence on constant 'PulseLength', max. 400mSec) SYMBOL Gong1 = PORTA.2 ;Pulses high every whole hour, to steer the gong SYMBOL Gong2 = PORTA.3 ;Pulses high every whole and half hour, to steer the gong SYMBOL Volume = PORTA.4 ;Changing between output (GND) and input (3-state) for regulate the volume level from the gong SYMBOL AM_PM = PORTA.6 ;Connect for 24 hours mode to +5V, for 12 hours AM/PM notation to GND SYMBOL LDR = PORTA.7 ;When it's dark (LDR sensor), sounds the gong softly SYMBOL DCF_Signal = PORTB.0 ;Input for the DCF77 module (receiver) signal (i.e. Conrad BN641138) SYMBOL Test = PORTB.1 ;Input leave normaly open, for adjusting LDR, make low BEFORE startup the PIC, for adjusting gong, make low AFTER startup the PIC ; 76543210 TRISA = %11100000 TRISB = %11111111 PORTB_PULLUPS ON ;Pull-up for DCF77 receiver ;WORD DIM SignalCheck AS WORD DIM Teller AS WORD ;Teller = Counter ;BYTE DIM BitNumber AS BYTE DIM CursorPos AS BYTE ;Cursor x-position on LCD DIM Day AS BYTE DIM GeneralCounter AS BYTE DIM Hour AS BYTE DIM HourDCF AS BYTE DIM HourLCD AS BYTE DIM Minute AS BYTE DIM MinuteDCF AS BYTE DIM Month AS BYTE DIM ReceiveByte AS BYTE DIM Second AS BYTE DIM WeekDay AS BYTE DIM Year AS BYTE DIM BD1 AS BYTE ;Byte Dummy 1 ;BIT DIM Initialize AS BIT ;TRUE when initializing DIM ParityBit AS BIT DIM ParityError AS BIT DIM ReceiveBit AS BIT DIM Signal AS BIT ;TRUE when signal is found DIM ID1 AS BIT ;Bit Dummy 1 PORTA = 0 PORTB = 0 CLEAR DELAYMS 200 ;LCD stabilize CLS PRINT "www.picbasic.nl" DELAYMS 3000 CLS PRINT "DCF16UKD V-2" ;Version number DELAYMS 2000 CLS PRINT "Adjust LDR" WHILE Test = AAN ;If Test = LOW when startup the PIC, the LDR can adjust with the indication from the Daylightsave LED IF LDR = ON THEN DaylightSav = TRUE ELSE DaylightSav = FALSE ENDIF WEND CLS PRINT "Search signal" Initialize = TRUE LOW Volume ;When startup the PIC, the gong volume is loud GOTO MainLoop ;Jump over the subroutines ;SUBROUTINES ReceiveBitNumber: ;Fill the receivebyte with the requested amount of bits in BCD mode CLEAR ReceiveByte FOR BD1 = 0 TO BitNumber - 1 GOSUB ReceiveOneBit IF ReceiveBit = 1 THEN SETBIT ReceiveByte, BD1 NEXT ReceiveByte = ((ReceiveByte >> 4) * 10) + (ReceiveByte & 15) ;BCD (Binary-Coded Decimal) to byte conversion RETURN ReceiveOneBit: ;Receive a bit and store it in ReceiveBit CLEAR Teller WHILE DCF_Signal = AAN DELAYMS 1 INC Teller IF Teller > 3000 THEN ReceiveError WEND DELAYMS 155 ;135 - 175msec ReceiveBit = DCF_Signal IF Initialize = TRUE THEN CursorPos = (Second * LCD_Char) / 58 ;Calculate where the cursor position must be (dependence on waittime and LCD device) PRINT AT 2, CursorPos, CharType ;Make time bar actual ELSE PRINT AT 1, CursorPos, DEC2 Second ;Print the seconds on LCD ParityBit = ParityBit + ReceiveBit ;Bit, thus 0+0=0 / 0+1=1 / 1+1=0 / 1+0=1 On the end you can see if it is even or uneven ENDIF INC Second ;Because we are in this routine every second, we can use it to count seconds SecondPulse = ON ;Outputsignal second pulse DELAYMS 500 SecondPulse = OFF DELAYMS PulseLength - 500 RETURN ;MAINPROGRAM MainLoop: CLEAR GeneralCounter Second = 00 ;Start with first second Signal = FALSE WHILE Signal = FALSE ;Wait while DCF_Signal is high CLEAR Teller WHILE DCF_Signal = UIT INC Teller DELAYMS 1 IF Teller > StartUpTime THEN ReceiveError ;Startup time for the DCF77 receiver by power-on WEND CLEAR Teller WHILE DCF_Signal = AAN ;Start counting time between pulses INC Teller DELAYMS 10 IF Initialize = FALSE THEN SELECT CASE Teller CASE 85 - PulseLengthX ;85 = (1000mSec - 155mSec) / 10 PRINT AT 1, CursorPos, "59";xx:xx:59, Print the missing second on LCD CASE 25 SecondPulse = ON CASE 75 SecondPulse = OFF CASE 250 GOTO ReceiveError ;Signal doesn't change within time ENDSELECT ELSE IF Teller > (StartUpTime / 10) THEN ReceiveError ;Startup time for the DCF77 receiver by power-on ENDIF WEND IF GeneralCounter >= MaxFindTime THEN ReceiveError ;If more then 'MaxFindTime' seconds counted then we have a problem INC GeneralCounter IF Teller > 100 THEN Signal = TRUE ;We count per 10mSec, so we need at least 100x10mSec for finding the startsequence IF Initialize = TRUE THEN CursorPos = (GeneralCounter * LCD_Char) / MaxFindTime ;Calculate where the cursor position must be (dependence from first waittime and LCD type) PRINT AT 1, 1, "Found signal " PRINT AT 2, CursorPos , CharType ;Put block characters on LCD while waiting for initialize signal ENDIF WEND IF Initialize = TRUE THEN CLS PRINT "Initialize..." PRINT AT 2, 1, REP "_" \ LCD_Char ;Draw the waiting time bar length ELSE CursorPos = LCD_Char - 9 ;X-position from time on LCD CURSOR 1, CursorPos IF ParityError = TRUE THEN ;Tijd not 100%, the PIC self calculates the time now for actualising IF Minute < 59 THEN INC Minute ELSE Minute = 00 ;\ IF Hour < 23 THEN ;-Time is synchronized, in spite of parityerror INC Hour ;/ ELSE Hour = 00 ;Actualising the date have no sense, when an error occured then it isn't gonna changed ENDIF ENDIF ELSE ;No error, put the normal DCF time on LCD Minute = MinuteDCF Hour = HourDCF ENDIF IF AM_PM = AAN THEN ;If AM/PM notation is selected then... SELECT CASE Hour CASE 0 :HourLCD = 12 ;If it's 00:xx then put on LCD: 12:xx CASE > 12:HourLCD = Hour - 12 ;If it's between 13:xx and 23:xx then put on LCD: 1:xx / 11:xx END SELECT ELSE ;...else, it's 24 hour notation... HourLCD = Hour ;The hour which comes on LCD is equal to the DCF hour PRINT " " ;Erase AM/PM when switching from 12 hours notation to 24 hours notation ENDIF IF HourLCD < 10 THEN PRINT " " ;Clear leading zero (08:00:00 --> 8:00:00) (Null suppression) PRINT DEC HourLCD, ":", DEC2 Minute, ":00" ;Put actual time on LCD every minute IF AM_PM = AAN THEN ;If AM/PM notation is selected then... IF Hour < 12 THEN ;...if it's AM then... PRINT "am" ;...put AM on LCD next to the time... ELSE ;...anders (afternoon)... PRINT "pm" ;...put PM on LCD next to the time ENDIF CursorPos = LCD_Char - 3 ;Put cursor position already for the seconds, 2 more to the left for AM/PM ELSE CursorPos = LCD_Char - 1 ;Put cursor position already for the seconds ENDIF IF Minute = 00 OR Minute = 30 OR Test = AAN THEN GongSignal ;Every whole and half hour a gong signal (and every minute if Test is enabled) ENDIF IF Signal = TRUE THEN ;Startbit succesfull received, start filling data GOSUB ReceiveOneBit ;0: Bit 0 (reserved (DCF77 protocol)) Further: ;When the gong sounds, bit 0 is ignored, after a gong, it goes further here FOR BD1 = 1 TO 14 ;1...14: first 15 bits are reserved (info see www.picbasic.nl --> DCF77 info) GOSUB ReceiveOneBit NEXT CLEAR ParityBit ;If ParityError is set, then ParityBit is also mutated, thus reset CLEAR ParityError ;Reset PariteitsError (Leave these 2 resets on this place, because when a parityerror happens during initializing) GOSUB ReceiveOneBit ;15: Which send antenna is used? ; IF ReceiveBit = 0 THEN... ;Signal is sended by the standard antenna, this bit is not used in this program ; IF ReceiveBit = 1 THEN... ;Signal is sended by a reserve antenna, this bit is not used in this program GOSUB ReceiveOneBit ;16: Switch summer/winter next hour? ; IF ReceiveBit = 1 THEN... ;This bit is not used in this program GOSUB ReceiveOneBit ;17: Daylight Savings Time? IF ReceiveBit = 1 THEN DaylightSav = ON ;Summer ELSE DaylightSav = OFF ;Winter ENDIF FOR BD1 = 18 TO 19 ;18...19: Skip bits (Time Zone bit 2 and leap-second bit) GOSUB ReceiveOneBit NEXT GOSUB ReceiveOneBit ;20: Startbit, must always be a 1, not used in this program ; IF ReceiveBit = 0 THEN... ;This bit is not used in this program BitNumber = 7 ;21...27: Receive Minute (7 bits) GOSUB ReceiveBitNumber MinuteDCF = ReceiveByte GOSUB ReceiveOneBit ;28: Parity bit 1, minutes IF ParityBit = ON THEN ParityError = TRUE BitNumber = 6 ;29...34: Receive Hour (6 bits) GOSUB ReceiveBitNumber HourDCF = ReceiveByte GOSUB ReceiveOneBit ;35: Parity bit 2, hours IF ParityBit = ON THEN ParityError = TRUE BitNumber = 6 ;36...41: Receive Day (6 bits) GOSUB ReceiveBitNumber Day = ReceiveByte BitNumber = 3 ;42...44: Receive WeekDay (3 bits) GOSUB ReceiveBitNumber WeekDay = ReceiveByte BitNumber = 5 ;45...49: Receive Month (5 bits) GOSUB ReceiveBitNumber Month = ReceiveByte BitNumber = 8 ;50...57: Receive Year (8 bits) GOSUB ReceiveBitNumber Year = ReceiveByte GOSUB ReceiveOneBit ;58: Parity bit 3, date IF ParityBit = ON THEN ParityError = TRUE IF Initialize = TRUE THEN IF ParityError = FALSE THEN ;If there is NO parityerror, then we are... Initialize = FALSE ;...ready with initializing (If there IS a parityerror while initializing, then initialize again) ENDIF CLS ENDIF IF ParityError = FALSE THEN;Only actualise the date when there is no parity error ' PRINT AT 1, 1, REP " " \ 9 ;Erase the day on LCD (Only necessary with full name of the day) PRINT AT 2, 1, REP " " \ LCD_Char ;Erase the whole 2nd line CURSOR 1, 1 SELECT CASE WeekDay CASE 1: PRINT "Mon" CASE 2: PRINT "Tue" CASE 3: PRINT "Wed" CASE 4: PRINT "Thu" CASE 5: PRINT "Fry" CASE 6: PRINT "Sat" CASE 7: PRINT "Sun" ENDSELECT PRINT AT 2, 1, DEC Day, " " SELECT CASE Month CASE 1: PRINT "January" CASE 2: PRINT "February" CASE 3: PRINT "March" CASE 4: PRINT "April" CASE 5: PRINT "May" CASE 6: PRINT "June" CASE 7: PRINT "July" CASE 8: PRINT "August" CASE 9: PRINT "September" CASE 10: PRINT "October" CASE 11: PRINT "November" CASE 12: PRINT "December" END SELECT IF Month = 9 THEN PRINT " '", DEC2 Year ELSE PRINT " 20", DEC2 Year ENDIF ENDIF ELSE ;Else 'Signal' = FALSE, we have no signal found GOTO ReceiveError ENDIF GOTO MainLoop GongSignal: IF LDR = ON THEN ;If there is daylight or the shadedlamps are switched on, then... LOW Volume ;...Volumepin low = Volume from gong louder... ELSE ;...else (is it dark in the living-room)... INPUT Volume ;...Volumepin 3-state = Volume from gong softly ENDIF DELAYMS 155 IF Minute = 00 OR Test = AAN THEN Gong1 = ON ;On the whole hours (and with Test) follows a 3-tone signal Gong2 = ON ;On the half hours only a 2-tone signal SecondPulse = ON ;Ondertussen gaat ook de seconde puls gewoon door DELAYMS 500 Gong1 = OFF Gong2 = OFF SecondPulse = OFF DELAYMS 330 ;985 - 500 - 155 INC Second ; When using the method underneath (with time instead of LDR), then erase the LDR syntax part above ; IF Hour > 5 THEN ;Between 0:30 and 6:30 is the gong volume not loud ; LOW Volume ;Volumepin low = Volume louder ; ELSE ; INPUT Volume ;Volumepin 3-state = Volume softly ; ENDIF GOTO Further ;Go further with receiving the DCF bits from bit 15 (we omit bits 0...14) ReceiveError: CLS PRINT "No signal" ;Startbit not found or no signal? REPEAT CLEAR ;Erase all RAM ID1 = DCF_Signal WHILE DCF_Signal = ID1 ;Wait for signal changing INC SignalCheck DELAYMS 1 WEND ID1 = ID1 ^ 1 WHILE DCF_Signal = ID1 ;Wait for signal changing INC SignalCheck DELAYMS 1 WEND UNTIL SignalCheck < 990 Initialize = TRUE ;Initialize again after the error GOTO MainLoop ;V-2 to V-1: ;With parity check ;PulseLength as long as possible ;SecondPulse is now precise 0,5 sec