Friday, August 26, 2016

Vintage Atari CX22 Trak-Ball with USB interface.

The trackball, or Trak-Ball as Atari dubbed it, was a fascinating controller to me in the arcades. There was a mystery at work under the controller panel transferring the rolling motion of the ball to the character on the screen. They were too expensive to own for my Atari computer, so I played Centipede (not very well) at the arcade. I didn't really understand how trackballs worked until college. That's when I found out the mouse was just an upside down trackball with the optical encoder wheels and all. I recently picked up an Atari CX22 Trak-Ball at a vintage gaming show and turned it into a USB device. I plan on taking it to work to control my Mac just for the sake of irony. In the meantime, it's great for a mean game of Missile Command on the emulator.

Thank-you Dan Kramer for designing this awesome piece of hardware!



Atari made a few Trak-Ball models: the CX22 (for the 2600 and 8-bits) in the classic brown and beige colors, the CX80 with the mid-80's XL motif, and the CX53 for the 5200. The original CX22 only worked as a joystick simulator and a revision included a selector switch to allow true trackball control. This true trackball mode can be used to play Missile Command on the 8-bit computers by pressing control-T. Totally different and much closer to the arcade experience than the joystick. I put some links to trackball history and documentation at the bottom of this post - make sure you check out the concept drawings at the online Atari museum in the last link.

Decoding the Trak-Ball

The CX22 service manual gives a nice theory of operation for the trackball. The ball spins two rollers which rotate optical encoder wheels in each axis. The encoders have two signals in quadrature phase (like a sine and a cosine waves) used to determine the speed and direction. The direction is determine by finding which signal leads the other. To have a joystick mode, the CX22 does this with discrete CMOS logic chips. The quadrature square waves are fed to a CD4013B D-type Flip Flop - one signal acts as clock and the other data. When the data lags the clock, a logic 1 is latched in at the falling clock edge. When the data leads, the result is logic 0. In trackball mode, the clock and direction out are selected by the switch and sent to the controller port. In joystick mode, the clock triggers a one-shot 4538B chip configured for a 9-ms pulse width. The one-shot output gates the direction signal sent to the output. Thus, there's at least one 9-ms joystick direction pulse that is extended for each encoder wheel clock tick occurring before the 9-ms is up.

USB Device

Using the HID-Project library for the Arduino, I configured my Leonardo board to act like a USB mouse. The code is incredibly simple because the direction decoding is already performed by the CX22 hardware. (The Arduino could certainly decode the signals if the clocks were sent directly - here's a tutorial with multiple software approaches to quadrature decoding. Good to keep in your back pocket for building a spinner controller.) The code polls the clock signals and sends incremental mouse movements over USB to the OS. It also polls the digital input for the fire button(s) and sends mouse clicks. For some visual feedback, I blink the on board LED every time motion is detected. 
Here's my sketch:

#include <HID-Project.h>
#include <HID-Settings.h>

const int XINC = 6;
const int YINC = 6;

int xdir = LOW;
int xmot = LOW;
int ydir = LOW;
int ymot = LOW;

void setup() {
  // put your setup code here, to run once:
  for (int i=0; i<6; i++) {
  pinMode(i,INPUT_PULLUP);
  }
  pinMode(13,OUTPUT);
  Mouse.begin();
}

void loop() {
// poll the xmot and ymot looking for state changes
// when state change, increment mouse movement


int xold=xmot;
int yold=ymot;

ydir=digitalRead(0)*2-1;
ymot=digitalRead(1);
xdir=digitalRead(2)*2-1;
xmot=digitalRead(3);

if (xmot!=xold) {
  Mouse.move(xdir*XINC,0);
  digitalWrite(13,1);
}

if (ymot!=yold) {
  Mouse.move(0,ydir*YINC);
  digitalWrite(13,1);
}

int f=!digitalRead(4);
if (f) {
  Mouse.press();
  digitalWrite(13,1);
} else {
  Mouse.release();
  digitalWrite(13,0);
}

//Serial.println(x);
}

Thanks for stopping by!

Trackball Links

No comments:

Post a Comment