; $Id: timer.asm 13 2007-07-23 18:56:18Z kpreid $ include "p16f877.inc" movlwf macro lv, fv movlw lv movwf fv endm ; --------------------------------------------------------------------------- bcd_input equ PORTC ; high 4 bits set_button_port equ PORTB set_button_bit equ d'4' run_button_port equ PORTB run_button_bit equ d'5' bankl_button_port equ PORTB bankl_button_bit equ d'6' bankh_button_port equ PORTB bankh_button_bit equ d'7' speaker_port equ PORTE speaker_tris equ TRISE speaker_bit equ 0 ; register assignments, shared-across-banks range (s_*, intsave_*) intsave_w equ 0x70 intsave_pclath equ 0x71 intsave_status equ 0x72 s_user_bank equ 0x73 s_d_phase equ 0x74 s_tmp_0 equ 0x75 ; register assignments, banks 0-3 (arrays) mode_a equ 0x20 seconds_a equ 0x21 minutes_a equ 0x22 hours_a equ 0x23 ; register assignments, bank 0 20-6f (must not overlap arrays) display_data equ 0x6f ticks equ 0x6e delay_c1 equ 0x6d delay_c2 equ 0x6b d_phase_ticks equ 0x6a input_target equ 0x69 beeping equ 0x68 mode_run_bit equ 0x0 mode_ascend_bit equ 0x1 input_target_count equ d'2' ticks_per_second equ d'240' ticks_per_d_phase equ d'20' d_phase_last equ d'34' ; first phase index is 1, so this is also the count ; --------------------------------------------------------------------------- org 0 goto main org 0x4 goto interrupt org 0x20 main: banksel OPTION_REG bcf OPTION_REG, NOT_RBPU ; enable PORTB pullups banksel TRISB movlwf b'11111111', TRISB ; switch inputs banksel TRISD movlwf b'00000000', TRISD ; LED outputs banksel TRISC movlwf b'11110000', TRISC ; switch inputs, reserved outputs banksel PORTD movlwf b'00000000', PORTD ; switch on all segments initially banksel speaker_port movlwf b'00000000', speaker_port banksel speaker_tris bcf speaker_tris, speaker_bit banksel 0x00 movlwf 0, seconds_a movlwf 0, minutes_a movlwf 0, hours_a movlwf 0, mode_a banksel 0x80 movlwf 0, seconds_a movlwf 0, minutes_a movlwf 0, hours_a movlwf 0, mode_a banksel 0x100 movlwf 0, seconds_a movlwf 0, minutes_a movlwf 0, hours_a movlwf 0, mode_a banksel 0x180 movlwf 0, seconds_a movlwf 0, minutes_a movlwf 0, hours_a movlwf 0, mode_a movlwf 0, s_user_bank movlwf d_phase_last, s_d_phase banksel input_target movlwf 0, input_target banksel beeping movlwf 0, beeping banksel OPTION_REG bcf OPTION_REG, T0CS ; run Timer0 off instruction cycle clock bcf OPTION_REG, PSA ; enable Timer0 prescaler bcf OPTION_REG, PS1 ; 1:64 prescaler banksel INTCON bsf INTCON, T0IE ; enable Timer0 interrupt bsf INTCON, GIE ; again: ; --- action buttons banksel set_button_port btfss set_button_port, set_button_bit call set_button btfss run_button_port, run_button_bit call start_button ; --- bank (timer array) 2-bit selector clrf s_user_bank banksel bankl_button_port btfsc bankl_button_port, bankl_button_bit bsf s_user_bank, RP0 banksel bankh_button_port btfsc bankh_button_port, bankh_button_bit bsf s_user_bank, RP1 ; --- display painting movf s_user_bank, W movwf STATUS ; select bank for current displayed timer movlwf HIGH d_phase_table, PCLATH ; PCLATH is in all banks movf s_d_phase, W d_phase_table: addwf PCL, F ; jump table nop ; value 0 never occurs goto blank goto blank goto blank goto blank goto blank goto digit_ls goto digit_ls goto digit_ls goto blank goto digit_hs goto digit_hs goto digit_hs goto blank goto blank goto digit_lm goto digit_lm goto digit_lm goto blank goto digit_hm goto digit_hm goto digit_hm goto blank goto blank goto digit_lh goto digit_lh goto digit_lh goto blank goto digit_hh goto digit_hh goto digit_hh goto blank goto d_do_which goto d_do_which goto d_do_which ; end of jump table -- size of table is d_phase_last blank: banksel PORTD movlwf 0xFF, PORTD goto again d_do_which: movf s_user_bank, W xorlw 0xFF banksel PORTD movwf PORTD goto again digit_ls: movf seconds_a, W goto digit digit_hs: swapf seconds_a, W goto digit digit_lm: movf minutes_a, W goto digit digit_hm: swapf minutes_a, W goto digit digit_lh: movf hours_a, W goto digit digit_hh: swapf hours_a, W digit: banksel display_data movwf display_data call display xorlw 0xFF banksel PORTD movwf PORTD goto again interrupt: movwf intsave_w ; save W swapf STATUS, W ; save STATUS clrf STATUS movwf intsave_status movf PCLATH, W ; save PCLATH movwf intsave_pclath clrf PCLATH ; only Timer0 interrupt used, so no flag testing ; speaker driving banksel beeping movf beeping, W bz not_beeping banksel ticks movf ticks, W andlw (1 << speaker_bit) banksel speaker_port movwf speaker_port not_beeping: ; step fraction-of-second counter banksel ticks incf ticks, F ; our ticks register serves as another scaler btfss STATUS, Z goto not_tick_roll movlwf 0xFF - (ticks_per_second), ticks ; a second has passed - start adjusting timers ; hm, should we have individual tick-counters for each timer instead? clrf STATUS ; bank 0 step_one_time: ; returns to here for each bank btfss mode_a, mode_run_bit goto done_one_time btfsc mode_a, mode_ascend_bit ; counting up or down goto ascend_one_time ;descend_one_time: call dectime ; counting down movf hours_a, F ; test for reaching 0 bnz done_one_time movf minutes_a, F bnz done_one_time movf seconds_a, F bnz done_one_time bcf mode_a, mode_run_bit ; reached 0: stop timing, banksel beeping movlwf 1, beeping ; start alarm goto done_one_time ascend_one_time: call inctime movf hours_a, W ; test for reaching 99:59:59 xorlw 0x99 bnz done_one_time movf minutes_a, W xorlw 0x59 bnz done_one_time movf seconds_a, W xorlw 0x59 bnz done_one_time btfsc STATUS, Z ; if maximum, bcf mode_a, mode_run_bit ; stop timer ;goto done_one_time done_one_time: btfss STATUS, RP0 ; if we haven't reached bank 3, ... goto step_next_time btfss STATUS, RP1 goto step_next_time goto done_all_times step_next_time: movlw (1 << RP0) ; ... increment bank selector addwf STATUS, F goto step_one_time done_all_times: not_tick_roll: ; display phase ticking banksel d_phase_ticks incf d_phase_ticks, F btfss STATUS, Z goto end_phase_tick_roll movlwf 0xFF - (ticks_per_d_phase), d_phase_ticks decfsz s_d_phase, F goto end_phase_tick_roll movlwf d_phase_last, s_d_phase ; start over ;movf s_user_bank, W ; increment bank selection ;addlw (1 << RP0) ;andlw (1 << RP0) | (1 << RP1) ;movwf s_user_bank end_phase_tick_roll: banksel INTCON bcf INTCON, T0IF ; clear interrupt flag movf intsave_pclath, W movwf PCLATH swapf intsave_status, W movwf STATUS swapf intsave_w, F swapf intsave_w, W retfie set_button: banksel beeping movf beeping, W clrf beeping skpz return movf s_user_bank, W movwf STATUS ; select bank for current selected timer bcf INTCON, GIE ; disable interrupts while we twiddle the value swapf hours_a, W ; left shift hours by digit andlw 0xF0 movwf hours_a swapf minutes_a, W ; move minutes high into hours low andlw 0x0F iorwf hours_a, F swapf minutes_a, W ; left shift minutes andlw 0xF0 movwf minutes_a swapf seconds_a, W ; move seconds high into minutes low andlw 0x0F iorwf minutes_a, F swapf seconds_a, W ; left shift seconds andlw 0xF0 movwf seconds_a banksel bcd_input swapf bcd_input, W ; read digit input, 4 high bits andlw 0xF ; ignore other bits movwf s_tmp_0 ; stash in bank-shared register movf s_user_bank, W ; switch back to timer's bank movwf STATUS movf s_tmp_0, W ; retrieve read digit iorwf seconds_a, F ; put in low digit of seconds bcf mode_a, mode_ascend_bit ; if user-set, then assume descending; ; start button routine checks for 0 ;incf input_target, F ; cycle input target ;movlw input_target_count ;xorwf input_target, W ;btfsc STATUS, Z ; xorwf input_target, F bsf INTCON, GIE ; enable interrupts call delay banksel set_button_port iwait: btfss set_button_port, set_button_bit goto iwait movlwf 0xFF, PORTD call delay return start_button: banksel beeping movf beeping, W clrf beeping skpz return bcf INTCON, GIE ; disable interrupts while we twiddle the mode movf s_user_bank, W movwf STATUS ; select bank for current selected timer movlw (1 << mode_run_bit) xorwf mode_a, F ; toggle run bit btfss mode_a, mode_run_bit ; if now running, test for zero goto start_finish movf seconds_a, F btfss STATUS, Z goto start_finish movf minutes_a, F btfss STATUS, Z goto start_finish movf hours_a, F btfss STATUS, Z goto start_finish bsf mode_a, mode_ascend_bit ; starting at 0, so count ascending start_finish: bsf INTCON, GIE ; enable interrupts call delay ; debounce banksel run_button_port dwait: btfss run_button_port, run_button_bit ; wait for button up goto dwait banksel PORTD movlwf 0xFF, PORTD ; make wait interval visible call delay ; debounce return ;; segment layout: ;; 0 ;; 1 5 ;; 6 ;; 7 4 ;; 2 3 display: banksel display_data btfsc display_data, 3 goto d8_15 ; d0_7: btfsc display_data, 2 goto d4_7 ; d0_3: btfsc display_data, 1 goto d2_3 ; d0_1: btfss display_data, 0 retlw b'10110111' ; 0 retlw b'00110000' ; 1 d2_3: btfss display_data, 0 retlw b'11100101' ; 2 retlw b'01110101' ; 3 d4_7: btfsc display_data, 1 goto d6_7 ; d4_5: btfss display_data, 0 retlw b'01110010' ; 4 retlw b'01010111' ; 5 d6_7: btfss display_data, 0 retlw b'11010111' ; 6 retlw b'00110001' ; 7 d8_15: btfsc display_data, 2 goto d12_15 ; d8_11: btfsc display_data, 1 goto d10_11 ; d8_9: btfss display_data, 0 retlw b'11110111' ; 8 retlw b'01110111' ; 9 d10_11: btfss display_data, 0 retlw b'11110011' ; A retlw b'11010110' ; b d12_15: btfsc display_data, 1 goto d14_15 ; d12_13: btfss display_data, 0 retlw b'10000111' ; C retlw b'11110100' ; d d14_15: btfss display_data, 0 retlw b'11000111' ; E retlw b'11000011' ; F delay: banksel delay_c1 loopb: decfsz delay_c1, F goto loopb decfsz delay_c2, F goto loopb return dectime: movf seconds_a, W ; dec seconds call decbcd movwf seconds_a xorlw 0xF9 ; test for borrow skpz return movlwf 0x59, seconds_a movf minutes_a, W ; dec minutes call decbcd movwf minutes_a xorlw 0xF9 ; test for borrow skpz return movlwf 0x59, minutes_a movf hours_a, W ; dec hours call decbcd movwf hours_a return inctime: movf seconds_a, W ; inc seconds call incbcd movwf seconds_a xorlw 0x60 ; test for carry skpz return movlwf 0, seconds_a movf minutes_a, W ; inc minutes call incbcd movwf minutes_a xorlw 0x60 ; test for carry skpz return movlwf 0, minutes_a movf hours_a, W ; inc hours call incbcd movwf hours_a return incbcd: ; supplied by Ben Jackson addlw d'7' skpdc addlw (d'256'-d'6') return decbcd: addlw (d'256'-d'1') skpdc addlw (d'256'-d'6') return end