Close

PC Gameport - Basic circuit

A project log for Control Freak

Collection of Arduino Micro firmware and schematics for connecting your retro Joysticks and Gamepads to USB port.

danjovicdanjovic 04/12/2020 at 03:250 Comments

The basic PC Gameport provides connection for up two controllers with 2 axes and 2 buttons each. 

Each axis is composed by a 100k potentiometer in series with a internal capacitor to form the RC timing network of a timer chip. Every time the PC wants to sample the axes it discharges the capacitors and then counts the time that it takes until each axis reach the Vih (input threshold voltage of the timer chip).
When in idle, both axes shall present half of the potentiometer value (50K). When the stick LEFT and UP the potentiometer of X and Y  axes shall respectively present a resistance close to zero. On the opposite side, when the the stick is RIGHT and DOWN the resistance should be close to 100K.

To measure the potentiometers using the Arduino, there are two possibilities:

First method provides a more precise method but requires a protection resistor and a timing capacitor for each axis.

The timing measured shall be done using a constant time loop so the value of one axis do not interfere in the measurement of another. That can be accomplished without counting cycles in assembly. The trick is simple: Make a loop and add the variable that counts each axis with the corresponding bit of that axis. The time required to mask and shift the bits is independent of their values
void samplePCjoystick() {
  static volatile uint16_t counter=0;
  uint8_t  sample;
  a1X=0;   a1Y=0;   a2X=0;   a2Y=0;
   releaseCaps();
   delayMicroseconds(5);

  counter = 1024;
  do  {   // sample inputs with time constant loop
      sample = ~PINC;
      delayMicroseconds(3);
      a2Y+=sample & 1; 
      sample>>=1;
      a2X+=sample & 1; 
      sample>>=1;
      a1Y+=sample & 1; 
      sample>>=1;
      a1X+=sample & 1; 
    } while (--counter);

  // reset caps
  holdCaps();
} 

The second method requires only a pull down resistor but provides a non-linear curve that must be linearized. 

If we choose a 39k ohm for the pull down resistor we can count on PCJoy library that perform the linearization
int8_t PCJoy::_adcTo100(uint16_t adc)
{
	int32_t retval = ((int32_t) 79794 / adc) - 178;

	if (retval < -99)
		return -99;
	else if (retval > 99)
		return 99;
	else
		return retval;
}

That will return a value within -99 to 99.


Discussions