; ; General notes about Assembler compatibility ; ------------------------------------------- ; ;Since there are a number of different development systems floating around, ;and since they all have slightly different syntax requirements, please refer to these notes ;if you have problems. The syntax used in this program is compatible with the development system ;that I custom wrote to run on my Macintosh computer. So, I can guarantee that you will need to ;make a few changes before this will assemble on another system. ; ;Following are general notes. They won't all necessarily apply to this particular application. ; ;The 'ds' define storage instruction reserves the specified number of bytes of memory. If your ;asssembler does not have this instruction, then you will need to manually specify the address of ;each variable using the 'equ' instruction. ; ;The 'data' instruction specifies literal data to be stored in the specified location. Numeric data is ;truncated as necessary to fit a single memory location. String data will use as many memory locations ;as necessary to store all the data. ; ;Literal numbers are interpreted as being in decimal unless they begin with '0x' or end with 'h'. ;So, 0x23 and 23h are hexadecimal numbers, while 23 is a decimal number. All literal numbers must ;begin with a numeral 0..9 to distinguish them from variable names. So, 0xff and 0ffh are correct, ;but ffh is not correct. ;Binary numbers can be specifed by using a 'b' or 'B' at the end. Eg, 01001101b is a valid number. ;The assembler allows arithmetic expressions wherever an operand is required. ;The following operators are valid: ; ;Bitwise logical: and, or, not, xor ;Integer arithmetic: +, -, /, *, ^, mod ('^' is exponentiation) ;Comparison: <, >, <=, >=, =, <> ;Parentheses may be used to control order of execution. ; ; ;Case is ignored in all labels and instructions. So, 'HELLO', 'hello' and 'Hello' ;are all equivalent. ; ;The instructions: 'ramspace', 'codespace', 'romspace' and 'configspace' set the program counter ;to the correct starting values to locate the subsequent program in the proper memory locations. ;You will need to replace these with the appropriate 'org' instructions. ; ; A few nonstandard shorthand instructions are recognized to help avoid some often needed ; but confusing bit instructions: ; The instruction 'bank0' is equivalent to 'bcf status,RP0' or 'bcf status,5' ; The instruction 'bank1' is equivalent to 'bsf status,RP0' or 'bsf status,5' ; The instructions 'skpzer' and 'skpeq' are equivalent to 'btfss status,z' or 'btfss status,2' ; The instructions 'skpnzr' and 'skpneq' are equivalent to 'btfsc status,z' or 'btfsc status,2' ; ; ;; ; ******************************************** ;******************************************** ; ; Nixie Frequency Counter ; ; 2008-01-15: ; 2008-12-28: No change to program; just corrected comments ; ; I/O pin assignment ; 1 - Vdd ; 2 - External clock in ; 3 - GP4 output hardwired to GP2, see note below ; 4 - GP3 input for config pushbutton ; 5 - GP2 Serial data out to display (when in output mode) ; - T0CKI frequency count in (when in timer mode) ; 6 - GP1 Serial out strobe to display driver ; 7 - GP0 Serial out clock to display driver ; ; Signal Input to I/O pin shared with data out ; gp2 and gp4 (pins 3 & 5) are wired together so that GP4 can toggle ; and flush the prescaler of the counter (GP2) ; When GP2 is in output mode, it is serial data out to display driver ; When GP2 is in input mode, it is the frequency count input ; Config mode added to allow selection of IF offset ; ; ;******************************************** ; ; Use PIC type 12F629 with external 3.5795 Mhz clock ; ;******************************************** ; Equates ; GPIO Bits SerClk equ 0 ;the bit in gpio used for serial clock SerStb equ 1 ;the bit in gpio used for serial strobe SerDat equ 2 ;the bit in gpio used for serial data PushBtn equ 3 ;Pushbutton input, for counter configuration FlushClk equ 4 ;the bit in gpio used for prescaler flush clock bit ; TRISIO modes DsplMode equ 00111000b ;trisio for display mode CountMode equ 00111100b ;trisio for count mode FlushMode equ 00101100b ;trisio for tmr0 prescaler flush ; Timer 0 config Tmr0Mode equ 10100111b ; Pullups off, set tmr0 as counter with 256 prescale ;GP2Mode equ 10000000b ; Pullups off, set gp2 active ; Interrupt routine Pstate constants IOCon equ 80h ; Pstate parameter high bit Tdbnc equ 03h ; debounce time constant 73ms Thold equ 0Eh ; 1s delay Twait equ 44h ; 5 second delay ; Nixie Shift Offsets D0offset equ 1 ;First driver output no. for digit 0 D1offset equ 11 ;First driver output no. for digit 1 D2offset equ 22 ;First driver output no. for digit 2 D3offset equ 32 ;Driver output no. for digit 3 ColonPos equ 21 ;Driver output no. for colon ; Misc constants blinktime equ 250 ; Slow blink blinktimeF equ 100 ;Fast blink FCdelay equ 30 ; delay between Freq samples to slow down display flicker ; Variables ; ramspace org 20h ; start of user ram wsave ds 1 ; save reg for interrupt statsave ds 1 ; save reg for interrupt ShiftArray ds 6 ; array of 6 registers to hold output digit positions ColonStat ds 1 ; blink state of colon (not used in freq. counter) IFoffsetLo ds 1 IFoffsetHi ds 1 Count_H ds 1 ; Frequency counter high byte Count_L ds 1 ; the counter low byte derived from the prescaler BlinkCtr ds 1 ; Encoder count LastEnc ds 1 ; Last encoder value dlyctrH ds 1 ; outer loop counter dlyctrL ds 1 ; inner loop counter BlinkFlg ds 1 ; flag bits to determine digit blink state BlinkMsk ds 1 ; Mask to select which digits to blink ; Other interrupt variables saveIntcon ds 1 ; temp save intcon state T1oflowctr ds 1 ; counts number of T1 overflow interrupts Pstate ds 1 ; state no. for pushbutton state machine Cstate ds 1 ; state no. for configuration state machine LastPstate ds 1 ; save the last Pstate value LastCstate ds 1 ; save the last Cstate value bitctr ds 1 ; bit counter used by display output LastIO ds 1 ; previous state of GPIO ;ChgFlag ds 1 ; count has changed ; The following are used by the binary to BCD conversion routine count ds 1 temp ds 1 Bin_H ds 1 ; High byte of binary count value Bin_L ds 1 ; Low byte of binary count value R0 ds 1 ; Packed BCD digit 4 (msd) R1 ds 1 ; Packed BCD digits 2,3 R2 ds 1 ; Packed BCD digits 0,1 (0 is lsd) ; ; ; ******************************************** codespace org 0x00 Reset goto Initialize ; Entry point for program from Reset nop nop nop goto IntHndl ; Vector for interrupt routine ; Lookup table to translate non sequential digit arrangement in PCB layout. NixLkp andlw 0fh ; clear the high nibble addwf pcl,f retlw 4 retlw 5 retlw 1 retlw 7 retlw 8 retlw 9 retlw 0 retlw 6 retlw 2 retlw 3 ; Values >9 should never happen, but just in case... retlw 4 ; return 0 digit location for illegal value 10 retlw 4 ; return 0 digit location for illegal value 11 retlw 4 ; return 0 digit location for illegal value 12 retlw 4 ; return 0 digit location for illegal value 13 retlw 4 ; return 0 digit location for illegal value 14 retlw 4 ; return 0 digit location for illegal value 15 ; Pstate machine lookup tables GetNewPstate andlw 0fh ; just to be safe addwf pcl,f ; set jump offset retlw 0 ; state 0 on tmr1 int shouldn't happen retlw 2 ; state 1 on tmr1 int retlw 3 ; state 2 on tmr1 int retlw 3 ; state 3 on tmr1 int retlw 5 ; state 4 on tmr1 int retlw 6 ; state 5 on tmr1 int retlw 6 ; state 6 on tmr1 int retlw 0 ; state 7 on tmr1 int this shouldn't happen retlw 1 ; state 0 on PBtn int retlw 4 ; state 1 on PBtn int retlw 4 ; state 2 on PBtn int retlw 4 ; state 3 on PBtn int retlw 7 ; state 4 on PBtn int retlw 1 ; state 5 on PBtn int retlw 1 ; state 6 on PBtn int retlw 0 ; state 7 on PBtn int this shouldn't happen GetNewPparams andlw 7 ; just to be safe addwf pcl,f ; set jump offset retlw Tdbnc+IOCon ; state 0, Tdbnc is used to ensure a value in the oflow ctr retlw Tdbnc ; state 1 retlw Thold+IOCon ; state 2 retlw Thold+IOCon ; state 3 retlw Tdbnc ; state 4 retlw Twait+IOCon ; state 5 retlw Twait+IOCon ; state 6 retlw 0 ; Should never exit in state 7 ; Config commmand lookup table ; This table is called when a new Pstate interrupt occurs. ; Otherwise, the state is controlled by the main program. ; State 0 - Config inactive, in freq. counter mode ; State 1 - Button down but not debounced, just blink the current display value ; State 2 - Button debounced or held down, display next value ; State 3 - Timed out, save current value and exit config mode ConfigCmd andlw 7 addwf pcl,f retlw 0 ; 0 - inactive state retlw 2 ; 1 - Button down, do nothing but blink retlw 1 ; 2 - Button down debounced, display next value retlw 1 ; 3 - Button held down, display next value retlw 2 ; 4 - Button up, do nothing but blink retlw 2 ; 5 - Button up debounced, do nothing but blink retlw 3 ; 6 - Timed out, save current value and exit retlw 0 ; 7 - inactive state Initialize clrf status ; clear status reg clrf pclath ; clear PCH bank0 ; Sel Bank 0 clrf CMCON ; digital I/O movf gpio,w movwf LastIO bank1 ; Sel Bank 1 call OscConst ; calls RETLW with factory setting movwf OSCCAL ; Set int OSC to factory calibrated movlw tmr0Mode ; disable tmr0, enable gp2 output movwf option_reg movlw DsplMode ; Config GPIO port for display mode movwf TRISIO ; ; Interrupt stuff clrf Pstate ; always start in state zero clrf Cstate ; ditto movlw 2 movwf T1oflowctr ; just in case bsf PIE1,tmr1ie ; Timer 1 interrupt enable movlw 00001000b movwf IOC ; Enable GP3 (pushbutton) interrupt bank0 ; Sel Bank 0 movf gpio,w movwf gpio clrf pir1 movlw 00010000b movwf T1CON ; init timer 1 with prescale x1, but don't start movlw 11001000b movwf intcon ; enable peripheral and gpio interrupts ; Load and display the IF offset movlw CurrentIF ; Loc'n of low byte of IF offset in EEPROM bank1 movwf eeadr bsf eecon1,RD ; read it movf eedata,w movwf IFoffsetLo movwf Count_L incf eeadr,f ; High byte is in the next location bsf eecon1,RD ; read it movf eedata,w movwf IFoffsetHi movwf Count_H call NegIF ; negate the IF offset value bank0 call Bin_to_BCD ; convert to bcd movlw 15 movwf BlinkCtr clrf BlinkMsk comf BlinkMsk,f IFblink call DisplayNxH ; and output to display movlw blinktime call delay comf BlinkMsk,f decfsz BlinkCtr,f goto IFblink movlw 5 movwf BlinkCtr IFblink2 movlw 1 movwf BlinkMsk IFblink3 ; sequence digits call DisplayNxH ; and output to display movlw blinktimef call delay bcf status,c rlf BlinkMsk,f btfss status,c ; exit if bit has shifted into carry goto IFblink3 decfsz BlinkCtr,f goto IFblink2 ; End of initializations ; ; ; start of main program loop ; MainEntry clrf BlinkMsk comf BlinkMsk,f ;initialize/reset colon and counter to zero bank0 ; Sel Bank 0 MainLoop ;Jump table to select routine depending on config mode movf Cstate,w andlw 3 addwf pcl,f goto Fcount ; 0 - Config inactive, just do the regular count routine goto CfNext ; 1 - Display next IF offset goto CfBlink ; 2 - Blink the display and wait goto CfSav ; 3 - Save selected IF offset and exit config mode ;Begin frequency count routine ; ; This uses the technique described in Microchip App note AN592 to read the ; prescaler contents. Fcount clrf tmr0 ; this clears counter reg and prescaler bank1 movlw CountMode ; trisio pattern for count mode movwf trisio ; enable the count input starting counter call Delay10mS ; delay for 10mS bank1 ; this shouldn't be required movlw flushMode movwf trisio ; change to flushmode disabling the counter bank0 movf tmr0,w ; get the high byte from tmr0 movwf Count_H ; and save it clrf Count_L ; clear low byte ; Now flush the prescaler into the timer register flushloop decfsz Count_L,f ; count the toggles goto FlshContin goto FlshExit ; Bail out, something went wrong FlshContin bsf gpio,FlushClk ; toggle the flush bit bcf gpio,FlushClk movf tmr0,w ; see if the count has changed subwf Count_H,w ; compare to original count btfsc status,z goto flushloop FlshExit ; Prescaler flush is complete bank1 movlw DsplMode ; switch back to display mode movwf trisio bank0 ; Sel Bank 0 ;End frequency count routine ;Now add the negated IF offset movf IFoffsetLo,w addwf Count_L,f btfsc status,c incf Count_H,f movf IFoffsetHi,w addwf Count_H,f call Bin_to_BCD ; convert to bcd call DisplayNxH ; and output to display movlw FCdelay call delay ; a short delay to reduce flicker goto MainLoop ; back to start of main loop ; Configuration routines InitConfig ; do this at start of config mode ; Called from interrupt routine when entered in state 0 clrf Count_H clrf Count_L clrf r0 clrf r1 clrf r2 LodAdr bank1 clrf eeadr bsf eecon1,rd ; read the count of IF offsets movf eedata,w movwf eeadr ; and put it in the EE address reg return CfNext bank1 ;the eeadr will already be loaded with a valid value bsf eecon1,rd ; read the high byte movf eedata,w movwf Count_H decf eeadr,f bsf eecon1,rd ; read the low byte movf eedata,w movwf Count_L decf eeadr,f ; set up the adress register for the next read movlw 4 ; see if it's reached the end subwf eeadr,w skppos call LodAdr ; pointer moved past end, so reset movlw 2 movwf Cstate ; change to blink mode state call Bin_to_BCD ; and go right into Blink routine CfBlink comf BlinkMsk,f bank0 Call DisplayNxH movlw blinktimeF call delay goto Mainloop CfSav ; Save the IF offset and exit config mode bank1 movlw 2 movwf eeadr ; IF offset Lo byte is stored in eeprom location 2 movf Count_L,w movwf eedata call EEwrite bank1 movlw 3 movwf eeadr ; IF offset Hi byte is stored in eeprom location 3 movf Count_H,w movwf eedata call EEwrite ; bank0 ; clrf Cstate ; reset back to freq count mode ; clrf BlinkMsk ; comf BlinkMsk,f ; Make sure all digits are on goto Initialize ; Restart prog just in case ; End of main program ; ; NegIF ; Negate the IF offset value comf IFoffsetLo,f incf IFoffsetLo,f btfsc status,Z decf IFoffsetHi,f comf IFoffsetHi,f return ;; This was used for testing ;; Load and display all the IF offsets last to first ; movlw IFlast-IFfirst ; count of IF offset values ; bank1 ; movwf eeadr ;EEloop2 ; bsf eecon1,RD ; read it ; movf eedata,w ; movwf IFoffsetLo ; movwf Count_L ; incf eeadr,f ; bsf eecon1,RD ; read it ; movf eedata,w ; movwf IFoffsetHi ; movwf Count_H ; call NegIF ; negate the IF offset value ; bank0 ; call Bin_to_BCD ; convert to bcd ; movlw 15 ; movwf BlinkCtr ; clrf BlinkMsk ; comf BlinkMsk,f ;IFblink2 ; call DisplayNxH ; and output to display ; movlw blinktime ; call delay ; comf BlinkMsk,f ; decfsz BlinkCtr,f ; goto IFblink2 ; bank1 ; movlw 3 ; subwf eeadr,f ; skpneg ;skip if negative ; goto EEloop2 ; bank0 ; return EEwrite bank0 bcf pir1,eeif bank1 movf intcon,w movwf saveIntcon bsf eecon1,wren bcf intcon,gie ; disable interrupts ;start of critical code movlw 55h movwf eecon2 movlw 0aah movwf eecon2 bsf eecon1,wr ;end of critical code bank0 btfss pir1,eeif ;wait for write to finish goto $-1 movf saveIntcon,w movwf Intcon ;restore intcon register bank1 movf eedata,w ;compare data bsf eecon1,rd xorwf eedata,w return ;if successful w=0 and z is set ; ; The following was lifted directly from Microchip's app note AN526 ; ; minor changes were made due to Assembler syntax differences ; ;******************************************************************** ; Binary To BCD Conversion Routine ; This routine converts a 16 Bit binary Number to a 5 Digit ; BCD Number. This routine is useful since PIC16C55 & PIC16C57 ; have two 8 bit ports and one 4 bit port ( total of 5 BCD digits) ; ; The 16 bit binary number is input in locations Bin_H and ; Bin_L with the high byte in Bin_H. ; The 5 digit BCD number is returned in R0, R1 and R2 with R0 ; containing the MSD in its right most nibble. ; ; Performance : ; Program Memory : 35 ; Clock Cycles : 885 ; ; ; Program: B16TOBCD.ASM ; Revision Date: ; 1-13-97 Compatibility with MPASMWIN 1.40 ; ;*******************************************************************; ; ; Bin_to_BCD movf Count_H,w ; Load the counter value into movwf Bin_H ; the conversion input registers movf Count_L,w movwf Bin_L ;B2_BCD bcf STATUS,0 ; clear the carry bit movlw 16 movwf count clrf R0 clrf R1 clrf R2 loop16 rlf Bin_L, F rlf Bin_H, F rlf R2, F rlf R1, F rlf R0, F ; decfsz count, F goto adjDEC RETLW 0 ; adjDEC movlw R2 movwf FSR call adjBCD ; movlw R1 movwf FSR call adjBCD ; movlw R0 movwf FSR call adjBCD ; goto loop16 ; adjBCD movlw 3 addwf INDF,W movwf temp btfsc temp,3 ; test if result > 7 movwf INDF movlw 30h addwf INDF,W movwf temp btfsc temp,7 ; test if result > 7 movwf INDF ; save as MSD RETLW 0 ; ; Frequency count 10ms gate time delay routine ; ; Assuming 4 MHz clock, 1uS cycle time: ; For 10mS we need to waste 10000 cycles: 9997 cycles here, leaving 3 cycle in the main prog ; to switch off counter. ; Use 12 for long dalay value, and 248 for short delay value. ; ; For 3.579545 MHz clock, cycle time = 1.117uS ; Then we need to waste 8949 cycles: 8946 cycles here, leaving 3 cycles in the main prog ; to switch off counter. ; Use 11 for long delay value, and 154 for short delay value ; ; Delay10mS ; 00002 cycles for subroutine call movlw 11 ; 00001 cycle - long delay value call delay ; 08474 cycles movlw 154 ; 00001 cycle - short delay value movwf dlyctrL ; 00001 cycle decfsz dlyctrL,f ; 00462 cycles for loop goto $-1 ; -0001 cyc for loop exit goto $+1 ; 00002 cycles - a short tweak goto $+1 ; 00002 cycles - a short tweak Return ; 00002 cycles, total ????? cycles ; Delay subroutine delay cycles = 770*w+4, or w*0.86 ms (at 3.57 MHz clock) delay ; 00002 cyc for entry call movwf dlyctrH ; 00001 cyc, w contains delay value subloop decfsz dlyctrL,f ; 00768 cyc for loop * w goto $-1 ; -0001 cyc for loop exit * w decfsz dlyctrH,f ; 00001 * w goto subloop ; 00002 * w ; -0001 for loop exit return ; 00002 ;Nixie display routines ; The binary to BCD conversion routine generates 5 digits, but the display has only 4. ; So, there are two routines: displayNxL & displayNxH depending on whether we want to ; display the leftmost digits, or the rightmost digits. ; ; BCD values are stored in R0, R1, R2 in packed form. The SWAP command is used to juggle the ; nibbles into the correct position. ; ; The nixie driver outputs are active low, so a zero will turn on the digit, and a one will ; turn it off. Therefore the shift register is filled mostly with ones, and a few zeros are ; shifted in at the appropriate points to turn on the correct digits. ; ; Also note, to simplify the circuit board traces, the nixies' cathodes are not connected ; in sequential order, the order of digits is 6 2 8 9 0 1 7 3 4 5. ; To sort these out, a lookup table subroutine, NixLkp, is called. ; ; Display the low 4 digits displayNxL movlw ShiftArray ;load addr of Shiftarray movwf FSR ; and store in indirect ptr reg ; digit 0 movf R2,w call NixLkp ;Get relative Sreg position for digit addlw D0offset ; Add the digit start position movwf indf ;Store in digit array incf FSR,f ; digit 1 swapf R2,w call NixLkp ;Get relative Sreg position for digit addlw D1offset ; Add the digit start position movwf indf ;Store in digit array incf FSR,f ; colon movlw ColonPos movwf indf ; save it in digit array movf ColonStat,f ; test Colon blink state for <>0 btfss status,z ; don't increment FSR if zero incf FSR,f ; ... effectively skipping colon ; digit 2 movf R1,w call NixLkp ;Get relative Sreg position for digit addlw D2offset ; Add the digit start position movwf indf ;Store in digit array incf FSR,f ; digit 3 (zero or one only) movlw D3offset movwf indf ; save it in digit array btfsc R1,4 ; test bit 4 =0, if so don't incr the pointer incf FSR,f ; ...effectively skipping digit goto NxShiftEntry ; Display the High 4 digits displayNxH movlw ShiftArray ;load addr of Shiftarray movwf FSR ; and store in indirect ptr reg ; digit 0 swapf R2,w call NixLkp ;Get relative Sreg position for digit addlw D0offset ; Add the digit start position movwf indf ;Store in digit array incf FSR,f ; digit 1 movf R1,w call NixLkp ;Get relative Sreg position for digit addlw D1offset ; Add the digit start position movwf indf ;Store in digit array incf FSR,f ; colon movlw ColonPos movwf indf ; save it in digit array movf ColonStat,f ; test Colon blink state for <>0 btfss status,z ; don't increment FSR if zero incf FSR,f ; ... effectively skipping colon ; digit 2 swapf R1,w call NixLkp ;Get relative Sreg position for digit addlw D2offset ; Add the digit start position movwf indf ;Store in digit array incf FSR,f ; digit 3 (zero or one only) movlw D3offset movwf indf ; save it in digit array btfsc R0,0 ; test if bit0 =1, if zero don't incr the pointer incf FSR,f ; ...effectively skipping digit ; Now shift out the data NxShiftEntry movf BlinkMsk,w ; this needs to be relocated to work properly movwf BlinkFlg bcf gpio,SerStb ;ensure strobe is zero bcf gpio,SerClk ;ensure clock is zero movlw 32 ; initialize the bit counter movwf bitctr ; counts the shifts in the 32 bit SReg decf FSR,f ; move indir ptr back to last element ShiftLoopN bsf gpio,SerDat ;init serial data bit to one movf INDF,w ; get the next zero bit location subwf bitctr,w ; check if at next 0 bit locn btfss status,z goto Shiftout decf FSR,f ; this is the next bit position ; check blink state rrf BlinkFlg,f ; this needs to be relocated to work properly btfsc status,c ; don't display if flag is 0 bcf gpio,SerDat ;set data to 0 Shiftout bsf gpio,SerClk ;raise the clock line bcf gpio,SerClk ;lower the clock line decfsz bitctr,f ; decr. bit counter and goto ShiftLoopN ; loop if more bits to shift bcf gpio,SerDat ; Set data bit to zero bsf gpio,SerStb ; Strobe the data into the output buffers bcf gpio,SerStb return ; ; ; Interrupt handler - service encoder input ; ; IntHndl ; save w and status registers movwf wsave ; save w register swapf status,w ; get current status with nibbles reversed bank0 ; switch to bank 0 movwf statsave ; and save status ; start of interrupt service bcf t1con,0 ; stop timer while in interrupt routine movf Pstate,w movwf LastPstate ; Save the last Pstate skpnzr ;Check for state 0 and Init the IFoffset data pointer if necessary call InitConfig bank0 ;again ; Pushbutton input handler is set up as a state machine, 7 states and two inputs ; input 1 is tmr1 overflow interrupt ; input 2 is Pushbutton input change of state; ; states are as follows: ; state 0 - initial state, input is inactive, button is up; also final state at end of config ; state 1 - button pressed, but not yet debounced ; state 2 - button pressed and debounced ; state 3 - button held down for at least Thold (1 second) ; state 4 - button released but not debounced ; state 5 - button released and debounced ; state 6 - timeout, button up and no activity for time Twait (5 seconds) ; state 7 - dummy state representing previous state, allows state maching to back up one step ; Only two interrupts are enabled, Timer1 and Pushbutton input ; check to see which it is btfss pir1,tmr1if ; this bit will be set if it's tmr1 interrupt goto PBchk ; else it was a pushbutton state change ; now decrement the post-scale counter which acts as a 3rd byte of timer register decfsz T1oflowctr,f ; if timer is not zero, pretend it's not a tmr int, and exit goto IntEnd PBchk ; May need to check here for illegal button up in state 0 condition ; ***IMPORTANT*** must read gpio in order to reset the ioc int latch movf gpio,w ; see if pushbutton has changed state xorwf LastIO,f ; these 3 xors swap w with LastIO xorwf LastIO,w xorwf LastIO,f xorwf LastIO,w ; do one last xor to detect PushBtn change andlw 2^PushBtn movlw 8 skpneq movlw 0 addwf Pstate,w ; results in 0..7 for no gp chg; 8..15 for gp chg NewPstate ;Determine new Pstate from old state and input status Call GetNewPstate ; use lookup table movwf Pstate sublw 7 ; if 7 was returned then return to the previous state swapf Pstate,w ; use swapf instead of movf, to leave z undisturbed skpneq ; now test and skip swapf LastPstate,w ; replace 7 with last state movwf Pstate ; save swapf Pstate,f ; and unswap nibbles ; Now do the config handler code according to the Pstate CnfigR ; Execute the appropriate config command movf Cstate,w movwf LastCstate ; store the current state in case revert is req'd movf Pstate,w Call ConfigCmd movwf Cstate ; Except for 6 & 7, The main prog will do the rest. ; movlw 7 ; 7 means revert to last state ; subwf Cstate,w ; swapf LastCstate,w ; revert to last state if 7 ; skpeq ; was it 7? ; swapf Cstate,w ; no, replace with current cstate ; movwf Cstate ; and save ; swapf Cstate,f ; then flip movlw 3 ; 3 is the save & exit state subwf Cstate,w skpneq clrf Pstate ; If exit state, then reset the input mode cfgxit ; End of config handler, now set up the Pstate params and exit movf Pstate,w Call GetNewPparams ; use lookup table; bits 0..6 are new tmr value, bit 7 is IOC movwf T1oflowctr clrf tmr1L clrf tmr1H rrf T1oflowctr,f rrf tmr1H,f ;set the intcon GPIE interrupt on change bit according to Pstate param bcf intcon,gpie ; clear the ioc parameter btfsc T1oflowctr,6 ; IOC param has been shifted from 7 to 6 bsf intcon,gpie ; set the ioc parameter movlw 00111111b ; clear the IOC & bit7 leaving only the preset timer value andwf T1oflowctr,f IntEnd bank0 movf Pstate,f ; check if Pstate is zero skpzer bsf t1con,0 ; restart timer when exiting unless state 0 IntEnd2 bank0 bcf intcon,gpif ; clear the ioc flag clrf PIR1 ; clear the timer int flag RegRestore ; Restore w and status registers swapf statsave,w ; get status back without changing flags movwf status ; and restore status register swapf wsave,f ; this reloads w without changing status bits swapf wsave,w ; w is now restored retfie ; interrupt routine done ; Configuration routine, another state machine configHdl ; for testing just set the display digit1 to current Pstate incf Count_L,f ; Just so it's not zero return ; config handler ; Oscillator calibration constant ; org 0x3ff OscConst retlw 0x98 configspace org 0 data 1,3f7fh,3f7fh,3f7fh org 6 data 0fcfh ; data 1184h ;old configuration word internal osc data 0183h ; ; romspace ; Start of Rom data ; ; These are the IF offsets which may be selected. ; The first memory location is the number of choices. ; The 3rd and 4th are the currently configured offset ; Note an implied decimal is before the last digit ; ********* PICkit programmer bug alert! *********** ; The Mac PICkit programmer application has a bug which causes ; adjacent pairs of bytes to be reversed when programmed, and the first ; byte to be skipped. ; Therefore, the current fix is to have the data in Hi-Lo order ; so that they will end up in Lo-Hi order in ROM. ; IF you are using the MPLab programming software, you will need to modify the ; following code to re-reverse the bytes. ; the first 0 byte below is due to the above noted bug IFcount data 0,IFlast+1; Position of last pair of IFoffset bytes CurrentIF data 2625 / 256,2625 mod 256 ; default IF offset IFfirst data 0,0 ; no offset data 500 / 256, 500 mod 256 ; 50 kHz data 750 / 256, 750 mod 256 ; 75 kHz data 1500 / 256, 1500 mod 256 ; 150 kHz data 1750 / 256, 1750 mod 256 ; 175 kHz data 2625 / 256, 2625 mod 256 ; 262.5 kHz data 2850 / 256, 2850 mod 256 ; 285 kHz (for my proj. radio) data 4500 / 256, 4500 mod 256 ; 450 kHz data 4550 / 256, 4550 mod 256 ; 455 kHz data 4560 / 256, 4560 mod 256 ; 456 kHz data 4600 / 256, 4600 mod 256 ; 460 kHz IFlast data 16000 / 256, 16000 mod 256 ; 1600 kHz data 255,255,255,255,255,255,255,255 ; ; End of program ;