Propeller C for PS2 Game Controller?
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
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
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.
Thank you so very much!! Will give it a try!!
Very Much Appreciate It!!!
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.
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
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.
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
As always your help is very much appreciated.
Brian
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.
Andy
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.
Thank you,
Brian
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
Tried pressing "B" with no change on buttons or joysticks. What do you all think?
Brian
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?
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
/* 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
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
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.
Very helpful and informative. I very much appreciate the "Education Lesson" also!
Thank you again for your work on this!!
Awesome!
Brian
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 | Technologys 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. */
Thanks Brian
Happy Thanksgiving!! Brian.