Pages

How to read a PC Joystick.

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 asimple 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

0 comments:

Post a Comment