PIC Tutorial Four - JoysticksFor this tutorial you require the Main Board, LCD Board and the Joystick Board, as written the tutorials use the LCD Board on PortA and the Joystick Board on PortB - although these could easily be swapped over, as the Joystick Board doesn't use either of the two 'difficult' pins for PortA, pins 4 and 5. Download zipped tutorial files. Tutorial 4.1 - requires Main Board, LCD Board and Joystick Board. This program uses the LCD module to give a hexadecimal display of the values of the X and Y resistors in the joystick, and an indication if the buttons are pressed or not. It uses the LCD subroutines from the previous tutorial, and subroutines written to read the joystick. ;Joystick routines with LCD display ;Nigel Goodwin 2002 LIST p=16F628 ;tell assembler what chip we are using include "P16F628.inc" ;include the defaults for the chip ERRORLEVEL 0, -302 ;suppress bank selection messages __config 0x3D18 ;sets the configuration settings (oscillator type etc.) cblock 0x20 ;start of general purpose registers count ;used in looping routines count1 ;used in delay routine counta ;used in delay routine countb ;used in delay routine tmp1 ;temporary storage tmp2 templcd ;temp store for 4 bit mode templcd2 HiX ;result for X pot LoX HiY ;result for Y pot LoY Flags endc LCD_PORT Equ PORTA LCD_TRIS Equ TRISA LCD_RS Equ 0x04 ;LCD handshake lines LCD_RW Equ 0x06 LCD_E Equ 0x07 JOY_PORT Equ PORTB JOY_TRIS Equ TRISB PotX Equ 0x06 ;input assignments for joystick PotY Equ 0x03 SW1 Equ 0x07 SW2 Equ 0x02 SW1_Flag Equ 0x01 ;flags used for key presses SW2_Flag Equ 0x02 org 0x0000 goto Start ;TABLES - moved to start of program memory to avoid paging problems, ;a table must not cross a 256 byte boundary. HEX_Table ADDWF PCL , f RETLW 0x30 RETLW 0x31 RETLW 0x32 RETLW 0x33 RETLW 0x34 RETLW 0x35 RETLW 0x36 RETLW 0x37 RETLW 0x38 RETLW 0x39 RETLW 0x41 RETLW 0x42 RETLW 0x43 RETLW 0x44 RETLW 0x45 RETLW 0x46 Xtext addwf PCL, f retlw 'J' retlw 'o' retlw 'y' retlw '-' retlw 'X' retlw ' ' retlw 0x00 Ytext addwf PCL, f retlw 'J' retlw 'o' retlw 'y' retlw '-' retlw 'Y' retlw ' ' retlw 0x00 presstext addwf PCL, f retlw 'C' retlw 'l' retlw 'o' retlw 's' retlw 'e' retlw 0x00 nopresstext addwf PCL, f retlw 'O' retlw 'p' retlw 'e' retlw 'n' retlw ' ' retlw 0x00 ;end of tables Start movlw 0x07 movwf CMCON ;turn comparators off (make it like a 16F84) Initialise clrf count clrf PORTA clrf PORTB bcf Flags, SW1_Flag ;clear button pressed flags bcf Flags, SW2_Flag SetPorts bsf STATUS, RP0 ;select bank 1 movlw 0x00 ;make all LCD pins outputs movwf LCD_TRIS movlw 0xff ;make all joystick pins inputs movwf JOY_TRIS bcf STATUS, RP0 ;select bank 0 call JOY_Init ;discharge timing capacitors call Delay100 ;wait for LCD to settle call LCD_Init ;setup LCD module Main call ReadX ;read X joystick call ReadY ;read Y joystick call ReadSW ;read switches call LCD_Line1 ;set to first line call XString ;display Joy-X string movf HiX, w ;display high byte call LCD_HEX movf LoX, w ;display low byte call LCD_HEX movlw ' ' call LCD_Char call DisplaySW1 call LCD_Line2 ;set to second line call YString ;display Joy-Y string movf HiY, w ;display high byte call LCD_HEX movf LoY, w ;display low byte call LCD_HEX movlw ' ' call LCD_Char call DisplaySW2 goto Main ;loop for ever ;Subroutines and text tables DisplaySW1 btfsc Flags, SW1_Flag goto Press_Str btfss Flags, SW1_Flag goto NoPress_Str retlw 0x00 DisplaySW2 btfsc Flags, SW2_Flag goto Press_Str btfss Flags, SW2_Flag goto NoPress_Str retlw 0x00 XString clrf count ;set counter register to zero Mess1 movf count, w ;put counter value in W call Xtext ;get a character from the text table xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess1 YString clrf count ;set counter register to zero Mess2 movf count, w ;put counter value in W call Ytext ;get a character from the text table xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess2 Press_Str clrf count ;set counter register to zero Mess3 movf count, w ;put counter value in W call presstext ;get a character from the text table xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess3 NoPress_Str clrf count ;set counter register to zero Mess4 movf count, w ;put counter value in W call nopresstext ;get a character from the text table xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess4 ;LCD routines ;Initialise LCD LCD_Init movlw 0x20 ;Set 4 bit mode call LCD_Cmd movlw 0x28 ;Set display shift call LCD_Cmd movlw 0x06 ;Set display character mode call LCD_Cmd movlw 0x0d ;Set display on/off and cursor command call LCD_Cmd call LCD_Clr ;clear display retlw 0x00 ; command set routine LCD_Cmd movwf templcd swapf templcd, w ;send upper nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bcf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high movf templcd, w ;send lower nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bcf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high call Delay5 retlw 0x00 LCD_CharD addlw 0x30 ;convert numbers to ASCII values LCD_Char movwf templcd ;display character in W register swapf templcd, w ;send upper nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bsf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high movf templcd, w ;send lower nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bsf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high call Delay5 retlw 0x00 LCD_Line1 movlw 0x80 ;move to 1st row, first column call LCD_Cmd retlw 0x00 LCD_Line2 movlw 0xc0 ;move to 2nd row, first column call LCD_Cmd retlw 0x00 LCD_CurOn movlw 0x0d ;Set block cursor on call LCD_Cmd retlw 0x00 LCD_CurOff movlw 0x0c ;Set block cursor off call LCD_Cmd retlw 0x00 LCD_Clr movlw 0x01 ;Clear display call LCD_Cmd retlw 0x00 LCD_HEX movwf tmp1 ;display W as hexadecimal byte swapf tmp1, w andlw 0x0f call HEX_Table call LCD_Char movf tmp1, w andlw 0x0f call HEX_Table call LCD_Char retlw 0x00 Pulse_e bsf LCD_PORT, LCD_E nop bcf LCD_PORT, LCD_E retlw 0x00 ;end of LCD routines ;joystick routines JOY_Init ;setup joystick port bsf STATUS, RP0 ;select bank 1 bcf JOY_TRIS, PotX ;make PotX an output bcf JOY_PORT, PotX ;discharge capacitor bcf JOY_TRIS, PotY ;make PotY an output bcf JOY_PORT, PotY ;discharge capacitor bcf STATUS, RP0 ;select bank 0 retlw 0x00 ReadX clrf HiX ;reset counter registers clrf LoX bsf STATUS, RP0 ;select bank 1 bsf JOY_TRIS, PotX ;make PotX an input bcf STATUS, RP0 ;select bank 0 x1 btfsc JOY_PORT, PotX ;keep going until input high goto EndX incfsz LoX,f goto x1 incfsz HiX,f goto x1 EndX bsf STATUS, RP0 ;select bank 1 bcf JOY_TRIS, PotX ;make PotX an output bcf JOY_PORT, PotX ;discharge capacitor bcf STATUS, RP0 ;select bank 0 retlw 0x00 ReadY clrf HiY ;reset counter registers clrf LoY call Delay5 bsf STATUS, RP0 ;select bank 1 bsf JOY_TRIS, PotY ;make PotY an input bcf STATUS, RP0 ;select bank 0 y1 btfsc JOY_PORT, PotY ;keep going until input high goto EndY incfsz LoY,f goto y1 incfsz HiY,f goto y1 EndY bsf STATUS, RP0 ;select bank 1 bcf JOY_TRIS, PotY ;make PotY an output bcf JOY_PORT, PotY ;discharge capacitor bcf STATUS, RP0 ;select bank 0 retlw 0x00 ReadSW btfss JOY_PORT, SW1 call Sw1On btfss JOY_PORT, SW2 call Sw2On btfsc JOY_PORT, SW1 call Sw1Off btfsc JOY_PORT, SW2 call Sw2Off retlw 0x00 Sw1On bsf Flags, SW1_Flag retlw 0x00 Sw2On bsf Flags, SW2_Flag retlw 0x00 Sw1Off bcf Flags, SW1_Flag retlw 0x00 Sw2Off bcf Flags, SW2_Flag retlw 0x00 ;end of joystick routines ;Delay routines Delay255 movlw 0xff ;delay 255 mS goto d0 Delay100 movlw d'100' ;delay 100mS goto d0 Delay50 movlw d'50' ;delay 50mS goto d0 Delay20 movlw d'20' ;delay 20mS goto d0 Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock) d0 movwf count1 d1 movlw 0xC7 ;delay 1mS movwf counta movlw 0x01 movwf countb Delay_0 decfsz counta, f goto $+2 decfsz countb, f goto Delay_0 decfsz count1 ,f goto d1 retlw 0x00 ;end of Delay routines end Tutorial 4.2 - requires Main Board, LCD Board and Joystick Board. This second program is very similar to the previous one, except it uses a different method of counting the time taken to charge the capacitor. Whereas the first example used a simple software counter, this one uses a hardware timer for the lower byte, and an interrupt driven routine for the upper byte. This has the major advantage of being more accurate, giving 1uS resolution. ;Joystick routines with LCD display ;Nigel Goodwin 2002 LIST p=16F628 ;tell assembler what chip we are using include "P16F628.inc" ;include the defaults for the chip ERRORLEVEL 0, -302 ;suppress bank selection messages __config 0x3D18 ;sets the configuration settings (oscillator type etc.) cblock 0x20 ;start of general purpose registers count ;used in looping routines count1 ;used in delay routine counta ;used in delay routine countb ;used in delay routine tmp1 ;temporary storage tmp2 templcd ;temp store for 4 bit mode templcd2 HiX ;result for X pot LoX HiY ;result for Y pot LoY Timer_H Flags endc LCD_PORT Equ PORTA LCD_TRIS Equ TRISA LCD_RS Equ 0x04 ;LCD handshake lines LCD_RW Equ 0x06 LCD_E Equ 0x07 JOY_PORT Equ PORTB JOY_TRIS Equ TRISB PotX Equ 0x06 ;input assignments for joystick PotY Equ 0x03 SW1 Equ 0x07 SW2 Equ 0x02 SW1_Flag Equ 0x01 ;flags used for key presses SW2_Flag Equ 0x02 org 0x0000 goto Start ORG 0x0004 BCF INTCON, T0IF INCF Timer_H, f RETFIE Start movlw 0x07 movwf CMCON ;turn comparators off (make it like a 16F84) Initialise clrf count clrf PORTA clrf PORTB bcf Flags, SW1_Flag ;clear button pressed flags bcf Flags, SW2_Flag SetPorts bsf STATUS, RP0 ;select bank 1 movlw 0x00 ;make all LCD pins outputs movwf LCD_TRIS movlw 0xff ;make all joystick pins inputs movwf JOY_TRIS MOVLW 0x88 ;assign prescaler to watchdog MOVWF OPTION_REG bcf STATUS, RP0 ;select bank 0 CLRF INTCON BSF INTCON , T0IE ;enable timer interrupts call JOY_Init ;discharge timing capacitors call Delay100 ;wait for LCD to settle call LCD_Init ;setup LCD module Main call ReadX ;read X joystick call ReadY ;read Y joystick call ReadSW ;read switches call LCD_Line1 ;set to first line call XString ;display Joy-X string movf HiX, w ;display high byte call LCD_HEX movf LoX, w ;display low byte call LCD_HEX movlw ' ' call LCD_Char call DisplaySW1 call LCD_Line2 ;set to second line call YString ;display Joy-Y string movf HiY, w ;display high byte call LCD_HEX movf LoY, w ;display low byte call LCD_HEX movlw ' ' call LCD_Char call DisplaySW2 goto Main ;loop for ever ;Subroutines and text tables DisplaySW1 btfsc Flags, SW1_Flag goto Press_Str btfss Flags, SW1_Flag goto NoPress_Str retlw 0x00 DisplaySW2 btfsc Flags, SW2_Flag goto Press_Str btfss Flags, SW2_Flag goto NoPress_Str retlw 0x00 XString clrf count ;set counter register to zero Mess1 movf count, w ;put counter value in W bsf PCLATH, 1 call Xtext ;get a character from the text table bcf PCLATH, 1 xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess1 YString clrf count ;set counter register to zero Mess2 movf count, w ;put counter value in W bsf PCLATH, 1 call Ytext ;get a character from the text table bcf PCLATH, 1 xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess2 Press_Str clrf count ;set counter register to zero Mess3 movf count, w ;put counter value in W bsf PCLATH, 1 call presstext ;get a character from the text table bcf PCLATH, 1 xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess3 NoPress_Str clrf count ;set counter register to zero Mess4 movf count, w ;put counter value in W bsf PCLATH, 1 call nopresstext ;get a character from the text table bcf PCLATH, 1 xorlw 0x00 ;is it a zero? btfsc STATUS, Z retlw 0x00 ;return when finished call LCD_Char incf count, f goto Mess4 ;LCD routines ;Initialise LCD LCD_Init movlw 0x20 ;Set 4 bit mode call LCD_Cmd movlw 0x28 ;Set display shift call LCD_Cmd movlw 0x06 ;Set display character mode call LCD_Cmd movlw 0x0d ;Set display on/off and cursor command call LCD_Cmd call LCD_Clr ;clear display retlw 0x00 ; command set routine LCD_Cmd movwf templcd swapf templcd, w ;send upper nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bcf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high movf templcd, w ;send lower nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bcf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high call Delay5 retlw 0x00 LCD_CharD addlw 0x30 ;convert numbers to ASCII values LCD_Char movwf templcd ;display character in W register swapf templcd, w ;send upper nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bsf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high movf templcd, w ;send lower nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bsf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high call Delay5 retlw 0x00 LCD_Line1 movlw 0x80 ;move to 1st row, first column call LCD_Cmd retlw 0x00 LCD_Line2 movlw 0xc0 ;move to 2nd row, first column call LCD_Cmd retlw 0x00 LCD_CurOn movlw 0x0d ;Set block cursor on call LCD_Cmd retlw 0x00 LCD_CurOff movlw 0x0c ;Set block cursor off call LCD_Cmd retlw 0x00 LCD_Clr movlw 0x01 ;Clear display call LCD_Cmd retlw 0x00 LCD_HEX movwf tmp1 ;display W as hexadecimal byte swapf tmp1, w andlw 0x0f bsf PCLATH, 1 call HEX_Table bcf PCLATH, 1 call LCD_Char movf tmp1, w andlw 0x0f bsf PCLATH, 1 call HEX_Table bcf PCLATH, 1 call LCD_Char retlw 0x00 Pulse_e bsf LCD_PORT, LCD_E nop bcf LCD_PORT, LCD_E retlw 0x00 ;end of LCD routines ;joystick routines JOY_Init ;setup joystick port bsf STATUS, RP0 ;select bank 1 bcf JOY_TRIS, PotX ;make PotX an output bcf JOY_PORT, PotX ;discharge capacitor bcf JOY_TRIS, PotY ;make PotY an output bcf JOY_PORT, PotY ;discharge capacitor bcf STATUS, RP0 ;select bank 0 retlw 0x00 ReadX clrf Timer_H ;clear timer hi byte bsf STATUS, RP0 ;select bank 1 bsf JOY_TRIS, PotX ;make PotX an input bcf STATUS, RP0 ;select bank 0 clrf TMR0 bcf INTCON, T0IF ;start timer bsf INTCON, GIE ;start interrupts btfss JOY_PORT, PotX goto $-1 ;loop until input high clrw iorwf TMR0, f ;stop timer (for 3 cycles) movf TMR0, W movwf LoX ;and read immediately movf Timer_H, W movwf HiX bcf INTCON, GIE ;turn off interrupts btfsc INTCON, GIE goto $-2 bsf STATUS, RP0 ;select bank 1 bcf JOY_TRIS, PotX ;make PotX an output bcf JOY_PORT, PotX ;discharge capacitor bcf STATUS, RP0 ;select bank 0 retlw 0x00 ReadY clrf Timer_H ;clear timer hi byte bsf STATUS, RP0 ;select bank 1 bsf JOY_TRIS, PotY ;make PotY an input bcf STATUS, RP0 ;select bank 0 clrf TMR0 bcf INTCON, T0IF ;start timer bsf INTCON, GIE ;start interrupts btfss JOY_PORT, PotY goto $-1 ;loop until input high clrw iorwf TMR0, f ;stop timer (for 3 cycles) movf TMR0, W movwf LoY ;and read immediately movf Timer_H, W movwf HiY bcf INTCON, GIE ;turn off interrupts btfsc INTCON, GIE goto $-2 bsf STATUS, RP0 ;select bank 1 bcf JOY_TRIS, PotY ;make PotY an output bcf JOY_PORT, PotY ;discharge capacitor bcf STATUS, RP0 ;select bank 0 retlw 0x00 ReadSW btfss JOY_PORT, SW1 call Sw1On btfss JOY_PORT, SW2 call Sw2On btfsc JOY_PORT, SW1 call Sw1Off btfsc JOY_PORT, SW2 call Sw2Off retlw 0x00 Sw1On bsf Flags, SW1_Flag retlw 0x00 Sw2On bsf Flags, SW2_Flag retlw 0x00 Sw1Off bcf Flags, SW1_Flag retlw 0x00 Sw2Off bcf Flags, SW2_Flag retlw 0x00 ;end of joystick routines ;Delay routines Delay255 movlw 0xff ;delay 255 mS goto d0 Delay100 movlw d'100' ;delay 100mS goto d0 Delay50 movlw d'50' ;delay 50mS goto d0 Delay20 movlw d'20' ;delay 20mS goto d0 Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock) d0 movwf count1 d1 movlw 0xC7 ;delay 1mS movwf counta movlw 0x01 movwf countb Delay_0 decfsz counta, f goto $+2 decfsz countb, f goto Delay_0 decfsz count1 ,f goto d1 retlw 0x00 ;end of Delay routines ORG 0x0200 ;TABLES - moved to avoid paging problems, ;a table must not cross a 256 byte boundary. HEX_Table ADDWF PCL , f RETLW 0x30 RETLW 0x31 RETLW 0x32 RETLW 0x33 RETLW 0x34 RETLW 0x35 RETLW 0x36 RETLW 0x37 RETLW 0x38 RETLW 0x39 RETLW 0x41 RETLW 0x42 RETLW 0x43 RETLW 0x44 RETLW 0x45 RETLW 0x46 Xtext addwf PCL, f retlw 'J' retlw 'o' retlw 'y' retlw '-' retlw 'X' retlw ' ' retlw 0x00 Ytext addwf PCL, f retlw 'J' retlw 'o' retlw 'y' retlw '-' retlw 'Y' retlw ' ' retlw 0x00 presstext addwf PCL, f retlw 'C' retlw 'l' retlw 'o' retlw 's' retlw 'e' retlw 0x00 nopresstext addwf PCL, f retlw 'O' retlw 'p' retlw 'e' retlw 'n' retlw ' ' retlw 0x00 ;end of tables end The first change in this program is the main program start address, previously we started as immediately after the reset vector (0x0000) as possible, but the interrupt vector is located at 0x0004 so we need to skip over this with a 'goto Start' command. The small interrupt routine itself is located an 0x0004 and simply increments the high byte counter every time the hardware low byte counter overflows. The other two lines in the interrupt routine re-enable interrupts (they are cancelled automatically when called) and the return from the routine, this time using 'retfie' (Return From Interrupt) rather than 'retlw'. |