;;;;;;; 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_ 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 BREAK_ ENDIF_ movlw 0xff ;Add -1 to decrement KEYCODE addwf KEYCODE,F ;Done when 00 + ff produces no carry UNTIL_ .NC. 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 movlw 0x30 ;Form ASCII value addwf KEYCODE,W ELSE_ IF_ .Z. ;* movlw 0x2a ELSE_ ;# movlw 0x23 ENDIF_ ENDIF_ 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 call AnyKey ;Set Z if no key is pressed IF_ .NZ. ;Key is pressed so change to KEYSTATE = 1 incf KEYSTATE,F ENDIF_ ELSE_ decf TEMP,F IF_ .Z. ;KEYSTATE = 1 SCANKEYS ;Set Z if a valid KEYCODE is found IF_ .Z. ;Valid keycode FORMASCII ;Convert KEYCODE to ASCII incf KEYSTATE,F ;Change to KEYSTATE = 2 ELSE_ ;Invalid keycode clrf KEYSTATE ;Change to KEYSTATE = 0 ENDIF_ ELSE_ decf TEMP,F IF_ .Z. ;KEYSTATE = 2 call AnyKey ;Set Z if no key is pressedd IF_ .Z. ;Key has been released incf KEYSTATE,F ;Change to KEYSTATE = 3 ENDIF_ ELSE_ ;KEYSTATE = 3 call AnyKey ;Set Z if no key is pressed IF_ .Z. ;Key has, indeed, been released clrf KEYSTATE ;Start over, looking for a new key press ELSE_ decf KEYSTATE,F ;Aberration occurred, return to KEYSTATE = 2 ENDIF_ ENDIF_ ENDIF_ ENDIF_ 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. call BitWait ;Wait for bit time bcf PORTC,3 ;START bit MOVLF 8,TEMP ;Initialize bit counter REPEAT_ rrf ASCII,F ;Peel off a bit into C call BitWait ;Wait for bit time IF_ .C. bsf PORTC,3 ELSE_ bcf PORTC,3 ENDIF_ decf TEMP,F UNTIL_ .Z. call BitWait ;Wait for bit time bsf PORTC,3 ;STOP bit clrf ASCII ;Done with character ENDIF_ 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_ 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 IF_ TEMP,7 == 0 ; but is now 0 BREAK_ ; so exit loop ENDIF_ ENDIF_ movwf TMR0LAST ;Copy W (i.e., TEMP) to TMR0LAST ENDLOOP_ 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_ UNTIL_ TMR0,0 == 1 ;Wait for high TMR0, bit 0 REPEAT_ UNTIL_ TMR0,0 == 0 ;Wait for falling edge retlw 0 ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline INITIAL ;Initialize everything LOOP_ KEYSWITCH ;Check keys UART ;Send ASCII keycode LOOPTIME ;Set loop time to 13.1 milliseconds ENDLOOP_ end