;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; 8051 NIXIE tube clock - Uses DS1307 for RTC - Set by WWVB Radio ; ; Copyright 2005 TMD Innovations, LLC ; ; Written by Len Bayles ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; TODO ; ; ; If Radio Signal Stays low for to long bail out ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; ; Timer 0 Running Mode 1 ; ; Preload Value 0xEC8A (Xtal = 12 Mhz .01 delay) ; Count goes in R7 for Radio Pulse Delay ; Count goes in R6 For Switch Delay ; ; ; Timer 1 Running Mode 1 as Counter ; ; Preload Value 0xFF/0xFF (Driven as an external counter 1PPS) ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; M12_24 BIT 00H DST BIT 01H RTC_UPDATE BIT 03H SET_PRE BIT 04H SET_SW BIT 05H MODE_PRE BIT 06H MODE_SW BIT 07H LED BIT P3.0 POWER BIT P3.3 ; Radio On/Off SIGNAL BIT P3.4 ; Radio Signal MARKER BIT 07FH ; Radio Marker Bit REG_SET BIT 07EH ; Radio Register Select ZERO_SEC BIT 07DH ; Zero Seconds RADIO_ERROR BIT 07CH ; Set if radio set failed SDA BIT P3.7 ; I2C SCL BIT P3.6 ; I2C DS1307 DATA 0D0H ; DS1307 Slave Address SECONDS DATA 30H MINUTES DATA 31H HOURS DATA 32H TZ DATA 33H STACK EQU 50H ; LOOK HERE TH0_PRELOAD DATA 0ECH TL0_PRELOAD DATA 08AH ORG 0000H SJMP INIT ORG 000BH ; Timer 0 Interrupt CLR EA ; Disable Interupts CLR TR0 ; Stop Timer 0 SJMP TIME0 ; Jump to routine ORG 001BH ; Timer 1 Interrupt PUSH PSW PUSH ACC CLR TR1 ; Stop Timer 1 CLR EA ; Disable Interupts MOV TH1, #0FFH ; Set to 0xFF Causes timer 1 Interupt each pps MOV TL1, #0FFH ; Set to 0xFF Causes timer 1 Interupt each pps MOV A, SECONDS ; Move Seconds to A ADD A, #01H ; Increment Seconds DA A ; BCD Adjust CJNE A, #60H, SEC_DONE ; Seconds at 60? MOV SECONDS, #00H ; Clear Seconds MOV A, MINUTES ; Move Minutes to A ADD A, #01H ; Increment Minutes DA A ; BCD Adjust CJNE A, #60H, MIN_DONE ; Minutes at 60? MOV MINUTES, #00H ; Clear Minutes MOV A, HOURS ; Move Hours to A ADD A, #01H ; Increment Hours DA A ; BCD Adjust CJNE A, #24H, HRS_DONE ; Hours at 24? MOV HOURS, #00H ; Clear Hours SJMP TIME1_DONE SEC_DONE: MOV SECONDS, A ; Copy seconds back from A SJMP TIME1_DONE MIN_DONE: MOV MINUTES, A ; Copy Minutes back from A SJMP TIME1_DONE HRS_DONE: MOV HOURS, A ; Copy Hours back from A TIME1_DONE: INC 36H ; INCrement Radio Set Watch Dog CJNE R5, #00, TIME1_DONE_ALL ; If we're in setup jump around INC INC 37H ; INCrement "out of mode" count TIME1_DONE_ALL: POP ACC POP PSW SETB EA ; Enable Interupts SETB TR1 ; Start Timer 1 RETI TIME0: ; Timer 0 Interrupt Handler INC R7 ; Increment R7 (10ms count) INC R6 ; Increment R6 (Debounce) ;;;;;;; Outputs 100hz on P3.0 ;CPL P3.0 ; Toggle P3.0 Bit for 100 HZ Out NOP NOP ; Nop's used to tune 10ms NOP NOP NOP ;;;;;;; 100 Hz MOV TL0, #TL0_PRELOAD ; Set T0 Low preload MOV TH0, #TH0_PRELOAD ; Set T0 High preload SETB EA ; Enable Interupts SETB TR0 ; Start Timer 0 RETI INIT: MOV SP, #STACK ; Set stack pointer MOV R7, #00H ; Clear Register 7 (10 ms count) MOV R6, #00H ; Clear Register 6 (10 ms debounce count) MOV R5, #00H ; Clear Register 5 (MODE) MOV R4, #00H ; Clear Radio Set mode (1 starts) MOV SECONDS, #00H ; Clear Seconds MOV MINUTES, #00H ; Clear Minutes MOV HOURS, #00H ; Clear Hours MOV 20H, #00H ; Clear Flag Bits MOV TZ, #00H ; Clear TZ MOV TL0, #TL0_PRELOAD ; Set T0 Low preload MOV TH0, #TH0_PRELOAD ; Set T0 High preload MOV TL1, #0FFH ; Set to 0xFF Causes timer 1 Interupt each pps MOV TH1, #0FFH ; Set to 0xFF Causes timer 1 Interupt each pps MOV TMOD, #51H ; Set Timer 0 to Mode 1 ; Set Timer 1 to Mode 1 Counter SETB ET0 ; Enable Timer 0 Interupt SETB ET1 ; Enable Timer 1 Interupt SETB EA ; Enable Interupts SETB TR0 ; Start Timer 0 SETB TR1 ; Start Timer 1 ACALL DS1307_READ ; Read DS1307 to get current setup ACALL DS1307_CKECK_STATE ; Check to see if DS1307 needs to be init MAIN: ACALL DISPLAY ACALL SET_SWITCH ACALL MODE_SWITCH ACALL CHECK_RTC_UPDATE ACALL RADIO_LED ACALL RADIO_SET ACALL SCHEDULE_RADIO SJMP MAIN ;############################################################################## ;# ;# DISPLAY Routines Routines to output time and setup ;# ;############################################################################## DISPLAY: CJNE R5, #00H, SETUP_DISPLAY ; Jump to Setup Mode display RUNING_DISPLAY: MOV P2, SECONDS MOV P0, MINUTES JNB 00, RUNING_DISPLAY_12 ; Is this a 12 or 24 hour format? MOV P1, HOURS ; Display 24 hour format SJMP DISPLAY_END RUNING_DISPLAY_12: MOV A, HOURS ; Put 24 hr value in A for conversion ACALL CONVERT_24_12 ; Call convert function MOV P1, A ; Display the value SJMP DISPLAY_END ;####################################### ; ; Setup Display ; ;####################################### SETUP_DISPLAY: ; Set DP's to show Mode and Setup Values MOV 2EH, R5 ; Copy MODE to Memory 0x2E (Bit addresable) SET_DISPLAY_SEC: ; Toggle High bit for DP Mode display CJNE R5, #03H, SET_DISPLAY_SEC_NOT_TZ ; Are we in TZ set mode? MOV A, TZ ; Get TZ ANL A, #0FH ; Mask out "sign" bit MOV B, A ; Keep Tmp cpy in B ADD A, #06H ; Add 6 for BCD fix JB AC, SET_DISPLAY_SEC_DONE_MODE_BIT ; If AC set then we are done MOV A, B ; Otherwise A didn't need fix SJMP SET_DISPLAY_SEC_DONE_MODE_BIT SET_DISPLAY_SEC_NOT_TZ: CJNE R5, #05H, SET_DISPLAY_NOT_DST ; We need to show DST flag if mod 5 MOV A, #00 ; Clear A JNB DST, SET_DISPLAY_SEC_DONE_MODE_BIT ; DST = 0 jump INC A ; Set to 1 for DST = 1 SJMP SET_DISPLAY_SEC_DONE_MODE_BIT SET_DISPLAY_NOT_DST: MOV A, SECONDS ; Copy Seconds to A SET_DISPLAY_SEC_DONE_MODE_BIT: JNB 70H, SET_DISPLAY_SEC_DONE ; Jump if bit = 0 SETB 0E7H ; Set bit 7 in A to 1 SET_DISPLAY_SEC_DONE: MOV P2, A ; Output Seconds SET_DISPLAY_MIN: ; Toggle Mid bit for DP Mode display CJNE R5, #03H, SET_DISPLAY_MIN_NOT_TZ ; Check for TZ display MOV A, TZ ; Get TZ ANL A, #80H ; Mask out Everything but "sign" bit RL A ; Rotate "sign" bit to bit 0 SJMP SET_DISPLAY_MIN_DONE_MODE_BIT SET_DISPLAY_MIN_NOT_TZ: MOV A, MINUTES ; Copy Minutes to A SET_DISPLAY_MIN_DONE_MODE_BIT: JNB 71H, SET_DISPLAY_MIN_DONE ; Jump if bit = 0 SETB 0E7H ; Set bit 7 in A to 1 SET_DISPLAY_MIN_DONE: MOV P0, A SET_DISPLAY_HR: ; Toggle High bit for DP Mode display CJNE R5, #01H, SET_DISPLAY_HR_NOT_1 ; If we're in mode 1 (set 12/24) ; display setting JB 00, SET_DISPLAY_HR_24 ; If bit is set we're in 24 hr mode MOV A, #12H ; Otherewise show 12 SJMP SET_DISPLAY_HR_C SET_DISPLAY_HR_24: MOV A, #24H ; Put a 24 in A SJMP SET_DISPLAY_HR_C SET_DISPLAY_HR_NOT_1: MOV A, HOURS ; Copy Hours to A SET_DISPLAY_HR_C: JNB 72H, SET_DISPLAY_HR_DONE ; Jump if bit = 0 SETB 0E7H ; Set bit 7 in A to 1 SET_DISPLAY_HR_DONE: MOV P1, A DISPLAY_END: RET ;############################################################################## ; ; CONVERT_24_12 ; Function to convert 24 to 12 hr display ; ;############################################################################## CONVERT_24_12: MOV B, #12H ; Display 12 for Zero hours JZ CONVERT_24_12_BVAL ; If it's Zero Hours we're done MOV B, A ; Keep copy of A CLR C ; Clear Carry flag for subtract SUBB A, #12H ; JC CONVERT_24_12_BVAL ; If Carry then it's 1 to 11 hours JZ CONVERT_24_12 ; If subtract resulted in Zero it's 12pm JNB AC, CONVERT_24_12_DONE ; Check for an Aux carry SUBB A, #06H ; If there was an Aux carry fix BCD SJMP CONVERT_24_12_DONE CONVERT_24_12_BVAL: MOV A, B ; Our display vale, move from B to A CONVERT_24_12_DONE: RET ;############################################################################## ; ; SET_SWITCH Checks Set Switch ; ;############################################################################## SET_SWITCH: JNB P3.1, SET_SWITCH_END ; Jump out if Mode switch pushed JB MODE_PRE, SET_SWITCH_END ; Jump out if Mode switch in debounce JB MODE_SW, SET_SWITCH_END ; Jump out if Mode switch in progress MOV A, R5 ; Copy R5 to A for Mode Check JZ SET_SWITCH_END ; Not in Setup Mode if = 0 JNB P3.2, SET_PUSH ; Set Switch Pushed JNB SET_PRE, SET_UP ; Not In Debounce MOV A, R6 ; Copy Debounce Count to A ADD A, #0FCH ; Add 252 to A Will set carry if 4 or over JNC SET_SWITCH_END ; Jump out and wait, still in Debounce CLR SET_PRE ; Must be noise, clear bit SJMP SET_SWITCH_END SET_UP: CLR SET_SW ; Button up, clear Set bit SJMP SET_SWITCH_END SET_PUSH: JNB SET_SW, SET_DEBOUNCE ; SET_SW Not set, do debounce MOV A, R6 ; Copy Debounce Count to A ADD A, #0CDH ; Add 205 to A Will set carry if 50 or over JNC SET_SWITCH_END ; Jump out and wait MOV R6, #00 ; Zero Out Bounce Timer ACALL DO_SET SJMP SET_SWITCH_END SET_DEBOUNCE: ; Debounce Routine JNB SET_PRE, SET_DEB_START ; If SET_PRE not set Initiliaze Debounce MOV A, R6 ; Copy Debounce Count to A ADD A, #0FDH ; Add 253 to A Will set carry if 3 or over JNC SET_SWITCH_END ; Jump out and wait SET_DEB_DONE: SETB SET_SW CLR SET_PRE MOV R6, #00 ; Zero Out Bounce Timer SETB RTC_UPDATE ; Set flag for RTC Update ACALL DO_SET SJMP SET_SWITCH_END SET_DEB_START: SETB SET_PRE ; Set "Set" Pre Bit (Delay For Bounce) MOV R6, #00 ; Zero Out Bounce Timer SET_SWITCH_END: RET ;############################################################################## ; ; MODE_SWITCH Checks Mode Switch ; ;############################################################################## MODE_SWITCH: JNB P3.1, MODE_PUSH ; Mode Switch Pushed JNB MODE_PRE, MODE_UP ; Not In Debounce MOV A, R6 ; Copy Debounce Count to A ADD A, #0FCH ; Add 252 to A Will set carry if 4 or over JNC MODE_SWITCH_END ; Jump out and wait, still in Debounce CLR MODE_PRE ; Must be noise, clear bit SJMP MODE_SWITCH_END MODE_UP: CLR MODE_SW ; Button up, clear Mode bit SJMP MODE_SWITCH_END MODE_PUSH: JNB MODE_SW, MODE_DEBOUNCE ; MODE_SW Not set, do debounce MOV A, R6 ; Copy Debounce Count to A ADD A, #0CDH ; Add 205 to A Will set carry if 50 or over JNC MODE_SWITCH_END ; Jump out and wait MOV R6, #00 ; Zero Out Bounce Timer INC R5 ; Increment Mode CJNE R5, #07H, MODE_SWITCH_END ; Only allow mode 0 through 6 MOV R5, #00H ; Set mode to 0 SJMP MODE_SWITCH_END MODE_DEBOUNCE: ; Debounce Routine JNB MODE_PRE, MODE_DEB_START ; If MODE_PRE not set Initiliaze Debounce MOV A, R6 ; Copy Debounce Count to A ADD A, #0FDH ; Add 253 to A Will set carry if 3 or over JNC MODE_SWITCH_END ; Jump out and wait MODE_DEB_DONE: SETB MODE_SW CLR MODE_PRE MOV R6, #00 ; Zero Out Bounce Timer MOV 37H, #00H ; Clear count on "end of setup mode" INC R5 ; Increment Mode CJNE R5, #07H, MODE_SWITCH_END ; Only allow mode 0 through 6 MOV R5, #00H ; Set mode to 0 SJMP MODE_SWITCH_END MODE_DEB_START: SETB MODE_PRE ; Set Mode Pre Bit (Delay For Bounce) MOV R6, #00 ; Zero Out Bounce Timer MODE_SWITCH_END: RET ;############################################################################## ; ; DO_SET Looks at Mode and Increments settings ; ; Set Modes ; ; 1. 12/24 ; 2. Hour Set ; 3. Time Zone ; 4. Minute Set ; 5. DST Enable ; 6. Seconds Zero ; ;############################################################################## DO_SET: MOV A, R5 ; Move Mode to A for compare DO_SET_1: CJNE A, #01H, DO_SET_2 ACALL SET_12_24 RET DO_SET_2: CJNE A, #02H, DO_SET_3 ACALL SET_HOUR RET DO_SET_3: CJNE A, #03H, DO_SET_4 ACALL SET_TZ RET DO_SET_4: CJNE A, #04H, DO_SET_5 ACALL SET_MIN RET DO_SET_5: CJNE A, #05H, DO_SET_6 ACALL SET_DST RET DO_SET_6: ACALL SET_SEC RET ;############################################################################## ; ; SET_12_24 Toggle 12/24 bit ; ;############################################################################## SET_12_24: CPL 00 ; Toggle between 0 and 1 for 12/24 display RET ;############################################################################## ; ; SET_HOUR Set Hour ; ;############################################################################## SET_HOUR: MOV A, HOURS ; Move Hours to A ADD A, #01H ; Increment Hours DA A ; BCD Adjust CJNE A, #24H, SET_HOUR_DONE ; Hours at 24? MOV A, #00H ; Clear Hours SET_HOUR_DONE: MOV HOURS, A RET ;############################################################################## ; ; SET_TZ - Bit 7 indicates +/- ; ;############################################################################## SET_TZ: MOV B, TZ ; Store old value in B for "sign" ANL B, #80H ; Keep only "sign" bit MOV A, TZ ; TZ Value to A ANL A, #0FH ; Mask out "sign" bit INC A CJNE A, #0DH, SET_TZ_DONE ; Is 13? MOV A, #00 ; Reset A to 0 CPL B.7 ; Toggle +/- SET_TZ_DONE: ORL A, B ; Or +/- flag back in MOV TZ, A ; Move new value back to TZ RET ;############################################################################## ; ; SET_MIN Set Minutes ; ;############################################################################## SET_MIN: MOV A, MINUTES ; Move Minutes to A ADD A, #01H ; Increment Minutes DA A ; BCD Adjust CJNE A, #60H, SET_MIN_DONE ; Minutes at 60? MOV A, #00H ; Clear Minutes SET_MIN_DONE: MOV MINUTES, A ; Move A to Minutes RET ;############################################################################## ; ; SET_DST ; ;############################################################################## SET_DST: CPL DST ; Toggle between DST 0/1 RET ;############################################################################## ; ; SET_SEC Zero Seconds Count ; ;############################################################################## SET_SEC: MOV SECONDS, #00H RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; CHECK_RTC_UPDATE - See if the RTC needs updating ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CHECK_RTC_UPDATE: JNB RTC_UPDATE, CHECK_RTC_UPDATE_END ; Jump out if Update flag 0 MOV A, 37H ; Seconds out of "setup mode" CLR C ; Clear carry flag for sub SUBB A, #0AH ; Subtract 10 JC CHECK_RTC_UPDATE_END ; Not been 10 seconds yet CLR RTC_UPDATE ; Move Time and config data ro I2C Scratchpad, then update MOV 22H, 30H ; Copy Running Sec to RTC Scratchpad MOV 23H, 31H ; Copy Running Min to RTC Scratchpad MOV 24H, 32H ; Copy Running Hrs to RTC Scratchpad MOV 2CH, 20H ; Copy Config Bts to RTC Scratchpad ANL 2CH, #03H ; Mask out all but DST and 12/24 MOV 2DH, TZ ; Copy Running TZ to RTC Scratchpad ACALL DS1307_WRITE ; Update The RTC CHECK_RTC_UPDATE_END: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; DS1307_CKECK_STATE - Check to see if the DS1307 has been Initilized ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DS1307_CKECK_STATE: MOV A, 2AH ; Signature Byte Should be 0xAA CJNE A, #0AAH, DS1307_SET_STATE MOV A, 2BH ; Signature Byte Should be 0x55 CJNE A, #055H, DS1307_SET_STATE SJMP DS1307_CKECK_STATE_UPDATE DS1307_SET_STATE: MOV 22H, #000H MOV 23H, #000H MOV 24H, #000H MOV 25H, #007H MOV 26H, #001H MOV 27H, #001H MOV 28H, #005H MOV 29H, #010H MOV 2AH, #0AAH MOV 2BH, #055H MOV 2CH, #000H MOV 2DH, #000H ACALL DS1307_WRITE DS1307_CKECK_STATE_UPDATE: MOV 30H, 22H ; Copy RTC Scratchpad Running Sec MOV 31H, 23H ; Copy RTC Scratchpad Running Min MOV 32H, 24H ; Copy RTC Scratchpad Running Hrs MOV A, 2CH ; Copy RTC Scratchpad Running Hrs (CFG bits) ANL A, #03H ; Mask out DST and 12/24 ORL 20H, A ; Or Bits into Running Location MOV TZ, 2DH ; Copy RTC Scratchpad Running TZ RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; DS1307_READ ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DS1307_READ: ; Setup the DS1307's memory ptr to read at address 0 ACALL I2C_START ; Send I2C Start MOV A, #DS1307 ; Move the "slave address" to A ACALL I2C_WRITE ; Write Byte JNZ DS1307_READ_ERROR ; If A = 1 we didn't get an ack MOV A, #00H ; Move 0x00 "memory read address" to A ACALL I2C_WRITE ; Write Byte JNZ DS1307_READ_ERROR ; If A = 1 we didn't get an ack ACALL I2C_STOP ; Send I2C Stop ; Now read the DS1307 were the memory address ptr is set ACALL I2C_START ; Send I2C Start MOV A, #DS1307 ; Move the "slave address" to A SETB ACC.0 ; Set read bit in "slave address" ACALL I2C_WRITE ; Write Byte JNZ DS1307_READ_ERROR ; If A = 1 we didn't get an ack MOV R0, #22H ; Ptr to Memory Address 0x22 MOV R1, #0CH ; We're going to read 12 bytes DS1307_READ_LOOP: ACALL I2C_READ ; Read a byte INC R0 ; Increment our memory ptr DJNZ R1, DS1307_READ_LOOP ; Got all 12 bytes? ACALL I2C_STOP ; Send I2C Stop RET DS1307_READ_ERROR: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; DS1307_WRITE ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DS1307_WRITE: ACALL I2C_START ; Send I2C Start MOV A, #DS1307 ; Move the "slave address" to A ACALL I2C_WRITE ; Write Byte JNZ DS1307_WRITE_ERROR ; If A = 1 we didn't get an ack MOV A, #00H ; Move 0x00 "memory read address" to A ACALL I2C_WRITE ; Write Byte JNZ DS1307_WRITE_ERROR ; If A = 1 we didn't get an ack MOV R0, #22H ; Ptr to Memory Address 0x22 MOV R1, #0CH ; We're going to read 12 bytes DS1307_WRITE_LOOP: MOV A, @R0 ACALL I2C_WRITE ; Write a byte JNZ DS1307_WRITE_ERROR ; If A = 1 we didn't get an ack INC R0 ; Increment our memory ptr DJNZ R1, DS1307_WRITE_LOOP ; Wrote all 12 bytes? ACALL I2C_STOP ; Send I2C Stop RET DS1307_WRITE_ERROR: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_WRITE - Byte in A gets sent (R2 Loop counter) ; ; Return Value in A (0 Good) (1 Fail) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_WRITE: MOV R2, #08H ; We're going to do this for 8 bits I2C_WRITE_LOOP: JB ACC.7, I2C_WRITE_ONE ; If A bit 7 one - set SDA to one CLR SDA ; Otherwise clear bit SJMP I2C_WRITE_NOW I2C_WRITE_ONE: SETB SDA ; It's high I2C_WRITE_NOW: ACALL I2C_DELAY ; Settle delay ACALL I2C_STROBE ; Strobe RL A ; Shift left through byte DJNZ R2, I2C_WRITE_LOOP ; Do 8 times SETB SDA ; Raise SDA for read ACALL I2C_DELAY ; Settle delay ACALL I2C_READ_BIT ; Read Ack from Slave RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_READ ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_READ: SETB SDA ; Set SDA high for Read MOV A, #00H ; Clear A MOV R2, #08H ; Read 8 Bits I2C_READ_LOOP: PUSH ACC ; Save A, return will be in A ACALL I2C_READ_BIT ; Read Bit from Slave JZ I2C_READ_ZERO POP ACC ; Get A back from stack SETB ACC.0 ; Set low bit RL A ; Rotate left SJMP I2C_READ_DONE I2C_READ_ZERO: POP ACC ; Get A back from stack RL A ; Read 0 just rotate I2C_READ_DONE: DJNZ R2, I2C_READ_LOOP ; Read all 8 bits yet? RR A ; Undo last rotate MOV @R0, A ; Put received byte in add pointed to by R0 CJNE R1, #01H, I2C_READ_ACK ; Is this our last byte to read? SETB SDA ; Do a NOT ACK SJMP I2C_READ_ACK_STROBE I2C_READ_ACK: CLR SDA ; Lower SDA for ACK I2C_READ_ACK_STROBE: ACALL I2C_DELAY ACALL I2C_STROBE RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_START ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_START: CLR SDA ; Lower Data Line (SDA) ACALL I2C_DELAY CLR SCL ; Lower Clock Line (SCL) ACALL I2C_DELAY RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_STOP ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_STOP: CLR SDA ; Lower Data Line (SDA) ACALL I2C_DELAY SETB SCL ; Raise Clock Line (SCL) ACALL I2C_DELAY SETB SDA ; Raise Data Line (SDA) ACALL I2C_DELAY RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_STROBE ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_STROBE: SETB SCL ; Raise Clock Line (SCL) ACALL I2C_DELAY CLR SCL ; Lower Clock Line (SCL) ACALL I2C_DELAY RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_READ_BIT ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_READ_BIT: SETB SCL ; Raise Clock Line (SCL) ACALL I2C_DELAY JB SDA, I2C_READ_BIT_ONE ; Test SDA - Jump if one MOV A, #00H ; SDA Low, set A = 0 SJMP I2C_READ_BIT_DONE I2C_READ_BIT_ONE: MOV A, #01H ; SDA high, set A = 1 I2C_READ_BIT_DONE: CLR SCL ; Lower Clock Line (SCL) ACALL I2C_DELAY RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; I2C_DELAY ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I2C_DELAY: NOP RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; RADIO_SET ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RADIO_SET: CJNE R4, #00H, RADIO_MODE_ONE ; Check to see if we need to check radio AJMP RADIO_SET_END ; Nope R4 (Radio Set Mode = 0) RADIO_MODE_ONE: CJNE R4, #01H, RADIO_MODE_RUN ; Check for radio set mode 1 (INIT) CLR MARKER ; Clear Marker bit CLR REG_SET ; Clear to point to first time reg set CLR ZERO_SEC ; Clear Seconds Set CLR RADIO_ERROR ; Clear Las Radio Error MOV R3, #00H ; Clear Sec "tick" counter MOV 1EH, #00H ; Clear TMP data area (Incomming bits go here) MOV 1FH, #10H ; Clear Errors Count MOV 36H, #00H ; Clear Watchdog INC R4 ; Inc to mode 2. RADIO_MODE_RUN: ; CODE_CHECK JB SIGNAL, PULSE_UP ; Is Signal high? AJMP PULSE_CHECK_END ; Signal was low jump out PULSE_UP: MOV 36H, #00H ; Clear Watchdog MOV R7, #00 ; Clear 10 ms count JNB ZERO_SEC, PULSE_LOOP ; If bit is set we Zero the seconds CLR ZERO_SEC ; Clear bit, only do this once per set MOV SECONDS, #00 ; Zero Seconds PULSE_LOOP: MOV A, R7 ; Copy R7 to A ADD A, #0AH ; Allow for dropout PULSE_HIGH: JB SIGNAL, PULSE_LOOP ; Loop till signal drops CJNE A, 07H, PULSE_HIGH ; Allow for signal dropout PULSE_DONE: MOV B, R7 ; Mov pulse count (Pulse length) to B MOV A, B ; Copy Count to A CLR C ; Clear C for Sub SUBB A, #25H ; Anything Under This is an error JC PULSE_ERROR ; It's garbage MOV A, B ; Move Count to A CLR C ; Clear C for Sub SUBB A, #3FH ; Anything Under This is a Zero JC PULSE_ZERO ; It's A Zero MOV A, B ; Move Count to A CLR C ; Clear C for Sub SUBB A, #7FH ; Anything Under This is a One JC PULSE_ONE ; It's A One MOV A, B ; Move Count to A CLR C ; Clear C for Sub SUBB A, #0BFH ; Anything Under This is a Marker JC PULSE_MARKER ; It's A Marker PULSE_ERROR: JB MARKER, PULSE_ERROR_RESYNC ; Haven't sync'd yet or no radio signal DJNZ 1FH, PULSE_CHECK_END_PE ; Haven't Sync'd yet and still taking errs MOV R4, #00H ; Too many errors, bail out of set mode SETB RADIO_ERROR ; Blink the error LED PULSE_CHECK_END_PE: AJMP RADIO_SET_END PULSE_ERROR_RESYNC: CLR MARKER ; Clear Marker and try to resync CLR REG_SET ; Clear to point to first time reg set MOV R3, #00H ; Clear Sec "tick" counter MOV 1EH, #00H ; Clear TMP data area (Incomming bits go here) INC R4 ; We only FIVE times to get the time CJNE R4, #07, PULSE_ERROR_RETRY ; Do we retry? MOV R4, #00 ; We're at mode 7 Clear Mode and stop SETB RADIO_ERROR ; Blink the error LED PULSE_ERROR_RETRY: AJMP RADIO_SET_END PULSE_ZERO: CLR MARKER CJNE R3, #00H, PULSE_ZERO_DO ; Check to see if we are sync'd AJMP RADIO_SET_END ; Nope, jump out PULSE_ZERO_DO: MOV A, 1EH ; Get Radio Data Tmp Byte RL A ; Rotate to next bit position ACALL PROCESS_TMP_BYTE SJMP RADIO_SET_END PULSE_ONE: CLR MARKER CJNE R3, #00H, PULSE_ONE_DO ; Check to see if we are sync'd SJMP RADIO_SET_END ; Nope, jump out PULSE_ONE_DO: MOV A, 1EH ; Get Radio Data Tmp Byte RL A ; Rotate to next bit position SETB ACC.0 ; Toggle the One ACALL PROCESS_TMP_BYTE SJMP RADIO_SET_END PULSE_MARKER: JNB MARKER, PULSE_MARKER_SET CJNE R3, #00, PULSE_MARKER_LOOP_TWO CLR REG_SET ; Clear to point to first time reg set MOV 1EH, #00H ; Clear TMP data area (Incomming bits go here) SJMP PULSE_MARKER_TICK PULSE_MARKER_LOOP_TWO: JB REG_SET, PULSE_MARKER_LOOP_THREE ; If bit set we are done CJNE R3, #60, PULSE_ERROR_RESYNC ; Something bad happend... SETB REG_SET ; Point to second Reg set MOV 1EH, #00H ; Clear TMP data area (Incomming bits go here) MOV R3, #00 ; Clear Click Counter SJMP PULSE_MARKER_TICK PULSE_MARKER_LOOP_THREE: ACALL ADD_MINUTE ; Add minute for Reg 0/1 compare MOV A, 18H ; Compare Minutes CJNE A, 1BH, PULSE_MARKER_DATA_FAIL MOV A, 19H ; Compare Hours CJNE A, 1CH, PULSE_MARKER_DATA_FAIL MOV A, 1AH ; Compare DST Bits CJNE A, 1DH, PULSE_MARKER_DATA_FAIL ACALL ADD_MINUTE ; Add minute to make current ACALL FIX_TZ ; Now we have a got a good time FIX TZ MOV MINUTES, 18H ; Copy Radio Minutes to Running Minutes MOV HOURS, 19H ; Copy Radio Hours to Running Hours SETB RTC_UPDATE ; Set the RTC update bit MOV R4, #00 ; Clear the set mode SJMP RADIO_SET_END PULSE_MARKER_DATA_FAIL: AJMP PULSE_ERROR_RESYNC PULSE_MARKER_SET: SETB MARKER CJNE R3, #00H, PULSE_MARKER_TICK ; If not zero inc the "tick" SJMP PULSE_MARKER_SET_END PULSE_MARKER_TICK: ACALL PROCESS_TMP_BYTE PULSE_MARKER_SET_END: PULSE_CHECK_END: MOV A, 36H ; Check Watchdog Count CLR C SUBB A, #0B4H ; Subtract out 180 (3 minutes) JC RADIO_SET_END ; Watchdog has not times out yet MOV R4, #00 ; We've tried to long with no signal SETB RADIO_ERROR ; Indicate that we have no signal RADIO_SET_END: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; PROCESS_TMP_BYTE - Process the Tmp Radio data byte - In A ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PROCESS_TMP_BYTE: TICK_1: CJNE R3, #01, TICK_2 ; BCD "40" value for Minutes MOV 1EH, A ; Re-store as tmp byte AJMP PROCESS_TMP_BYTE_DONE ; Done TICK_2: CJNE R3, #02, TICK_3 ; BCD "20" value for Minutes MOV 1EH, A ; Re-store as tmp byte AJMP PROCESS_TMP_BYTE_DONE ; Done TICK_3: CJNE R3, #03, TICK_5 ; BCD "10" value for Minutes MOV 1EH, A ; Re-store as tmp byte AJMP PROCESS_TMP_BYTE_DONE ; Done TICK_5: CJNE R3, #05, TICK_6 ; BCD "8" value for Minutes MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_6: CJNE R3, #06, TICK_7 ; BCD "4" value for Minutes MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_7: CJNE R3, #07, TICK_8 ; BCD "2" value for Minutes MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_8: CJNE R3, #08, TICK_12 ; BCD "1" value for Minutes JB REG_SET, TICK_8_REG1 ; Check to see if Reg set 0/1 MOV 18H, A ; Move tmp byte to 0 Radio Min MOV 1EH, #00 ; Clear tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_8_REG1: MOV 1BH, A ; Move tmp byte to 1 Radio Min MOV 1EH, #00 ; Clear tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_12: CJNE R3, #12, TICK_13 ; BCD "20" value for Hours MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_13: CJNE R3, #13, TICK_15 ; BCD "10" value for Hours MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_15: CJNE R3, #15, TICK_16 ; BCD "8" value for Hours MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_16: CJNE R3, #16, TICK_17 ; BCD "4" value for Hours MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_17: CJNE R3, #17, TICK_18 ; BCD "2" value for Hours MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_18: CJNE R3, #18, TICK_57 ; BCD "1" value for Hours JB REG_SET, TICK_18_REG1 ; Check to see if Reg set 0/1 MOV 19H, A ; Move tmp byte to 0 Radio Hours MOV 1EH, #00 ; Clear tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_18_REG1: MOV 1CH, A ; Move tmp byte to 1 Radio Hours MOV 1EH, #00 ; Clear tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_57: CJNE R3, #57, TICK_58 ; First DST bit MOV 1EH, A ; Re-store as tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_58: CJNE R3, #58, TICK_59 ; Second DST bit JB REG_SET, TICK_58_REG1 ; Check to see if Reg set 0/1 MOV 1AH, A ; Move tmp byte 0 DST byte MOV 1EH, #00 ; Clear tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_58_REG1: MOV 1DH, A ; Move tmp byte 1 DST byte MOV 1EH, #00 ; Clear tmp byte SJMP PROCESS_TMP_BYTE_DONE ; Done TICK_59: CJNE R3, #59, PROCESS_TMP_BYTE_DONE ; 59 bit JNB REG_SET, PROCESS_TMP_BYTE_DONE ; Jump if not second set loop SETB ZERO_SEC ; This will zero seconds next marker PROCESS_TMP_BYTE_DONE: INC R3 ; Increment for next bit comming in RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; FIX_TZ ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; FIX_TZ: MOV A, TZ ; Get Time Zone Correction ANL A, #0FH ; Mask out "sign" bit MOV B, A ; Keep Tmp cpy in B ADD A, #06H ; Add 6 for BCD fix JNB AC, TZ_2_BCD ; If AC Not set then we are done MOV B, A ; Otherwise Move BCD Adj to B TZ_2_BCD: ; Exit with TZ in B ; WORK ACALL DST_FIX ; Do DST correction MOV A, TZ ; Look at TZ "sign" JNB ACC.7, MINUS_TS ; Check to see if Add or subtract TZ MOV A, 19H ; Mov Hours to A ADD A, B ; Add TZ correction DA A ; BCD Adj MOV B, A ; Save copy in B CLR C ; Clear Carry for subtract SUBB A, #24H ; Subtract 24 Hrs JC PLUS_UNDERFLO ; Check if over 24 - Not if carry set JNB AC, PLUS_BCD_NOFIX ; AC will tell us if we need a BCD fix CLR C ; Clr C for subtract SUBB A, #06 ; Sub 6 for bcd fix PLUS_BCD_NOFIX: MOV B, A ; Yes, use corrected value PLUS_UNDERFLO: MOV 19H, B ; Value didn't need correction SJMP FIX_TZ_END MINUS_TS: MOV A, 19H ; Put Hours in A CLR C ; Clear C for Subtraction SUBB A, B ; Subtract TZ correction out JNC MINUS_UNDERFLO ; Check for underflow MOV A, 19H ; If there was an underflow ADD A, #24H ; Add 24 Hours (BCD) DA A CLR C ; Clear carry for sub SUBB A, B ; Then sub out the TZ MINUS_UNDERFLO: JNB AC, MINUS_UNDERFLO_NO_BCD CLR C SUBB A, #06H MINUS_UNDERFLO_NO_BCD: MOV 19H, A ; Move Corrected time back to register FIX_TZ_END: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; DST_FIX - Do DST fixup if necessary - Enter with BCD TZ in B ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; WORK DST_FIX: JNB DST, DST_FIX_END ; Check to see if we're configured for DST MOV A, 1AH ; Copy DST radio bits to A JZ DST_FIX_END ; Both bits are Zero, nothing to do CLR C ; Clear C for Subtract SUBB A, #03 ; Check to see if both bits set JC DST_DAY ; If we have a carry today is the DST change DST_DO_FIX: MOV A, B ; Get the TZ fixup JZ DST_FIX_END ; Don't fix if Zero CLR C SUBB A, #01 ; Subtract 1 for DST fix JNB AC, DST_FIX_DONE ; We don't need a BCD fix CLR C SUBB A, #06H ; DO bcd fixup DST_FIX_DONE: MOV B, A ; Put TZ correction back in B SJMP DST_FIX_END ; We're Done DST_DAY: MOV A, HOURS ; We Start doing this correction at 2:00 am lt CLR C SUBB A, #02 ; 2 or greater doesn't carry JC DST_FIX_END ; We're not at 2am yet, bail out MOV A, 1AH ; Copy DST radio bits to A RRC A ; Check bit 0, if it's 1 we're going DST -> ST JC DST_FIX_END ; We don't need to fix SJMP DST_DO_FIX ; Carry was Zero, we're going ST -> DST - FIX DST_FIX_END: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ADD_MINUTE - Adds a minute to the Reg 1 radio time ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ADD_MINUTE: MOV A, 18H ; Get the minutes count ADD A, #01H ; Increment, (should match second val received) DA A ; BCD Adjust CJNE A, #60H, RADIO_INC_MIN_DONE ; Minutes at 60? MOV 18H, #00 ; Clear minutes in reg MOV A, 19H ; Move Hours to A ADD A, #01H ; Increment Hours DA A ; BCD Adjust CJNE A, #24H, RADIO_INC_HRS_DONE ; Hours at 24? MOV 19H, #00H ; Clear Hours SJMP RADIO_INC_DONE RADIO_INC_MIN_DONE: MOV 18H, A ; Move incremented minutes back to reg SJMP RADIO_INC_DONE RADIO_INC_HRS_DONE: MOV 19H, A ; Move incremented hrs back to reg RADIO_INC_DONE: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; SCHEDULE_RADIO - Schedule when to try setting by WWVB ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SCHEDULE_RADIO: CJNE R4, #00H, SET_AGAIN_NO ; If we are in Radio Set mode bail out MOV A, MINUTES SET_AGAIN_00: CJNE A, #00H, SET_AGAIN_30 ; If it's 00 Minutes Try setting MOV R4, #01 SJMP SET_AGAIN_END SET_AGAIN_30: CJNE A, #30H, SET_AGAIN_NO ; If it's 30 Minutes Try setting MOV R4, #01 SJMP SET_AGAIN_END SET_AGAIN_NO: SET_AGAIN_END: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; RADIO_LED ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RADIO_LED: CJNE R4, #00H, RADIO_LED_ON ; If R4 is not zero turn LED on JB RADIO_ERROR, RADIO_LED_ERROR ; Check Radio Error bit RADIO_LED_OFF: CLR LED ; No Error and Radio mode 0, led off SJMP RADIO_LED_DONE RADIO_LED_ERROR: ; There was a error on the last try MOV A, SECONDS ; Get seconds, we alternate LED state RRC A ; Look at the low bit JNC RADIO_LED_OFF ; Second 0, ture off error LED ; Otherwise turn it on RADIO_LED_ON: SETB LED ; Radio mode NOT 0 turn LED on RADIO_LED_DONE: RET END