Saturn Controller Protocol MK80117 and Emulation

Started by RDC, March 15, 2013, 10:44:55 PM

Previous topic - Next topic

RDC

After tinkering around with the standard 6 button Saturn pads, MK-80116 and MK-80100. http://www.acidmods.com/forum/index.php/topic,42733.0.html

I figured I'd give the 3D (Nights) Controller a look see. This is based on the reader understanding Binary, specifically 8-bits ( or 1 Byte) or at the very least being able to count to 15 (4 bits) which if you can't do, Google it up, it's fun, you'll learn something and you can then appreciate that old joke, "There are 10 kinds of people that understand Binary, those that do, and those that don't. ;)

Now, the 3D Controller is by far different from the protocol used on the standard 6 buttons pads. Those only use 6 lines for communication, 2 Select bits and 4 Data, while the 3D Controller uses 7 lines.

The connector pinout is the same, looking into the end of the controller's connector, beveled side up.


/-----------\
| 987654321 |
|___________|


1 - 5v (Power to Controller)
2 - D1 (Data 1)
3 - D0 (Data 0)
4 - Request or REQ (Select 1, also known as TR)
5 - Select or SEL (Select 0, also known as TH)
6 - Acknowledge or ACK (also known as TL)
7 - D3 (Data 3)
8 - D2 (Data 2)
9  - Ground

NOTE* All 4 Data and the TR, TH, TL lines are pulled up to 5v with 100k Resistors inside the controller.

So here we see the data that's sent when the 3D Controller has the switch in the 'Digital' position.



Then here it is with the switch in the 'Analog' position.



The 4 Data lines still do the same thing, which is send 4 bits of Data at once. This is half of a Byte, which is 8 bits, so the 4 bits of Data here is called a 'nibble'. But the 3 T lines on the 3D controller differs in how that Data is sent.

The TH line acts like a 'Select', when it's pulled Lo then that controller is the one that's about to be queried for Data.

The TR line is a 'Request' from the Saturn for Data to be sent from the controller. When it changes states that's when it's Requesting Data to be sent, so from Hi to Lo is a Request, as is going from Lo to Hi.

The TL line is an 'Acknowledge' that the Data is ready for the Saturn to read it. This line 'follows' the TR 'Request' line, so when TR goes from Hi to Lo, after the Data is ready to be read, then the TL line also goes from Hi to Lo, or vice-versa depending on where you're starting from.

The 6 button pads only used 2 Bytes of Data, which were derived from the 4 states that the TR (S1) and TL (S0) lines could be put into. The 3D controller does this a little differently. The first Byte of Data sent is for both the ID for the type of controller, Digital, Analog, etc., and also the number of Bytes needed for the rest of the Data.

The first Byte sent goes like this.

First the SEL line is pulled Lo (yellow arrow down) This means the Saturn is getting ready to ask for Data from that device.
Second the REQ line goes Lo (green arrow down) This is the Saturn asking for Data from the controller.
Third the ACK line goes Lo (blue arrow down) This is the controller Acknowledging that the Data, 0000 for Digital controller in this case, is ready for the Saturn to read from D3~D0.
Fourth the REQ line goes Hi (green arrow up) This is the Saturn again requesting Data from the controller.
Fifth the ACK line goes Hi (blue arrow up) This is the controller again Acknowledging that the Data is ready, but this time it is 0010, which means 2 Bytes for the remaining Data.

This is how that first Byte looks on the Logic Analyzer.



Now, following those same steps, when the controller is in the 'Analog' mode, the first Byte is then 0001-0110, which is 0001 for Analog, then 0110 for 6 Bytes of Data.



Like the 6 button pads, my test setup used the PIC16F1516, PicKit 3, Saleae Logic Analyzer and 16x2 LCD screen.



Close up of the screen in Digital mode.



..Analog mode..



..and no 3D controller connected.




I'll post the code up for this one later on once I go back thru it all as I want to give it a bit of an overhaul.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Now, Emulating the 3D controller. That is, making the Saturn 'think' you have a 3D controller connected, when it fact, it's just a  PIC, which is in this case again, the 16F1516. When using an external Crystal like I did here, this thing has just enough I/O (Input/Output) pins to pull this off.

This is also not 100% complete yet either. I do have both Digital and Analog modes working, but I don't have them setup so I can just flip a switch and go back and forth between them yet.

The buttons at the top are, from left to right, L, R, X, Y, Z, Start, A, C, B, D-pad R, L, D and U. The Stick is just from an old 360 controller. The original 3D controller uses a Hall Sensor type of Stick, which uses magnets, sensors and an OpAmp to do pretty much the exact same thing that a POT style Stick does.



This is the Data being sent between the PIC and the Saturn, which looks a lot like the pic above from the Data capture from the 3D controller in the Analog mode, but that was the idear. ;)



This is just the Digital portion of the code.


/*******************************************************************************
SEGA SATURN 3D CONTROLLER EMULATOR

PIC16F1516
EXTERNAL OSC @ 16MHz
5v POWER FROM SATURN

RDC 2013
*******************************************************************************/

char SPID, DATA_SIZE;

char DATA1_1, DATA1_2;
char DATA2_1, DATA2_2;
char DATA3_1, DATA3_2;

#define TH_SEL RA3_bit                   // Configure as Input
#define TR_REQ RA4_bit                   // Configure as Input
#define TL_ACK RA5_bit                   // Configure as Output

// Initialize the PIC
void INIT() {
ANSELA = 0b00000011;                     // AN0 and AN1 Analog to X and Y Axis
ANSELB = 0b00000000;                     // All AN are Digital
ANSELC = 0b00000000;                     // All AN are Digital
TRISA = 0b00011111;                      // PORTA
TRISB = 0b11111111;                      // PORTB, pins 6 and 7 also for ICSP
TRISC = 0b11110000;                      // PORTC
TRISE = 0b00001000;                      // PORTE, only 1 pin, RE3
TL_ACK = 1;
}


void main() {

   INIT();
     
  SPID = 0b00000000;
  DATA_SIZE = 0b00000010;

///// DIGITAL:

/////  Set Initial Button DATA for power up

    DATA1_1 = 0b11111111;                 // DATA1_1
    DATA1_2 = 0b11111111;                 // DATA1_2
    DATA2_1 = 0b11111111;                 // DATA2_1
    DATA2_2 = 0b11111111;                 // DATA2_2
    DATA3_1 = 0b00000000;                 // DATA3_1
    DATA3_2 = 0b00000001;                 // DATA3_2

  delay_ms(500);   // Wait for the Saturn to start up (takes around 1.5s)

  while(1) {

///// START COMMS WITH SATURN

    while(TH_SEL == 1) {}   // Wait for the Saturn to select controller
    delay_us(40);

    while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request SPID
    PORTC = SPID;           // SPID  0000 = Digital  // 0001 = Analog
    TL_ACK = 0;             // Here is the data
     
    while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request Data Size
    PORTC = DATA_SIZE;      // DATA_SIZE  0010 = 2 Bytes, 0110 = 6 Bytes
    TL_ACK = 1;             // Here is the data
     
///// START FIRST BYTE
   
    while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request first half of Data1
    PORTC = DATA1_1;        // DR, DL, DD, DU
    TL_ACK = 0;             // Here is the data
     
    while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request second half of Data1
    PORTC = DATA1_2;        // ST, A, C, B
    TL_ACK = 1;             // Here is the data
   
///// END FIRST BYTE

///// START SECOND BYTE

    while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request first half of Data2
    PORTC = DATA2_1;        // R, X, Y, Z
    TL_ACK = 0;             // Here is the data

    while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request second half of Data2
    PORTC = DATA2_2;        // L, 1, 1, 1
    TL_ACK = 1;             // Here is the data

///// END SECOND BYTE

///// START THIRD BYTE, END OF DATA

    while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request first half End
    PORTC = DATA3_1;        // 0000
    TL_ACK = 0;             // Here is the data

    while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request second half of End
    PORTC = DATA3_2;        // 0001
    TL_ACK = 1;             // Here is the data

///// END THIRD BYTE

    while(TH_SEL == 0) {}   // Wait for the Saturn to deselect controller
   
///// END COMMS WITH SATURN
   
/////  Get Current Button DATA (in less than 15ms, less than 300us if an Action Replay is used)

    DATA1_1 = PORTC >> 4;                 // DATA1_1
    DATA1_2 = PORTB;                      // DATA1_2
    DATA2_1 = PORTB >> 4;                 // DATA2_1
    DATA2_2 = (PORTA << 1) + 7;           // DATA2_2
    DATA3_1 = 0b00000000;                 // DATA3_1
    DATA3_2 = 0b00000001;                 // DATA3_2

  }
}



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

If anyone can, or would like to, build and test out the 3D controller Emulator, here are the hex and schematic files.

http://rapidshare.com/files/1982581024/Saturn%203D%20Controller%20EMU.rar

The PIC needs to be a 16F1516, and it requires the PicKit 3 to flash it.

X1 is a 16MHz Crystal, the C1 and C2 values depend on the Crystal used, but 20pf is a 'ballpark' value if you don't know what value it requires.

C3 is a DeCoupling Capacitor and is optional but recommended, anything from 0.01uf to 1uf will do.

S1 is a SPDT switch for the Digital/Analog modes.

Any 10k POT style stick can be used for the X-Axis and Y-Axis. (['m sure this needs some adjusting in code)

J1 is the controller cable that plugs into the Saturn. 1- 5v, 2- D1, 3- D0, 4- REQ, 5- SEL, 6- ACK, 7- D3, 8- D2, 9- GND

J2 is the programming header for the PIC. 1- VPP, 2- VDD, 3- VSS, 4- DAT, 5- CLK

R1 thru R21 can be anything from 10k to 100k in value.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

NFG


RDC

Thanks. I need to do some more testing with it, mainly to tweak the Analog bit of the code, since the Hall stick in the 3D controller is capable of hitting it's min/max values in every direction, where a POT style stick can't because it's usually mechanically limited by the shell it's in.

Before that though I want to build it into some controller shell for testing, as trying to play Nights with it on the bread board like that is more of a challenge than the game itself. I'll probably build one into a 360 shell at some point, as I can use my 36X board for it.  http://s50.beta.photobucket.com/user/RDCXBG/library/36X%20Controller%20PCB
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

fluxcore

I 'recently' adapted some code for a PIC16F4550 (20MHz xtal) to emulate the 3d controller in digital mode (for arcade stick use) as per the information in this thread, and it works great - EXCEPT in the Action Replay menu. Which stinks, since it's the very first thing you encounter, and don't want to take it out often at all.

It seems like the Action Replay just stops polling the relevant data lines. I don't have an official 3d controller to use to confirm it works, but I've asked some other people and they claim it does.

I wonder if there's some extra part of the spec that's missing, but that seems hard to believe...

RDC

#4
"Rise from your grave"

It's been almost 2 years since I messed with any of this, but I never did any testing with an Action Replay (mainly as I don't have one) or really anything else at all with it after this. Only the Saturn and the controller and then the controller and a PIC, but there is nothing missing here in how that all worked. Everything that the Logic Analyzer saw is pretty much here, but again, not with an AR in the Saturn, so I couldn't even guess what might be going on there.

Provided that an official controller does work, maybe try a faster or slower INIT of your code as maybe something is just slightly askew there and hanging things up with the AR and detecting the controller. I could only take wild stabs in the dark like that without having one here and digging all of that back out and connecting it up to see what was really going on. That is, after testing it straight up with the controller to make sure that it does work. If I get the retro itch again and have $30 or so bucks not doing anything I'll pick up and AR and see what's going on there with the 3D controller.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

RDC

OK, for any others that are playing along here. I have an AR Plus now (v2.02) and it does work fine with the 3D controller in either the Digital or Analog modes, though the Analog nub does nothing.

I have observed that the AR is in part causing the 'issue' here. For whatever reason, it's only polling the controller for 14.25ms, with around 310us pauses in between, so it's polling much quicker, but the real issue is after this 14.25ms is up, it then drives the SEL line Hi and goes about it's business for ~2.5ms after that, then back to polling.



It does not wait for the current controller polling 'session' to finish before it does this, and that just screws the timing of everything all up as the code wasn't designed to handle that, so it just locks it all up.



I've edited the code now to also check on the state of the SEL line while waiting for the REQ line to change, so when the AR drives that SEL line Hi now the code will not hang up.

Code used to be..

while (TR_REQ == 1) {}

while (TR_REQ == 0) {}


..and all 4 instances of each in the Digital section (8 of each in the Analog section) were changed to..


while (TR_REQ == 1 && TH_SEL == 0) {}

while (TR_REQ == 0 && TH_SEL == 0) {}


It's not exactly how I'd want it to handle the issue, but so far it works and has been tested from power up, then into the console settings, to the AR menu and then to launching the game from there, so there are no more hang ups currently. More testing will need done, but for now this should correct the issue, and hopefully not cause any more.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

Xabaras76

Hello to everyone,

I'm considering to build this controller emulator, but I think that the link to all the files is dead.

Is there any possibility to get those files again.?

Thank you so much in advance.!

RDC

Here you go - https://mega.nz/#!rk0WCKJL!Zg_CDYkPJIXGjWYMC_DIIhHs1j-bnyYgdq5Z5xdhLS0

If that link says you need the decryption key, it is - !Zg_CDYkPJIXGjWYMC_DIIhHs1j-bnyYgdq5Z5xdhLS0
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

Xabaras76

Thank you so much.!!! :D
I've been looking for something like this for a long time.
It is strange but it is difficult to build a custom pad for Saturn .
I've found  some info in gamesx.com site...but I've read that the schematics there are not right.

And all the post regarding that scheme are quite old and all the pic loaded on Imageshack are no longer available.
And also I have found different port pinout betweek the scheme of Kashima(the one provided in gamesx site) and the one reported in gamesx.com Saturn controller page.

I will make a new scheme in Eagle so , maybe there will be a clear and corrected schematic.
I will test my scheme on my Saturn before post it here.

Thanks again for your precious help.!

I wish everybody a great day  :)


graze

#9
Seems like I need some help with my implementation of this code :(
Anyone up for the challenge?

I am trying to use an Arduino with a PS4 controller connected using a usb shield for this(plan is to go wireless with Bluetooth).
My code somewhat works while in the main Saturn menu however in some menus it and games it doesn't work quite as well. In the Saturn date menu it seems like its constantly registering a click up and a click down.
In the main menu when I click the shoulder buttons it seems to hold them until I click them again. This is most frustrating to me since I went over everything and have done this using a zillion ways based on RDC's method but it yields the same result. it shouldn't be a timing issue since the Saturn waits for the controller ACK before moving forward?
Help!


#include <PS4USB.h>

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

USB Usb;
PS4USB PS4(&Usb);

uint8_t oldL2Value, oldR2Value;



#define pin_In_S0 3 //select0 th
#define pin_In_S1 4 //select1 tr
#define pin_Out_Y0 A0 //data0
#define pin_Out_Y1 A1 //data1
#define pin_Out_Y2 A2 //data2
#define pin_Out_Y3 A3 //data3
#define pin_ACK_Y4 A4 //ack line

bool ACK_LINE = 0;
int  mode_Selector = 0; //0 is digital mode 1 is analog

void setup() {
  pinMode(pin_In_S0, INPUT);
  pinMode(pin_In_S1, INPUT); 
  pinMode(pin_Out_Y0, OUTPUT);
  pinMode(pin_Out_Y1, OUTPUT);
  pinMode(pin_Out_Y2, OUTPUT);
  pinMode(pin_Out_Y3, OUTPUT);
  pinMode(pin_ACK_Y4, OUTPUT);
 

//pull the ack line high on startup
changeACKState();
 
 
  Serial.begin(115200);
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  if (Usb.Init() == -1) {
    Serial.print(F("\r\nOSC did not start"));
    while (1); // Halt
  }
Serial.print(F("\r\nPS4 USB Library Started")); 
 
//we need to wait for the saturn to startup, 1.5 seconds
//delay(1500);
delayMicroseconds(500);
}


void loop() {
Usb.Task();

//Changes the controller from Digital to Analog
if (PS4.connected()) {
if (PS4.getButtonClick(SHARE) &&  mode_Selector==0){
mode_Selector=1; //change state to now operating in analog mode
Serial.print(F("\r\nController switched to Analog"));
}
if(PS4.getButtonClick(SHARE) &&  mode_Selector==1){
mode_Selector=0; //change state to now operating in digital mode
Serial.print(F("\r\nController switched to Digital"));
}


if(mode_Selector==0){
emulateDigitalController();
PS4.setLed(Blue);
}
if(mode_Selector !=0){
emulateAnalogController();
PS4.setLed(Green);
}

}
}


void emulateDigitalController(){

if (PS4.connected()) {

////////Handshake////////
//we now start communication with the saturn

// Wait for the Saturn to select controller
while(digitalRead(pin_In_S0)==1){

}
delayMicroseconds(40);

//waiting for the saturn
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==1){

}

//0000 = Digital  // 0001 = Analog
//pulled low to 0000 to id itself as a digital controller

digitalWrite(pin_Out_Y0, LOW);
digitalWrite(pin_Out_Y1, LOW);
digitalWrite(pin_Out_Y2, LOW);
digitalWrite(pin_Out_Y3, LOW);

//this is set to low to let the saturn know the controller is ready to id itself
//digitalWrite(pin_ACK_Y4, LOW);
changeACKState();

//at this stage in the sequence s1 goes high which is the saturn again requesting data from the controller

// Wait for Saturn to request Data Size
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==0){

}

//we now set 0010, which means 2 Bytes for the remaining Data. 6 for the analog controller(0110)


//digitalWrite(pin_Out_Y0, LOW);
digitalWrite(pin_Out_Y1, HIGH);
//digitalWrite(pin_Out_Y2, LOW);
//digitalWrite(pin_Out_Y3, LOW);

//the ack line will now go high telling the saturn it is ready for the data to be read from the controller
//digitalWrite(pin_ACK_Y4, HIGH);
changeACKState();


///////Handshake ends///////

///// START FIRST BYTE
 
    /* while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request first half of Data1
    PORTC = DATA1_1;        // DR, DL, DD, DU
    TL_ACK = 0;             // Here is the data */
 
  // Wait for Saturn to request first half of Data1
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==1){

}

clearOutputState();

//If the Up button pressed
if(PS4.getButtonClick(UP)){
  digitalWrite(pin_Out_Y0, HIGH);
   Serial.print(F("\r\ndUp"));
}//If the Down button pressed
if (PS4.getButtonClick(DOWN)){
   Serial.print(F("\r\ndDown"));
}//If the Left button pressed
if (PS4.getButtonClick(LEFT)){
  digitalWrite(pin_Out_Y2, HIGH);
   Serial.print(F("\r\ndLeft"));
}//If the RIght button pressed
if (PS4.getButtonClick(RIGHT)){
  digitalWrite(pin_Out_Y3, HIGH);
   Serial.print(F("\r\ndRight"));
}
changeACKState();


   /* while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request second half of Data1
   PORTC = DATA1_2;        // ST, A, C, B
   TL_ACK = 1;             // Here is the data */

// Wait for Saturn to request second half of Data1
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==0){     
}
clearOutputState();

//If the B button pressed
if(PS4.getButtonClick(CIRCLE)){
digitalWrite(pin_Out_Y0, HIGH);
Serial.print(F("\r\nCircle"));
}//If the C button pressed
if (PS4.getButtonClick(L3)){
  digitalWrite(pin_Out_Y1, HIGH);
Serial.print(F("\r\nL3"));
}
//If the A button pressed
if (PS4.getButtonClick(CROSS)){
digitalWrite(pin_Out_Y2, HIGH);
Serial.print(F("\r\nCross"));
}//If the Start button pressed
if (PS4.getButtonClick(OPTIONS)){
digitalWrite(pin_Out_Y3, HIGH);
   Serial.print(F("\r\nOptions"));
}
   changeACKState();
///// END FIRST BYTE

///// START SECOND BYTE

   /* while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request first half of Data2
   PORTC = DATA2_1;        // R, X, Y, Z
   TL_ACK = 0;             // Here is the data */

//Wait for Saturn to request first half of Data2
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==1){   
}
clearOutputState();

//If the Z button pressed
if(PS4.getButtonClick(R3)){
digitalWrite(pin_Out_Y0, HIGH);
Serial.print(F("\r\nR3"));
}
//If the Y button pressed
if (PS4.getButtonClick(TRIANGLE)){
digitalWrite(pin_Out_Y1, HIGH);
   Serial.print(F("\r\nTriangle"));
}//If the X button pressed
if (PS4.getButtonClick(SQUARE)){
digitalWrite(pin_Out_Y2, HIGH);
   Serial.print(F("\r\nSquare"));
}//If the Right trigger button pressed
if (PS4.getButtonClick(R1)){
digitalWrite(pin_Out_Y3, HIGH);
   Serial.print(F("\r\nR1"));
}
changeACKState();

   /* while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request second half of Data2
   PORTC = DATA2_2;        // L, 1, 1, 1
   TL_ACK = 1;             // Here is the data */

  // Wait for Saturn to request second half of Data2
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==0){   

}
clearOutputState();
//If the Left trigger button pressed
if(PS4.getButtonClick(L1)){
  digitalWrite(pin_Out_Y3, HIGH);
   Serial.print(F("\r\nL1"));
}

digitalWrite(pin_Out_Y0, HIGH);
digitalWrite(pin_Out_Y1, HIGH);
digitalWrite(pin_Out_Y2, HIGH);
changeACKState();

 
///// END SECOND BYTE

///// START THIRD BYTE, END OF DATA
 
   /* while (TR_REQ == 1 && TH_SEL == 0) {}  // Wait for Saturn to request first half End
   PORTC = DATA3_1;        // 0000
   TL_ACK = 0;             // Here is the data */

// Wait for Saturn to request first half End
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==1){

}
clearOutputState();

changeACKState();

  /*  while (TR_REQ == 0 && TH_SEL == 0) {}  // Wait for Saturn to request second half of End
   PORTC = DATA3_2;        // 0001
   TL_ACK = 1;             // Here is the data */

// Wait for Saturn to request second half of End
while(digitalRead(pin_In_S0)==0 && digitalRead(pin_In_S1)==0){ 
}

//clearOutputState();

digitalWrite(pin_Out_Y0, HIGH);
digitalWrite(pin_Out_Y1, LOW);
digitalWrite(pin_Out_Y2, LOW);
digitalWrite(pin_Out_Y3, LOW);

changeACKState();

//// END THIRD BYTE
 
  // Wait for the Saturn to deselect controller
/* while(TH_SEL == 0) {}   // Wait for the Saturn to deselect controller */

while(digitalRead(pin_In_S0)==0){

}
///// END COMMS WITH SATURN


}
}


//use to determine the status of the ACK return line and send back the opposite.eg. If its low now then return a high;
void changeACKState(){
  if(ACK_LINE==0){
    digitalWrite(pin_ACK_Y4, HIGH);
    ACK_LINE =1;
//Serial.print(F("\r\nACK High"));
  }
  else{
   digitalWrite(pin_ACK_Y4, LOW);
   ACK_LINE =0;
   //Serial.print(F("\r\nACK Low"));
  }

}

void clearOutputState(){
  digitalWrite(pin_Out_Y0, LOW);
  digitalWrite(pin_Out_Y1, LOW);
  digitalWrite(pin_Out_Y2, LOW);
  digitalWrite(pin_Out_Y3, LOW);
}






RDC

I wouldn't be getting the PS4 controller data while the Saturn is polling the controller. There is around 15ms between controller polls (and only 300us if an Action Replay is being used) where you can do that and have the data all ready for when the Saturn polls again.

All of those Serial.print are probably screwing the timing up something royal.

I also wouldn't do the clearOutputState(); everywhere either as it's just redundant.

Try commenting out all of the Serial.Print and ClearOutputState in your code and see how it acts.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

Thanks for the reply.
I have implemented this is about 6 different ways some getting the PS4 button read outside of when the Saturn is polling the port and even used port manipulation to try to speed things up but my efforts were futile.

You stating however that the serial.print could be an issue got me started looking into things further. I removed all of that code and it still did the same thing.
I then fed all zero's on d0 t0 d3(where the Saturn requested button input)  and commented out all code for the PS4 controller and it was doing the same thing still. I then commented out the Usb.task() method and this solved the issue. This task method is however needed to read the PS4 controller. I will look into this later but I am not hopeful this is my first go at anything hardware in regards to programming.

Thanks RDC for all the work you contributed to the Saturn community. I wouldn't be here playing with the Arduino if you didn't inspire me.

RDC

Welcome, though it's been years since I messed with any of this, and over a year since I dug it back out to correct the issue that fluxcore found.

Feeding it all zeros when it requests data would mean that all buttons are pressed at the same time. While I've never tried that, I doubt the Saturn would care much for it and for sure act weird. The default state for all of the data lines is Hi, or all ones, give that a try instead of setting them all Lo.

If you don't have a Logic Analyzer there, I'd seriously consider getting one, as sometimes when messing with code you just need to 'see' what it is really doing to the hardware there in order to track down the problem.

I used a Saleae Logic 8 here, which is a $220 analyzer now, but if you're going to be doing this kind of thing more and more you'll want to look into getting a decent one at some point with as many channels as you can find, because you'll almost always need just 1 more than you have. You can pick up one of the knock off Logic 8 analyzers from eBay for around $12. It only works with the older version of the software, but if all you need is an analyzer that will just be used for tinkering around at this level it will fit that bill and not put a dent in the wallet, and will for sure work here when looking at the Saturn protocol to see what is going on.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

Wait! Isnt the default state all zero's when using the 3 wire handshake protocol and all ones when using the 6 button protocol?

Yeah I was thinking about looking for a cheap logic analyzer solution if I plan to continue thinking with hardware.

RDC

There is no 3 wire protocol here, this is a 4bit wide bus with 3 control lines. With no controller attached, the data lines are always Hi, likewise they are still Hi when a controller is attached and the controller is being polled for the status of the buttons, after it goes thru the first 2 nibbles of the controller SPID and Byte size that is. Then the Analog data is a mix of Hi and Lo depending on the value it's reporting.

When the Saturn is polling any controller for the button status, a Lo is read as a button press. So if you're tossing all Lo bits in there then it's reading all of the buttons pressed at the same time and the D-pad pressed in every direction all at once, and that's bound to cause some kind of issue or weird behavior.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

NFG

All the photos in the first posts are dead.  You can attach them to the post itself here, or if you need a place to host 'em, I've got the space.  ^_^

RDC

#16
Yeah, apparently the bandwidth on the account capped this month. I haven't had that happen in quite awhile, and the free account at BotoPhucket doesn't let you track where the hole is at so you can do anything about it. It will reset here in a week or so and they should be back up, and when it does please feel free to save the pics and host them if you like, and Thanks for the offer. Most images I upload are edited from my originals, and once they are uploaded they get tossed here, so those are the only ones like them. I'd have to go thru a mess of time and work to redo them all, and there's no way I'm coughing up the $400 they want to upgrade the account for 1 year so I can find what exactly is getting hammered, ridiculous.

EDIT: So it looks like they have straight up disabled them for 3rd party hosting, and the only way to get that restored is to cough up the 4 bills, talk about some BS there.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

#17
What the hell!!! I was doing the exact opposite! The funny thing is that the directional buttons and abc and xyz worked(mostly) outside the sub menus.
I got the thing working properly(at least in the Saturn sub menus haven't tested in game) now.
I feel like an idiot. :o
Major props for your RDC patience.


RDC

If you're of the mindset that 0 = off and 1 = on, which when you first start off coding before poking around with the hardware you kind of learn it that way for 1 = set and 0 = cleared or something similar, so it's easy to apply that where it just doesn't work the same way.

Most controllers use a common ground, or active Lo buttons, so when any button is pressed the line goes to ground, so a 0 = pressed. They aren't all like that, for example the DS4 controller there has mostly active Lo buttons, but the L1, R1, L3 and R3 are all active Hi buttons, so when they are pressed the button line goes up to 3.2v, then the L2 and R2 are a pulsed line with a voltage divider so they can be Analog.

Glad you have it going there now, and no biggie at all man, it's all part of the 'fun' of doing this kind of thing, and you'll have far more adventures in coding if you keep at it. ;)
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

hmm interesting. Yeah in my head it always been 0 = off and 1 = on. LOL

So I have emulating the controller when in digital mode down perfectly(played a little Sega Rally) but it seems like I have an issue with the analog inputs(my Sonic R Character just refuse to go right).
The analog readings are broken up into two nibbles correct? I remember coming across somewhere that stated  that the value of each nibble was reversed. eg 10100001(161 from DS4) would be sent to the Saturn in Data3_1 as 0101 and Data3_2 as 1000.

RDC

Analog is sent as 2 nibbles, correct, but nothing is really reversed, it just sends the Hi nibble first.

For example: Lets use 10000000 = 128, which is the center value for 8bit Analog. That gets sent to the Saturn as a Hi nibble 1000, and then a Lo nibble 0000, so it would be 1000 in DATA3_1 and 0000 in DATA3_2

For your value of 10100001 = 161, you would just send the last 4 bits (Hi nibble) of that 1010 in DATA3_1, and then the first 4 bits (Lo nibble) of it 0001, in DATA3_2

The way I'd take that value and set it up would be something like this..


    X_AXIS =    // Get your X-Axis Value here from whatever source, setup here for an 8bit value only.
   
//    if (X_AXIS <= 138 && X_AXIS >= 118)     // Dead Zone for X-Axis, if needed because the Saturn can be picky about the center value, and this would be adjusted accordingly
//     {X_AXIS = 128;}

    DATA3_2 = X_AXIS;               // Save for Lo Nibble         
                                     
    DATA3_1 = X_AXIS >> 4;          // Shift 4 places and save for Hi Nibble
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

Got everything working like a charm. Thanks dude.

I put everything on my github so anyone can have a Wireless 3D controller, via the Dual Shock 4. Any capable person could edit the code and use any controller supported by the USB Shield library.


https://github.com/garybethel/DS4toSaturn

RDC

Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

I hate to resurrect this but recently in my attempt to eliminate lag I have been doing some timing testing and it just struck me that there is indeed a 15ms delay between call to the controller.
RDC I know at the end of your code you stated that you have 15ms to read the inputs, less if you use the action replay but I stupidly read it as the Saturn was tolerant of a 15ms delay here(Don't know what I was thinking).

The 3D controller seems so very slow and would introduce a noticeable delay in games.

RDC

That is only if you are making a 3D controller and/or are talking to the Saturn with that protocol, as the controller is only polled every 16ms while the TV is blanking, remember this is getting to be old tech now and that's how the
Saturn works, no getting around that. You'd never notice any lag there with the emulated 3D controller code versus using an actual 3D controller unless you're introducing it elsewhere.

If you are just polling the actual 3D controller for it's data to use elsewhere, then you can for sure poll it faster if you want, but with 1 poll every 16ms it's being checked over 60 times per second already.
Screwing up is one of the best learning tools, so long as the only thing you're not learning is how to screw up.

graze

#25
ah ok interesting. This polling is slow enough for me to not have to worry about having to aggressively optimize code for tight timing.

I play nights with the controller and that seems fine. It is in Sonic R(which supports the 3D controller too) that when I jump there seem to be a delay. This is what lead me to test all the timing.
This may just be normal. My scart to HDMI adapter is introducing at least 4ms of lag plus my tv's 5 so it all adds up. 
I guess for Fighting games like Street Fighter it may be best to not use the 3D controller. I not would even try to emulate this protocol with the Arduino because I would have to dive into assembly and the delay introduced by having to call the USB Shield Usb.Task() will throw things off.


edit://seems like polling every 15ms is normal for consoles of this time indeed and the non 3D controller on the saturn is no different.