49-Way Joystick Conversion Board
Revision as of 18:45, 25 April 2020 by Braedel (talk | contribs) (→Programmed Joystick Emulation Modes)
Contents
Custom PCB - Drop-In
This board is a drop-in replacement for the original 49-way(Sinistar/ArchRivals/Pigskin 621 AD).
- What does it do?
- Converts 49-Way Joysticks to 8-Way (or other types if desired)
- Uses all other hardware from the joystick besides the board
- ALL components on board are new including the Optical Switches
- Super easy installation
- Provides user ability to select Joystick type with DIP switches
- 4 outputs are provided
- Up
- Down
- Left
- Right
- Uses Williams Standard 7-Pin connector and Pinout (Bubbles, Spat, Inferno, Joust-with rare 2-way optical sticks)
- Default setting is for 8-Way that duplicates the dedicated Williams 8-Way Optical Joystick
Programmed Joystick Emulation Modes - 10 Unique
- Thanks to Sean Riddle for the work he did building an EPROM based translator. Lots of the information I used to develop this was from him. You can visit his site HERE. (Thanks Sean!)
Mode 0: 8-Way | Standard
Mode 1: 8-Way | No Dead Zone
Mode 2: 8-Way | With Dead Zone
Mode 3: 4-Way | Standard
Mode 4: 4-Way | With Dead Zone
Mode 5: 4-Way | Corners Left/Right
Mode 6: 4-Way | Corners Up/Down
Mode 7: 2-Way | Horizontal
Mode 8: 2-Way | Vertical
Mode 9: Diagonal
Image:49-Way Matrix Locations.PNG | (49-Way Matrix Locations)
- ==DIP Switch Settings for Different Emulation Modes==
- 8-Way Standard - DIP.PNG
(8-Way - Standard)
- 8-Way - No Dead Zone - DIP.PNG
(8-Way - With Dead Zone)
Original 49-Way
Operation - Pinout
- 3 Optical Interrupter Switches are used for each direction (Up/Down-Y and Left/Right -X) .Total of 6 switches
- This provides a total of 7 possible positions in each X and Y axis. (3 Up/1 Center/3 Down - 3 Left/1 Center/3 Right) (7 * 7 = 49 Unique positions)
- There are 8 unique outputs
- Opto Swtich 1 (U/D)
- Opto Switch 2 (U/D)
- Opto Switch 3 (U/D)
- Up/Down Direction
- Opto Switch 4 (L/R)
- Opto Switch 5 (L/R)
- Opto Switch 6 (L/R)
- Left/Right Direction
- 12 Pin Molex Header Pin-Out
- +5 Volts
- Opto Switch 1 (U/D)
- Opto Switch 2 (U/D)
- Opto Switch 3 (U/D)
- Up/Down Direction (Up=Low / Down = High)
- Opto Switch 4 (L/R)
- Opto Switch 5 (L/R)
- Opto Switch 6 (L/R)
- Left/Right Direction (Right = Low / Left = High)
- Key - No Connection
- Ground
- Ground
49-Way Joystick Information
The Williams 49-Way Optical Joystick was advanced for the time. As the device relied on optical interrupter switches, there were no contacts to wear out or adjust.
Amtel Microprocessor Source Code:
//Program Written By: Brad Raedel //03242020 - Revision A //** All 8 Profiles active //** Outputs now change to inputs when not active - This allows the pull-up resistors to control the high logic //03162020 - Revision 1 - Just get it working //Up int Opto1 = 2; //Input Pin 2 int Opto2 = 3; //Input Pin 3 int Opto3 = 4; //Input Pin 4 int Up = 9; //Output to Game - Pin 10 int Dn = 10; //Output to Game - Pin 11 //Down int Opto4 = 5; //Input Pin 5 int Opto5 = 6; //Input Pin 6 int Opto6 = 7; //Input Pin 7 int Left = 11; //Output to Game - Pin 12 int Right = 12; //Output to Game - Pin 13 //Option Switches - These will be used to select mode/options (0 to 15) int Op1 = A0; //1 int Op2 = A1; //2 int Op3 = A2; //4 int Op4 = A3; //8 int OpVal = 0; //Default to zero bool Opto1St; //Opto States bool Opto2St; bool Opto3St; bool Opto4St; bool Opto5St; bool Opto6St; bool UpSt; //Set HIGH when stick is UP bool DnSt; //Set HIGH when Stick is DOWN bool LtSt; //Set HIGH when stick is LEFT bool RtSt; //Set HIGH when Stick is RIGHT bool LockedStUD;//Used to Latch the UP or DOWN position bool LockedStLR;//Used to Latch the LEFT or RIGH postion int UpVal = 3; //Store the Up position (0-2) int DnVal = 3; //Store the Down postion (0-2) int UpDnPos = 3; //Absolute postion of Up/Down 0 = Top, 6 = Bottom, 3 is Center/Neutral int LtVal = 3; //Store the Left position (0-2) int RtVal = 3; //Store the Right position (0-2) int LtRtPos = 3; //Absolute postion of Left/Right 0 = Left, 6 = Right, 3 is Center/Neutral int AbsPos = 24; int i = 0; //Rows Then Columns - Stored Like this { {Row 0 - 0 to 48},{Row 2 - 0 to 48}...etc.. // 8-Way Standard const bool Up8WayStd[49] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn8WayStd[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ; const bool Lt8WayStd[49] = {1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0} ; const bool Rt8WayStd[49] = {0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1} ; // 8-Way with NO Dead Zone const bool Up8WayNoDZ[49] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn8WayNoDZ[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ; const bool Lt8WayNoDZ[49] = {1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0} ; const bool Rt8WayNoDZ[49] = {0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1} ; // 8-Way with Dead Zone const bool Up8WayDZ[49] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn8WayDZ[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ; const bool Lt8WayDZ[49] = {1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0} ; const bool Rt8WayDZ[49] = {0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1} ; //4-Way Standard const bool Up4WayStd[49] = {0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn4WayStd[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0} ; const bool Lt4WayStd[49] = {0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Rt4WayStd[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0} ; //4-Way Corners L/R const bool Up4WayLR[49] = {0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn4WayLR[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,0} ; const bool Lt4WayLR[49] = {1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0} ; const bool Rt4WayLR[49] = {0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1} ; //4-Way Corners U/D const bool Up4WayUD[49] = {1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn4WayUD[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1} ; const bool Lt4WayUD[49] = {0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Rt4WayUD[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0} ; // 2-Way Horizontal const bool Up2WayH[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn2WayH[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Lt2WayH[49] = {1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0} ; const bool Rt2WayH[49] = {0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1} ; // 2-Way Vertical const bool Up2WayV[49] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Dn2WayV[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ; const bool Lt2WayV[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; const bool Rt2WayV[49] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ; bool UpOut[49] = {}; bool DnOut[49] = {}; bool LtOut[49] = {}; bool RtOut[49] = {}; void setup() { //Serial.begin(57600); //Disable when not debugging pinMode(Opto1,INPUT); //Set Direction of physical inputs/outputs pinMode(Opto2,INPUT); pinMode(Opto3,INPUT); pinMode(Opto4,INPUT); pinMode(Opto5,INPUT); pinMode(Opto6,INPUT); pinMode(Up,INPUT); pinMode(Dn,INPUT); pinMode(Left,INPUT); pinMode(Right,INPUT); pinMode(Op1,INPUT); pinMode(Op2,INPUT); pinMode(Op3,INPUT); pinMode(Op4,INPUT); //Let's get our DIP Switch settings (Will = 0 to 15) if (digitalRead(Op1) == HIGH) { OpVal = 1; } if (digitalRead(Op2) == HIGH) { OpVal = OpVal + 2; } if (digitalRead(Op3) == HIGH) { OpVal = OpVal + 4; } if (digitalRead(Op4) == HIGH) { OpVal = OpVal + 8; } //Serial.println(OpVal); switch (OpVal) { case 0: //DIP Switch 0 - 8-Way - No Dead zone - This routine copies the array - Only on setup, not during loop/runtime // 8WayStd i = 0; while( i < 49 ){ UpOut[ i ] = Up8WayStd[ i ]; DnOut[ i ] = Dn8WayStd[ i ]; LtOut[ i ] = Lt8WayStd[ i ]; RtOut[ i ] = Rt8WayStd[ i ]; ++i;} break; case 1: //DIP Switch 1 - // 8WayNoDZ i = 0; while( i < 49 ){ UpOut[ i ] = Up8WayNoDZ[ i ]; DnOut[ i ] = Dn8WayNoDZ[ i ]; LtOut[ i ] = Lt8WayNoDZ[ i ]; RtOut[ i ] = Rt8WayNoDZ[ i ]; ++i;} break; case 2: //DIP Switch 2 - // 8WayDZ i = 0; while( i < 49 ){ UpOut[ i ] = Up8WayDZ[ i ]; DnOut[ i ] = Dn8WayDZ[ i ]; LtOut[ i ] = Lt8WayDZ[ i ]; RtOut[ i ] = Rt8WayDZ[ i ]; ++i;} break; case 3: //DIP Switch 3 - // 4WayStd i = 0; while( i < 49 ){ UpOut[ i ] = Up4WayStd[ i ]; DnOut[ i ] = Dn4WayStd[ i ]; LtOut[ i ] = Lt4WayStd[ i ]; RtOut[ i ] = Rt4WayStd[ i ]; ++i;} break; case 4: //DIP Switch 4 - // 4WayLR i = 0; while( i < 49 ){ UpOut[ i ] = Up4WayLR[ i ]; DnOut[ i ] = Dn4WayLR[ i ]; LtOut[ i ] = Lt4WayLR[ i ]; RtOut[ i ] = Rt4WayLR[ i ]; ++i;} break; case 5: //DIP Switch 5 - // 4WayUD i = 0; while( i < 49 ){ UpOut[ i ] = Up4WayUD[ i ]; DnOut[ i ] = Dn4WayUD[ i ]; LtOut[ i ] = Lt4WayUD[ i ]; RtOut[ i ] = Rt4WayUD[ i ]; ++i;} break; case 6: //DIP Switch 6 - // 2WayH i = 0; while( i < 49 ){ UpOut[ i ] = Up2WayH[ i ]; DnOut[ i ] = Dn2WayH[ i ]; LtOut[ i ] = Lt2WayH[ i ]; RtOut[ i ] = Rt2WayH[ i ]; ++i;} break; case 7: //DIP Switch 7 - // 2WayV i = 0; while( i < 49 ){ UpOut[ i ] = Up2WayV[ i ]; DnOut[ i ] = Dn2WayV[ i ]; LtOut[ i ] = Lt2WayV[ i ]; RtOut[ i ] = Rt2WayV[ i ]; ++i;} break; } } //Main Program that loops void loop() { // Up/Down Logic Opto1St = digitalRead(Opto1); //Read Optical Switch 1 Opto2St = digitalRead(Opto2); //Read Optical Switch 2 Opto3St = digitalRead(Opto3); //Read Optical Switch 2 if (Opto1St == true && Opto2St == true && Opto3St == true) //Determine if the joystick is in the center { LockedStUD = false; //Unlatch to allow Up or Down to assume control UpSt = false; //Clear Up State DnSt = false; //Clear Down State UpDnPos = 3; //Set Vertical position to center } //Latch Direction (Determines if up or down if (Opto1St == false && LockedStUD == false) //UP if True { UpSt = true; //Set UP State to TRUE LockedStUD = true; //We lock the State so DOWN can't Steal it! } if (Opto3St == false && LockedStUD == false) //DOWN if True { DnSt = true; //Set DOWN State to TRUE LockedStUD = true; //We lock the State so UP can't Steal it! } //We will figure out what row we are in (0 to 6 - 3 is center) if (UpSt == true) //Up doesn't need any change since adding the Optical Switches is 0 to 2 { UpVal = Opto1St + Opto2St + Opto3St; UpDnPos = UpVal; } if (DnSt == true) //For Down, we determine how many Optical Switches are Low, add them up then use a look-up table to generate the position 4 to 6 { DnVal = Opto1St + Opto2St + Opto3St; switch (DnVal) { case 0: //All Optos are LOW UpDnPos = 6; break; case 1: //Two of the Optos are LOW UpDnPos = 5; break; case 2: //One of the Optos are LOW (The others are still High) UpDnPos = 4; break; } } // Left/Right Logic Opto4St = digitalRead(Opto4); //Read Optical Switch 4 Opto5St = digitalRead(Opto5); //Read Optical Switch 5 Opto6St = digitalRead(Opto6); //Read Optical Switch 6 if (Opto4St == true && Opto5St == true && Opto6St == true) //Determine if the joystick is in the center { LockedStLR = false; //Unlatch to allow Left or Right to assume control LtSt = false; //Clear Left State RtSt = false; //Clear Right State LtRtPos = 3; //Set Horizontal position to center } //Latch Direction (Determines if Left or Right if (Opto4St == false && LockedStLR == false) //Right if True { RtSt = true; //Set Right State to TRUE LockedStLR = true; //We lock the State so Left can't Steal it! } if (Opto6St == false && LockedStLR == false) //Left if True { LtSt = true; //Set Left State to TRUE LockedStLR = true; //We lock the State so Right can't Steal it! } //We will figure out what row we are in (0 to 6 - 3 is center) if (LtSt == true) //Up doesn't need any change since adding the Optical Switches is 0 to 2 { LtVal = Opto4St + Opto5St + Opto6St; LtRtPos = LtVal; } if (RtSt == true) //For Down, we determine how many Optical Switches are Low, add them up then use a look-up table to generate the position 4 to 6 { RtVal = Opto4St + Opto5St + Opto6St; switch (RtVal) { case 0: //All Optos are LOW LtRtPos = 6; break; case 1: //Two of the Optos are LOW LtRtPos = 5; break; case 2: //One of the Optos are LOW (The others are still High) LtRtPos = 4; break; } } //Calculate the absolute position AbsPos = (UpDnPos * 7) + LtRtPos; //Serial.println(AbsPos); //Output //UP if (UpOut[AbsPos] == true){ pinMode(Up,OUTPUT); digitalWrite(Up,LOW); } else { pinMode(Up,INPUT); } //Down if (DnOut[AbsPos] == true){ pinMode(Dn,OUTPUT); digitalWrite(Dn,LOW); } else { pinMode(Dn,INPUT); } //Left if (LtOut[AbsPos] == true){ pinMode(Left,OUTPUT); digitalWrite(Left,LOW); } else { pinMode(Left,INPUT); } //Right if (RtOut[AbsPos] == true){ pinMode(Right,OUTPUT); digitalWrite(Right,LOW); } else { pinMode(Right,INPUT); } }