Propeller C for PS2 Game Controller?

briank77479briank77479 Posts: 36
edited 2015-06-21 - 20:55:50 in Learn with BlocklyProp
I would like to integrate a Lynxmotion PS2 Controller to the Propeller Activity Board. Two questions;
1. Pin connection diagram for the PS2 Controller to the Prop?
2. Is there a Prop C program for the PS2 Controller?

Thanks, Brian

Comments

  • edited 2015-04-10 - 21:24:28
    Here is a PlayStation 2 Propeller C library and test code for reading buttons and joysticks.

    attachment.php?attachmentid=113856&d=1428729304

    It's probably best to first check Lynxmotion's documentation on the product, but if all else fails, this PS2 Adapter Cable diagram seems helpful.

    attachment.php?attachmentid=113854&d=1428728461
  • briank77479briank77479 Posts: 36
    edited 2015-04-11 - 11:36:05
    Andy,
    Thank you so very much!! Will give it a try!!

    Very Much Appreciate It!!!
    Brian
  • Duane DegnDuane Degn Posts: 10,119
    edited 2015-04-11 - 13:19:59
    Andy and Brian,

    Are you aware most of the buttons on a PS2 controller are pressure sensitive? The controller has to be put into analog mode to read the analog values from the buttons.

    I keep hoping to come up with some cool application for the analog buttons on a PS2 controller but I have yet to do so.

    I added the ability to read these analog values to Juan Carlos Orozco's PS2 object. Here's a link to the thread on the subject.

    http://forums.parallax.com/showthread.php/139611-PlayStation-2-Analog-Buttons

    If someone wants to add analog button support to the C code they could look through my Spin/PASM code for some hints.
  • briank77479briank77479 Posts: 36
    edited 2015-04-11 - 15:37:59
    Andy, Duane
    Success, sort of.
    Duane,
    Thanks for the reminder about the "Analog" mode; however;
    Andy,
    I checked the connection diagram from Lynxmotion and it matched your diagram except it didn't show a "branch" coming off of the DAT line going towards 3.3v, through the 10k resistor. I ran the program with the controller connected as per the Lynxmotion diagram and the results were;
    I could read all of the buttons, however when I tried the "pressjoystick", nothing happened and when I tried moving the joysticks it was reading as though I was pressing a button. Example, right joystick up, read a "1" on the "triangle"; right joystick down, read a "1" on the "cross". The same on the left joystick, reading "1's" as though I'm pressing the 4-pos switch above it. I added the branch line with the 10k resistor just to eliminate that difference and got the same results.
    Any ideas as to what I'm doing incorrectly?
    Thank you for your help,
    Brian
  • Duane DegnDuane Degn Posts: 10,119
    edited 2015-04-11 - 16:44:02
    I checked the connection diagram from Lynxmotion and it matched your diagram except it didn't show a "branch" coming off of the DAT line going towards 3.3v, through the 10k resistor. I ran the program with the controller connected as per the Lynxmotion diagram and the results were;

    The Lynxmotion diagram probably assumes the microcontroller has internal pull-up resistors. The AVR chips used in Arduino boards have internal pull-up resistors but the Propeller doesn't.

    I suggest testing the code using Andy's schematic and see if it works correctly.
  • edited 2015-04-12 - 14:00:38
    Hi Brian,

    The controller might start in button-only mode, and has to be switched over to add joystick info. If you changed the wiring already, go back to the setup that worked with the buttons, and verify that it is working. Then, press the Analog button on your controller (or it might say Mode). After that, try the joysticks, and hopefully the data will be correct.

    Andy
  • briank77479briank77479 Posts: 36
    edited 2015-04-12 - 14:04:56
    The last test was with the controller connected as per Andy's diagram, 1k resistors in place with 10k resistor added, the results were the same; reading all buttons and the joysticks read as one of the buttons above it. Perplexed.
    As always your help is very much appreciated.
    Brian
  • edited 2015-04-12 - 14:10:07
    We were probably replying at the same time. Let us know what happens when you try the suggestion in post #7, it's new.

    Also, the circuit that branches off the DAT line is called a pull-up resistor. The SONY wireless controller I used for prototyping needed it because its receiver sends either low or input. When it sends input, the pull-up resistor, pulls the voltage up to a 3.3 V high signal. The Lynxmotion version might have that pull-up resistor built-in. If so, it would make sense that it works either with or without the pull-up.
  • edited 2015-04-12 - 14:59:40
    Alright, so I tried it on an older vintage Lynxmotion wireless controller. On that one, the pull-up resistor was necessary, and the buttons worked, but the joysticks did not, unless I pressed the controller's Analog button to enable the feature. Joysticks did not alias to button function outputs when in button-only mode. The SONY behaved the same, no joystick output until its Mode button was pressed to switch from button-only to analog mode.

    Andy
  • Duane DegnDuane Degn Posts: 10,119
    edited 2015-04-12 - 16:00:08
    the joysticks did not, unless I pressed the controller's Analog button to enable the feature.

    I remember this button only mode now. I'm pretty sure it's possible to switch modes with software.

    As I mentioned earlier there's yet another mode which will read most of the buttons as analog values.
  • briank77479briank77479 Posts: 36
    edited 2015-04-13 - 10:52:08
    Okay, I hooked up the controller without the 10k pull-up resistor connected and it still read the buttons only. However, something did peak my curiousity. When I power up the controller, the "Analog" light is illuminated. I do recall an older Lynxmotion controller that required the "Analog" button to be pressed before that light would come on; putting it into Analog mode. With this controller I can press the "Analog" button without affecting a change in the light; it stays on. The light will flash when I remove power from adapter card. With the light on that would indicate the controller is already in Analog mode; however pressing the button doesn't take it out of Analog mode. Does this seem right to you all?

    Thank you,
    Brian
  • edited 2015-04-13 - 13:13:35
    Duane, thanks for the suggestions. I started with a different OBEX library and tried to port it, but got stuck on a bug somewhere. After that, I hastily wrote the library we are experimenting with from scratch, and as soon as I got the basics going, I posted it here. I might try again with yours. It may be a few weeks before I can pick this back up again though.

    Brian, Let's try Duane's library and see if it works for you. That could provide clues as to whether we need to examine my code or your controller more closely. I modified the attached example code to work with your existing wiring and the Parallax Serial Terminal's default baud rate, and tested to make sure it communicated with my controllers.

    Since this is a Spin example and library, here is a link to the Propeller Tool and Parallax Serial Terminal: https://www.parallax.com/downloads/propeller-tool-software
  • briank77479briank77479 Posts: 36
    edited 2015-04-13 - 15:00:06
    I tried the Spin code connecting the controller adapter to the Prop as shown on the code. As before as soon as I applied power to the controller the Analog light came on. However no matter what buttons pressed nothing registered in the Serial Terminal. Entering "D" to switch it to digital wouldn't change anything; analog light still on and nothing registering when I pressed buttons or moved joysticks.
    Tried pressing "B" with no change on buttons or joysticks. What do you all think?

    Brian
  • edited 2015-04-13 - 15:13:33
    Ah, I didn't see that the code had other wiring instructions.

    I modified the Spin code so that it works with the wiring shown in post #2. Please try the Spin code attached to post #13 with the wiring that was already working (partially) with the C code.

    Also, what happens when you press the Analog button twice, or three times?
  • briank77479briank77479 Posts: 36
    edited 2015-04-14 - 09:58:55
    Andy,
    I wired the controller per the diagram in Post #2 and ran the C-Code again just to verify it was responding as before. And, yes, the Analog light stayed on no matter what I pressed. All the buttons recorded a "1" when I pressed them and when I operated the joysticks, like before the button identifier for the button above that joystick would register a "1"; i.e. right joystick moved forward, triangle button would read a "1". Left joystick did the same thing. Pressing the "Analog" button on the controller didn't have any effect; light on constantly. Without changing any wiring I immediately ran the Spin code you provided with the Serial Terminal Enabled and the controller functioned correctly in "Analog" and "Analog Button" mode. On the joysticks I had full 0-255 on both up/down and left/right. All the buttons registered a "1" when pressed. The "Analog" light did stay on even in the "D" mode. The only time I got any change on the light was if I left the controller on and turn off power to the Prop and Controller Adapter card; the light would begin to flash. It appears the controller is working correctly, except for the light issue, just need guidance on what to change in the "C" code to read the joysticks.

    Thank you again for your help!!
    Brian
  • edited 2015-04-14 - 15:58:34
    This particular Spin object might be a bug-free conversion to C. I'll take a look...
  • edited 2015-04-15 - 21:10:33
    Okay, this version (attached) puts my controllers into digital button + analog joystick mode automatically. Brian, could you try it out with yours?
    /* 
      Test ps2gamelite Library.c
    */  
      
    #include "simpletools.h"
    #include "ps2gamelite.h"
        
    ps2game_t *player1;
    
    int status;
    int square, cross, circle, triangle,
        R1, R2, L1, L2, 
        left, backward, right, forward,
        start, select, joyPressL, joyPressR, 
        joyLX, joyLY, joyRX, joyRY;
    
    int main()
    {  
      //                    dat cmd att clk
      player1 = ps2game_open(4,  5,  6,  7);
      while(1)
      {
        print("%c", HOME);
    
        status = ps2game_status(player1);
        square = ps2game_square(player1);
        cross = ps2game_cross(player1);
        circle = ps2game_circle(player1);
        triangle = ps2game_triangle(player1);
        R1 = ps2game_1R(player1);
        L1 = ps2game_1L(player1);
        R2 = ps2game_2R(player1);
        L2 = ps2game_2L(player1);
        left = ps2game_left(player1);
        backward = ps2game_backward(player1);
        right = ps2game_right(player1);
        forward = ps2game_forward(player1);
        start = ps2game_start(player1);
        select = ps2game_select(player1);
        joyPressL = ps2game_joyPressL(player1);
        joyPressR = ps2game_joyPressR(player1);
        joyLX = ps2game_joyLX(player1);
        joyLY = ps2game_joyLY(player1);
        joyRX = ps2game_joyRX(player1);
        joyRY = ps2game_joyRY(player1);
    
        print("status = %2x\n\n", status);
        print("square = %d\n", square);
        print("cross = %d\n", cross);
        print("circle = %d\n", circle);
        print("triangle %d\n\n", triangle);
        print("R1 = %d\n", R1);
        print("L1 = %d\n", L1);
        print("R2 = %d\n", R2);
        print("L2 = %d\n\n", L2);
        print("left = %d\n", left);
        print("backward = %d\n", backward);
        print("right = %d\n", right);
        print("forward = %d\n\n", forward);
        print("start = %d\n", start);
        print("select = %d\n", select);
        print("joyPressL = %d\n", joyPressL);
        print("joyPressR = %d\n\n", joyPressR);
        print("joyLX = %03d\n", joyLX);
        print("joyLY = %03d\n", joyLY);
        print("joyRX = %03d\n", joyRX);
        print("joyRY = %03d\n", joyRY);
    
        pause(1000);
      }    
    }
    
    

    Andy
  • briank77479briank77479 Posts: 36
    edited 2015-04-16 - 09:56:48
    Andy,
    The new code did work, THANK YOU! Can you tell me what you did differently? For my education.
    Interestingly, the ANALOG light on the controller stayed on no matter what I did. That is weird.

    Again, Thank you Andy and Thank you Duane for your help!!!
    Brian
  • edited 2015-04-16 - 15:43:37
    You're welcome! I sure am glad it worked, especially since I have next to no time for coding these days.

    The code (header, test harness, and library functions all flattened into a single file) is below. Hopefully you, Duane and others will add your names to the authors list in the comments as you make it awesome. After that, maybe it can be re-bundled and added to OBEX, or to the Simple Libraries.
    /**
     * @file ps2gamelite.h
     *
     * @author Andy Lindsay (Parallax)
     * Libary and example code with basic digital button and analog joystick 
     * functionality.  SPI in ps2game function needs to be rewritten with full 
     * duplex SPI in place of the shift_out shift_in hack.  There's also lots
     * of room for more features, and it of course needs comments.  
     * 
     * @author <your name here>
     * <your contribution(s) here>
     *
     * @author <your name here>
     * <your contribution(s) here>
     *
     * @version 0.1
     *
     * @copyright
     * Copyright (C) Parallax, Inc. 2015. All Rights MIT Licensed.
     *
     * @brief Communicate with PlayStation 2 controller(s) and return
     * button and joystick data.
     */
    
    #ifndef PS2LITE_H
    #define PS2LITE_H
    
    #if defined(__cplusplus)
    extern "C" {
    #endif
    
    
    #include "simpletools.h"
    
    typedef struct ps2game_st{
      volatile int dat;
      volatile int cmd;
      volatile int att;
      volatile int clk;
      volatile int thumbL;
      volatile int thumbR;
      volatile int status;
      volatile int joyRX;
      volatile int joyRY;
      volatile int joyLX;
      volatile int joyLY;
      volatile int set;
      volatile int byteCount;
      volatile int *array;
      volatile int cog; 
               int stack[128];
    } ps2game_t; 
    
    //extern volatile int enterEscape[9];
    //extern volatile int setAnalogMode[9];
    //extern volatile int exitEscape[9];
     
    
    int ps2game_setAnalog(ps2game_t *device); 
    
    /**
     * @brief Open a ps2game device.
     *
     * @param dat number of pin connected to dat line.
     * @param cmd number of pin connected to cmd line.
     * @param att number of pin connected to att line.
     * @param clk number of pin connected to clk line.
     *
     * @returns Game controller process identifier for use with other functions.
     */
    ps2game_t *ps2game_open(int dat, int cmd, int att, int clk);
    
    
    /**
     * @brief Close a ps2game device, and recover cog, and memory.
     *
     * @param *device process ID returned by ps2game_open.
     */
    void ps2game_close(ps2game_t *device);
    
    
    /**
     * @brief Check connection status.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns status 90 (0x5a) if connected.
     */
    int ps2game_status(ps2game_t *device);
    
    
    /**
     * @brief Check square button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_square(ps2game_t *device);
    
    
    /**
     * @brief Check cross button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_cross(ps2game_t *device);
    
    
    /**
     * @brief Check circle button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_circle(ps2game_t *device);
    
    
    /**
     * @brief Check triangle button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_triangle(ps2game_t *device);
    
    
    /**
     * @brief Check upper-right-front button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_1R(ps2game_t *device);
    
    
    /**
     * @brief Check upper-left-front button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_1L(ps2game_t *device);
    
    
    /**
     * @brief Check lower-right-front button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_2R(ps2game_t *device);
    
    
    /**
     * @brief Check lower-left-front button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_2L(ps2game_t *device);
    
    
    /**
     * @brief Check left arrow button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_left(ps2game_t *device);
    
    
    /**
     * @brief Check backward arrow button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_backward(ps2game_t *device);
    
    
    /**
     * @brief Check right arrow button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_right(ps2game_t *device);
    
    
    /**
     * @brief Check forward arrow button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_forward(ps2game_t *device);
    
    
    /**
     * @brief Check start button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_start(ps2game_t *device);
    
    
    /**
     * @brief Check select button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_select(ps2game_t *device);
    
    
    /**
     * @brief Check left joystick button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_joyPressL(ps2game_t *device);
    
    
    /**
     * @brief Check right joystick button.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns 1 if pressed, 0 if not pressed.
     */
    int ps2game_joyPressR(ps2game_t *device);
    
    
    /**
     * @brief Check left joystick x-axis.
     *
     * @param *device  process ID returned by ps2game_open.
     *
     * @returns 0(far left)...128(center)...255(far right).
     */
    int ps2game_joyLX(ps2game_t *device);
    
    
    /**
     * @brief Check left joystick y-axis.
     *
     * @param *device  process ID returned by ps2game_open.
     *
     * @returns 0(all the way forward)...128(center)...255(all the way back).
     */
    int ps2game_joyLY(ps2game_t *device);
    
    
    /**
     * @brief Check right joystick x-axis.
     *
     * @param *device  process ID returned by ps2game_open.
     *
     * @returns 0(far left)...128(center)...255(far right).
     */
    int ps2game_joyRX(ps2game_t *device);
    
    
    /**
     * @brief Check right joystick y-axis.
     *
     * @param *device  process ID returned by ps2game_open.
     *
     * @returns 0(all the way forward)...128(center)...255(all the way back).
     */
    int ps2game_joyRY(ps2game_t *device);
    
    
    #if defined(__cplusplus)
    }
    #endif
    /* __cplusplus */ 
    #endif
    /* PS2LITE_H */ 
    
    
    /**
    * TERMS OF USE: MIT License
    *
    * Permission is hereby granted, free of charge, to any person obtaining a
    * copy of this software and associated documentation files (the "Software"),
    * to deal in the Software without restriction, including without limitation
    * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    * and/or sell copies of the Software, and to permit persons to whom the
    * Software is furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in
    * all copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    * DEALINGS IN THE SOFTWARE.
    */
    
    
    /**************************************************************************/
    /**************************************************************************/
    /**************************************************************************/
    
    /* 
      Test ps2gamelite Library.c
    */  
      
    #include "simpletools.h"
    // #include "ps2gamelite.h"                // ~~~ Commented for flat file
        
    ps2game_t *player1;
    
    int status;
    int square, cross, circle, triangle,
        R1, R2, L1, L2, 
        left, backward, right, forward,
        start, select, joyPressL, joyPressR, 
        joyLX, joyLY, joyRX, joyRY;
    
    int main()
    {  
      //                    dat cmd att clk
      player1 = ps2game_open(4,  5,  6,  7);
    
        /*
        print("status = %2x\n\n", player1->status);
        print("thumbL = %08b\n", player1->thumbL);
        print("thumbR = %08b\n", player1->thumbR);
        print("joyRX = %03d\n", player1->joyRX);
        print("joyRY = %03d\n", player1->joyRY);
        print("joyLX = %03d\n", player1->joyLX);
        print("joyLY = %03d\n\n", player1->joyLY);
        */ 
    
      while(1)
      {
        print("%c", HOME);
    
        status = ps2game_status(player1);
        square = ps2game_square(player1);
        cross = ps2game_cross(player1);
        circle = ps2game_circle(player1);
        triangle = ps2game_triangle(player1);
        R1 = ps2game_1R(player1);
        L1 = ps2game_1L(player1);
        R2 = ps2game_2R(player1);
        L2 = ps2game_2L(player1);
        left = ps2game_left(player1);
        backward = ps2game_backward(player1);
        right = ps2game_right(player1);
        forward = ps2game_forward(player1);
        start = ps2game_start(player1);
        select = ps2game_select(player1);
        joyPressL = ps2game_joyPressL(player1);
        joyPressR = ps2game_joyPressR(player1);
        joyLX = ps2game_joyLX(player1);
        joyLY = ps2game_joyLY(player1);
        joyRX = ps2game_joyRX(player1);
        joyRY = ps2game_joyRY(player1);
    
        print("status = %2x\n\n", status);
        print("square = %d\n", square);
        print("cross = %d\n", cross);
        print("circle = %d\n", circle);
        print("triangle %d\n\n", triangle);
        print("R1 = %d\n", R1);
        print("L1 = %d\n", L1);
        print("R2 = %d\n", R2);
        print("L2 = %d\n\n", L2);
        print("left = %d\n", left);
        print("backward = %d\n", backward);
        print("right = %d\n", right);
        print("forward = %d\n\n", forward);
        print("start = %d\n", start);
        print("select = %d\n", select);
        print("joyPressL = %d\n", joyPressL);
        print("joyPressR = %d\n\n", joyPressR);
        print("joyLX = %03d\n", joyLX);
        print("joyLY = %03d\n", joyLY);
        print("joyRX = %03d\n", joyRX);
        print("joyRY = %03d\n", joyRY);
    
        pause(1000);
      }    
    }
    
    
    
    /**************************************************************************/
    /**************************************************************************/
    /**************************************************************************/
    
    
    /*
     * @file ps2gamelite.c
     *
     * @author Andy Lindsay (Parallax)
     * Libary and example code with basic digital button and analog joystick 
     * functionality.  SPI in ps2game function needs to be rewritten with full 
     * duplex SPI in place of the shift_out shift_in hack.
     * 
     * @author <your name here>
     * <your contribution(s) here>
     *
     * @author <your name here>
     * <your contribution(s) here>
     *
     * @version 0.1
     *
     * @copyright
     * Copyright (C) Parallax, Inc. 2012. All Rights MIT Licensed.
     *
     * @brief Communicate with PlayStation 2 controller(s) and return
     * button and joystick data.
     */
    
    // #include "ps2gamelite.h"                // ~~~ Commented for flat file
    #include "simpletools.h"
    
    void ps2_game();
    //void ps2game_write(ps2game_t *device, int *arrayPointer, int byteCount);
    
    static volatile int poll[2] = {0x01, 0x42}; 
    static volatile int enterEscape[9] = {0x01, 0x43, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0};
    static volatile int setAnalogMode[9] = {0x01, 0x44, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0};
    static volatile int exitEscape[9] =  {0x01, 0x43, 0x0, 0x0, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
    
    
    ps2game_t *ps2game_open(int dat, int cmd, int att, int clk)
    {
      ps2game_t *device = malloc(sizeof(ps2game_t)+4);
      device->dat = dat;
      device->cmd = cmd;
      device->att = att;
      device->clk = clk;
      device->set = 0;
      device->cog = 1 + cogstart(ps2_game, 
                                 device, 
                                 &device->stack, 
                                 128*4);
      pause(100);
      ps2game_setAnalog(device);
      return device;
    }
    
    
    void ps2game_close(ps2game_t *device)
    {
      if(device->cog > 0)
      {
        cogstop(device->cog - 1);
        device->cog = 0;
        free(device);
      }  
    }
    
    int ps2game_status(ps2game_t *device)
    {
      return device->status;
    }  
    
    int ps2game_square(ps2game_t *device)
    {
      return (device->thumbR & (1<<7)) ? 0 : 1;
    }  
    
    int ps2game_cross(ps2game_t *device)
    {
      return (device->thumbR & (1<<6)) ? 0 : 1;
    }  
    
    int ps2game_circle(ps2game_t *device)
    {
      return (device->thumbR & (1<<5)) ? 0 : 1;
    }  
    
    int ps2game_triangle(ps2game_t *device)
    {
      return (device->thumbR & (1<<4)) ? 0 : 1;
    }  
    
    int ps2game_1R(ps2game_t *device)
    {
      return (device->thumbR & (1<<3)) ? 0 : 1;
    }  
    
    int ps2game_1L(ps2game_t *device)
    {
      return (device->thumbR & (1<<2)) ? 0 : 1;
    }  
    
    int ps2game_2R(ps2game_t *device)
    {
      return (device->thumbR & (1<<1)) ? 0 : 1;
    }  
    
    int ps2game_2L(ps2game_t *device)
    {
      return (device->thumbR & 1) ? 0 : 1;
    }  
    
    int ps2game_left(ps2game_t *device)
    {
      return (device->thumbL & (1<<7)) ? 0 : 1;
    }  
    
    int ps2game_backward(ps2game_t *device)
    {
      return (device->thumbL & (1<<6)) ? 0 : 1;
    }  
    
    int ps2game_right(ps2game_t *device)
    {
      return (device->thumbL & (1<<5)) ? 0 : 1;
    }  
    
    int ps2game_forward(ps2game_t *device)
    {
      return (device->thumbL & (1<<4)) ? 0 : 1;
    }  
    
    int ps2game_start(ps2game_t *device)
    {
      return (device->thumbL & (1<<3)) ? 0 : 1;
    }  
    
    int ps2game_joyPressR(ps2game_t *device)
    {
      return (device->thumbL & (1<<2)) ? 0 : 1;
    }  
    
    int ps2game_joyPressL(ps2game_t *device)
    {
      return (device->thumbL & (1<<1)) ? 0 : 1;
    }  
    
    int ps2game_select(ps2game_t *device)
    {
      return (device->thumbL & 1) ? 0 : 1;
    }  
    
    int ps2game_joyLX(ps2game_t *device)
    {
      return device->joyLX;
    }  
    
    int ps2game_joyLY(ps2game_t *device)
    {
      return device->joyLY;
    }  
    
    int ps2game_joyRX(ps2game_t *device)
    {
      return device->joyRX;
    }  
    
    int ps2game_joyRY(ps2game_t *device)
    {
      return device->joyRY;
    } 
    
    int ps2game_setAnalog(ps2game_t *device)
    {
      device->byteCount = 9;
      device->array = enterEscape;
      pause(2);
      device->set = 3;
    //  ps2game_write(device, enterEscape, 9);
      while(device->set == 3);
      device->byteCount = 9;
      device->array = setAnalogMode;
      pause(2);
      device->set = 2;
      while(device->set == 2);
      device->byteCount = 9;
      device->array = exitEscape;
      pause(2);
      device->set = 1;
      while(device->set == 1);
      //print("device->array = %d\n", device->array);
      //print("exitEscape = %d\n", exitEscape);
      //while(1);  
    }  
    
    void ps2game_configure(ps2game_t *device)
    {
      int i=0;
      low(device->att);
      //print("array address = %d\n\n", device->array);
      for(i = 0; i < device->byteCount; i++)
      {
        //print("device[%d] = %d\n", i, device->array[i]);
        int bit = (device->array[i]) & 1;
        if(bit) high(device->cmd); else low(device->cmd);
        //set_output(device->cmd, bit);
        low(device->clk);
        shift_out(device->cmd, device->clk, LSBFIRST, 7, device->array[i] >> 1);
        high(device->clk);
      }  
      //print("\n");
      high(device->att);
      device->set--;
    }                                   
      
    void ps2_game(ps2game_t *device)
    {
      //simpleterm_open();
      high(device->att);
      high(device->clk);
      low(device->cmd);
      pause(1);
      while(1)
      {
        if(device->set != 0)
        {
          ps2game_configure(device);
          //if(device->set==0)while(1); 
        }
        else
        {      
          int bit = poll[0] & 1;
          set_output(device->cmd, bit);
          low(device->att);
          low(device->clk);
          shift_out(device->cmd, device->clk, LSBFIRST, 8, (poll[0] >> 1) | (poll[1] & 0b10000000));
          shift_out(device->cmd, device->clk, LSBFIRST, 8, poll[1] >> 1);
          device->status = shift_in(device->dat, device->clk, LSBPRE, 8);
          device->thumbL = shift_in(device->dat, device->clk, LSBPRE, 8);
          device->thumbR = shift_in(device->dat, device->clk, LSBPRE, 8);
          device->joyRX  = shift_in(device->dat, device->clk, LSBPRE, 8);
          device->joyRY  = shift_in(device->dat, device->clk, LSBPRE, 8);
          device->joyLX  = shift_in(device->dat, device->clk, LSBPRE, 8);
          device->joyLY  = shift_in(device->dat, device->clk, LSBPRE, 8);
          high(device->clk);
          high(device->att);
        }      
        pause(10);
      }
    }      
    
    /**
     * TERMS OF USE: MIT License
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     * DEALINGS IN THE SOFTWARE.
     */
    


    About your question, it's probably a good idea to take a look at this page for background info on how the signalling and packet exchanges work this page.

    The code that communicates with the controller is near the bottom, in the ps2_game function. That function gets launched into another cog by the pstgame_open function. I was in a hurry due to some pressing deadlines, so the ps2_game function uses a couple of hacks. The first is that shift_out and shift_in work with an active-high clock signal, but the PS2 controller depends on an active-low clock. So, the trick I used was to manually shift the first bit using a high to low signal. Then, the other shift_out functions that followed had the data shifted one bit to the right to make up for it and correctly transfer the bytes in the poll array to request updated data before shifting that data in. The shift_out and shift_in functions are half duplex SPI. Full duplex would be more appropriate because data also comes in with each clock pulse.

    The ps2_game function (in a hurry) hacks seemed to work until your PS2 controller's Analog button refused to take the device into Analog joystick mode. But, Duane's spin object succeeded. To address this, I first revisited the spin library I was converting, and found the bug I had introduced while I was porting it. After that, I found out that that library (which shall not be named) had a bug in the PASM code that shifted bits and was missing one of the buttons, so I abandoned that approach and went back to the ps2gamelite library.

    In ps2gamelite, I started by adding set, bytecount and *array to the ps2game_st structure so that the two functions running in different cogs could communicate. ps2game_setAnalog sets those values, and ps2_game was modified to monitor device->set, and call ps2_game_configure if device->set is not zero. (Note device-> is a pointer to an instance of the ps2game_t data type, which is a ps2game_st structure. This allows C to emulate Spin's object multi-instance.) The thing that causes the value of device->set to change from zero is a call to ps2game_start, which calls ps2game_setAnalog after launching ps2_game into another cog.

    The ps2game_setAnalog function sets device->byteCount to 9, device->array to point at the first of three arrays that need to be shifted to the PS2 controller. After a 2 ms pause (where locks would probably be an improvement), device->set is set to 3. The ps2_game function running in the other cog notices this, and calls ps2game_configure, which has some custom shifting to transmit other array strings. It uses the address stored in device->array to transmit the enterEscape string. setAnalog waits until device->set drops from 3 to 2, which is the ps2game_configure function's way of signaling that it's done transmitting the array. After that ps2game_setAnalog repeats the process by setting up the setAnalogMode string for transmission, and after that, the exitEscape string. That's how the Propeller C code moves the controller into analog mode.

    If you want the option of using the buttons without any analog joystick info, you could comment the ps2game_setAnalog(device) call from the ps2game_open function, and then just add it to your main function as a ps2game_setAnalog(player1) call, after a call to ps2game_open.
  • briank77479briank77479 Posts: 36
    edited 2015-04-21 - 11:28:45
    Andy,
    Very helpful and informative. I very much appreciate the "Education Lesson" also!

    Thank you again for your work on this!!
    Awesome!
    Brian
  • Hal AlbachHal Albach Posts: 747
    edited 2015-06-21 - 20:55:50
    I have found this thread extremely useful in my quest to control my bot using a wireless PS2 Dual Shock controller. I loaded the code Andy provided in post #20 and had no problem making it work, although because of all the screen updates response to the controller was very slow. In the program header Andy invited someone - anyone to add to the program, particularly rewriting the "hacked" shift_in and shift_out functions with a full duplex io scheme that would send as well as receive with each clock pulse. I think I have found one from Professor Dariusz Caban, which he shares with us at http://www.rpi.edu/dept/ecse/mps/Coding_SPI_sw.pdf and which I modified for the Propeller. I replaced the shift functions with this code and it seems to work very well, although the clock rate was reduced from 89 KHz to 68 KHz. Since I plan on using most of this code for the robot control I eliminated the lengthy print outputs to increase controller response. That helped tremendously and by just using the alternate output in Main, where it just prints Byte 4 and Byte 5 along with the analog readings response has really picked up.
    The code I attached is still a work in progress, I left a lot of commented code in place to illustrate the changes made. Please accept that I am still a novice in C, have never worked with structures before, so things may look a little messy - but it does work.
    /**
     * @file ps2gamelite.h
     *
     * @author Andy Lindsay (Parallax)
     * Libary and example code with basic digital button and analog joystick 
     * functionality.  SPI in ps2game function needs to be rewritten with full 
     * duplex SPI in place of the shift_out shift_in hack.  There's also lots
     * of room for more features, and it of course needs comments.  
     * 
     * @author Hal Albach
     * Replaced all simplex SPI calls with a Full Duplex SPI call, 
     * int spiEx(ps2game_t *device, int). Maybe someone can come up with a better name.
     * Also, my intent is to use this program for robot control so I removed most 
     * of the print output and enabled a much shorter version Andy had left in Main. 
     * (Sorry, Andy!)  Still a novice at C, I'm not quite sure how all the structure 
     * statements work but was able to gleen enough from Andy's code (and a few 
     * books on C) to wedge in the duplex code.  Per my Saleae Analyser this change has 
     * dropped the clock rate from 89 KHz to 68 KHz. The program still responds
     * quickly to button pokes and joystick excursions.  Hoping that some of the more
     * gifted programmers here can improve the clock rate.
     * I also added a few comments but still needs more.
     * 
     *
     * @author <your name here>
     * <your contribution(s) here>
     *
     * @version 0.1
     *
     * @copyright
     * Copyright (C) Parallax, Inc. 2015. All Rights MIT Licensed.
     *
     * @brief Communicate with PlayStation 2 controller(s) and return
     * button and joystick data.
     */
    
    #ifndef PS2LITE_H
    #define PS2LITE_H
    
    #if defined(__cplusplus)
    extern "C" {
    #endif
    
    
    #include "simpletools.h"
    
    typedef struct ps2game_st{          // define a structure ps2game_st
      volatile int dat;                 // 
      volatile int cmd;                 //
      volatile int att;                 //
      volatile int clk;                 //
      volatile int Byte_4;              //
      volatile int Byte_5;              //
      volatile int status;              //
      volatile int joyRX;               //  Members of structure ps2game_st
      volatile int joyRY;               //
      volatile int joyLX;               //
      volatile int joyLY;               //
      volatile int set;                 //
      volatile int byteCount;           //
      volatile int *array;              //
      volatile int cog;                 //
               int stack[128];          //
    } ps2game_t;                        //  a synonym for structure ps2game_st called ps2game_t
    
    int ps2game_setAnalog(ps2game_t *device);
    
    /**
     * @brief Open a ps2game device.
     *
     * @param dat number of pin connected to dat line.
     * @param cmd number of pin connected to cmd line.
     * @param att number of pin connected to att line.
     * @param clk number of pin connected to clk line.
     *
     * @returns Game controller process identifier for use with other functions.
     */
    ps2game_t *ps2game_open(int dat, int cmd, int att, int clk);
    
    /**
     * @brief Check connection status.
     *
     * @param *device process ID returned by ps2game_open.
     *
     * @returns status 90 (0x5a) if connected.
     */
    int ps2game_status(ps2game_t *device);
    
    /**
     * @brief Full Duplex SPI function.                                 added
     *                                                                    |
     * @param *device process ID returned by ps2game_open.                |
     * @param byte to send to slave                                       |
     *                                                                    |
     * @returns slave response.                                           |
     */
    int spiEx(ps2game_t *device, int); //                               added
    
    #if defined(__cplusplus)
    }
    #endif
    /* __cplusplus */ 
    #endif
    /* PS2LITE_H */ 
    
    /**************************************************************************/
    /****************************   MAIN   ************************************/
    /**************************************************************************/
    
    /* 
      Test ps2gamelite Library.c
    */  
      
    #include "simpletools.h"
    // #include "ps2gamelite.h"                // ~~~ Commented for flat file
        
    ps2game_t *player1;
    int status;
    /*
    int square, cross, circle, triangle,                                removed
        R1, R2, L1, L2,                                                     |
        left, backward, right, forward,                                     |
        start, select, joyPressL, joyPressR,                                |
        joyLX, joyLY, joyRX, joyRY;                                     removed
    */
    int main()
    {  
      //                    dat cmd att clk
      player1 = ps2game_open(4,  5,  6,  7);
    
      while(1)
      {
        printi("%c", HOME);
        
        printi("status = %2x\n\n", player1->status);
        printi("Byte_4 = %08b\n", player1->Byte_4);
        printi("Byte_5 = %08b\n", player1->Byte_5);
        printi("joyRX = %03d\n", player1->joyRX);
        printi("joyRY = %03d\n", player1->joyRY);
        printi("joyLX = %03d\n", player1->joyLX);
        printi("joyLY = %03d\n", player1->joyLY);
    
        pause(300);
      }    
    }
    
    /**************************************************************************/
    /**************************************************************************/
    /**************************************************************************/
    
    /*
     * @file ps2gamelite.c
     *
     * @author Andy Lindsay (Parallax)
     * Libary and example code with basic digital button and analog joystick 
     * functionality.  SPI in ps2game function needs to be rewritten with full 
     * duplex SPI in place of the shift_out shift_in hack.
     * 
     * @author <your name here>         see above
     * <your contribution(s) here>
     *
     * @author <your name here>
     * <your contribution(s) here>
     *
     * @version 0.1
     *
     * @copyright
     * Copyright (C) Parallax, Inc. 2012. All Rights MIT Licensed.
     *
     * @brief Communicate with PlayStation 2 controller(s) and return
     * button and joystick data.
     */
    
    // #include "ps2gamelite.h"                // ~~~ Commented for flat file
    #include "simpletools.h"
    
    void ps2_game();
    
    static volatile int poll[3] = {0x01, 0x42, 0x00}; //                    added 3rd element 
    static volatile int enterEscape[9] = {0x01, 0x43, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0};
    static volatile int setAnalogMode[9] = {0x01, 0x44, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0};
    static volatile int exitEscape[9] =  {0x01, 0x43, 0x0, 0x0, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
    
    
    ps2game_t *ps2game_open(int dat, int cmd, int att, int clk)
    {
      ps2game_t *device = malloc(sizeof(ps2game_t)+4); 
      device->dat = dat;
      device->cmd = cmd;
      device->att = att;
      device->clk = clk;
      device->set = 0;
      device->cog = 1 + cogstart(ps2_game, 
                                 device, 
                                 &device->stack, 
                                 128*4);
      pause(100);
      ps2game_setAnalog(device);
      return device;
    }
    
    int ps2game_setAnalog(ps2game_t *device)
    {
      
    /**********************  enter Escape  ************************************/    
      
      device->byteCount = 9;
      device->array = enterEscape;
      pause(2);
      device->set = 3;
    //  ps2game_write(device, enterEscape, 9);
      while(device->set == 3);
      
    /*********************  set Analog Mode  **********************************/  
            
      device->byteCount = 9;
      device->array = setAnalogMode;
      pause(2);
      device->set = 2;
      while(device->set == 2);
      
    /***********************  exit Escape  ************************************/  
      
      device->byteCount = 9;
      device->array = exitEscape;
      pause(2);
      device->set = 1;
      while(device->set == 1);
      
    /**************************************************************************/  
      
      //printi("device->array = %d\n", device->array);
      //printi("exitEscape = %d\n", exitEscape);
      //while(1);  
    }  
    
    void ps2game_configure(ps2game_t *device)
    {
      int i=0;
      low(device->att);           // begin a transfer
    //  printi("array address = %d\n\n", device->array);
      for(i = 0; i < device->byteCount; i++)
      {
        spiEx(device, device->array[i]);     // full duplex transfer               added
        
    /*    
        //printi("device[%d] = %d\n", i, device->array[i]);                       removed
        int bit = (device->array[i]) & 1;                                            |
        if(bit) high(device->cmd); else low(device->cmd);                            |
        //set_output(device->cmd, bit);                                              |
        low(device->clk);                                                            |
        shift_out(device->cmd, device->clk, LSBFIRST, 7, device->array[i] >> 1);     |
        high(device->clk);                                                        removed
    */    
      }  
      //printi("\n");
      high(device->att);
      device->set--;
    }                                   
    /**************************************************************************/
    /***********************    CONTROLLER COG    *****************************/
    /**************************************************************************/
      
    void ps2_game(ps2game_t *device)
    {
      //simpleterm_open();
      high(device->att);
      high(device->clk);
      low(device->cmd);
      pause(1);
      while(1)
      {
        if(device->set != 0)
        {
          ps2game_configure(device);
          //if(device->set==0)while(1); 
        }
        else                                        // else do normal polling of controller
        {  
        
    /*
          int bit = poll[0] & 1;                                                                      removed
          set_output(device->cmd, bit);                                                                   |
          low(device->att);                                                                               |
          low(device->clk);                                                                               |
          shift_out(device->cmd, device->clk, LSBFIRST, 8, (poll[0] >> 1) | (poll[1] & 0b10000000));      |
          shift_out(device->cmd, device->clk, LSBFIRST, 8, poll[1] >> 1);                                 |
          device->status = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          device->Byte_4 = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          device->Byte_5 = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          device->joyRX  = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          device->joyRY  = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          device->joyLX  = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          device->joyLY  = shift_in(device->dat, device->clk, LSBPRE, 8);                                 |
          high(device->clk);                                                                              |
          high(device->att);                                                                           removed
    */
    
          low(device->att);                         // attention to controller                          added
          spiEx(device, poll[0]);                   // send 0x01 poll code 1, expect 0xFF return          |
          spiEx(device, poll[1]);                   // send 0x42 poll code 2, expect 0x41 return          |
          device->status = spiEx(device, poll[2]);  // send dummy, receive status                         |
          device->Byte_4 = spiEx(device, poll[2]);  // send dummy, receive Byte_4                         |
          device->Byte_5 = spiEx(device, poll[2]);  // send dummy, receive Byte_5                         |
          device->joyRX  = spiEx(device, poll[2]);  // send dummy, receive joyRX                          |
          device->joyRY  = spiEx(device, poll[2]);  // send dummy, receive joyRY                          |
          device->joyLX  = spiEx(device, poll[2]);  // send dummy, receive joyLX                          |
          device->joyLY  = spiEx(device, poll[2]);  // send dummy, receive joyLY                          |
          high(device->att);                                                    //                      added
        }      
        pause(10);
      }
    }      
    //**************************************************************************                        added
    /*                                                                                                    |
                                                                                                          |
    Full Duplex SPI Transfer Routine by Dariusz Caban, of the Silesian University of                      |
    Technology’s Institute of Informatics (Gliwice, Poland).                                              |
                                                                                                          |
    http://www.rpi.edu/dept/ecse/mps/Coding_SPI_sw.pdf                                                    |
                                                                                                          |
    Modified for Propeller and PS2                                                                        |
                                                                                                          |
    */
    
    int spiEx(ps2game_t *device, int io)    //                                                            |
    {                                       //                                                            |
      for(char i = 8; i; i--)               //                                                            |
      {                                     //                                                            |
        if (io & 1) high(device->cmd);      // if io & 1 is true set cmd high                             |
          else low(device->cmd);            //   else set pin cmd to low                                  |
        low(device->clk);                   // slave latches input data bit                               |
        io >>= 1;                           // shift io right once, mSb = 0                               |
        if(input(device->dat)) io |= 0x80;  // MISO to io msb                                             |
        high(device->clk);                  // signal slave to ready next bit                             |
      }                                     //                                                            |
      return io;                            //                                                            |
    }                                       //                                                          added
    
    /**
     * TERMS OF USE: MIT License
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     * DEALINGS IN THE SOFTWARE.
     */
    
    
  • After a couple of "life" interruptions I'm finally back to experimenting with the controller and the PS2. Just curious can the code be modified such that the y-axis on the joysticks read 0-255 backward to forward rather than 0-255 forward to backward. It would help in setting up some motor control applications I'd like to try.

    Thanks Brian
  • I hope this isn't too simplistic, but couldn't you just integer subtract the value given by the joystick from 255? That way 0 becomes 255 and 255 becomes 0, and all values between follow suit.
  • Oh man, excuse me while I slap my forehead and roll my eyes. I over thought the problem. Thanks for the suggestion.
    Happy Thanksgiving!! Brian.
Sign In or Register to comment.