; KEYPAD.ASM ; written by ; Fr. Thomas McGahee ; Don Bosco Technical High School ; 202 Union Ave. ; Paterson, NJ 07502 ; tom_mcgahee@sigmais.com ; ; March, 1997 ; Microchip MPASM format ; Specifically designed for PIC16C84 ; but will also run on 16F84 ; with minor changes ; ; note: written in all lower case so case sensitivity doesn't matter. ; however: set assembler to case-insensitive, except within strings using /c- option ; ; ; directives ; ; search for ### to locate items that need to be changed if using the 16f84 ; instead of the 16c84 list p=16c84 ;this directive must come first ;### p=16f84 if using 16f84 ; instead of using the [ include <16c84.inc> ] directive, we have placed the ; contents of the microchip-supplied include file below for documentation purposes. ; ; this section defines configurations, registers, and other useful bits of ; information for the pic16c84 microcontroller. these names are taken to match ; the latest data sheets as closely as possible. ; note that the processor must be selected before this file is ; included. the processor may be selected the following ways: ; 1. command line switch: ; c:\ mpasm myfile.asm /pic16c84 ; 2. list directive in the source file ;(this is the method chosen here) ; list p=pic16c84 ;### 16c84. ; 3. processor type entry in the mpasm full-screen interface ;========================================================================== ; ; revision history ; ;========================================================================== ;rev: date: reason: ;1.00 10/31/95 initial release ;========================================================================== ; ; verify processor ; ;========================================================================== ifndef __16c84 ;### 16c84. change to ifndef __16f84 if using 16f84 messg "processor-header file mismatch. verify selected processor." endif ;========================================================================== ; ; register definitions ; ;========================================================================== w equ h'0000' f equ h'0001' ;----- register files------------------------------------------------------ indf equ h'0000' tmr0 equ h'0001' pcl equ h'0002' status equ h'0003' fsr equ h'0004' porta equ h'0005' portb equ h'0006' eedata equ h'0008' eeadr equ h'0009' pclath equ h'000a' intcon equ h'000b' option_reg equ h'0081' trisa equ h'0085' trisb equ h'0086' eecon1 equ h'0088' eecon2 equ h'0089' ;----- status bits -------------------------------------------------------- irp equ h'0007' rp1 equ h'0006' rp0 equ h'0005' not_to equ h'0004' not_pd equ h'0003' z equ h'0002' dc equ h'0001' c equ h'0000' ;----- intcon bits -------------------------------------------------------- gie equ h'0007' eeie equ h'0006' t0ie equ h'0005' inte equ h'0004' rbie equ h'0003' t0if equ h'0002' intf equ h'0001' rbif equ h'0000' ;----- option bits -------------------------------------------------------- not_rbpu equ h'0007' intedg equ h'0006' t0cs equ h'0005' t0se equ h'0004' psa equ h'0003' ps2 equ h'0002' ps1 equ h'0001' ps0 equ h'0000' ;----- eecon1 bits -------------------------------------------------------- eeif equ h'0004' wrerr equ h'0003' wren equ h'0002' wr equ h'0001' rd equ h'0000' ;========================================================================== ; ; ram definition ; ;========================================================================== __maxram h'af' __badram h'07', h'30'-h'7f', h'87' ;### 16c84. ;(__badram h'07', h'50'-h'7f', h'87' ;use *this* one if using 16f84) ;========================================================================== ; ; configuration bits ; ;========================================================================== _cp_on equ h'3fef' _cp_off equ h'3fff' _pwrte_on equ h'3fff' _pwrte_off equ h'3ff7' _wdt_on equ h'3fff' _wdt_off equ h'3ffb' _lp_osc equ h'3ffc' _xt_osc equ h'3ffd' _hs_osc equ h'3ffe' _rc_osc equ h'3fff' ;end of file stuff ;============================================================================== ; set configuration bits ;============================================================================== ;we have to set the configuration bits ; __config a & b & c ; _rc_osc, _xt_osc, _hs_osc, _lp_osc oscillator type ; _wdt_on, _wdt_off watchdog timer ; _cp_on, _cp_off code protect ; _pwrte_on, _pwrte_off power up timer enable __config _xt_osc & _wdt_off & _pwrte_on & _cp_off ;============================================================================== ; some constant equates ;============================================================================== ; note use of 4.00 mhz xtal. this will affect timing loops xtal_freq = d'4000000' ;crystal frequency clock = xtal_freq/4 ;base operating frequency ;============================================================================== ; pic16c84 pinouts and hardware details ;============================================================================== ; pic16c84 pinouts ; ; ra2 <1> <18> ra1 ; ra3 <2> <17> ra0 ; (oc) ra4/tmr0<3> <16> osc1/clkin ; !mclr! <4> <15> osc2/clkout ; gnd <5> <14> +2 to +6 volts ; rb0/int <6> <13> rb7 ; rb1 <7> <12> rb6 ; rb2 <8> <11> rb5 ; rb3 <9> <10> rb4 ; ;osc1 & osc2 using 4.00 mhz ceramic resonator or xtal with 20 pf caps on each lead. ; ;!mclr! tied high via 10K. use a switch to force it low for manual reset. ;porta assignments ;ra4 and ra3 are not used in this design. ;ra0 is connected to cathode of green led with 330 ohm to +5. ;ra1 is connected to cathode of red led with 330 ohm to +5. ;ra2 is connected to 1K to base of npn. npn drives 5v relay. ;coil of relay is shunted with diode. cathode to v+, anode to npn collector. ;common and normally open contacts of relay are used to control door opener. ;============================================================================== ; porta *bit* assignments ;============================================================================== n_red equ 0 ;low on ra0 turns red led on n_green equ 1 ;low on ra1 turns green led on relay equ 2 ;high on ra2 turns relay on ra3 equ 3 ;**not used** ra4 equ 4 ;**not used** ;============================================================================== ; portb *bit* assignments ;============================================================================== ;rb7-rb0 are ttl. weak pullups are programmed using option_reg<7>=0 ;rb7-rb4 assigned as *inputs* ;rb7-rb4 will generate an interrupt. set intcon<3> rbie=1. intcon<0> rbif is flag. software reset. ; ;rb3-rb0 are assigned as *outputs*. low is output to "scan" the inputs of the keypad. ;============================================================================== ; keypad details ;============================================================================== ;keypad layout (grayhill 8622 with additional marking 8637. 8 pins) ;pin numbers of keypad shown in <> brackets ; ; <5> <6> <7> <8> ; <1> 7 8 9 (up) ; <2> 4 5 6 (down) ; <3> 1 2 3 (run) ; <4> (load) 0 (clr) (ent) ; *top view* of keypad pins: 1,2,3,4,5,6,7,8 ; pin 1 is marked with the letter M on my units. ; interconnect was done using a 20 pin idc cable, with the #1 ; pin of the connector going to keypad #1 and pcb header #1. ; being cheap, i used a ten pin header strip on the pcb. ; pcb header pins 9 and 10 are on pc board, but not used. ; inputs will be rb4,5,6,7 outputs will be rb0,1,2,3 ; so we can take advantage of interrupt on rb 4-7 going low. ; this leads us to the following coding/decoding scheme ; based on keeping the interface wiring straight forward. ; *keypad pin* 1=rb0 2=rb1 3=rb2 4=rb3 5=rb4 6=rb5 7=rb6 8=rb7 ; x refers to the original code, and y to the decoded value, ; which has been chosen to be a *character* type e.g. '1' instead of 1. ; note that the method used to allow a variable length code ; requires a dummy filler code to pad out unused digits. ; h'00' has been chosen as the filler code, as it is very easy to test for. ;============================================================================== ; decoding of keypad *scancodes* to stored *character* codes: ;============================================================================== xload = b'11100111' yload = 'l' ;load character 'l' (el), *not* a <'1'> x0 = b'11010111' y0 = '0' ;0 xclr = b'10110111' yclr = 'c' ;clear xent = b'01110111' yent = 'e' ;enter x1 = b'11101011' y1 = '1' ;1 (one) x2 = b'11011011' y2 = '2' ;2 x3 = b'10111011' y3 = '3' ;3 xrun = b'01111011' yrun = 'r' ;run x4 = b'11101101' y4 = '4' ;4 x5 = b'11011101' y5 = '5' ;5 x6 = b'10111101' y6 = '6' ;6 xdown = b'01111101' ydown = 'd' ;down x7 = b'11101110' y7 = '7' ;7 x8 = b'11011110' y8 = '8' ;8 x9 = b'10111110' y9 = '9' ;9 xup = b'01111110' yup = 'u' ;up xx = b'00000000' ;filler code. allows simple z test for end. ;============================================================================== ; eeprom data area 64x8 bytes of eeprom starting at h'2100' ;============================================================================== org h'2100' ;set data eeprom origin ;user codes can be from 1 to 8 digits in length ;and may also contain and . ;the special codes and may not appear in a user code ;since codes are stored decoded, use y0-y9 and yup, ydown, and yload to specify code set. ;xx indicates blank filler code usercode0 de xx, xx, xx, xx, xx, xx, xx, xx ;user 1 usercode1 de xx, xx, xx, xx, xx, xx, xx, xx ;user 2 usercode2 de xx, xx, xx, xx, xx, xx, xx, xx ;user 3 usercode3 de xx, xx, xx, xx, xx, xx, xx, xx ;user 4 usercode4 de xx, xx, xx, xx, xx, xx, xx, xx ;user 5 usercode5 de xx, xx, xx, xx, xx, xx, xx, xx ;user 6 umastercode de y1, y2, y3, y4, y5, y6, y7, y8 ;user mastercode. ; 1-8 digits ; *can* be changed. mastercode de y1, y0, y2, y8, y1, y9, y4, y6 ;factory mastercode. ; 8 digits ; can *not* be changed. ;============================================================================== ; explanation of how data entry occurs ;============================================================================== ;normal codes: hit , enter code, hit . relay will close for 10 sec. ;you may terminate relay closure early by hitting *any* key. ;hitting will always re-synchronize system. ;in addition to allowing regular relay closure, the mastercodes can be used ;to do special things. the factory master code is never changed, but the user ;may enter their own easy-to-remember personal mastercode, and up to 6 ;regular user codes. ;hit , enter one of the mastercodes, hit , hit set # <0-6>, hit , ;enter 1-8 digits, and terminate with . if no digits are entered, then set is ;cleared. This allows you to remove any of the existing codes, except the factory ;supplied mastercode. ;user set #0 is actually the user-selectable mastercode. It may be used just ;like the regular factory-set mastercode. it is suggested that this be an ;8 digit code. ;remember that , , and are all valid keys and can be used ;the same as if they were digits. , , and may *not* be used ;as part of the user code, since they are used to direct program flow and select ;what type of entry is in progress. ;============================================================================== ; file register ram useage h'oc' to h'2f'. 36 bytes available ;============================================================================== ;(### if using 16f84 sram begins at h'0c' and ends at h'4f'. room for 68 bytes.) ;note that the assembler can *not* use db or similar codes to load the contents. ;the use of the cblock/endc directives allow us to assign ram storage ;without having to use equate statements. this allows the programmer ;to add/remove/move variable assignments without having to worry about fixing ;up the actual assigned *value*. makes life simpler for the programmer! cblock h'0c' keycode ;storage for undecoded/decoded keycode savew ;for inthandler savestatus ;for inthandler savefsr ;for inthandler eeadr1 ;storage for eeadr xmillisec ;outer loop counter for milliseconds ymillisec ;inner loop counter for milliseconds keycounter1 ;outer loop counter keycounter2 ;inner loop counter ok ;non-zero means ok blinks ;number of blinks to perform (green) exitkey ;holds value of key used to terminate entry usercounter1 ;multiple uses involving user key entry user0 ;user input is stored here. user1 ; user0-user7 hold the code entered user2 ; via the keypad both for normal entry, user3 ; master code entry, and the entry of user4 ; *new* user code user5 user6 user7 endc ;============================================================================== ; program space 1kx14 (h'400') can only be changed via programmer. ;============================================================================== ;because we stay within a 1K bank, no need to worry about bank selection. org h'0000' ;set code origin within rom space start goto setup ;must get past interrupt vector at h'0004' ;============================================================================== ; interrupt handler: handles all keypad input and decoding. ; *decoded* value is placed in . ; ; main program looks for non-zero value while in loop. ; after being used, main program must clrf keycode! ;============================================================================== ; there is a single interrupt location at 004 ; in our case there is only 1 source of interrupt, ; so we don't have to worry about that. ; see section for interrupt assignment stuff. org h'0004' ;interrupt vector at h'0004' ;interrupt occurs when any key is hit. ;this routine handles debounce and returns the key code in keycode. ;on any error it returns 0 in keycode. debounce is executed at ;beginning and end of keypress. Only the first key detected will ;be captured if there are multiple simultaneous keys pressed. ;keypress can be of any duration. inthandler ;global interrupts automatically ;disabled on entry! movwf savew ;save w register! swapf status,w ;(twisted) ; swapf used so as not to disturb ; the contents of the status register movwf savestatus ;save status register! movf fsr,w ;save fsr! movwf savefsr ;instead of doing a regular delay on entry for debounce, we 'waste' the time ;doing something useful: we search for the scancode. We do this 65K times ;before assuming there was no key hit. clrf keycode ;clear keycode. clrf keycounter1 ;initialize outer counter loop256x256 decfsz keycounter1,f ;256 outer loops... goto more256x256 goto nogood ;65K tries & it was no good! more256x256 clrf keycounter2 ;initialize inner counter loopinner256 decfsz keycounter2,f ;256 inner loops per outer loop... goto innertest goto loop256x256 innertest comf portb,w andlw b'11110000' ;test for *any* key hit first... btfsc status,z goto loopinner256 ;if we got here, we might have a hit. scankey0 movlw b'11111110' ;load bitmask movwf portb ;twiddle one bit at a time nop ;give signal a chance to settle down movf portb,w ;read current value into keycode movwf keycode ;now we can play with it. comf keycode,w ;get complemented form andlw b'11110000' ;look only at high bits btfss status,z ;no hit sets z flag goto decode ;if hit, decode & debounce... ;if no hit, check next key set scankey1 movlw b'11111101' ;load bitmask movwf portb ;twiddle one bit at a time nop ;give signal a chance to settle down movf portb,w ;read current value into keycode movwf keycode ;now we can play with it. comf keycode,w ;get complemented form andlw b'11110000' ;look only at high bits btfss status,z ;no hit sets z flag goto decode ;if hit, decode & debounce... ;if no hit, check next key set scankey2 movlw b'11111011' ;load bitmask movwf portb ;twiddle one bit at a time nop ;give signal a chance to settle down movf portb,w ;read current value into keycode movwf keycode ;now we can play with it. comf keycode,w ;get complemented form andlw b'11110000' ;look only at high bits btfss status,z ;no hit sets z flag goto decode ;if hit, decode & debounce... ;if no hit, check next key set scankey3 movlw b'11110111' ;load bitmask movwf portb ;twiddle one bit at a time nop ;give signal a chance to settle down movf portb,w ;read current value into keycode movwf keycode ;now we can play with it. comf keycode,w ;get complemented form andlw b'11110000' ;look only at high bits btfss status,z ;no hit sets z flag goto decode ;if hit, decode & debounce... goto loopinner256 ;if there were no hits at all! nogood clrf keycode ;reset keycode to 0 (indicates no hit) ;the following decodes the scan code value into a ycode character. ;due to the nature of the scancodes, we can not use a decode table efficiently, ;so we will use a skip method instead. decode movf keycode,w ;copy to w and set flags btfsc status,z goto fillercode ;if keycode=0 convert to filler decode0 movf keycode,w ;reload keycode to convert sublw x0 ;compare with xcode definition btfss status,z goto decode1 ;if no match, check next movlw y0 ;if match, load ycode into w goto savecode ;save converted code. decode1 movf keycode,w ;reload keycode to convert sublw x1 ;compare with xcode definition btfss status,z goto decode2 ;if no match, check next movlw y1 ;if match, load ycode into w goto savecode ;save converted code. decode2 movf keycode,w ;reload keycode to convert sublw x2 ;compare with xcode definition btfss status,z goto decode3 ;if no match, check next movlw y2 ;if match, load ycode into w goto savecode ;save converted code. decode3 movf keycode,w ;reload keycode to convert sublw x3 ;compare with xcode definition btfss status,z goto decode4 ;if no match, check next movlw y3 ;if match, load ycode into w goto savecode ;save converted code. decode4 movf keycode,w ;reload keycode to convert sublw x4 ;compare with xcode definition btfss status,z goto decode5 ;if no match, check next movlw y4 ;if match, load ycode into w goto savecode ;save converted code. decode5 movf keycode,w ;reload keycode to convert sublw x5 ;compare with xcode definition btfss status,z goto decode6 ;if no match, check next movlw y5 ;if match, load ycode into w goto savecode ;save converted code. decode6 movf keycode,w ;reload keycode to convert sublw x6 ;compare with xcode definition btfss status,z goto decode7 ;if no match, check next movlw y6 ;if match, load ycode into w goto savecode ;save converted code. decode7 movf keycode,w ;reload keycode to convert sublw x7 ;compare with xcode definition btfss status,z goto decode8 ;if no match, check next movlw y7 ;if match, load ycode into w goto savecode ;save converted code. decode8 movf keycode,w ;reload keycode to convert sublw x8 ;compare with xcode definition btfss status,z goto decode9 ;if no match, check next movlw y8 ;if match, load ycode into w goto savecode ;save converted code. decode9 movf keycode,w ;reload keycode to convert sublw x9 ;compare with xcode definition btfss status,z goto decodeload ;if no match, check next movlw y9 ;if match, load ycode into w goto savecode ;save converted code. decodeload movf keycode,w ;reload keycode to convert sublw xload ;compare with xcode definition btfss status,z goto decodeclr ;if no match, check next movlw yload ;if match, load ycode into w goto savecode ;save converted code. decodeclr movf keycode,w ;reload keycode to convert sublw xclr ;compare with xcode definition btfss status,z goto decodeent ;if no match, check next movlw yclr ;if match, load ycode into w goto savecode ;save converted code. decodeent movf keycode,w ;reload keycode to convert sublw xent ;compare with xcode definition btfss status,z goto decoderun ;if no match, check next movlw yent ;if match, load ycode into w goto savecode ;save converted code. decoderun movf keycode,w ;reload keycode to convert sublw xrun ;compare with xcode definition btfss status,z goto decodedown ;if no match, check next movlw yrun ;if match, load ycode into w goto savecode ;save converted code. decodedown movf keycode,w ;reload keycode to convert sublw xdown ;compare with xcode definition btfss status,z goto decodeup ;if no match, check next movlw ydown ;if match, load ycode into w goto savecode ;save converted code. decodeup movf keycode,w ;reload keycode to convert sublw xup ;compare with xcode definition btfss status,z goto fillercode ;if no match, use fillercode movlw yup ;if match, load ycode into w goto savecode ;save converted code. fillercode clrf keycode ;set up filler code goto intreturn ;skip delay since it is empty savecode movwf keycode ;store converted keycode in keycode! bsf porta,n_red ;turn off red led until key is ;released. used as a visual ;indicator that we got it. movlw b'11110000' ;allow any key hit to be detected movwf portb ;make it so nop ;wait to stabilize. release comf portb,w ;wait for all keys to be released... andlw b'11110000' ;only have to check upper 4 bits. btfss status,z goto release ;if any key is down, wait... movlw d'25' ;set debounce delay to 25 ms. call wmillisec ;wait after last key is released. ;clean up and get ready to return. comf portb,w ;check for *really* bad bounce! andlw b'11110000' ;only have to check upper 4 bits. btfss status,z goto release ;if key is still down, wait... movlw d'25' ;set debounce delay to 25 ms call wmillisec ;wait after last key is released ;clean up and get ready to return. intreturn movlw b'11110000' ;set up default keyscan (all) movwf portb ;so new key presses can be detected. bcf porta,n_red ;turn red back on after keypress bcf intcon,rbif ;clear appropriate interrupt flag! ;(in this example we used rbif) ;restore stuff we saved. movf savefsr,w ;restore fsr! movwf fsr swapf savestatus,w ;untwist twisted saved status movwf status ;restore normalized status! swapf savew,f ;restore w! first twist nibbles swapf savew,w ;then twist again and place ;result in w. ;we had to use swapf to ensure that ;status register was not clobbered ;while recovering w. retfie ;return from interrupt! ;gie is auto-re-enabled. ;keycode contains character code if OK ;else keycode is 00000000. ;============================================================================== ; setup is the initialization code for ports and registers ;============================================================================== ;page 1 stuff includes option_reg, trisa, trisb, eecon1, eecon2 setup bsf status,rp0 ;allow access to page 1 stuff! movlw b'00000000' ;set porta direction for i/o pins movwf trisa ;0=output 1=input movlw b'11110000' ;set portb direction for i/o pins movwf trisb ;0=output 1=input ;although we could use a simple movlw/movwf option_reg ;instruction to setup all option_reg bits at once, it is ;preferable to use individual bit twiddling during ;development, so changes can be easily made and commented. bcf option_reg,not_rbpu ;!rbpu! rb_pullup ; 0=enabled 1=disabled ; enables only portb inputs. ;(enable weak pullups on portb inputs) bcf option_reg,intedg ;intedg ; 0=int on falling 1=int on rising ; note: intedg and t0se use ; opposite definitions! ;(interrupt on falling edge of portb) bcf option_reg,t0cs ;t0cs timer0clocksource ; 0=internal clkout 1=ra4/int ;(enable internal clkout) bcf option_reg,t0se ;t0se timer0signaledge ; 0=inc on rising 1=inc on falling ; note: intedg and t0se use ; opposite definition! ;(inc on rising edge of clock) bcf option_reg,psa ;psa pre scaler assignment ; 0=tmr0 1=wdt ;ps2-ps0 determine prescalerrate, ; which is dependent also on whether ; tmr0 or wdt is selected: ;wdt from 0-7 is ; div by 1 2 4 8 16 32 64 128 ;tmr0 from 0-7 is ; div by 2 4 8 16 32 64 128 256 ;we have chosen tmr0 prescaler of 2. ;we will not be using tmr0 or the wdt, ;but we will state and set ;bits as if we might use tmr0 ;since we do *not* want to use wdt. bcf option_reg,ps2 ;ps2 of psa bcf option_reg,ps1 ;ps1 of psa bcf option_reg,ps0 ;ps0 of psa bcf status,rp0 ;allow access to page 0 stuff again. ; back to normal! ;============================================================================== ; enable interrupt related stuff ;============================================================================== ;intcon register: ;enables... 1=enable 0=disable ;<7>=gie=global_int_enable ;<6>=eeie=eeprom_int_enable ;<5>=t0ie=tmr0_int_enable (enables <2> t0if) ;<4>=inte=int_enable (rb0/int) (enables <1> intf) ;<3>=rbie=rb_int_enable (enables <0> rbif) ;flags. software reset. 0=reset 1=flagged ;<2>=t0if=tmr0_int_flag ;<1>=intf=int_flag (rb0/int) ;<0>=rbif=rb_int_flag (rb7-rb4) ;upon power up and !mclr!, intcon will contain 0000 000x ;this means that initially all interrupts are disabled. ;note: option_reg register is used to program use of tmr0 and wdt clrf tmr0 ;reset tmr0 (not used) clrf intcon ;clear any pending interrupt requests bsf intcon,gie ;set global interrupt enable bsf intcon,rbie ;enable interrupt on portb change ;pins 7,6,5,4 clrf keycode ;clear any existing keycode. bcf porta,relay ;turn relay off. ;============================================================================== ; main program ;============================================================================== ;main program is state-driven. ;entry begins with the user hitting the clr key. ;this is indicated with a quick blink of the green led. ;the red led is usually on. it goes off while any key is depressed. ;key input is handled by an interrupt driven routine based on ;int on portb<7:4>. ;extensive debouncing ensures accurate entry of keys. ;keys are decoded from row/column form into simple ascii character form ;by interrupt handler. ;ascii character code representations used are: ; '0'-'9' and 'c' clr, 'e' ent, 'r' run, 'u' up, 'd' down, 'l' load. ;the clr key will always cause synchronization of the system, and ;*all* valid entry begins with a clr. ;green led is blinked on clr release. ;digits are then entered. valid codes can be from 1 to 8 digits long. ;while any digit is being entered, the red led will go off. ;this provides a visual feedback of proper data entry. ;normal entry mode: if a set of key entries ends with ent, ;then all 8 code sets (including the master codes) are compared ;with the user entries. entry of more than 8 digits will be detected ;as a 'no-match'. ;if entry was 1-8 digits, these are compared and if any set matches, ;then the green and red leds will alternate on and off for ten ;seconds while the relay is closed for ten seconds. at the end ;of ten seconds the relay will re-open and the system will be waiting ;for user input. user may terminate the relay on condition ;by hitting any key. ;run mode: if the run key is used to terminate a set of digits, then ;the system goes into a special mode that allows the user to change ;any of 7 sets of user entry codes. the ** master code ** that must be ;entered (followed by run) *must* be 8 digits. since there must ;be some means to indicate which set is to be changed, the following ;sequence is followed: ;1) clr followed by 8 digit master code followed by run ;2) flashing red led comes on to indicate you have entered run mode. ; green led comes on steady ;3) next entry *must* be a digit from 0-6 to indicate which *set*. ; (0 indicates entry of new user-mastercode) ; as soon as this digit has been entered the red led goes steady, ; and the green led will stay on to indicate run normal mode. ;4) the next entry *must* be the load key, or input is terminated. ;5) up to 8 digits may now be entered. ; a) if *no* digits are entered, set is invalid (never matches). ; this is useful in *removing* a code without entering a new one. ; b) if more than 8 digits are entered, then entry is ignored. ;6) entry of valid set is complete when terminating ent key is hit. ;7) the set # entered is now available. mainprog movlw b'11110000' ;scan all 4 lines at once. movwf portb ;initialize keyboard scanning bcf porta,relay ;turn off relay bcf porta,n_red ;turn red led on while waiting for keypress. ;interrupt handler will turn red led off ;during keypress and back on when ;key is released. bsf porta,n_green ;turn green led off. movf keycode,w ;check for yclr (may have occurred in subroutine) sublw yclr btfsc status,z goto gotyclr ;yclr is handled special. bsf porta,n_green ;turn green led off. when green is on ;it indicates special master code mode. clrf keycode ;clear keycode to allow new keycode. mainloop movf keycode,w ;see if keycode is present btfsc status,z goto mainloop ;loop while empty... ;eventually inthandler loads keycode sublw yclr ;was it yclr? btfsc status,z goto gotyclr ;if so,continue... goto mainloop ;else keep looking for yclr gotyclr bcf porta,n_green ;blink green led *once* to indicate yclr movlw d'20' call wmillisec bsf porta,n_green ;turn green led off. clrf keycode ;clear keycode & wait for next key gotyclr1 movf keycode,w ;check keycode btfsc status,z goto gotyclr1 ;loop while empty... ;eventually inthandler loads keycode movf keycode,w ;it might be another yclr! sublw yclr btfsc status,z goto gotyclr ;if so, handle it properly. call getuser ;get a set of inputs. (have 1 already) movf exitkey,w ;check for exitmode. sublw 0 ;was it error? btfsc status,z goto mainprog ;on error or yclr start all over. movf exitkey,w sublw yent ;check for yent btfsc status,z goto wasent ;handle elsewhere if yent. movf exitkey,w sublw yrun ;check for yrun btfsc status,z goto wasrun ;handle elsewhere if yrun. goto mainprog ;anything else was an error. ;(yclr is handled by mainprog) wasent ;compare user set with all eight ;code sets. If ANY set matches, ;then open door. wait, then goto mainprog movlw 0*8 ;8 checks are very similar... movwf eeadr ;set up eeadr for proper set. call compare8 ;do generic compare of user/eeprom sets movf ok,w ;if ok is not zero then we have a match. btfss status,z ;0-5 are 6 user sets, and goto entok ;6 and 7 are mastercode sets. movlw 1*8 ;if there was no match, then program movwf eeadr ;checks *next* set of codes in eeprom call compare8 movf ok,w btfss status,z goto entok movlw 2*8 ;etc. movwf eeadr call compare8 movf ok,w btfss status,z goto entok movlw 3*8 ;etc. movwf eeadr call compare8 movf ok,w btfss status,z goto entok movlw 4*8 ;etc. movwf eeadr call compare8 movf ok,w btfss status,z goto entok movlw 5*8 ;etc movwf eeadr call compare8 movf ok,w btfss status,z goto entok movlw 6*8 ;include user mastercode check. movwf eeadr call compare8 movf ok,w btfss status,z goto entok movlw 7*8 ;include factory mastercode check. movwf eeadr call compare8 movf ok,w btfss status,z goto entok goto mainprog ;if no sets matched ;then incorrect codes were entered. ;if we got this far, then it was a complete match! entok bsf porta,relay ;turn relay on movlw d'100' ;for 100*100 msec=1 sec movwf usercounter1 clrf keycode ;clear keycode to allow new key ldelay movf keycode,w ;any key hit will terminate. btfss status,z goto mainprog bcf porta,n_red ;turn red led on. bsf porta,n_green ;and green led off. movlw d'50' call wmillisec ;delay 50 msec bsf porta,n_red ;turn red led off. bcf porta,n_green ;and green led on. movlw d'50' call wmillisec ;delay 50 msec decfsz usercounter1,f ;update counter goto ldelay goto mainprog ;run mode: if the run key is used to terminate a set of digits, then ;the system goes into a special mode that allows the user to change ;any of 7 sets of user entry codes. the *** master code *** that must be ;entered (followed by run) *must* be 8 digits. since there must ;be some means to indicate which set is to be changed, the following ;sequence is followed: ;1) clr followed by 8 digit master code followed by run ;2) flashing red led comes on to indicate you have entered run set mode. ; green led comes on steady ;3) next entry *must* be a digit from 0-6 to indicate which *set*. ; (0 indicates entry of new user-mastercode) ; as soon as this digit has been entered the red led goes steady, ; and the green led will stay on to indicate run normal mode. ;4) the next entry *must* be the load key, or input is terminated. ;5) up to 8 digits may now be entered. ; a) if *no* digits are entered, then set is made invalid (never matches) ; this is useful in *removing* a code without entering a new one. ; b) if more than 8 digits are entered, then entry is ignored. ;6) entry of valid set is complete when terminating ent key is hit. ;7) the set # entered is now available. wasrun ;compare user set with mastercode. ;and also with user-mastercode! ;if ok, then allow new entry set ;turn on green led. ;1st key is pointer, next 8 max ;get entered at pointer in eeprom ;then goto mainprog movlw 6*8 ;compare user with user-mastercode movwf eeadr call compare8 movf ok,w btfss status,z goto runok movlw 7*8 ;compare user with factory-mastercode movwf eeadr call compare8 movf ok,w btfss status,z goto runok goto mainprog ;if no match, begin again. runok clrf keycode ;clear out any old key data. bcf porta,n_green ;turn green led on. runloop1 bcf porta,n_red ;turn red led on. movlw d'35' call wmillisec ;delay 35 msec bsf porta,n_red ;then turn red led off. movlw d'35' call wmillisec ;delay 35 msec movf keycode,w ;interrupt will load it! btfsc status,z goto runloop1 ;wait for key. ;once a key is found bcf porta,n_red ;turn red led on. bcf porta,n_green ;turn green led on. sublw yclr btfsc status,z goto mainprog ;handle yclr special. movf keycode,w ;look at code again sublw '6' ;allow only stuff <= '6' btfss status,c ;if w<='6', c is set goto mainprog ;if w>'6', c is cleared, so ignore. movf keycode,w ;look at code again andlw b'00000111' ;convert from ascii to binary ; 0-6 max! movwf eeadr1 ;save it decf eeadr1,f ;reduce address by 1 btfss eeadr1,7 ;0-1 would give us 11111111 goto rollem ;if msb=0 handle normal. movlw b'0000110' ;convert 0 to 6 movwf eeadr1 ;so it is now user mastercode ;then save it in eeadr1. rollem bcf status,c ;initial carry in must be 0! rlf eeadr1,f ;*2 rlf eeadr1,f ;*4 rlf eeadr1,f ;*8 ;we now have proper ee address set in eeadr1 eeadrok clrf keycode ;get ready for next key. loadloop movf keycode,w ;interrupt will load it! btfsc status,z goto loadloop ;wait for key. sublw yload ;it *must* be the load key btfss status,z goto mainprog ;if not, forget it! clrf keycode call getuser ;get user input(s) movf exitkey,w ;check for exitmode. sublw yent ;was it ent? btfss status,z goto mainprog ;on error or yclr start all over. ent2stuff movlw d'8' ;eight digits to be moved movwf usercounter1 ;use this as a counter movf eeadr1,w ;recover starting address movwf eeadr ;set up ee address movlw user0 ;point to user set in ram. movwf fsr ;make it indirect pointer. nextent2 movf indf,w ;use indirect pointer movwf eedata ;copy ram to eedata call writeeedata ;use subroutine to do that. decfsz usercounter1,f ;update loop goto next1ent2 goto mainprog ;when done, start all over. next1ent2 incf eeadr,f ;update ee address incf fsr,f ;update indirect pointer goto nextent2 ;do all 8 digits... ;============================================================================== ; getuser: (subroutine) : gets up to 8 digits and a terminator ; returns exitkey with value of terminator, or 0 if error ; such as too many digits entered or clr hit during entry. ;============================================================================== getuser clrf exitkey ;get ready for next key... movf keycode,w ;always check for yclr! sublw yclr ;was it yclr? btfss status,z goto get8keys ;if not, do usual handleyclr movlw h'00' movwf exitkey ;tell 'em via exitkey goto checkret ;handle yclr special get8keys clrf user0 ;clear all key holders clrf user1 clrf user2 clrf user3 clrf user4 clrf user5 clrf user6 clrf user7 movlw user0 movwf fsr ;allow indirect addressing. clrf usercounter1 ;we handle 8 keys, 1-8 get8wait movf keycode,w ;updated via interrupt! btfsc status,z goto get8wait ;if empty, keep looking... sublw yclr ;always check for yclr btfsc status,z goto handleyclr ;yclear always synchronizes things! ;now check for yent and yrun *early*. ent movf keycode,w ;get current key input into w sublw yent ;yent means terminate entry btfsc status,z goto checkent ;handle yent elsewhere... movf keycode,w ;check for yrun sublw yrun btfsc status,z goto checkrun ;if yrun, handle special... ;if you got here, then last key was not special movf keycode,w movwf indf ;indirect keycode to user elements clrf keycode ;reset keycode to allow next key entry. incf fsr,f ;update indirect pointer incf usercounter1,f ;update counter movf usercounter1,w sublw 9 ;only 8 digits allowed max (1-8) btfss status,z goto get8wait ;if not '8' yet, get next entry... ;if you got here, then 9th key was not special ;which means it *must* be an error! goto checkret ;return with exitkey=0 ;to indicate error. checkent movlw yent movwf exitkey goto checkret ;return with yent in exitkey. checkrun movlw yrun movwf exitkey checkret return ;return with yrun in exitkey. compare8 clrf ok ;assume not ok at first. movf user0,w ;look at first key entry btfsc status,z goto compareret ;return with error set if null set ;(this prevents null set from being ok) ;compare user set with eeadr set. ;returns with ok non-zero if matched. movlw 1 ;set ok for now. movwf ok movlw 8 movwf usercounter1 ;initialize counter to 8 movlw user0 movwf fsr ;allow indirect addressing compareloop movf indf,w ;recover user data bsf status,rp0 ;we do this stuff on page 1 bsf eecon1,rd ;set up read bcf status,rp0 ;we do usual stuff on page 0 subwf eedata,w ;compare with eeprom data btfss status,z goto compareerror ;if different, error! incf eeadr,f ;get ready for next... direct incf fsr,f ;and indirect decfsz usercounter1,f ;update counter goto compareloop ;if not all 8 done, do more. goto compareret ;if all 8 matched, all done! compareerror clrf ok ;on error, say so. compareret return ;============================================================================== ; eeprom stuff ;============================================================================== ;eecon1 register: ;<7,6,5> unused. rest are r/w. ;<4> eeif. eeprom_int_flag. see intcon eeie. can be software polled / reset. ;<3> wrerr. write_error flag (mclr or wdt). software reset. ;<2> wren. write_enable. 1=enable. software set/reset. ;<1> wr. write. set=1 to initiate write. cleared automatically by hardware. ;<0> rd. read. set=1 to initiate read. cleared automatically by hardware. ; ;reading/writing eeprom data area on-the-fly: ; ;accessed via eedata and eeadr. eedata is accessed for r/w data, and ;eeadr is used to hold the address of the desired data. ;eecon1 and eecon2 are eeprom_control registers to make sure that there ;can never be an accidental write to the eeprom. ; ;to read: ;load eeadr with the desired address. ;set eecon1<0> rd=1. (it is automatically cleared by hardware) ;read eedata. data remains in eedata until next read/write. ; ;to write: follow example below exactly! ;============================================================================== ; writeeedata: (subroutine) : enter with eedata and eeadr loaded ;============================================================================== writeeedata bsf status,rp0 ;we do this stuff on page 1 bcf eecon1,eeif ;we have to clear this sucker! bsf eecon1,wren ;enable writes! bcf intcon,gie ;disable interrupts! movlw h'55' ; note: code sequence must be movwf eecon2 ;*exactly* as shown. movlw h'aa' movwf eecon2 bsf eecon1,wr eewait btfss eecon1,eeif ;poll eeif. goto eewait bcf eecon1,eeif ;we have to clear this sucker! bcf eecon1,wren ;disable further writes! bsf intcon,gie ;re-enable interrupts! bcf status,rp0 ;back to page 0 return ;============================================================================== ; wblinks: (subroutine) : enter with number of blinks in w. blinks green led. ;============================================================================== wblinks movwf blinks ;save # of blinks incf blinks,f ;adjust for initial decrement wblinksloop decfsz blinks,f ;update goto blinkit ;on/off once return ;return when all done. blinkit bcf porta,n_green ;on call xmillisecs ;delay bsf porta,n_green ;off call xmillisecs ;delay goto wblinksloop ;do more blink sets... ;============================================================================== ; xmillisecs: (subroutine) : 200 msec delay. flows into wmillisec. ;============================================================================== xmillisecs movlw d'200' ;delay... ;============================================================================== ; wmillisecs: (subroutine) : delay for w millisecs. tuned for 4.00 mhz xtal. ;============================================================================== ;called subroutine set to generate delays. ;enter with # of milliseconds to delay in W wmillisec movwf xmillisec ;save # of millisecs in w to delay. incf xmillisec,f ;adjust to account for initial decrement. ;first (outer) loop. wmloop1 decfsz xmillisec,f ;update first (outer) loop goto wmloopa ;if more to do, do it. return ;done when xmillisec=0. wmloopa clrf ymillisec ;second (inner) loop ;256 loops (256+1 becomes 0) wmloopb decfsz ymillisec,f ;update 256 part of inner loop. goto wmloopb ;3 usec per loop movlw d'75'+1 movwf ymillisec ;75 loops for third loop wmloopc decfsz ymillisec,f ;update 75 part of inner loop. goto wmloopc ;3 usec per loop goto wmloop1 ;continue with next outer loop ;total for inner loop sets is ;3*(256+75)=993 usec. ;this together with the overhead ;makes the total as close to 1000 usec ;as we can get it. ;1000 usec = 1 millisecond ;============================================================================== ; end of program ;============================================================================== end