PDA

View Full Version : PICAXE 28X2 Code for Sony Playstation PS2 Controller



henryarnold
31-03-2010, 17:08
This code is a complete example of communicating with a Sony PlayStation PS2 wireless controller. It uses the hardware SPI port. Bit banging won't work on the PS2 wireless controllers because the clock is too slow and the controller errors out. The hardware SPI is very fast. The hardware SPI port sends and receives data MSB first while the PS2 requires LSB first. I flipped the commands going in and the joystick readings coming out. This means that the normal PS2 $01 and $42 header becomes $80 and $42. $42 flipped is $42.

This program will connect to the PS2, set it to analog mode, read 7 bytes of data and display in a PICAXE terminal any buttons pressed or joystick readings.

I would like to hear of a better way to flip bytes MSB for LSB. The method I used is crude but I couldn't think of anything else. NOTE: BeanieBots posted the solution which is to use REV (not the same as reverse the command). REV flips up to 16 bits.

Best Regards,
Henry Arnold


'================================================= ========================================
' Software to Interface PICAXE 28X2 to Sony Playstation PS2 Controller
' written by: Henry Arnold
' March 30, 2010
' This software uses the hardware SPI port of the PICAXE
' There are digital and anlog mode examples.
' The software prints the results to a PICAXE Terminal
' IMPORTANT the PIC hardware SPI does MSB first not LSB first so commands going to PS2
' controller and data coming from PS2 must be flipped. $01 becomes $80,
' and $43 becomes $C2. For data coming back, I did not flip the button bytes. I used
' them as is but they do not match normal PS2 documentation. For Joystick bytes, I
' flipped the data so it would become the normal 128 idle and 0/255 min/max.
' I ignore ACK but checking it could be an enhancement to the program.
'
' SPI Connections for PICAXE 28X2:
' DAT - pin C.4 pin 15 needs pullup (4-10K is fine)
' CMD - pin C.5 pin 16
' CLK - pin C.3 pin 14
' ATT - pin B.0 pin 21 or any other output

'Connections to the PS2 Controller
'
' Looking into the controller plug
'
' 1 2 3 4 5 6 7 8 9
' .----------.---------.----------.
' | o o o | o o o | o o o |
' \_________|_________|_________/
'
' D C N G V A C N A
' A M C N C T L C C
' T D D C T K K
'
' . | | | | | |
' | | | `--|---|------------ +V
' | | | | |
' pinC.4 w/ pullup--' | `-----|---|------------ 0V
' pinC.5-------' | |
' pinB.0--------------------' |
' pinC.3------------------------'
'================================================= ========================================
#picaxe 28x2
#no_table
#com 4
#Terminal 9600

Symbol DAT = pinC.4 'Input with pull-up
Symbol CMD = pinC.5 'Output
Symbol CLK = pinC.3 'Output
Symbol ATT = pinB.0 'Output

'===============MAIN part of the program=========================================== =======
We_Start_Here:

dirsB = %00000001 ' B.0 is output

ATT = 1
spimode10e, spimedium 'SPI HW setup, uses cmd msb first so we must flip commands
goto Analog_Mode 'or to Digital mode by commenting this out

Digital_Read: 'basic digital query
ATT =0 'ATT =attention must go low during transaction
hspiout($80, $42) 'output the header bytes $01 and $42 flipped is $80 & $42
hspiin (b7,b0,b1) ' input three bytes of data. b7 should be $5A
ATT =1 'flipped is also $5A
pause 50
if b7 <> $5A then Digital_Read
if w0 = $FFFF then Digital_Read
gosub display_button_results
goto Digital_Read

Analog_Mode:
gosub Analog_Enable
Analog_Read:
ATT =0
hspiout($80, $42) 'analog query
hspiin (b7,b0,b1,b2,b3,b4,b5) 'analog mode has 7 bytes, last four are joysticks
ATT =1
pause 50
if b7 <> $5A then Analog_Read
if w0 = $FFFF then joystick
gosub display_button_results
joystick:
b2 = b2 REV 8 'reverse the order of bits to make MSB first LSB first
b3 = b3 REV 8 'because SPI hardware does MSB first, this corrects bit order
b4 = b4 REV 8 'I did not do this for buttons
b5 = b5 REV 8
if b2 <> 128 Then : SerTxd("Right JSTK L/R ", #B2, CR,LF) : End If
if b3 <> 128 Then : SerTxd("Right JSTK U/D ", #B3, CR,LF) : End If
if b4 <> 128 Then : SerTxd("Leftt JSTK L/R ", #B4, CR,LF) : End If
if b5 <> 128 Then : SerTxd("Leftt JSTK U/D ", #B5, CR,LF) : End If
goto Analog_Read


'=======================subroutines=============== ======================
display_button_results:
w0 = w0 ^ $FFFF
If bit15 = 1 Then : SerTxd( "Left2 " ) : End If
If bit14 = 1 Then : SerTxd( "Right2 " ) : End If
If bit13 = 1 Then : SerTxd( "Leftt1 " ) : End If
If bit12 = 1 Then : SerTxd( "Right1 " ) : End If
If bit11 = 1 Then : SerTxd( "Triangle " ) : End If
If bit10 = 1 Then : SerTxd( "Circle " ) : End If
If bit9 = 1 Then : SerTxd( "Cross " ) : End If
If bit8 = 1 Then : SerTxd( "Square " ) : End If
If bit7 = 1 Then : SerTxd( "Select " ) : End If
If bit6 = 1 Then : SerTxd( "Left JSTK " ) : End If
If bit5 = 1 Then : SerTxd( "Right JSTK ") : End If
If bit4 = 1 Then : SerTxd( "Start " ) : End If
If bit3 = 1 Then : SerTxd( "Up " ) : End If
If bit2 = 1 Then : SerTxd( "Right " ) : End If
If bit1 = 1 Then : SerTxd( "Down " ) : End If
If bit0 = 1 Then : SerTxd( "Left " ) : End If
SerTxd( CR, LF )
return


Analog_Enable:
ATT =0
hspiout($80,$C2,$0,$80,$0) 'enter config mode
ATT =1

pause 1
ATT =0
hspiout($80,$22,$0,$80,$C0,$0,$0,$0,$0) 'Set Mode to Analog
ATT =1

ATT =0
hspiout($80,$C2,$0,$0,$0,$0,$0,$0,$0) 'exit config mode
ATT =1
pause 50
return

Jeremy Leach
31-03-2010, 18:26
Well, I can't think of an elegant way of reversing the bits, but you could have a routine like this (using appropriate b variables). Works in the Sim:



Goto Test

ReverseBits:
Symbol BitIndex = b0
Symbol DataByte = b1
Symbol ReverseDataByte = b2

'Initialise
ReverseDataByte = 0

For BitIndex = 0 to 7
ReverseDataByte = ReverseDataByte * 2
ReverseDataByte = DataByte//2 + ReverseDataByte
DataByte = DataByte/2
Next
Return

Test:
DataByte = %10101111
Gosub ReverseBits
End

BeanieBots
31-03-2010, 19:11
Well, I can't think of an elegant way of reversing the bits, but you could have a routine like this (using appropriate b variables). Works in the Sim:


I can....



REV
The REV (reverse) command reverses the order of the specified number of bits of a 16 bit number. Therefore to reverse the 8 bits of %10110000 (to %00001101)
the command would be
let b1 = %10110000 REV 8


I've used it quite a bit but it is well hidden in the manual.

Jeremy Leach
31-03-2010, 20:41
Ha ... I actually LOOKED at the manual before replying. That'll teach me to put my specs on first ...

henryarnold
31-03-2010, 22:19
Thanks guys, that was what I was looking for. I'll update my code with the new method.

I just tried it and it works. Sure cleans up some ugly code!

westaust55
01-04-2010, 14:32
Ha ... I actually LOOKED at the manual before replying. That'll teach me to put my specs on first ...

Never mind Jeremy - have been down the same path myself in the past and overlooked a line of text when actually looking in the manuals before endeavouring to answer.

Have used the REV command myself previously when using a series of LEDs as a bar graph.
By using the REV could quickly based on a selector, change from the origin/base being on one side of the bar to the other side.

D n T
11-04-2010, 14:07
Was it the genuine playstation wireless.
Very nice piece of code.
How did you work out what was what? Where did you start?

henryarnold
14-04-2010, 22:01
No, it was not a Sony Playstation 2 wireless controller. It was a Logitech PS2 Wireless Controller. The Lynxmotion Wireless Controller I started with which works with my software appears to be an off brand. I wrote the original code based on the many websites that explain how to talk to Playstation 2 controllers. I used a Logic Analyzer to debug my code and to discover a few issues. The issues were; there is a minumum speed at which the wireless controller communication ceases to work. I had good success talking to my Lynxmotion controller at 62K - 250K bps. Second, I discovered that despite many web sites that said the input data is ready on the falling edge of the clock, that it is not ready until the rising edge. Last, I realized that the data was coming back MSB first which is stated in the PIC data sheet. The PIC SPI port receives and sends data MSB first while the PS2 Controller uses LSB first. You can either manually flip the commands or use the PICAXE REV command. I already flip the joystick data being returned.

I have ordered a Playstation 2 Controller adapter which allows me to connect my wireless controller to a PC USB port. I will use this adapter to discover why the Logitech Controller does not work and will post an updated version of my software.

D n T
15-04-2010, 11:36
Thanks, I would like to use a playstation 2 controller in a robot idea, but I will use a wired unit and connect it tthe PICAXE then push the signal out through a HOPERF module.
Your work have saved me a lot of mucking aroud hacking the circuit board (caveman technology) thank you very much, I look forward to any more information you might post.

hippy
15-04-2010, 12:34
I've recently been playing with a PS2 DVD Remote Control and Memory Cards.

The Remote has a plug-in IR receiver which reports itself as a Digital Button Pad ( $41 response ) and handles the normal gamepad buttons but none of the others ( they all put out IR ). Not sure if that's a limitation of the IR receiver or not using the right protocol or not configuring it. Couldn't find any information on that.

Memory Cards are 'no joy', not even a response. Not sure if PS2 and need a different protocol, PS1 and should work, or are faulty. They look to be PS1 but aren't Sony branded.

One thing which is clear is that timing seems to be important, especially for the Remote. Needed to add a delay between reads ( with ATT high ) and the input is very sluggish and not always responsive. Probably because I'm not doing something right.

Trial and error is tedious, frustrating and mostly guess work. To do it properly I recommend a physical PS1 so you can check the parts work, slap a logic analyser on the lines to see what's happening, then replicate it, tweak after that. For some controllers there seems a lot more to it than 'read the data'.

Non-Sony stuff may be easier to work with as they are probably 'designed to work with the PS1/PS2', more tolerant of what signals they receive and less likely to have checks that they are being controlled by a PS1/PS2 with the exact timing they generate.

D n T
16-04-2010, 19:00
I tried your code (attached to your first post) with a genuine PS2 wired controller.
I have had to use a 40X2 instead of a 28X2 (don't have one), but I changed the directive at the start of the program.
The syntax checker pickered up an error so I added to it ( hspisetup) t make the syntax checker accept it:
ATT = 1
hspisetup spimode10e, spimedium
goto Analog_Mode

I am not getting any response on the terminal and I dropped a debug into the code at the start then at the digital read and analogue ( on seperate occassions) and got no response for the variables.
The circuit is correct according to you design.
Help I'm only a bit stuck (like a beached whale).