Navigation
Login
|
USB Joystick
Introduction
With the phasing out of
game, serial and parallel ports from modern computers and the ever increasing popularity
of USB, it makes sense that hobbyists start getting to grips working with USB. Unfortunately,
USB is not a simple protocol and can be daunting to implement. Luckily, there are
several solutions on the market that can make implementing a USB device much easier.
This project focuses on
the use of a USB PIC and the mikroC compiler to convert an old game port joystick
to utilize USB. One of the advantages of the mikroC compiler are the built USB HID
libraries that make creating a USB HID device a doddle. When writing USB code using
the mikroC compiler, the USB device produced is a generic HID device that can be
used to transfer data to and from the PIC. However, it is possible to modify the
USB descriptor generated by mikroC so that it produces a specific USB HID device,
like a keyboard, mouse, joystick or graphics tablet.
Compiler
This project uses the
mikroC v8 compiler.
However, the methods used will most probably be relevant to other PIC language compilers
that can generate HID code.
The Joystick
The joystick that I used for this project is an old IBM
76H1571, photographed below:
The 76H1571 is a 4-button joystick with a throttle and
POV hat. What’s interesting to note is that you can’t use the throttle and POV hat
at the same time – you can only use one or neither. The two slide switches on the
front of the joystick are used to switch the throttle and POV hat on and off, so
that you could choose which one you wanted to use.
Because there is no point for this restriction with USB,
the converted joystick will be able to use both the throttle and POV hat at the
same time. This then makes the two switches at the front redundant, so why not add
those as two extra buttons?
In summary, the converted joystick will have the following
specifications:
-
2-axis joystick
-
Throttle
-
4 direction POV hat
-
6 buttons
HID Report Descriptor
When the compiler generates
the USB HID code, it creates a descriptor that is sent to the USB host that tells
it what type of USB device it is. A HID device descriptor is slightly different
as it has an extra descriptor embedded in it that specifies the type of HID device
and how it is used. It is this section that will be modified to change our device
into a joystick
Creating the Descriptor
The USB IF website contains
a useful tool that greatly facilitates the creation of HID report descriptors. It’s
called the HID Descriptor Tool and can be downloaded for free from the
HID Tools page.
Once downloaded, extract the archive and run
Dt.exe.
Using the tool, it is possible
to create your own report descriptor for your joystick (or any other HID device),
specifying the number of axis and buttons it has and any other features (rudder
pedals, throttles etc). However, the tool also comes with some premade descriptors
that you can immediately use or modify to suit your needs. They are found in the
same folder as the executable and are distinguishable by their
.hid extension. The premade joystick descriptor is called
joystk.hid and this is the one I used. Once loaded, you’ll be presented
with the following screen:
I’ve highlighted the important sections
in red. From these sections you can deduce the following specs:
-
1 throttle, described by an 8 bit value from -127 to 127
-
X and Y axes
-
1 POV hat, taking 4 positions (0-3) and representing the angle 0-270, described
by a 4-bit value
-
4 buttons, with each button described by 1 bit
You’ll notice that the
REPORT_SIZE
property defines the width of the data used to represent the parameter
and the REPORT_COUNT property determines
how many reports are sent to represent the parameter.
Modifying the Descriptor
Unfortunately, at the time
of writing, I have not yet managed to modify the report descriptor and get it to
work with my modifications (i.e. 6 buttons instead of the default 4) and so I have
used the default joystick descriptor that comes with the HID Tool. If you know how,
I’d be very interested if you drop me an email.
Adding the Descriptor to your Code
With your report descriptor
created you need to export it into C code. To do that, click on
File->Save As in the HID Descriptor Tool. In the Save dialog that appears,
change the file type to Header File (*.h).
This will create a C header
file that you can then add into your project.
mikroC Intergration
Adding the header file
into the descriptor generated by mikroC requires a little bit of work. If you have
a look at the mikroC descriptor, you’ll notice that every byte is followed by ‘,0’ (ignoring the quotes, that’s
comma zero). You will need to modify the report descriptor you generated
to include this padding. After doing that you’ll end up with a descriptor that look
something like this:
0x05, 0,
0x01, 0,
// USAGE_PAGE (Generic Desktop)
0x15, 0,
0x00, 0,
// LOGICAL_MINIMUM (0)
0x09, 0,
0x04, 0,
// USAGE (Joystick)
0xa1, 0,
0x01, 0,
// COLLECTION (Application)
0x05, 0,
0x02, 0,
// USAGE_PAGE
(Simulation Controls)
0x09, 0,
0xbb, 0,
// USAGE
(Throttle)
0x15, 0,
0x81, 0,
// LOGICAL_MINIMUM
(-127)
0x25, 0,
0x7f, 0,
// LOGICAL_MAXIMUM
(127)
0x75, 0,
0x08, 0,
// REPORT_SIZE
(8)
0x95, 0,
0x01, 0,
// REPORT_COUNT
(1)
0x81, 0,
0x02, 0,
// INPUT
(Data,Var,Abs)
0x05, 0,
0x01, 0,
// USAGE_PAGE
(Generic Desktop)
0x09, 0,
0x01, 0,
// USAGE
(Pointer)
0xa1, 0,
0x00, 0,
// COLLECTION
(Physical)
0x09, 0,
0x30, 0,
/////////////////////////////////////////
// USAGE (X)
0x09, 0,
0x31, 0,
//
USAGE (Y)
0x95, 0,
0x02, 0,
//
REPORT_COUNT (2)
0x81, 0,
0x02, 0,
//
INPUT (Data,Var,Abs)
0xc0, 0,
// END_COLLECTION
0x09, 0,
/////////////////////////////////////////////
0x39, 0,
// USAGE
(Hat switch)
0x15, 0,
0x00, 0,
// LOGICAL_MINIMUM
(0)
0x25, 0,
0x03, 0,
//
LOGICAL_MAXIMUM (3)
0x35, 0,
0x00, 0,
// PHYSICAL_MINIMUM
(0)
0x46, 0,
0x0e, 0,
0x01, 0,
// PHYSICAL_MAXIMUM
(270)
0x65, 0,
0x14, 0,
// UNIT (Eng
Rot:Angular Pos)
0x75, 0,
0x04, 0,
// REPORT_SIZE
(4)
0x95, 0,
0x01, 0,
// REPORT_COUNT
(1)
0x81, 0,
0x02, 0,
// INPUT
(Data,Var,Abs)
0x05, 0,
/////////////////////////////////////
0x09, 0,
// USAGE_PAGE
(Button)
0x19, 0,
0x01, 0,
// USAGE_MINIMUM
(Button 1)
0x29, 0,
0x04, 0,
// USAGE_MAXIMUM
(Button 4)
0x15, 0,
0x00, 0,
// LOGICAL_MINIMUM
(0)
0x25, 0,
0x01, 0,
//
LOGICAL_MAXIMUM (1)
0x75, 0,
0x01, 0,
// REPORT_SIZE
(1)
0x95, 0,
0x04, 0,
// REPORT_COUNT
(4)
0x55, 0,
0x00, 0,
// UNIT_EXPONENT
(0)
0x65, 0,
0x00, 0,
// UNIT (None)
0x81, 0,
0x02, 0,
// INPUT
(Data,Var,Abs)
0xc0, 0
// END_COLLECTION
Now that the descriptor has been padded, the next step is to delete the report descriptor
generated by mikroC and replace it with your one. To do that, first create the mikroC
descriptor using the mikroC HID tool, then open it up in the editor.
The actual descriptor data itself is stored completely within the
DescTables array. The bottom 50 or so lines in the array are the report
descriptor (lines 109-160). Delete those lines, and then paste in the new descriptor
in its place. Now, the following modifications need to be made to the USBdsc.c file:
- Modify line 23 to correspond to the
unpadded size of the report descriptor (77 bytes in the case of the default
joystick descriptor):
- unsigned
char const HID_ReportDesc_len =
77;
- Delete the array bounds for the
DescTables array on line 36:
-
unsigned char
const DescTables[] = {
And that’s it. The descriptor should now be correctly mofied
to behave as a USB joystick. The easiest way to test this out is to compile the
code on to a PIC, plug it into a USB port on your PC and make sure that it is recognized
correctly by the PC. Then, go to Control Panel and open up the Game Controllers
options dialog. You should be able to see your joystick listed.
Sending Data to the PC
With the PIC recognized as a USB joystick, the hard part is over. Sending joystick
data to the PC is a simple matter. When we created the descriptor earlier, we could
work out most of the data format from looking at the descriptor. Some extra experimentation
has determined the following specification:
|
Value
|
Value
|
|
Throttle
|
-127 (min) to 127 (max)
|
|
X axis
|
-127 to 127
|
|
Y axis
|
-127 to 127
|
|
POV Hat Up
|
0
|
|
POV Hat Right
|
1
|
|
POV Hat Down
|
2
|
|
POV Hat Left
|
3
|
|
POV Hat Neutral
|
4
|
|
Button 1
|
0-1
|
|
Button 2
|
0-1
|
|
Button 3
|
0-1
|
|
Button 4
|
0-1
|
The throttle, X and Y values are all 8-bit values. However, the POV and button values
are 4-bits each and so they are packed into one byte. The data format is shown below:
|
Bit position
|
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
|
Throttle
|
|
X-axis
|
|
Y-axis
|
|
Button 4
|
Button 3
|
Button 2
|
Button 1
|
POV Hat
|
With the data format determined, it is a simple matter to write some firmware that
interfaces with the some buttons and potentiometers and sends the data to the PC,
to confirm that the firmware is working correctly. The behaviour of the PIC joystick
can be examined using the Game Controllers options dialog in Control Panel.
The Hardware
With the USB firmware tested and running fine, it is time
to move on to the actual joystick conversion. The first thing that needs to be done
is to open up the joystick and remove the existing circuitry and the gameport cable:
Switch Setup
The next step is to work out how all the switches and
potentiometers are wired together. The potentiometers for the joystick axes and
throttle will probably be simple to spot and simple to wire as well as all they
need is power, ground and a voltage out to the PIC. The switches might prove to
be a little tricky, depending on the joystick. By tracing the tracks on the PCB
in the stick, it has been determined that the switches are connected according to
the following diagram:
The interesting one to note is the POV hat. Instead of
being 4 separate switches, the POV hat is wired up as an analogue system, where
the resistance across the green and orange wires determines which button was pressed.
The following table shows the resistance of each switch:
|
Switch Position
|
Resistance (Ohms)
|
|
Neutral
|
80k
|
|
Up
|
200
|
|
Right
|
20k
|
|
Down | |