' ' Nixie Clock using Model 01 rev. B CPU/IO card and Nixie daughterboard. ' Will display YY/MM/DD or HH:MM:SS on the six Nixie displays. ' Various global modes (eg. 12/24 hour display; date order) are ' determined by power-up switch settings and remembered in EEPROM. ' AM/PM and blinking colon are supported on revision A board. ' ' mru 19 Apr 1999 ' Clear colon and PM when displaying date. Increased speed of ' BUTTON delays. ' ' mru 14 Apr 1999 ' Fixed handling of zero'd seconds when in SET mode. ' Date display doesn't seem to go crazy any more (cause?!) ' Made 24 hr. mode change date display to DD MM YY. ' Added AM/PM support (requires rev. A) ' Added blinking colon (requires rev. A) ' Determined power on/off clock data damage due to HV interference. ' Improved power on self-test. ' ' Switches: all are ground-asserted, with pullups on the board. ' ' RUN/SET Ground to enter SET mode. Display stops. ' TIME/DATE Ground to display (set) date, else time. ' INC H/M, M/D, Y/S Ground to increment; hold down for rapid. ' ' POWER-ON MODE SETS: ' Hold indicated switch down when powering up. ' ' HOUR/YEAR: Toggles 24/12 hour time display; when ' in 12 hour mode, date displays as ' MM DD YY; in 24 hr. mode it displays as ' DD MM YY. ' ' SEC/DAY: Toggles blink/no blink of colon. ' ' ' Tom Jennings ' written 17 July 98 ' ' Copyright Tom Jennings, 1999. ' ' BUGS: ' ' 29 July 98: still seems to be a problem wrecking RTC settings on ' power up or down. Works much better when RC filter installed on PA4. ' symbol BDELAY= 30 ' loop delay til autorepeat (button()) symbol BREPT= 4 ' loop delay between autorepeat symbol LPAUSE= 40 ' loop delay, mS ' e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e ' ' Bit flags are all in B0, which is stored in EEPROM, and reloaded ' on power up. Some values are global, eg. 12/24 hour display mode. Some ' are local, eg. TIME/DATE switch pressed. The colon and AM/PM indicators ' states are stored here as well. Most flags are bit-position-dependent, ' used for programming convenience. ' symbol flags= b0 ' reserved for bit flags ' ' flags dmode tmode ' 0= 0 0 time, 12hr mode, HH MM SS ' 1= 0 1 time, 24hr mode, HH MM SS ' 2= 1 0 date, MM DD YY ' 3= 1 1 date, DD MM YY ' symbol DISPBITS= 3 ' mask display mode bits symbol tmode= bit0 ' (0..1) GLOBAL 1 == 24 hour mode symbol dmode= bit1 ' (0..1) LOCAL 1 == display date else time ' symbol cblink= bit2 ' (0..1) LOCAL 1 == disable colon blink ' 'colon, pm must be MS, 2nd MS bits; they're ORed into SRA output directly. symbol HWBITS= 192 ' mask hardware bits symbol pm= bit6 ' (0..1) LOCAL PM else AM symbol colon= bit7 ' (0..1) LOCAL 1 == colons lit ' ' e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e symbol unused1= b1 symbol i= b2 symbol n= b3 symbol k= b4 symbol j= b5 symbol l= b6 symbol bvar1= b12 ' temp for button command symbol bvar2= b13 ' temp for button command symbol bvar3= b14 ' temp for button command ' ' Time & date data as used by the RTC routines. See the getclock() and setclock() ' code for data handling details (eg. packing/unpacking of BCD for the compiler ' RTC routines). These always contain the last-read RTC values. ' symbol year= b15 symbol month= b16 symbol day= b17 symbol daywk= b18 symbol hour= b19 symbol minute= b20 symbol second= b21 ' ' Pin assignments (eg. 3 = pin3). Very annoying that the compiler doesn't ' syntactically allow 'pin 3' or 'pin SD'. Inputs are pulled up, and are ' logically ON when pulled to ground. ' 'symbol SW_SET= SET switch is on PA4 ' (0..1) 0 == RUN/SET in SET position symbol SW_DATE= pin7 ' (0..1) 0 == TIME/DATE in DATE position symbol SW_HY= pin6 ' (0..1) 0 == INCREMENT HOUR(YEAR) switch pressed symbol BIT_HY= 6 ' number of the HOUR(YEAR) pin (aargh) symbol SW_MM= pin5 ' (0..1) 0 == INCREMENT MINS(MONTH) switch pressed symbol BIT_MM= 5 ' number of the MIN(MONTH) pin symbol SW_SD= pin4 ' (0..1) 0 == INCREMENT SEC(DAY) switch pressed symbol BIT_SD= 4 ' number of the SEC(DAY) pin ' ' Data is written to the shift registers by asserting data on the SD pin, and clocking ' the appropriate SR clock pin. 8 bits per shift register, MS bit output first. ' symbol SDpin= pin0 ' pin number, Serial Data to shift reg symbol SD= 0 ' Serial Data pin number symbol SRC= 3 ' SR C clock (SS, YY) symbol SRB= 2 ' SR B clock (MM, DD) symbol SRA= 1 ' SR A clock (HH, MO) ' ' Startup code starts here. - - - - - - - - - - - - - - - - - - - - - - - - - ' main: pause 1000 ' let power settle, preserve RTC! dirs= %00001111 ' MS bits inputs, rest outputs. pins= 0 ' deassert device selects peek 133, n : n= n | 16 : poke 5, n ' make PA4 an input year= 55 : month= 7 : day= 31 ' my birthday! (in case DATE pressed) flags= 1 ' force 24 hr, colon blink modes, hour= 0 for i= 0 to 99 step 11 ' test Nixies, minute= i : second= minute gosub display pause 150 next i for hour= 0 to 23 pm= hour / 12 ' test PM too, gosub display pause 100 next hour pause 700 ' ' Load global settings from EEPROM; if certain switches are pressed at power up, ' change global settings. ' read 0, flags ' read global settings from EEPROM if SW_HY = 1 then pu1 ' if INCR HOUR(YEAR) pressed, tmode= tmode + 1 ' toggle 12/24 hour mode, write 0, flags ' update EEPROM pu1: if SW_SD = 1 then pu2 ' if SECOND(DAY) pressed, cblink= cblink + 1 ' toggle colon blink, write 0, flags pu2: puz: ' End of startup, beginning of runtime. - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' ' The main loop here is the basic clock function. TIME/DATE determines which three ' bytes are displayed. RUN/SWITCH must be in the SET switch for a long time before ' we enter SET mode; this not only debounces the switch, it prevents spurious writes ' on power down where the switch pullups go down before CPU power. ' a1: gosub getclock ' read RTC data, unpack and copy, gosub display ' display current values, pause 200 ' don't hit RTC too often gosub setsw ' check SET switch, if n <> 0 then a1 ' loop if not. pause 200 ' long delay if SET detected bvar1= 0 : bvar2= 0 : bvar3= 0 ' clear SET function switch vars ' ' Check to see if SET is pressed; this is a big debounce for the first time ' through the loop, and the loop terminator. ' a2: gosub setsw if n <> 0 then a1 ' branch if SET switch not pressed. gosub display ' display new or current values, ' ' Set mode is pretty simple; pressing HOUR(YEAR), MINUTE(MONTH) ' or SECOND(DAY) increments the time/date field; "SECONDS" clears seconds ' to 0. NOTE: any clock set function sets seconds to zero! ' button BIT_HY, 0, BDELAY, BREPT, bvar1, 0, m2 ' if INCREMENT HR/YEAR pressed, if dmode = 0 then m1a ' and DATE mode, year= year + 1 // 100 : goto mw ' increment YEAR (0..99) m1a: hour= hour + 1 // 24 : goto mw ' else increment HOUR (0 - 23) m2: button BIT_MM, 0, BDELAY, BREPT, bvar2, 0, m3 ' if INCREMENT MIN/MON pressed, if dmode = 0 then m2a month= month // 12 + 1 : goto mw ' increment MONTH (1..12) m2a: minute= minute + 1 // 60 : goto mw ' increment MINUTE (0..59) m3: button BIT_SD, 0, BDELAY, BREPT, bvar3, 0, mw ' if INCREMENT SEC/DAY pressed, if dmode = 0 then mw ' increment DAY (1..N) lookup month, (0,31,28,31,30,31,30,31,31,30,31,30,31), n ' find N i= year // 4 + n ' will be 28 iff leap year and Feb. if i > 28 then m3a n= 29 m3a: day= day // n + 1 mw: second= 0 ' make display reflect reality, gosub setclock ' write new time or date, mz: pause LPAUSE ' make loop execute time more uniform, goto a2 ' repeat; we're just a stupid clock. ' ' Return 0 in N if SET switch is pressed. ' setsw: peek 5, n : n= n & 16 : return ' ' Write time or date to the six Nixie displays. ' Uses J, K, modifies flags. ' display: dmode= SW_DATE + 1 ' read TIME/DATE switch, k= hour ' preload for time display, colon= 0 : pm= 0 ' leave clear for date display j= flags & DISPBITS ' clear display mode bits, branch j, (d0, d1, d2, d3) ' see 'flags' comment. ' 12hr. time, AM, PM. ' ---------- AM ------------- ------------- PM (+128) ----------------------- d0: k= hour / 12 : pm= k ' PM on when hour > 12 lookup hour, (12,1,2,3,4,5,6,7,8,9,10,11, 12,1,2,3,4,5,6,7,8,9,10,11), k ' 12 or 24 hr. time (AM/PM pre-set, above). d1: colon= second + 1 | cblink ' colons ON on even seconds, gosub toBCD : call outA ' HH k= minute : gosub toBCD : call outB ' MM k= second : gosub toBCD : call outC ' SS return ' Date, MM DD YY. d2: k= month : gosub toBCD : call outA ' MM k= day : gosub toBCD : call outB ' DD k= year : gosub toBCD : call outC ' YY return ' Date, DD MM YY. d3: k= day : gosub toBCD : call outA ' DD k= month : gosub toBCD : call outB ' MM k= year : gosub toBCD : call outC ' YY dz: return ' ' Convert canonical value in K (0 - 99 max) to packed BCD. ' toBCD: j= k // 10 : k= k / 10 * 16 + j : return ' ' These two routines use an ugly peek/poke hack for compactness and speed. They ' originally came from MicroMint documentation; the numeric offsets are magic. ' ' Both use private variables J, K, L. ' ' Read the RTC, convert packed BCD to canonical, and copy HHMMSS or YYMMDD ' into the working data, as specified by the TIME/DATE switch. ' getclock: call clockget ' read the RTC gc1: for j= 27 to 33 ' Bxx register number peek j, k ' read packed-hex l= k / 16 * 10 : k= k & $0f : k= k + l poke j, k next j return ' ' Convert canonical time & date to packed BCD; write to the RTC; ' convert back to canonical. (Leaves the data consistently usable ' to other routines at little processing cost). ' setclock: for j= 27 to 33 ' Bxx register number peek j, k ' read packed-hex l= k / 10 * 16 : k= k // 10 : k= k + l poke j, k next j call clockset ' write to RTC, goto gc1 ' unpack again END 'BASIC asm ; ; Output all 8 bits of K, and the colon and PM bit, to SR A. Modifies J, K. ; _outA movlw 8 movwf _J ; set bit counter, movf _FLAGS, w ; get colon, AM/PM bits, andlw _HWBITS ; strip clean, iorwf _K, f ; OR in BCD bits, _oa1 bcf portB, _SD ; set data bit= 0 rlf _K, f ; move MSB to carry, btfsc status, 0 ; if carry set (MSB == 1) bsf portB, _SD ; assert a 1, bsf portB, _SRA ; clock data bit out, bcf portB, _SRA decfsz _J, f ; repeat. goto _oa1 goto done ; ; Output all 8 bits of K to SR B. Modifies J, K. ; _outB movlw 8 movwf _J ; set bit counter, _ob1 bcf portB, _SD ; set data bit= 0 rlf _K, f ; move MSB to carry, btfsc status, 0 ; if carry set (MSB == 1) bsf portB, _SD ; assert a 1, bsf portB, _SRB ; clock data bit out, bcf portB, _SRB decfsz _J, f ; repeat. goto _ob1 goto done ; ; Output all 8 bits of K to SR C. Modifies J, K. ; _outC movlw 8 movwf _J ; set bit counter, _oc1 bcf portB, _SD ; set data bit= 0 rlf _K, f ; move MSB to carry, btfsc status, 0 ; if carry set (MSB == 1) bsf portB, _SD ; assert a 1, bsf portB, _SRC ; clock data bit out, bcf portB, _SRC decfsz _J, f ; repeat. goto _oc1 goto done endasm