Universal PC GamePort to USB Adapter with LPC1768
.
Group Members
- Chris Blackstone
- Wade Sheldon
Project Description
The goal of this project was to create a PC Game Port to USB Adapter using the mbed LPC1768 which could then be used to with any standard game port joystick for pc gaming. The standard DA-15 Game Port Connector and Joysticks operate at 5 volts and supports two joysticks, each with two buttons. While the buttons for each joystick are simple buttons, the joysticks use a 0 - 100K Ohm rheostat, or potentiometers with no ground connection, each for the X and Y direction. This creates a variable current through with the least current when the joystick is pushed toward the bottom right and most toward the top left. The simplest way to measure the analog variance in current is to use several 555 timers for each joystick and direction that can be used to create and measure a pulse from the joystick. These pulse values and buttons are then mapped to digital values for a standard Windows USB Joystick driver, or Mouse and Keyboard depending on configuration.
Components
- (1) mbed LPC1768
- (1) GamePort Controller
- (1) DA15 GamePort Connector
- (4) NE555P IC Single Precision Timers
- (4) 4.7nF Capacitors
- (4) .01 uF Capacitors
- (4) 2.2 K Ohm Resistors
555 Timer Information
The standard GamePort joystick uses rheostats, which vary current, for measuring the x & y positions of the joystick. To convert this analog value into something that can be measured digitally, we used a series of 555 Timers in combination with several small capacitors to generate a pulse. When the 555 timers are triggered they discharge the capacitor and we start a timer on the mbed LPC1768. When as the capacitor charges the 555 timer will send a signal when it measures 2/3 of our 5V and we can stop the timer. As the rheostat changes the current from small to large depending on joystick position, varying length pulses indicate the position of the joystick.
Calibration for Windows USB Joystick
To map the joystick GamePort values to USB for windows we used the default Windows USB Joystick Drivers and the library USBJoystick which expects two 16-bit integers for the X & Y directions, then a single unsigned 32-bit integer where each bit is a button on the controller. To map the GamePort joystick pulses to these 16-bit values we first had to create a way to calibrate the current Joystick and find its Max, Min, and Median pulse length as every Joystick may very slightly.
Wiring Diagram
Project Code
Import programGamePortAdapter
GamePortAdapter
main.cpp
#include "mbed.h" #include "USBMouseKeyboard.h" #include "USBJoystick.h" #include "rtos.h" #include "string.h" extern "C" void mbed_reset(); Serial pc(USBTX, USBRX); //macOS: screen /dev/tty.usbmodem{num} {baud rate} //Windows: Realterm lol Timer y1_t; Timer x1_t; Timer y2_t; Timer x2_t; InterruptIn y1(p16); InterruptIn x1(p15); InterruptIn y2(p18); InterruptIn x2(p17); volatile int y1_pulse; volatile int x1_pulse; volatile int y2_pulse; volatile int x2_pulse; volatile int buttons; DigitalOut trig(p19); BusIn buttons_raw(p21, p22, p23, p24); //first row: necessary garbage, size, mode, reserved x2 //following rows: input, min, max, output, value int bindings[128][5]; /*int bindings[128][5] = {{0xFFFFFFFF, 7, 0, 0, 0}, {0, 0, 100, 4, (int)'a'}, {0, 400, 1000, 4, (int)'d'}, {1, 0, 100, 4, (int)'w'}, {1, 400, 1000, 4, (int)'s'}, {4, 128, 255, 4, (int)'1'}, {5, 128, 255, 4, (int)'2'}};*/ /*int bindings[8][5] = {{0, 0, 1000, 0, 0}, {1, 0, 1000, 1, 0}, {4, 128, 255, 4, 0x1}, {5, 128, 255, 4, 0x2}};*/ LocalFileSystem local("local"); void start_y1() { y1_t.reset(); y1_t.start(); } void stop_y1() { y1_t.stop(); y1_pulse = y1_t.read_us(); } void start_x1() { x1_t.reset(); x1_t.start(); } void stop_x1() { x1_t.stop(); x1_pulse = x1_t.read_us(); } void start_y2() { y2_t.reset(); y2_t.start(); } void stop_y2() { y2_t.stop(); y2_pulse = y2_t.read_us(); } void start_x2() { x2_t.reset(); x2_t.start(); } void stop_x2() { x2_t.stop(); x2_pulse = x2_t.read_us(); } void analog_thread() { buttons_raw.mode(PullUp); trig = 1; y1_pulse = 0; x1_pulse = 0; y2_pulse = 0; x2_pulse = 0; y1.rise(&start_y1); y1.fall(&stop_y1); x1.rise(&start_x1); x1.fall(&stop_x1); y2.rise(&start_y2); y2.fall(&stop_y2); x2.rise(&start_x2); x2.fall(&stop_x2); while(1) { trig = 0; trig = 1; buttons = ~buttons_raw&0xF; Thread::wait(1); } } void debug_thread() { pc.printf("Beginning Joystick Test...\r\n"); pc.printf("---------------------------------\r\n"); while(1) { pc.printf("Joystick 1 - %d, %d, %X \r\n", x1_pulse, y1_pulse, buttons&0x3); pc.printf("Joystick 2 - %d, %d, %X \r\n", x2_pulse, y2_pulse, (buttons>>2)&0x3); pc.printf("\r\n"); Thread::wait(500); } } void keys_mouse_output_thread() { USBMouseKeyboard keys_mouse; int mouse[4]; int value, trigval; while(true) { memset(mouse, 0, sizeof(mouse)); for (int i=1; i<bindings[0][1]; i++) { //pc.printf("Checking %d: ", i); switch (bindings[i][0]) { case 0: value = x1_pulse; trigval = x1_pulse; break; case 1: value = y1_pulse; trigval = y1_pulse; break; case 2: value = x2_pulse; trigval = x2_pulse; break; case 3: value = y2_pulse; trigval = y2_pulse; break; case 4: trigval = (buttons & 0x1)*255; value = bindings[i][4]; break; case 5: trigval = ((buttons>>1) & 0x1)*255; value = bindings[i][4]; break; case 6: trigval = ((buttons>>2) & 0x1)*255; value = bindings[i][4]; break; case 7: trigval = ((buttons>>3) & 0x1)*255; value = bindings[i][4]; break; } if (trigval >= bindings[i][1] && trigval <= bindings[i][2]) { pc.printf("Triggered : %d, %d, %d\r\n", i, trigval, value); switch (bindings[i][3]) { case 0: mouse[0] = value/10; break; //Mouse X case 1: mouse[1] = value/10; break; //Mouse Y case 2: mouse[2] |= bindings[i][4]; break; //Mouse buttons case 3: mouse[3] = value/10; break; //Mouse scroll case 4: keys_mouse.keyCode(bindings[i][4]); break; //Keypress } } } keys_mouse.update(mouse[0], mouse[1], mouse[2], mouse[3]); Thread::wait(20); } } void joystick_output_thread() { USBJoystick joystick; int joy[6]; int value, trigval; while(true) { memset(joy, 0, sizeof(joy)); for (int i=1; i<bindings[0][1]; i++) { //pc.printf("Checking %d: ", i); switch (bindings[i][0]) { case 0: value = x1_pulse; trigval = x1_pulse; break; case 1: value = y1_pulse; trigval = y1_pulse; break; case 2: value = x2_pulse; trigval = x2_pulse; break; case 3: value = y2_pulse; trigval = y2_pulse; break; case 4: trigval = (buttons & 0x1)*255; value = bindings[i][4]; break; case 5: trigval = ((buttons>>1) & 0x1)*255; value = bindings[i][4]; break; case 6: trigval = ((buttons>>2) & 0x1)*255; value = bindings[i][4]; break; case 7: trigval = ((buttons>>3) & 0x1)*255; value = bindings[i][4]; break; } if (trigval >= bindings[i][1] && trigval <= bindings[i][2]) { //pc.printf("Triggered : %d, %d, %d\r\n", i, trigval, value); switch (bindings[i][3]) { case 0: joy[2] = value; break;//Joy X case 1: joy[3] = value; break; //Joy Y case 2: joy[0] = value; break; //Joy throttle case 3: joy[1] = value; break; //Joy rudder case 4: joy[4] |= bindings[i][4]; break; //Joy buttons case 5: joy[5] |= bindings[i][4]; break; //Joy hat } } } joystick.update(joy[0], joy[1], joy[2], joy[3], joy[4], joy[5]); Thread::wait(20); } } int main() { //Load bindings from file FILE *bf = fopen("/local/bindings", "rb"); fread(bindings, 5*sizeof(int), 1, bf); fread(bindings[1], 5*sizeof(int), bindings[0][1]-1, bf); fclose(bf); Thread analogThread; Thread outputThread; analogThread.start(analog_thread); Thread::wait(100); switch(bindings[0][2]) { case 0: outputThread.start(keys_mouse_output_thread); break; case 1: outputThread.start(joystick_output_thread); break; } //Thread debugThread; //debugThread.start(debug_thread); while(1) Thread::yield(); }
Libraries
Import libraryUSBDevice
USB device stack
Import libraryUSBJoystick
USBJoystick updated for 32 buttons and added wait-for-connect.
Demo & Pictures
- Demo
- Breadboard & Joystick
- Breadboard Layout Only
- Male & Female DA-15 / GamePort Connectors
- DA-15 / GamePort Connector - Breadboard Breakout
- Simple GamePort Joystick
Please log in to post comments.