;-------- ;"The Propeller" mechanically scanned LED clock ; ; Original file mclock.asm ; copyright Bob Blick, all rights reserved February 12, 1997 ; ; modified by F.M. Bemelman / fbemel@euronet.nl, display of seconds added, ; colon between digits will blink every half a second ; ; modified by Victor Tihonov / jvt@iname.com 15-10-1997 ; turned off the blinking double dots ; ; modified by Jon Stanley / January 2006 ; character lookup table for nixie propeller clock ; ;-------- list p=16C84 radix hex include "p16c84.inc" ;-------- ; remember to set blast-time options: OSC=regular xtal, WDT=ON ; timings all based on 4 MHz crystal ;-------- w equ 0 f equ 1 ;-------- ; Start of available RAM. ;-------- cblock 0x0C safe_w ;not really temp, used by interrupt svc safe_s ;not really temp, used by interrupt svc period_count ;incremented each interrupt period_dup ;copy of period_count safe from interrupt period_calc ;stable period after hysteresis calc. period_use ;stable period after hysteresis calc. flags ;b2=int b1=minute b4=edge b5=seconds dot_index ;which column is being displayed digit_index ;which digit is being displayed hours ;in display format, not hex(01-12) minutes ;00 to 59 seconds ;00 to 59 bigtick_dbl ;incremented each interrupt bigtick_hi bigtick_lo sectick_hi sectick_lo keys ;key value scratch ;scratch value tick ;used by delay endc ;-------- ; Start of ROM ;-------- org 0x00 ;Start of code space goto Start ;-------- ; INTERRUPT SERVICE ROUTINE ;-------- org 0x04 ;interrupt vector Intsvc movwf safe_w ;save w swapf STATUS,w ;swap status, w movwf safe_s ;save status(nibble swap, remember) ;-------- ; done saving, now start working ;-------- ; clear watchdog timer to ensure startup clrwdt ; ; increment period count ; incf skip_cnt,f ; btfss STATUS,Z ; goto Bigticker ; movlw 0xfe ;play with this one for width of displayed characters ; movwf skip_cnt incf period_count,f btfsc STATUS,Z ;zero set means overflow decf period_count,f ; 234375 interrupts every minute. Increment the bigtick each time. Bigticker incf bigtick_lo,f btfsc STATUS,Z incf bigtick_hi,f btfsc STATUS,Z incfsz bigtick_dbl,f goto Bigtick_out ;-------- ; here? bigtick has rolled over to zero and one minute has passed. ; reload bigtick and set a flag for the main counter ;-------- movlw 0xFC ;234375 = 0x039387 movwf bigtick_dbl ;0 - 0x039387 = 0xFC6C79 movlw 0x6C movwf bigtick_hi movlw 0x79 movwf bigtick_lo bsf flags,1 ;notify Keep_time Bigtick_out ;-------- now start with the seconds incf sectick_lo,f btfsc STATUS,Z incfsz sectick_hi,f goto Sectick_out ;-------- ; here? sigtick has rolled over to zero and one second has passed. ; reload sectick and set a flag for the main counter ;-------- movlw 0xF0 ;3907 = 0x0F43 movwf sectick_hi ;0 - 0x0F43 = 0xF0BD movlw 0xBD movwf sectick_lo bsf flags,5 ;notify Keep_time Sectick_out ;-------- ; done working, start restoring ;-------- swapf safe_s,w ;fetch status, reswap nibbles movwf STATUS ;restore status swapf safe_w,f ;swap nibbles in preparation swapf safe_w,w ;for the swap restoration of w bcf INTCON,2 ;clear interrupt flag before return retfie ;return from interrupt ;-------- ; CHARACTER LOOKUP TABLE ; ignore high bit. set=LED off, clear=LED on, bit0=bottom LED, bit6=top LED ;-------- Char_tbl addwf PCL,f dt 0x00,0x00,0x01,0x00,0x00 ;"O" dt 0x00,0x00,0x02,0x00,0x00 ;"1" dt 0x00,0x00,0x04,0x00,0x00 ;"2" dt 0x00,0x00,0x08,0x00,0x00 ;"3" dt 0x00,0x00,0x10,0x00,0x00 ;"4" dt 0x00,0x00,0x20,0x00,0x00 ;"5" dt 0x00,0x00,0x40,0x00,0x00 ;"6" dt 0x00,0x00,0x80,0x00,0x00 ;"7" dt 0x00,0x00,0x30,0x00,0x00 ;"8" dt 0x00,0x00,0xC0,0x00,0x00 ;"9" ; dt 0x00,0x00,0x00,0x00,0x00 ;"A" ; dt 0x00,0x00,0x00,0x00,0x00 ;"B" ; dt 0x00,0x00,0x00,0x00,0x00 ;"C" ; dt 0x00,0x00,0x00,0x00,0x00 ;"D" ; dt 0x00,0x00,0x00,0x00,0x00 ;"E" ; dt 0x00,0x00,0x00,0x00,0x00 ;"F" dt 0x00,0x00,0x00,0x00,0x00 ;":" Char_tbl_end ;-------- ; SUBROUTINES STARTING HERE ;-------- ; clear important bits of ram ;-------- Ram_init movlw 0x07 movwf keys movlw 0x12 ;why do clocks always start movwf hours ;at 12:00 ? clrf minutes clrf seconds clrf dot_index clrf digit_index movlw 0xFC ;234375 = 0x039387 movwf bigtick_dbl ;0 - 0x039387 = 0xFC6C79 movlw 0x6C movwf bigtick_hi movlw 0x79 movwf bigtick_lo movlw 0xF0 ;3907 = 0x0F43 movwf sectick_hi ;0 - 0x0F43 = 0xF0BD movlw 0xBD movwf sectick_lo movlw 0x40 movwf period_calc retlw 0 ;-------- ; unused pins I am setting to be outputs ;-------- Port_init movlw 0x00 ;all output tris PORTB ;on port b attached to LEDs movlw b'00010111' ;port a has 5 pins. I need 4 inputs ;b0=minutes, b1=10mins, b2=hours ;b3=unused, b4=rotation index tris PORTA ;on port a retlw 0 ;-------- ; get timer-based interrupts going ;-------- Timer_init bcf INTCON,2 ;clear TMR0 int flag bsf INTCON,7 ;enable global interrupts bsf INTCON,5 ;enable TMR0 int clrf TMR0 ;clear timer clrwdt ;why is this needed? just do it.. movlw b'11011000' ;set up timer. prescaler(bit3)bypassed option ;send w to option. generate warning. clrf TMR0 ;start timer retlw 0 ;-------- ; test for index in rotation and store period in period_dup ;-------- Check_index movf PORTA,w ;get the state of port a xorwf flags,w ;compare with saved state andlw b'00010000' ;only interested in bit 4 btfsc STATUS,Z ;test for edge retlw 0 ;not an edge, same as last xorwf flags,f ;save for next time btfsc flags,4 ;test for falling edge retlw 0 ;must have been a rising edge movf period_count,w ;make a working copy movwf period_dup ;called period dup clrf period_count ;a fresh start for next rotation clrf digit_index ;set to first digit clrf dot_index ;first column ; calculate a period that does not dither or jitter ; period will not be changed unless new period is really different movf period_calc,w subwf period_dup,w ;find difference btfss STATUS,C ;carry flag set means no borrow goto Calc_period_neg ;must be other way sublw 2 ;allowable deviation = 3 btfss STATUS,C ;borrow won't skip incf period_calc ;new value much larger than calc movf period_calc,w movwf period_use bcf STATUS,C ;clear carry before a rotate rrf period_use,f bcf STATUS,C ;clear carry before a rotate rrf period_use,f retlw 0 Calc_period_neg addlw 2 ;allowable deviation = 3 btfss STATUS,C ;carry will skip decf period_calc ;no carry means it must be changed retlw 0 ;-------- ; change LED pattern based on state of digit_index and dot_index ;-------- Display_now movlw 0x05 xorwf dot_index,w ;test for end of digit movlw 0x00 ;pattern for blank column btfsc STATUS,Z goto D_lookup_3 ;it needs a blank bcf STATUS,C ;clear carry before a rotate rlf digit_index,w ;double the index because each addwf PCL,f ;takes two instructions D_10hr swapf hours,w ;D_10hr goto D_lookup ;what a great rush of power D_1hr movf hours,w ;I feel when modifying goto D_lookup ;the program counter D_colon movlw 0x0A ;the semicolon goto D_lookup D_10min swapf minutes,w goto D_lookup D_1min movf minutes,w goto D_lookup D_colon2 movlw 0x0A ;the semicolon goto D_lookup D_10sec swapf seconds,w goto D_lookup D_1sec movf seconds,w goto D_lookup D_nothing retlw 0 D_lookup andlw b'00001111' ;strip off hi bits movwf scratch ;multiply this by 5 for lookup addwf scratch,f ;table base position addwf scratch,f ;is this cheating? addwf scratch,f ;I think not. addwf scratch,f ;I think it is conserving energy! btfss STATUS,Z ;test for zero goto D_lookup_2 ;not a zero movf digit_index,f ;this is just to test/set flag movlw 0x00 ;this makes a blank LED pattern btfsc STATUS,Z ;test if it is 10 hrs digit goto D_lookup_3 ;it's a leading zero D_lookup_2 movf dot_index,w ;get column addwf scratch,w ;add it to digit base call Char_tbl ;get the dot pattern for this column D_lookup_3 movwf PORTB ;send it to the LEDs ; movlw 0x2C ;overhead value sub from period ; subwf period_use,w ;compensate for overhead and set movfw period_use call Delay ;width of digits with this delay incf dot_index,f ;increment to the next column movlw 0x06 ;6 columns is a digit plus space xorwf dot_index,w ;next digit test btfss STATUS,Z retlw 0 ;not a new digit clrf dot_index ;new digit time incf digit_index,f retlw 0 ;Display_now done. ;-------- ; a short delay routine ;-------- Delay movwf tick Delay_loop decfsz tick,f goto Delay_loop ;w is not damaged, so Delay can return ;be recalled without reloading ;-------- ; test for keypress and call time adjust if needed ;-------- Check_keys movf PORTA,w ;get port "a" xorwf keys,w ;compare with previous andlw b'00000111' ;only care about button pins btfsc STATUS,Z ;zero set=no buttons retlw 0 ;return xorwf keys,f ;store key value movlw 0x64 ;a fairly long delay will movwf scratch ;prevent key bounces Key_delay movlw 0xFF call Delay decfsz scratch goto Key_delay btfss keys,2 ;test "minutes" button goto Inc_mins btfss keys,1 ;test "tens" button goto Inc_tens btfss keys,0 ;test "hours" button goto Inc_hours retlw 0 ;must be a glitch. yeah, right! ;-------- ; increment ten minutes ;-------- Inc_tens movlw 0x0A movwf scratch ;scratch has ten Inc_tens_loop call Inc_mins decfsz scratch goto Inc_tens_loop ;another minute added retlw 0 ;-------- ; increment one hour ;-------- Inc_hours movlw 0x12 xorwf hours,w btfsc STATUS,Z goto Inc_hours_12 movlw 0x07 ;this part gets a little sloppy addwf hours,w movlw 0x07 btfss STATUS,DC movlw 1 addwf hours,f retlw 0 Inc_hours_12 movlw 0x01 movwf hours retlw 0 ;-------- ; increment the time based on flags,1 as sent by interrupt routine ; Inc_mins loop also used by time-setting routine ;-------- Keep_time btfss flags,5 ;the minutes flag goto Keep_time2 ;no change in seconds bcf flags,5 ;clear the minutes flag movlw 0x07 ;start incrementing time addwf seconds,f ;add 7 seconds into f movlw 0x06 ;'oops' decrementing time btfsc STATUS,DC ;did adding 7 cause digit carry? goto Keep_time2 ;then test for an hour change subwf seconds,f ;dec 6 seconds into f Keep_time2 btfss flags,1 ;the minutes flag retlw 0 ;not this time bcf flags,1 ;clear the minutes flag clrf seconds ; clear the seconds & seconds timervalues movlw 0xF0 ;3907 = 0x0F43 movwf sectick_hi ;0 - 0x0F43 = 0xF0BD movlw 0xBD movwf sectick_lo Inc_mins movlw 0x07 ;start incrementing time addwf minutes,w ;add 7 minutes into w btfsc STATUS,DC ;did adding 7 cause digit carry? goto Sixty_mins ;then test for an hour change incf minutes ;otherwise add 1 for real retlw 0 ;and go back Sixty_mins movwf minutes ;save the minutes movlw 0x60 ;test for 60 xorwf minutes,w ;are minutes at 60? btfss STATUS,Z retlw 0 ;no? go back clrf minutes ;otherwise zero minutes goto Inc_hours ;and increment hours ;-------- ; End of subroutines ; Program starts here ;-------- Start call Ram_init ;set variables to nice values call Port_init ;set port directions call Timer_init ;start timer based interrupt ;-------- ; Done initializing, start the endless loop. ;-------- ; Circle ;begin the big loop ; detect falling edge on PORTA,4 to determine rotary index ; calculate rotation period and store in period_dup ; compare with working period(period_calc) and adjust if way different call Check_index ; check display state and change if needed call Display_now ; check keyboard and adjust time call Check_keys ; check minute flag and increment time if a minute has passed call Keep_time goto Circle ;you heard the man, get going! end ;-------- ; end of file ;--------