;;;;;;; CH19_505 for PIC16C505 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Scan keypad. Bit-bang ASCII keycodes to PIC18F452's UART. ; ; Use 10 MHz crystal and 2.5 MHz internal clock rate. ; ; Assemble with sasm -16 ch19_505 ; ;;;;;;; Program subroutine hierarchy (one level deep) ;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Mainline ; AnyKey ; ScanKeys_Table ; BitWait ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Mainline ; INITIAL ; KEYSWITCH ; AnyKey ; SCANKEYS ; ScanKeys_Table ; FORMASCII ; UART ; BitWait ; LOOPTIME ; ;;;;;;; Assembler directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC16C505, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON #include P16C505.inc __CONFIG _HS_OSC & _WDT_ON & _MCLRE_OFF & _ExtRC_OSC_RB4EN & _CP_OFF ;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cblock 0x08 TEMP ;Temporary variable TMR0LAST ;LOOPTIME variable KEYSTATE ;KEYSWITCH variables TESTVEC KEYCODE ASCII endc ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF macro literal,dest movlw literal movwf dest endm ;;;;;;; INITIAL macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This macro initializes everything. INITIAL macro clrf FSR ;Clear bank switching bits for direct addressing movlw B'10000110' ;Initialize OPTION register option movlw B'11111111' ;PORTB inputs tris PORTB movlw B'11110000' ;PORTC outputs tris PORTC clrf KEYSTATE ;Initialize for "no keys pressed" endm ;; SCANKEYS macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This macro is invoked by KEYSWITCH macro. It checks each key of the 3x4 ; keypad to determine if a key is pressed. If so, it returns its value in ; KEYCODE (11,...,0) and sets Z. SCANKEYS macro MOVLF 11,KEYCODE ;REPEAT_ L1 movlw B'00000111' ;Drive all column lines high iorwf PORTC,F call ScanKeys_Table ;Get next table entry movwf TESTVEC ; and set it aside andlw B'00000111' xorwf PORTC,F ;Drive selected column line low swapf TESTVEC,W andlw B'00001111' andwf PORTB,W ;Z=1 if key is pressed ;IF_ .Z. ;Exit from REPEAT loop btfss STATUS,Z goto L2 ;BREAK_ goto RL1 ;ENDIF_ L2 movlw 0xff ;Add -1 to decrement KEYCODE addwf KEYCODE,F ;Done when 00 + ff produces no carry ;UNTIL_ .NC. btfsc STATUS,C goto L1 RL1 endm ;;;;;;; FORMASCII macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This macro converts the KEYCODE value to the ASCII code for the pressed key ; (i.e., 0 -> 0x30,...,9 -> 0x39, * -> 0x2a, and # -> 0x23) FORMASCII macro movlw 256 - 10 addwf KEYCODE,W ;Form 256 -10 + KEYCODE ;IF_ .NC. ;Digit btfsc STATUS,C goto L3 movlw 0x30 ;Form ASCII value addwf KEYCODE,W ;ELSE_ goto L4 L3 ;IF_ .Z. ;* btfss STATUS,Z goto L5 movlw 0x2a ;ELSE_ ;# goto L6 L5 movlw 0x23 ;ENDIF_ L6 ;ENDIF_ L4 movwf ASCII endm ;;;;;;; KEYSWITCH macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This macro implements a state machine to generate an ASCII keycode in ; response to the press of a key. KEYSWITCH macro movf KEYSTATE,W ;Copy KEYSTATE to TEMP; set Z if zero movwf TEMP ;IF_ .Z. ;KEYSTATE = 0 btfss STATUS,Z goto L7 call AnyKey ;Set Z if no key is pressed ;IF_ .NZ. ;Key is pressed so change to KEYSTATE = 1 btfsc STATUS,Z goto L8 incf KEYSTATE,F ;ENDIF_ L8 ;ELSE_ goto L9 L7 decf TEMP,F ;IF_ .Z. ;KEYSTATE = 1 btfss STATUS,Z goto L10 SCANKEYS ;Set Z if a valid KEYCODE is found ;IF_ .Z. ;Valid keycode btfss STATUS,Z goto L11 FORMASCII ;Convert KEYCODE to ASCII incf KEYSTATE,F ;Change to KEYSTATE = 2 ;ELSE_ ;Invalid keycode goto L12 L11 clrf KEYSTATE ;Change to KEYSTATE = 0 ;ENDIF_ L12 ;ELSE_ goto L13 L10 decf TEMP,F ;IF_ .Z. ;KEYSTATE = 2 btfss STATUS,Z goto L14 call AnyKey ;Set Z if no key is pressedd ;IF_ .Z. ;Key has been released btfss STATUS,Z goto L15 incf KEYSTATE,F ;Change to KEYSTATE = 3 ;ENDIF_ L15 ;ELSE_ ;KEYSTATE = 3 goto L16 L14 call AnyKey ;Set Z if no key is pressed ;IF_ .Z. ;Key has, indeed, been released btfss STATUS,Z goto L17 clrf KEYSTATE ;Start over, looking for a new key press ;ELSE_ goto L18 L17 decf KEYSTATE,F ;Aberration occurred, return to KEYSTATE = 2 ;ENDIF_ L18 ;ENDIF_ L16 ;ENDIF_ L13 ;ENDIF_ L9 endm ;;;;;;; UART macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This macro bit-bangs the non-zero value of ASCII out RC3, LSb first, ; embedded between a low START bit and a high STOP bit with a bit time of ; 102.4 microseconds, derived from the 1-to-0 transitions on TMR0's bit 0. UART macro movf ASCII,F ;Test for zero ;IF_ .NZ. btfsc STATUS,Z goto L19 call BitWait ;Wait for bit time bcf PORTC,3 ;START bit MOVLF 8,TEMP ;Initialize bit counter ;REPEAT_ L20 rrf ASCII,F ;Peel off a bit into C call BitWait ;Wait for bit time ;IF_ .C. btfss STATUS,C goto L21 bsf PORTC,3 ;ELSE_ goto L22 L21 bcf PORTC,3 ;ENDIF_ L22 decf TEMP,F ;UNTIL_ .Z. btfss STATUS,Z goto L20 RL20 call BitWait ;Wait for bit time bsf PORTC,3 ;STOP bit clrf ASCII ;Done with character ;ENDIF_ L19 endm ;;;;;;; LOOPTIME macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This macro waits for 1-to-0 transition on TMR0's bit 7 that occurs every ; 13.1 milliseconds. LOOPTIME macro ;LOOP_ L23 clrwdt ;Reset watchdog timer while waiting movf TMR0,W ;Read TMR0 just once movwf TEMP ; and copy it to TEMP ;IF_ TMR0LAST,7 == 1 ;TMR0's bit 7 was 1 at last check btfss TMR0LAST,7 goto L24 ;IF_ TEMP,7 == 0 ; but is now 0 btfsc TEMP,7 goto L25 ;BREAK_ ; so exit loop goto PL23 ;ENDIF_ L25 ;ENDIF_ L24 movwf TMR0LAST ;Copy W (i.e., TEMP) to TMR0LAST ;ENDLOOP_ goto L23 PL23 movwf TMR0LAST ;Copy W (i.e., TEMP) to TMR0LAST endm ;;;;;;; Reset vector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0000 ;Reset vector nop goto Mainline ;;;;;;; AnyKey subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine checks all keys at once and sets the Z bit if no keys are ; pressed. AnyKey movlw B'11111000' ;Force column lines low andwf PORTC,F movf PORTB,W ;Test for B'xxxx1111' xorlw B'00001111' ; by complementing lower nibble andlw B'00001111' ; and masking off upper nibble retlw 0 ;;;;;;; ScanKeys_Table subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine uses KEYCODE as an offset into its table, to return a byte ; in W that identifies the row and column of the KEYCODE key. ScanKeys_Table movf KEYCODE,W addwf PCL,F ;Jump to table entry retlw B'10000010' ;Test "0" key retlw B'00010100' ;Test "1" key retlw B'00010010' ;Test "2" key retlw B'00010001' ;Test "3" key retlw B'00100100' ;Test "4" key retlw B'00100010' ;Test "5" key retlw B'00100001' ;Test "6" key retlw B'01000100' ;Test "7" key retlw B'01000010' ;Test "8" key retlw B'01000001' ;Test "9" key retlw B'10000100' ;Test "*" key retlw B'10000001' ;Test "#" key ;;;;;;; BitWait subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine waits for the next 1-to-0 transition on TMR0's bit 0. BitWait ;REPEAT_ L26 ;UNTIL_ TMR0,0 == 1 ;Wait for high TMR0, bit 0 btfss TMR0,0 goto L26 RL26 ;REPEAT_ L27 ;UNTIL_ TMR0,0 == 0 ;Wait for falling edge btfsc TMR0,0 goto L27 RL27 retlw 0 ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline INITIAL ;Initialize everything ;LOOP_ L28 KEYSWITCH ;Check keys UART ;Send ASCII keycode LOOPTIME ;Set loop time to 13.1 milliseconds ;ENDLOOP_ goto L28 PL28 end