PICAXE 28X2 Code for Sony Playstation PS2 Controller

henryarnold

New Member
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

Code:
'=========================================================================================
' 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
 

Attachments

Last edited:

Jeremy Leach

Senior Member
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:

Code:
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
 
Last edited:

BeanieBots

Moderator
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.
 

henryarnold

New Member
Better Flip Than Before

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!
 
Last edited:

westaust55

Moderator
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

Senior Member
Which controller did you use?

Was it the genuine playstation wireless.
Very nice piece of code.
How did you work out what was what? Where did you start?
 
Last edited:

henryarnold

New Member
Update on PS2 Controller SW

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

Senior Member
Thank you

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

Senior Member
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

Senior Member
henry, help

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).
 

toxicmouse

Senior Member
I do not know the reasons but the code published by henryarnold in 2010 did not work for me at all with a PS2. I have published the code below that works for me and added a few notes. I used a 28x2 module.
I hope that this helps someone.

Code:
'=========================================================================================
' Software to Interface PICAXE 28X2 to Sony Playstation PS2 Controller
' written by: Henry Arnold, modified by toxicmouse (15 July 2019)
' 15 July 2019
' This software uses the hardware SPI port of the PICAXE
' The software prints the results to a PICAXE Terminal
'
' 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 (5v)
'                    |  |      |     |   |
' pinC.4 10k pullup--'  |      `-----|---|------------ 0V
'          pinC.5-------'            |   |
'          pinB.0--------------------'   |
'          pinC.3------------------------'
'=========================================================================================
#picaxe 28x2
#com 3
#Terminal 4800
#rem
Symbol DAT  = pinC.4     'Input with pull-up
Symbol CMD  = pinC.5     'Output
Symbol CLK  = pinC.3     'Output
Symbol ATT  = pinB.0     'Output
#endrem

initialise:
    setfreq m4
    sertxd("Reset",cr,lf) 
 
    high B.0 'ATT = 1. Default, do not select the PS2
      'hspisetup spimode00e, spimedium 'on this the left movement controls work.
      'hspisetup spimode01e, spimedium 'on this the left movement controls work. one top button works.
      'hspisetup spimode10e, spimedium 'nothing works on this
      hspisetup spimode11e, spimedium 'all buttons work on this
    
'=============== MAIN part of the program==================================================
main:

gosub read_ps2_controller

goto main
'============== subroutines ==================================
read_ps2_controller:             'basic digital query
   low B.0 'ATT =attention must go low during transaction
   hspiout($80, $42)      'output the header bytes $01 and $42 flipped is $80 & $42 
   hspiin (b0,b1,b2,b3,b4,b5,b6) 'b0 is just a spacer
   high B.0 'ATT = 1                   
   'sertxd("  0=",#b0,"  1=",#b1,"  2=",#b2,"  3=",#b3,"  4=",#b4,"  5=",#b5,"  6=",#b6,cr,lf) 'for the curious
   'sertxd("  3=",#b3,"    4=",#b4,"     5=",#b5,"    6=",#b6,cr,lf)'joystick outputs
   if b1 != 255 then gosub display_button_results
   if b2 != 255 then gosub display_button_results
 
'RIGHT JOYSTICK   
       if b3 = 0 then
           {
        sertxd("JS right = LEFT",cr,lf)
        }
    endif   
    if b3 = 255 then
           {
        sertxd("JS right = RIGHT",cr,lf)
        }
    endif   
    if b4 = 0 then
           {
        sertxd("JS right = UP",cr,lf)
        }
    endif   
    if b4 = 255 then
           {
        sertxd("JS right = DOWN",cr,lf)
        }
    endif   
'LEFT JOYSTICK
    if b5 = 0 then
           {
        sertxd("JS left = LEFT",cr,lf)
        }
    endif   
    if b5 = 255 then
           {
        sertxd("JS left = RIGHT",cr,lf)
        }
    endif   
    if b6 = 0 then
           {
        sertxd("JS left = UP",cr,lf)
        }
    endif   
    if b6 = 255 then
           {
        sertxd("JS left = DOWN",cr,lf)
        }
    endif   
    
    
return
 
  
'=======================subroutines=====================================   
display_button_results:       
        If b2 = 127 Then : SerTxd( "Left2 "     ) : End If
        If b2 = 191 Then : SerTxd( "Right2 "    ) : End If
        If b2 = 223 Then : SerTxd( "Leftt1 "    ) : End If
        If b2 = 239 Then : SerTxd( "Right1 "    ) : End If
        If b2 = 247 Then : SerTxd( "Triangle "  ) : End If
        If b2 = 251 Then : SerTxd( "Circle "    ) : End If
        If b2 = 253 Then : SerTxd( "Cross "     ) : End If
        If b2 = 254 Then : SerTxd( "Square "    ) : End If
        If b1 = 127 Then : SerTxd( "Select "    ) : End If
        If b1 = 239 Then : SerTxd( "Start "     ) : End If
        If b1 = 247 Then : SerTxd( "Up "        ) : End If
        If b1 = 251 Then : SerTxd( "Right "     ) : End If
        If b1 = 253 Then : SerTxd( "Down "      ) : End If
        If b1 = 254 Then : SerTxd( "Left "      ) : End If
        SerTxd( CR, LF )
        return
 
Top