;******************* ;* sbrclock.asm * ;******************* list p=16F628 radix hex include "p16f628.inc" ;****************************************************** ;* DISCLAIMER ;****************************************************** ;* ;* This software is provided "as is" without any guarantee ;* or statement of fitness for purpose expressed or implied. ;* It is offered as a courtesy to enable others to build a ;* Nixie tube clock described in my web pages at the following ;* URL: ;* ;* http://homepage.ntlworld.com/steven.rougier/Pixie_clock.html ;* ;* The author makes no claims regarding the correctness, ;* quality, efficiency or number of defects in this software. ;* ;* Anyone using this software does so at their own risk ;* and shall not hold the author responsible for any loss, ;* damages or injury incurred directy or indirectly from ;* the use of this software. ;* ;* This software shall not be reproduced without including ;* this DISCLAIMER in its entirety. ;* ;* Steve Rougier 3rd Oct 2003 ;****************************************************** ;************************ ; set config word options ;************************ __CONFIG _BODEN_OFF & _CP_OFF & _DATA_CP_OFF & _PWRTE_ON & _WDT_ON & _LVP_OFF & _MCLRE_OFF & _INTRC_OSC_CLKOUT & _XT_OSC ; timings all based on 4 MHz crystal ;-------- ; these equates may already be in the include file ;-------- w equ 0 f equ 1 ;-------- ; Adjustment values for crystal variation ;-------- ; Normal value = 4098 = 0x1002 = number of interrupts for 1 second ; 0 - 0x001002 = 0xEFFE = NORM_COUNT_MSB, NORM_COUNT_LSB ; leap value = 4164 = 1044 = fine adjustment for "number of interrupts for 1 second" ; 0 - 00108A = EFBC (only the lsbyte needs to be changed = LEAP_COUNT_LSB) NORM_COUNT_MSB equ 0xEF ; see above comments for derivation of these constants NORM_COUNT_LSB equ 0xFE LEAP_COUNT_LSB equ 0xBC ADJUSTMENT equ 0xB6 ; number of seconds between applying fine adjustment, default = 182 N_TONES equ 0x09 ; number of tones (range = 1 to 9) ;****************************** ;** DISPLAY RELATED SETTINGS ** ;****************************** ANODE_DELAY equ 0x06 ; compensate for anode slow switching SCAN_RATE equ 0x25 ; scan keys every nth interrupt ; SCAN_RATE = 6i+1 or 6i-1, where i is an integer FADE_REPS equ 0x03 ; default number of fade repeats FADE_LEVELS equ 0x0D ; default number of brightness levels. ; For FADE_LEVELS = n, ; fade time = (n-1) * 1.46ms for each level * (n-1) levels * FADE_REPS ALARM_BIT equ 0x80 ; portb bit 7 used to drive sounder KEY_BIT equ 0x40 ; key input on portb bit 6 DP_BIT equ 0x10 ; dp enable position for each digit ALARM_ENB_BIT equ 0x08 ; flags bit 3 enable alarm in flags ALARM_MOD_BIT equ 0x10 ; toggle ALARM_MOD bit 4 in flags H_12_BIT equ 0x40 ; toggle H_12 bit 6 in flags INIT_DIG equ 0x20 ; first digit is digit 6 KEY1 equ 3 ; bitmapped keys remapping KEY2 equ 1 KEY3 equ 4 KEY4 equ 2 KEY5 equ 0 KEY6 equ 5 MODE_TIMEOUT equ 0x14 ; number of secs before returning to mode 0 ; mode flag bits MODE0 equ 5 ; Normal MODE1 equ 4 ; Set time MODE2 equ 3 ; Set alarm MODE3 equ 2 ; Adjustment MODE4 equ 1 ; Tone select MODE5 equ 0 ; Display test ;UNUSED6 equ 6 ; Not used ;UNUSED7 equ 7 ; Not used ;flags bit assignments ROLLOVER equ 0 ; flags bit 0 to carry rollovers SECS equ 1 ; flags bit 1 for seconds ALARM equ 2 ; flags bit 2 to turn on alarm ALARM_ENB equ 3 ; flags bit 3 enable alarm ALARM_MOD equ 4 ; modulate alarm tone SNOOZE equ 5 ; snooze active H_12 equ 6 ; 12 hour mode on ;SPARE_7 equ 7 ; ;********************************************* ;* Start of available RAM. 80 bytes on bank 1 ;********************************************* cblock 0x20 ; 80 bytes of RAM w_temp ; used by interrupt svc status_temp ; used by interrupt svc fg_temp ; general temp for isr (foreground) bg_temp1 ; general temp for background bg_temp2 ; general temp for background flags ; bitmapped flag bit mode ; current mode flags mode_timer ; return to mode 0 if key not pressed for a while isr_ctr_hi ; count interrupts for 1 second msbyte isr_ctr_lo ; count interrupts for 1 second lsbyte leap_ctr ; account for fractional Hz in isr adjust ; fine adjustment of time keeping porta_shad ; port a shadow portb_shad ; port b shadow scan_ctr ; count calls for key scan blank_mask ; turn off bitmapped digits dig_ctr ; digit counter digit_1 ; next digit data digit_2 ; next digit data digit_3 ; next digit data digit_4 ; next digit data digit_5 ; next digit data digit_6 ; next digit data digit_new1 ; new digit data digit_new2 ; new digit data digit_new3 ; new digit data digit_new4 ; new digit data digit_new5 ; new digit data digit_new6 ; new digit data digit_old1 ; previous digit data digit_old2 ; previous digit data digit_old3 ; previous digit data digit_old4 ; previous digit data digit_old5 ; previous digit data digit_old6 ; previous digit data digit_work1 ; output digit data digit_work2 ; output digit data digit_work3 ; output digit data digit_work4 ; output digit data digit_work5 ; output digit data digit_work6 ; output digit data digit_n ; temp digit store time_1 ; time digit 1 (secs) time_2 ; time digit 2 (secs x 10) time_3 ; time digit 3 (mins) time_4 ; time digit 4 (mins x 10) time_5 ; time digit 5 (hours) time_6 ; time digit 6 (hours x 10) alarm_3 ; alarm digit 3 alarm_4 ; alarm digit 4 alarm_5 ; alarm digit 5 alarm_6 ; alarm digit 6 adj_1 ; adjust digit 1 adj_2 ; adjust digit 2 adj_3 ; adjust digit 3 keys_pressed ; bit-map of keys pressed old_keys ; last pass value of keys_pressed tone ; alarm sound pattern tone_work ; fg tone burst counter tone_ctr ; fg tone burst loop counter snooze_mins ; number of minutes left snz_delay ; number of mins fade_repeats ; current fade reps value fade_repwrk ; working fade reps value fade_level ; current fade rate value fade_counter ; adjust pwm for digit fade. pwm_counter ; duty cycle value endc ; ;*************** ;* Start of ROM ;*************** org 0x00 ;RESET VECTOR goto Start ; ;********************************** ;* INTERRUPT SERVICE ROUTINE ;********************************** org 0x04 ;interrupt vector Intsvc movwf w_temp ;save context - w register swapf STATUS,w ;get status (can't use movf, it would change status) movwf status_temp ;save status (nibbles are swapped) ;****************************************************** ; Context is now saved, start the service routine code (foreground) ;****************************************************** clrwdt ; clear watchdog timer ;************************************************* ;* Interrupt occurs each time TMR0 wraps FF->00 ;* TRM0 source = CLKOUT = osc/4 = 1MHz ;************************************************* ;********************************************** ; NB: DO NOT MODIFY CODE ABOVE WITHOUT ; CHECKING THE FOLLOWING LINE ;********************************************** movlw 0x16 ; TMR0 = 0x16 - Make timer wrap every 244 ticks movwf TMR0 ; Note - tmr0 updates inhibited for 2 ; cycles after writing ; 1MHz/244 = 4098.36066 interrupts every second. ; This frequency is chosen as the alarm sounder resonates at 2050Hz so ; toggling the output pin every isr call provides a simple drive signal call Alarm_output ; Drive sounder call Check_1sec ; Count interrupts call Refresh ; update the display and keys pressed Continue ;********************************** ; End of ISR, restore context ;********************************** swapf status_temp,w ;get status, swap nibbles back movwf STATUS ;restore status swapf w_temp,f ;swap nibbles ready for restore swapf w_temp,w ;restore w, use swap to preserve STATUS bcf INTCON,2 ;clear interrupt flag and retfie ;return from interrupt ;######################## ;# START OF SUBROUTINES # ;######################## ;******************** ;* initialise ram * ;******************** Ram_init movlw 0xFF ; first second is a short one movwf isr_ctr_hi movlw ADJUSTMENT movwf adjust ; Set default adjust value movlw SCAN_RATE movwf scan_ctr ; initialise display refresh movlw INIT_DIG movwf dig_ctr clrf digit_1 ; init display all digs 0, all dp's off clrf digit_2 ; init display clrf digit_3 ; init display clrf digit_4 ; init display clrf digit_5 ; init display clrf digit_6 ; init display clrf time_1 ; init time clrf time_2 clrf time_3 clrf time_4 clrf time_5 clrf time_6 clrf alarm_3 ; init alarm time clrf alarm_4 clrf alarm_5 clrf alarm_6 bcf flags,ALARM ; turn off alarm bcf flags,ALARM_ENB ; turn off alarm enable bcf flags,H_12 ; turn off 12 hour mode clrf mode bsf mode,MODE0 ; mode = normal call Update_dps ; set dp's movlw 0x01 movwf tone ; default alarm sound movwf tone_work ; initialise tone register movlw 0x09 movwf snz_delay ; default number of mins for snooze movlw FADE_LEVELS movwf fade_level ; fade_level = default movlw FADE_REPS movwf fade_repeats ; default return ; ;********************************************* ;* Set up the i/o ports ;********************************************* Port_init clrf PORTA movlw 0x07 ; turn off analogue mode on PORTA movwf CMCON bcf STATUS,RP1 bsf STATUS,RP0 ; select bank1 movlw 0x00 ; set all PORTA lines as outputs movwf TRISA ; RA5 unused, RA6,7 set up as osc pins movlw KEY_BIT ; set PORTB bit 6 as input movwf TRISB ; all other PORTB lines as outputs bcf STATUS,RP1 bcf STATUS,RP0 ; back to bank0 clrf porta_shad ; shadow for port a clrf portb_shad ; shadow for port b return ; ;******************************** ; setup timer-based interrupts ;******************************** 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 ;reset watchdog bcf STATUS,RP1 bsf STATUS,RP0 ; select bank1 movlw b'11011000' ;set up timer, prescaler (bit3) bypassed movwf OPTION_REG ;send to option register bcf STATUS,RP1 bcf STATUS,RP0 ; back to bank0 clrf TMR0 ;start timer return ;********************************************** ;* update the alarm output if required ;* called from isr ;********************************************** Alarm_output ; Update alarm output btfss flags,ALARM_ENB ; if alarm enabled and goto Tone_exit btfss flags,ALARM ; if alarm is requested goto Tone_exit decfsz tone_ctr,f ; every n loops, dec tone_work goto Tone_test movlw 0x80 ; n = 128 movwf tone_ctr decfsz tone_work,f ; measure tone burst goto Tone_test movlw ALARM_MOD_BIT xorwf flags,f ; toggle ALARM_MOD bit movf tone,w ; set up for next delay addwf tone,w ; w = tone * 2 addwf tone,w ; w = tone * 3 movwf tone_work ; store temp decf tone_work,f ; = ( tone * 3 ) - 1 decf tone_work,f ; = ( tone * 3 ) - 2 (range 1 - 25) Tone_test btfss flags,ALARM_MOD ; if alarm modulate on goto Tone_exit movf portb_shad,w ; toggle the alarm out bit xorlw ALARM_BIT movwf portb_shad movwf PORTB Tone_exit return ;********************************************** ;* Count isr calls for 1 second ;* called from isr ;********************************************** Check_1sec ; Increment the isr_ctr every isr, normally rollover at 4098 isr calls for 4.000MHz incf isr_ctr_lo,f btfsc STATUS,Z incfsz isr_ctr_hi,f goto Check_1sec_exit ; isr_ctr has rolled over to zero and one second has passed. ; Re-initialise isr_ctr and set a flag for the background tasks. bsf flags,SECS ; set flag every 1 second movlw NORM_COUNT_MSB ; movwf isr_ctr_hi movlw NORM_COUNT_LSB movwf isr_ctr_lo decfsz leap_ctr,f ; count 1 second ticks until goto Check_1sec_exit ; it is time for fine adjustment movlw LEAP_COUNT_LSB movwf isr_ctr_lo ; 0 - 00108A = EFBC (only the lsbyte needs to be changed) movf adjust,w ; movwf leap_ctr ; adjust until clock accurate Check_1sec_exit return ;********************************************** ;* PWM's the changing digits to achieve ;* a clean cross-fade effect ;********************************************** Cross_fade movf fade_repeats,f ; if fade_repeats = 0 btfsc STATUS,Z ; then don't do fade goto No_pwm ; see if data has changed movf digit_new1,w ; get current digit subwf digit_1,w ; compare with next digit btfss STATUS,Z ; skip if not changed goto Update_digs ; else check next digit movf digit_new2,w ; get current digit subwf digit_2,w ; compare with next digit btfss STATUS,Z ; skip if not changed goto Update_digs ; else check next digit movf digit_new3,w ; get current digit subwf digit_3,w ; compare with next digit btfss STATUS,Z ; skip if not changed goto Update_digs ; else check next digit movf digit_new4,w ; get current digit subwf digit_4,w ; compare with next digit btfss STATUS,Z ; skip if not changed goto Update_digs ; else check next digit movf digit_new5,w ; get current digit subwf digit_5,w ; compare with next digit btfss STATUS,Z ; skip if not changed goto Update_digs ; else check next digit movf digit_new6,w ; get current digit subwf digit_6,w ; compare with next digit btfsc STATUS,Z ; skip if changed goto Do_pwm Update_digs movf digit_new1,w ; get current digit movwf digit_old1 ; save current as old movf digit_1,w ; get next digit movwf digit_new1 ; save as new digit movf digit_new2,w ; get current digit movwf digit_old2 ; save current as old movf digit_2,w ; get next digit movwf digit_new2 ; save as new digit movf digit_new3,w ; get current digit movwf digit_old3 ; save current as old movf digit_3,w ; get next digit movwf digit_new3 ; save as new digit movf digit_new4,w ; get current digit movwf digit_old4 ; save current as old movf digit_4,w ; get next digit movwf digit_new4 ; save as new digit movf digit_new5,w ; get current digit movwf digit_old5 ; save current as old movf digit_5,w ; get next digit movwf digit_new5 ; save as new digit movf digit_new6,w ; get current digit movwf digit_old6 ; save current as old movf digit_6,w ; get next digit movwf digit_new6 ; save as new digit movf fade_level,w ; initiate cross-fade movwf fade_counter movwf pwm_counter movf fade_repeats,w movwf fade_repwrk Do_pwm ; update count and adjust duty cycle decfsz pwm_counter,f ; goto Pwm_not_zero movf fade_level,w ; reset pwm movwf pwm_counter decfsz fade_repwrk,f goto Pwm_not_zero movf fade_repeats,w ;reset repeats movwf fade_repwrk decfsz fade_counter,w ; ramp the duty down to 0 movwf fade_counter ; update file register if > 0 Pwm_not_zero ; if fade > pwm then use old ; else use new movf fade_counter,w subwf pwm_counter,w btfss STATUS,C ; c = 0 means f-w is negative (w>f) goto Use_old Use_new ; if w>f ( pwm > fade ) movf digit_new1,w ; get new digit movwf digit_work1 ; save as work digit movf digit_new2,w ; get new digit movwf digit_work2 ; save as work digit movf digit_new3,w ; get new digit movwf digit_work3 ; save as work digit movf digit_new4,w ; get new digit movwf digit_work4 ; save as work digit movf digit_new5,w ; get new digit movwf digit_work5 ; save as work digit movf digit_new6,w ; get new digit movwf digit_work6 ; save as work digit goto Pwm_exit Use_old ; else (pwm <= fade) movf digit_old1,w ; get old digit movwf digit_work1 ; save as work digit movf digit_old2,w ; get old digit movwf digit_work2 ; save as work digit movf digit_old3,w ; get old digit movwf digit_work3 ; save as work digit movf digit_old4,w ; get old digit movwf digit_work4 ; save as work digit movf digit_old5,w ; get old digit movwf digit_work5 ; save as work digit movf digit_old6,w ; get old digit movwf digit_work6 ; save as work digit goto Pwm_exit No_pwm movf digit_1,w ; get current digit movwf digit_work1 ; save as work digit movf digit_2,w ; get current digit movwf digit_work2 ; save as work digit movf digit_3,w ; get current digit movwf digit_work3 ; save as work digit movf digit_4,w ; get current digit movwf digit_work4 ; save as work digit movf digit_5,w ; get current digit movwf digit_work5 ; save as work digit movf digit_6,w ; get current digit movwf digit_work6 ; save as work digit Pwm_exit return ;********************************************** ;* update the display digits and read the keys ;* called from isr ;********************************************** Refresh ; turn off anode drive movf portb_shad,w ; get current port b andlw ALARM_BIT ; mask off all except alarm bit movwf portb_shad ; write to portb shadow movwf PORTB ; write to portb movlw ANODE_DELAY ; wait for drivers to respond call fg_delay ; (anode drivers turn off slowly) movlw 0x08 ; send out the work digits movwf fg_temp ; get ready for test for > 8 bcf STATUS,C ; clear carry (shift in 0) rrf dig_ctr,w ; select next digit btfsc STATUS,C ; if last digit done movlw INIT_DIG ; reset to initial digit movwf dig_ctr ; select digit to turn on btfsc dig_ctr,0 ; load value for selected digit movf digit_work1,w ; select digit 1 value btfsc dig_ctr,1 movf digit_work2,w ; select digit 2 value btfsc dig_ctr,2 movf digit_work3,w ; select digit 3 value btfsc dig_ctr,3 movf digit_work4,w ; select digit 4 value btfsc dig_ctr,4 movf digit_work5,w ; select digit 5 value btfsc dig_ctr,5 movf digit_work6,w ; select digit 6 value subwf fg_temp,f ; preserve w, check for 9 btfss STATUS,DC ; if lower nibble of digit = 9 addlw 0x03 ; then make it 0xXC (preserve dp) movwf porta_shad movwf PORTA ; update to new digit data movf portb_shad,w ; get current port b andlw ALARM_BIT ; mask off all except alarm bit iorwf dig_ctr,w ; add in digit select data movwf portb_shad ; write to portb shadow movwf fg_temp ; write to portb temp shadow movf blank_mask,w ; prepare for blanking andwf dig_ctr,w ; if current digit should be blanked btfsc STATUS,Z ; then update shadow goto Not_blank movf portb_shad,w ; get current port b andlw ALARM_BIT ; mask off all except alarm bit movwf portb_shad ; write to portb shadow Not_blank decfsz scan_ctr,f ; time for a key scan yet? goto Check_keys_exit ; movlw SCAN_RATE ; if ctr = 0 then scan keys movwf scan_ctr ; and reset ctr movf fg_temp,w ; snapshot the key input on portb bit6 movwf PORTB ; turn on digit scan line nop ; stabilise movf PORTB,w ; get coherent port b movwf fg_temp ; and save it movf portb_shad,w ; then blank the digit if reqd movwf PORTB ; blanked digit was on for 5us movf fg_temp,w ;read keys andlw KEY_BIT ;mask off all except key input btfsc STATUS,Z ;check if key pressed goto Not_pressed movf fg_temp,w ;Pressed: read active output andlw 0x3F ;select the scanned lines only iorwf keys_pressed,f ;record key pressed in bitmap goto Check_keys_exit ;and exit Not_pressed movf fg_temp,w ;Not pressed: get portb image xorlw 0xFF ;negate it andlw 0x3F ;get just the scan lines andwf keys_pressed,f ;turn off key in bitmap Check_keys_exit movf portb_shad,w ; Update port b every pass movwf PORTB ; btfsc dig_ctr,5 ; have we done digit 6 yet? call Cross_fade ; update fade after 6th digit return ;******** local subroutine fg_delay movwf fg_temp fg_delay_loop decfsz fg_temp,f goto fg_delay_loop return ; ;************************************ ;* set mode depending on keys pressed ;************************************ Set_mode movf keys_pressed,w ;get key bitmap xorwf old_keys,w ;compare with previous value btfsc STATUS,Z ;exit if nothing changed goto Mode_exit clrf mode_timer ;reset timer every time a key changes state andwf keys_pressed,w ;select just the bit(s) that changed movwf old_keys btfsc old_keys,KEY1 call Pressed1 btfsc old_keys,KEY2 call Pressed2 btfsc old_keys,KEY3 call Pressed3 btfsc old_keys,KEY4 call Pressed4 btfsc old_keys,KEY5 call Pressed5 btfsc old_keys,KEY6 call Pressed6 movf keys_pressed,w movwf old_keys ;save for next pass Mode_exit return Pressed1 bcf STATUS,C ; shift in 0 rrf mode,f ; next mode btfsc STATUS,C ; if carry set then reset mode goto Mode_reset call Update_dps goto Mode1_exit Mode_reset clrf mode bsf mode,MODE0 ; mode = normal call Update_dps Mode1_exit return Pressed2 btfsc mode,MODE0 call K2mode0 btfsc mode,MODE1 call K2mode1 btfsc mode,MODE2 call K2mode2 btfsc mode,MODE3 call K2mode3 btfsc mode,MODE4 call K2mode4 btfsc mode,MODE5 call K2mode5 return Pressed3 btfsc mode,MODE0 call K3mode0 btfsc mode,MODE1 call K3mode1 btfsc mode,MODE2 call K3mode2 btfsc mode,MODE3 call K3mode3 btfsc mode,MODE4 call K3mode4 btfsc mode,MODE5 call K3mode5 return Pressed4 btfsc mode,MODE0 call K4mode0 btfsc mode,MODE1 call K4mode1 btfsc mode,MODE2 call K4mode2 btfsc mode,MODE3 call K4mode3 btfsc mode,MODE4 call K4mode4 btfsc mode,MODE5 call K4mode5 return Pressed5 btfsc mode,MODE0 call K5mode0 btfsc mode,MODE1 call K5mode1 btfsc mode,MODE2 call K5mode2 btfsc mode,MODE3 call K5mode3 btfsc mode,MODE4 call K5mode4 btfsc mode,MODE5 call K5mode5 return Pressed6 btfsc mode,MODE0 call K6mode0 btfsc mode,MODE1 call K6mode1 btfsc mode,MODE2 call K6mode2 btfsc mode,MODE3 call K6mode3 btfsc mode,MODE4 call K6mode4 btfsc mode,MODE5 call K6mode5 return K2mode0 ;Alarm on/off bcf flags,ALARM ;turn off alarm movlw ALARM_ENB_BIT ;toggle alarm enable xorwf flags,f movlw DP_BIT ;toggle dp status xorwf digit_6,f bcf flags,SNOOZE ;turn off snooze return K2mode1 ;hours adjust, set time call Add_hours return K2mode2 ;hours adjust, set alarm movf alarm_5,w ; add 1 hour using call Incdig10 ; base 10 increment hours x 1 movwf alarm_5 btfss flags,ROLLOVER ; carry required? goto Check_24b movf alarm_6,w ; and add it using call Incdig6 ; base 6 increment hours x 10 movwf alarm_6 Check_24b ;now wrap hours 23->00 movf alarm_6,w sublw 0x02 ; btfss STATUS,Z ;exit if dig 6 is not 2 goto No_carry_b ; movf alarm_5,w sublw 0x04 btfss STATUS,Z ;exit if dig 5 is not 4 goto No_carry_b clrf alarm_5 ;else reset to 00 hours clrf alarm_6 No_carry_b return K3mode1 ;mins adjust, set time call Add_mins return K3mode2 ;mins adjust, set alarm movf alarm_3,w ; add 1 min using call Incdig10 ; base 10 increment (Mins) movwf alarm_3 btfss flags,ROLLOVER ; carry required? goto No_carry_a movf alarm_4,w ; and add it using call Incdig6 ; base 6 increment (10xMins) movwf alarm_4 No_carry_a return K5mode4 ;change fade rate incf fade_repeats,f ; movf fade_repeats,w sublw 0x09 btfsc STATUS,C ; if carry is clear, result is negative (w>9) return clrf fade_repeats return K3mode0 ;stop alarm sounding K5mode0 ;stop alarm sounding K6mode0 ;stop alarm sounding bcf flags,SNOOZE ;turn off snooze bcf flags,ALARM ;stop alarm sounding, leave enabled return K4mode0 ;snooze btfss flags,ALARM ;if alarm not sounding goto K4mode0_exit ;then exit bcf flags,ALARM ;else, turn off alarm bsf flags,SNOOZE ;turn on snooze movf snz_delay,w ;init delay movwf snooze_mins K4mode0_exit return K4mode1 ;not used here K4mode2 ;not used here K4mode3 ;not used here K4mode4 ;not used here return K5mode1 ;reset secs clrf time_1 clrf time_2 return K6mode1 ;exit to mode 0 K6mode2 ;exit to mode 0 K6mode3 ;exit to mode 0 K6mode4 ;exit to mode 0 K6mode5 ;exit to mode 0 bcf flags,ALARM ;stop alarm sounding, leave enabled clrf mode bsf mode,MODE0 ; mode = normal call Update_dps ;update dp's return K5mode2 ;set snooze delay movf snz_delay,w ; add 1 min using call Incdig10 ; base 10 increment (Mins) movwf snz_delay return K2mode3 ;timekeeping adjust - decf adjust,f ;make clock run slower (wrap okay) return K3mode3 ;timekeeping adjust + incf adjust,f ;clock runs faster (wrap okay) return K5mode3 ;toggle 12/24 hour mode movlw H_12_BIT xorwf flags,f return K2mode4 ;prev alarm tone bsf flags,ALARM_ENB ; enable alarm movlw DP_BIT ;update dp status iorwf digit_6,f bsf flags,ALARM ; turn on alarm decfsz tone,f ; if tone = 0 goto K2mode4_exit movlw N_TONES movwf tone ;then tone = max K2mode4_exit return K3mode4 ;next alarm tone bsf flags,ALARM_ENB ; enable alarm movlw DP_BIT ;update dp status iorwf digit_6,f bsf flags,ALARM ; turn on alarm incf tone,f ; tone = tone + 1 movf tone,w ; if tone <= max sublw N_TONES ; then exit btfsc STATUS,C ; if w>max then reset to 1 goto K3mode4_exit clrf tone ;else incf tone,f ;tone = 1 K3mode4_exit return K2mode5 ;not used yet K3mode5 ;not used yet K4mode5 ;not used yet K5mode5 ;not used yet return ;*************************************** ;* Update dp status based on mode value ;*************************************** Update_dps movlw 0xEF ;select dp bit andwf digit_1,f ;and turn it off andwf digit_2,f ;and turn it off andwf digit_3,f ;and turn it off andwf digit_4,f ;and turn it off andwf digit_5,f ;and turn it off movlw DP_BIT ;select dp bit btfsc mode,MODE1 iorwf digit_5,f ;and turn it on btfsc mode,MODE2 iorwf digit_4,f ;and turn it on btfsc mode,MODE3 iorwf digit_3,f ;and turn it on btfsc mode,MODE4 iorwf digit_2,f ;and turn it on btfsc mode,MODE5 iorwf digit_1,f ;and turn it on return ; ;************************************ ;* copy required data to display ;* depending on mode ;************************************ Update_display btfsc mode,MODE0 call Clock_mode btfsc mode,MODE1 call Clock_mode btfsc mode,MODE2 call Alarm_mode btfsc mode,MODE3 call Adjust_mode btfsc mode,MODE4 call Tone_mode btfsc mode,MODE5 call Display_test return Clock_mode ; copy time to display (dp=xx0000) movf digit_1,w ; get current digit data andlw 0xF0 ; wipe out current number, preserve dp iorwf time_1,w ; add in new number movwf digit_1 ; send it out movf digit_2,w ; andlw 0xF0 ; iorwf time_2,w movwf digit_2 movf digit_3,w ; andlw 0xF0 ; iorwf time_3,w movwf digit_3 movf digit_4,w ; andlw 0xF0 ; iorwf time_4,w movwf digit_4 ;Convert to 12 hour if required movf time_5,w ; load default hour values (24 hour) movwf bg_temp1 movf time_6,w movwf bg_temp2 btfss mode,MODE0 goto Update_hours ; if in clock mode btfss flags,H_12 goto Update_hours ; and in 12 hour mode movf time_6,f btfsc STATUS,Z ; and tens_of_hours != 0 goto May_add12 btfsc time_6,1 ; and tens_of_hours = 2 goto Sub12 ; or ( tens_of_hours = 1 movf time_5,w ; and hours > 2 ) sublw 0x02 btfsc STATUS,C ; c = 0 means 2-w is negative (w>2) goto Update_hours Sub12 ; then subtract 12 decf bg_temp1,f decf bg_temp1,f ; -2 btfss bg_temp1,7 ; if result negative goto Not_neg movf bg_temp1,w ; then borrow 10 addlw 0x0a movwf bg_temp1 decf bg_temp2,f ; perform borrow Not_neg decf bg_temp2,f ; -1 goto Update_hours May_add12 movf time_5,f ; test for 00 btfss STATUS,Z goto Update_hours ;if hours = 00 then add 12 incf bg_temp1,f incf bg_temp1,f ;+2 incf bg_temp2,f ;+1 Update_hours movf digit_5,w ; andlw 0xF0 ; iorwf bg_temp1,w movwf digit_5 movf digit_6,w ; andlw 0xF0 ; iorwf bg_temp2,w movwf digit_6 ;if digit 6 is 0 and dp is off then blank digit 6 movf digit_6,f ; test for 0, dp off btfss STATUS,Z goto Clock_mode_1 goto Clock_mode_2 Clock_mode_1 clrf blank_mask ; enable all digits Clock_mode_2 return Alarm_mode ; copy alarm time to display (dp = x01000) movlw 0x02 movwf blank_mask ; enable all digits except digit 2 movf digit_1,w ; get current digit data andlw 0xF0 ; wipe out current number, preserve dp iorwf snz_delay,w ; add in new number movwf digit_1 ; send it out movf digit_2,w ; andlw 0xF0 ; movwf digit_2 movf digit_3,w ; andlw 0xF0 ; iorwf alarm_3,w ; add in new number movwf digit_3 movf digit_4,w ; andlw 0xF0 ; iorwf alarm_4,w movwf digit_4 movf digit_5,w ; andlw 0xF0 ; iorwf alarm_5,w movwf digit_5 movf digit_6,w ; andlw 0xF0 ; iorwf alarm_6,w movwf digit_6 return Adjust_mode ; (dp = x00100) movlw 0x08 movwf blank_mask ; blank digit 4 clrf adj_2 ;unpack adjust into adj_1 - adj_3 clrf adj_3 movf adjust,w ;get value movwf adj_1 movlw 0x64 ;load 100, keep in w Hundreds incf adj_3,f ;count hundreds subwf adj_1,f ;subtract 100 until negative btfsc STATUS,C goto Hundreds addwf adj_1,f ;make it positive again decf adj_3,f ;adj_1 is in range 0 -> 99 movlw 0x0A ;load 10, keep in w Tens incf adj_2,f ;count tens subwf adj_1,f ;subtract 10 until negative btfsc STATUS,C goto Tens addwf adj_1,f ;make it positive again decf adj_2,f ;adj_1 is in range 0 -> 9 movf digit_1,w ; get current digit data andlw 0xF0 ; wipe out current number, preserve dp iorwf adj_1,w ; add in new number movwf digit_1 ; send it out movf digit_2,w ; andlw 0xF0 ; iorwf adj_2,w ; add in new number movwf digit_2 movf digit_3,w ; andlw 0xF0 ; iorwf adj_3,w ; add in new number movwf digit_3 movf digit_4,w ; andlw 0xF0 ; wipe out current number, preserve dp movwf digit_4 movlw 0x02 btfss flags,H_12 ; display 2 or 4 (for 12 or 24 hours) movlw 0x04 movwf bg_temp1 movf digit_5,w ; andlw 0xF0 ; wipe out current number, preserve dp iorwf bg_temp1,w movwf digit_5 movlw 0x01 btfss flags,H_12 ; display 1 or 2 (for 12 or 24 hours) movlw 0x02 movwf bg_temp1 movf digit_6,w ; andlw 0xF0 ; wipe out current number, preserve dp iorwf bg_temp1,w movwf digit_6 return Tone_mode ; (dp = x00010) movlw 0x24 ; 00100100 movwf blank_mask ; blank digits digits 6 and 3 movf digit_1,w ; get current digit data andlw 0xF0 ; wipe out current number, preserve dp iorwf tone,w ; add in new number movwf digit_1 ; send it out movf digit_2,w ; andlw 0xF0 ; movwf digit_2 movf digit_3,w ; andlw 0xF0 ; movwf digit_3 movf digit_4,w ; andlw 0xF0 ; wipe out current number, preserve dp iorwf fade_repeats,w ; add in new number movwf digit_4 movf digit_5,w ; andlw 0xF0 ; wipe out current number, preserve dp movwf digit_5 movf digit_6,w ; andlw 0xF0 ; wipe out current number, preserve dp movwf digit_6 return Display_test ; (dp = x00001) clrf blank_mask ; enable all digits movf digit_1,w ; get current digit data andlw 0xF0 ; wipe out current number, preserve dp movwf digit_1 ; send it out movf digit_2,w ; andlw 0xF0 ; movwf digit_2 movf digit_3,w ; andlw 0xF0 ; movwf digit_3 movf digit_4,w ; andlw 0xF0 ; wipe out current number, preserve dp movwf digit_4 movf digit_5,w ; andlw 0xF0 ; wipe out current number, preserve dp movwf digit_5 movf digit_6,w ; andlw 0xF0 ; wipe out current number, preserve dp movwf digit_6 return ;************************************* ;* Sound alarm if time matches ;************************************* Check_alarm movf time_1,f ; get time (seconds must be 0) btfss STATUS,Z ; exit if not 0 goto Check_alarm_exit movf time_2,f ; get time (seconds must be 0) btfss STATUS,Z ; exit if not 0 goto Check_alarm_exit movf time_3,w ; get time subwf alarm_3,w ; compare with alarm btfss STATUS,Z ; turn off if values do not match goto Check_alarm_off movf time_4,w ; get time subwf alarm_4,w ; compare with alarm btfss STATUS,Z ; turn off if values do not match goto Check_alarm_off movf time_5,w ; get time subwf alarm_5,w ; compare with alarm btfss STATUS,Z ; turn off if values do not match goto Check_alarm_off movf time_6,w ; get time subwf alarm_6,w ; compare with alarm btfss STATUS,Z ; turn off if values do not match goto Check_alarm_off bsf flags,ALARM ; all match - turn on alarm goto Check_alarm_exit Check_alarm_off bcf flags,ALARM ; turn off alarm after 1 minute Check_alarm_exit return ;************************************* ;* Sound alarm if delay expired ;************************************* Check_snooze btfss flags,SNOOZE ;exit if snooze not active goto Check_snooze_exit decfsz snooze_mins,f ;count down, check for 0 goto Check_snooze_exit bsf flags,ALARM ;times up - turn on alarm bcf flags,SNOOZE ;and cancel snooze Check_snooze_exit return ; ;******************************************** ;* Reset mode if keys not pressed for a while ;******************************************** Check_mode_timeout incf mode_timer,f ;add 1 second movlw MODE_TIMEOUT subwf mode_timer,w btfss STATUS,C ;C=0 if W>F goto Mode_timeout_exit ;if timeout expired clrf mode bsf mode,MODE0 ; mode = normal call Update_dps ; set dp's Mode_timeout_exit return ; ;************************************ ;* Add 1 second to time when flag set ;************************************ Keep_time btfss flags,SECS ;check the seconds flag goto Keep_time_exit ;exit if clear bcf flags,SECS ;else clear the flag call Check_mode_timeout ; return to mode 0 after a while movf time_1,w ; add 1 to seconds using call Incdig10 ; base 10 increment (Secs) movwf time_1 btfss flags,ROLLOVER ; carry required? goto No_carry movf time_2,w ; and add it using call Incdig6 ; base 6 increment (10xSecs) movwf time_2 btfss flags,ROLLOVER ; carry required? goto No_carry call Check_snooze ; check every min: delay expired? Add_mins movf time_3,w ; and add it using call Incdig10 ;base 10 increment (Mins) movwf time_3 btfss flags,ROLLOVER ; carry required? goto No_carry movf time_4,w ; and add it using call Incdig6 ;base 6 increment (10xMins) movwf time_4 btfss flags,ROLLOVER ; carry required? goto No_carry Add_hours movf time_5,w ; and add it using call Incdig10 ;base 10 increment (Hours) movwf time_5 btfss flags,ROLLOVER ; carry required? goto Check_24 movf time_6,w ; and add it using call Incdig10 ;base 10 increment (10xHours) movwf time_6 Check_24 ;now wrap hours 23.59.59->00.00.00 movf time_6,w sublw 0x02 btfss STATUS,Z ;exit if dig 6 is not 2 goto No_carry movf time_5,w sublw 0x04 btfss STATUS,Z ;exit if dig 5 is not 4 goto No_carry clrf time_5 ;else reset to 00 hours clrf time_6 No_carry call Check_alarm ;check every sec: does alarm = time? Keep_time_exit return ; ;*************************** ;* Add 1, base 10 ;*************************** Incdig10 bcf flags,ROLLOVER ; clear carry flag movwf digit_n incf digit_n,w movwf digit_n sublw 0x09 ; btfsc STATUS,C ; if carry is clear, result is negative (w>9) goto Ndig10 movlw 0 bsf flags,ROLLOVER ; indicate rollover 9->0 goto Incdig10_exit Ndig10 movf digit_n,w Incdig10_exit return ;*************************** ;* Add 1, base 6 ;*************************** Incdig6 bcf flags,ROLLOVER ; clear carry flag movwf digit_n incf digit_n,w movwf digit_n sublw 0x05 btfsc STATUS,C ; if carry is clear, result is negative (w>5) goto Ndig6 movlw 0 bsf flags,ROLLOVER ; indicate rollover 5->0 goto Incdig6_exit Ndig6 movf digit_n,w Incdig6_exit return ;-------- ; End of subroutines ; Program starts here ;-------- Start call Ram_init ;set variables to initial values call Port_init ;set up i/o ports call Timer_init ;start timer based interrupt ;-------- ; Done initializing, start the endless loop. ;-------- ; Main_loop ;start of main loop ; call Set_mode ;set mode based on keys pressed call Keep_time ;update time every sec call Update_display ;copy required data to display vars goto Main_loop end ;-------- ; end of file ;--------