Saturday 5 January 2013

Dual Boot R-Type 1 & 2 Arcade PCB - Day 6

 Dual Boot R-Type 1 & 2 Arcade PCB - Day 6


Devised a solution for the game select switch mechanism:


  • Cut a square of ABS plastic from some scrap from an IKEA cupboard corner (packing material)..

  • Melted a hole through the middle with the soldering iron & fitted a single pole dual throw toggle switch.

The whole assembly was then located in order that the switch sat within a retangular cut that was already present on the board:


Quick-dry Araldite was used to bond the plastic down to the PCB.

Two wires were connected to the left & right most pins of the switch & were soldered directly to the solder-pads of a nearby de-coupling capacitor (providing the required 0 & 5v signals).

The bydo-orange master A/B game-select line is tied to the switches middle pin.

The main idea of mounting the switch upside down between the boards is to avoid switching during game play.. The power must be disconnected while switching between games.

Perhaps a future iteration could perform a system-wide reset when the switch is toggled?

Fin.

DIY Jamma To USB Converter

  DIY Jamma To USB Converter


A couple of days ago there seemed to be an onslaught of soap-opera's :/ so it was back into the lab (read corner) to play with some tech..

In the past I had built a custom adapter that read the PS2-Keyboard signals from my HotRod Joystick controller & spat out USB joystick and Jamma switching signals (so I could play Mame & run my Jamma boards).

This time I wanted to be able to connect my lap-top to my BarTop (or indeed any other Jamma cabinet), so I grabbed one of the fingerboards kindly provided by Jenginner & a Teensy 2 board.

The end result was this:



And from below...



It's a pretty stright forward setup, the Teeny 2 processor has 22 I/O pins - so I connected 21 of them to the Player 1 & 2 joystick, coin & button (& service) connections of the finger board.

The power for the Teensy is provided by the computer connected to the USB, and the ground wire from the Jamma connector was wired to the Teensy's ground pin.

This arrangement allows the Teensy's I/O pins to be short circuited to ground by the controls in the cabinets CP. (My BarTop is based on a VGA LCD panel, so there is a VGA cable that can be brought out to the lap-top - handling the visual side of things :D)

Teensy 2's are available at: http://www.pjrc.com/teensy/ for $16 and for this project I used Teensyduino which is detailed on the same site.

Teensyduino basically puts the Teensy into a state where it thinks it's an Arduino, and as such is rendered much simpler to program.

Part of the Teensyduino integration is the ability to put the Teensy into a mode where it thinks it's a USB keyboard/mouse/Joystick (simultaniously as it goes), see:

http://www.pjrc.com/teensy/td_download.html
http://www.pjrc.com/teensy/td_joystick.html

Using the provided Teensy/Arduino editor (that can be found on the site above) I wrote the following program (don't worry - it's not too complicated):

//
// Jamma to USB converter... (BarTop Edge connector interface)
//

void setup()
{
  // Set all I/O pins to input (With pull-up!)...
  for (int p = 0; p < 21; p++)
  {
    pinMode(p, INPUT);
    digitalWrite(p, HIGH);
  }
 
  Joystick.useManualSend(true);
}

void loop()
{
  // Read all the Input pins...
  for (int p = 0; p < 21; p++)
  {
    Joystick.button(p+1, !(digitalRead(p) & 1));
  }

  Joystick.send_now();
 
  delay(5);
}


Once uploaded to the chip, the program scans the I/O pins 0 -> 20 which are setup as Inputs (using: pinMode(p, INPUT);) and the pins 5v pull-ups activated (by: digitalWrite(p, HIGH);).
This makes the pins sit at 5v, which can then be grounded by the switches within the Jamma controls.

The main loop basically loops over every one of the 21 pins we're interested in, reads the value of each one (using: !(digitalRead(p) & 1) (notice I'm inverting the signal with the ! NOT keyword)) & then outputs a corresponding USB-Joystick button press (using: Joystick.button()).
(I had to invert the signal read back from the pins as with the 5v pull-up - the signal is high when nothing is happening & low when a button / joystick direction is pressed.)

Joystick.useManualSend(true); stops the USB ouputing anything until you call: Joystick.send_now(); inorder that all the button presses make it to the lap-top at the same time.

And the delay(5); is to stop the chip kicking the shit out of the USB port - 200hz is more than enough..

And that's about it - after redefining the 'keys' in Mame, I loaded Puzzle-Bobble up & the misses was a happy bunny. :)

Edit:

Had to move a couple of wires around, ended up using the wires destined for Coin# 1 & 2 for Service & Test. Oh, and I had a bug - the Joystick.button() function only takes a button number starting from 1 - so I added +1 to the 'p' variable being passed into the first parameter (and remapped the keys again).

Edit #2: 


Had to work through a problem I was having with my cab->to->laptop adapter.. namely why Mame was forgetting some of it's key mappings on start-up.

The above code mapped the two player sticks & buttons across 20 USB joystick buttons.. and Mame does see them all when you configure the keys. But when you re-load Mame, half of the button mappings appear as N/A

I just noticed this morning that any buttons numbered 1-15 were okay.. so it looks like while the system does recognise my 32 button USB device.. the loading system only loops through and sets up the first the first 16!! :( (maybe normal USB controllers support 16 buttons max?)

So I did a re-write and moved the Player 1 stick to the analog X & Y Joystick axis and moved the Player 2 stick to the Left & Right analog shoulder axis.  The remaining Player 1 & 2: Fire 1-4, Coin, Start & Service buttons were then mapped to the first (lower) 12 USB button indices.

All works sweetly now & I can even use other DirectInput games on my cab, things like ZSnes, Sega Fusion, Etc..!! ¦)

The code became:

//
// Jamma to USB converter... (BarTop Edge connector interface)
//

uint16_t lastPlayer1 = 0x0000;
uint16_t lastPlayer2 = 0x0000;
uint16_t lastTest = 0x0000;

void setup()
{
  // Set all I/O pins to input (With pull-up!)...
  for (int p = 0; p < 21; p++)
  {
    pinMode(p, INPUT);
    digitalWrite(p, HIGH);
  }
 
  Joystick.useManualSend(true);
}

void loop()
{
  uint8_t p1Coin  = !(digitalRead(0) & 1);
  uint8_t p1Up    = !(digitalRead(18) & 1);
  uint8_t p1Down  = !(digitalRead(17) & 1);
  uint8_t p1Left  = !(digitalRead(16) & 1);
  uint8_t p1Right = !(digitalRead(15) & 1);
  uint8_t p1Fire4 = !(digitalRead(11) & 1);
  uint8_t p1Fire3 = !(digitalRead(12) & 1);
  uint8_t p1Fire2 = !(digitalRead(13) & 1);
  uint8_t p1Fire1 = !(digitalRead(14) & 1);
  uint8_t p1Start = !(digitalRead(19) & 1);
 
  uint8_t p2Coin  = !(digitalRead(10) & 1);
  uint8_t p2Up    = !(digitalRead(8) & 1);
  uint8_t p2Down  = !(digitalRead(7) & 1);
  uint8_t p2Left  = !(digitalRead(6) & 1);
  uint8_t p2Right = !(digitalRead(5) & 1);
  uint8_t p2Fire4 = !(digitalRead(1) & 1);
  uint8_t p2Fire3 = !(digitalRead(2) & 1);
  uint8_t p2Fire2 = !(digitalRead(3) & 1);
  uint8_t p2Fire1 = !(digitalRead(4) & 1);
  uint8_t p2Start = !(digitalRead(9) & 1);
 
  uint8_t testButton = !(digitalRead(20) & 1);

  // Read all the Input pins...
  Joystick.button(7, p1Coin); // Coin 1
  Joystick.button(8, p1Fire4); // P1 - Button 4
  Joystick.button(9, p1Fire3); // P1 - Button 3
  Joystick.button(10, p1Fire2); // P1 - Button 2
  Joystick.button(11, p1Fire1); // P1 - Button 1
  Joystick.button(12, p1Start); // P1 - Start
 
  Joystick.button(1, p2Coin);    // Coin 2
  Joystick.button(2, p2Fire4);   // P2 - Button 4
  Joystick.button(3, p2Fire3);   // P2 - Button 3
  Joystick.button(4, p2Fire2);   // P2 - Button 2
  Joystick.button(5, p2Fire1);   // P2 - Button 1
  Joystick.button(6, p2Start);   // P2 - Start
 
  Joystick.button(13, testButton); // Test
 
  uint16_t player1X = 511;
  if(p1Right) player1X = 1023;  // P1 - Right
  if(p1Left) player1X = 0;     // P1 - Left 
  Joystick.X(player1X);

  uint16_t player1Y = 511;
  if(p1Up)   player1Y = 0;    // P1 - Up
  if(p1Down) player1Y = 1023; // P1 - Down 
  Joystick.Y(player1Y);

  uint16_t player2X = 511;
  if(p2Right) player2X = 1023;  // P2 - Right
  if(p2Left)  player2X = 0;     // P2 - Left 
  Joystick.sliderLeft(player2X);

  uint16_t player2Y = 511;
  if(p2Up)   player2Y = 0;     // P2 - Up
  if(p2Down) player2Y = 1023;  // P2 - Down 
  Joystick.sliderRight(player2Y);

  // Gather all the bits together to test if anything has changed..
  int player1 = p1Coin | (p1Up << 1) | (p1Down << 2) | (p1Left << 3) | (p1Right << 4) | (p1Fire1 << 5) | (p1Fire2 << 6) | (p1Fire3 << 7) | (p1Fire4 << 8) | (p1Start << 9);
  int player2 = p2Coin | (p2Up << 1) | (p2Down << 2) | (p2Left << 3) | (p2Right << 4) | (p2Fire1 << 5) | (p2Fire2 << 6) | (p2Fire3 << 7) | (p2Fire4 << 8) | (p2Start << 9);

  // Only send USB data if something changes (stops Mame stalling)
  if ((player1 != lastPlayer1) || (player2 != lastPlayer2) || (testButton != lastTest))
  {
    Joystick.send_now();
  }
 
  lastPlayer1 = player1;
  lastPlayer2 = player2;
  lastTest = testButton;

  delay(2);    // 2 ms
}

On reflection, I might move the player 1 buttons to the first 4 USB buttons (in order: 1,2,3,4) so that games that can't have their keys redefined will see the cab's CP as a regular USB controller.. :D