Okay, now that we've got an understanding on how we can read the joystick port, let's look at writing functions that we can use in other programs (in other words, writing library functions). Well, when dealing with the reading of the joystick pots, we can either read them one at a time, or all at once and cache their values in variables (the way our last program did). If you are interested in writing functions that read a single port at a time, here's some sample code that allows you to do just that.
/* This function returns the value on a single joystick pot. */ #include <conio.h> /* Needed for inp and outp functions. */ #define JsA_Xaxis 0x01 #define JsA_Yaxis 0x02 #define JsB_Xaxis 0x04 #define JsB_Yaxis 0x08 unsigned int GetPotVal(char bitMask) { unsigned int retVal = 0; disable(); /* Disables interrupts */ outp(0x201, 0); /* Discharge caps */ while(++retVal && (inp(0x201) & bitMask)); /* Count while capacitor is charging */ enable(); /* Reenable interrupts */ return retVal; }
To use that code in your programs just simply call it with the appropriate bit mask. Some constants were also defined for the appropriate bit mask values. For example:
x = GetPotVal(JsA_Xaxis);
returns the joystick pot value for the X-axis on joystick A. Another way is to return them all at once. This technique is covered because when the code executes the command to discharge the joystick caps, they're discharged all at once. So it makes sense to go ahead and test all of them at once in a single count loop (like we did earlier). Here's a function that does just that:
/* This code reads all the joystick pots at once. */ #include <conio.h> /* Needed for inp and outp functions. */ #define JsA_Xaxis 0x01 #define JsA_Yaxis 0x02 #define JsB_Xaxis 0x04 #define JsB_Yaxis 0x08 unsigned short jsPotVal[4]; void Joyin(void) { unsigned short limit = 0; char joyState; /* Initialize joystick pot values to zero */ jsPotVal[0] = jsPotVal[1] = jsPotVal[2] = jsPotVal[3] = 0; disable(); /* Disable interrupts */ outp(0x201, 0); /* Discharge caps */ joyState = inp(0x201) & 0x0F; do { jsPotVal[0] += (joyState & 1); joyState >>= 1; jsPotVal[1] += (joyState & 1); joyState >>= 1; jsPotVal[2] += (joyState & 1); joyState >>= 1; jsPotVal[3] += (joyState & 1); limit++; } while (limit && (joyState = (inp(0x201) & 0x0F))); enable(); /* Enable interrupts */ }
Now I must caution you about this function. As it is currently written,
it will always test all four joystick pots. If only one joystick is
plugged in then only two joystick pots will return valid values, the other two
will time out when the limit
rolls back to zero. Waiting for the loop to
time out every time you wish to query the joystick pot values is time consuming
and can cause the software using this function to slow down considerably.
To prevent this, we should have a way to test which joystick pots are available
and mask out the bits that time out so those joystick pots will not be tested
again. The question here is, how can we determine whether or not a joystick
pot is present? Looking at the do...while
loop and you can see
that it ends when either all four caps become fully charged ((inp(0x201) &
0x0F) == 0
) or when the limit
variable times out (rolls back to zero).
The limit
variable will count up by one in each loop to its maximum value, 65535.
At the 65536th loop, the limit
variable will become zero again.
The four variables for all four joystick pots are also counting up by one on
each loop (as long as the corresponding capacitors haven't fully recharged yet) and will also roll
back to zero on the 65536th loop along with limit
. This means that
if any of these variables are equal to zero when the loop finishes, that variable
timed out and the corresponding joystick pot is not available.
There are, of course, some flaws in this and in any software timing loop used to read the joystick. What if you have a screaming fast system that can perform 65536 loops before any of the capacitors recharge? What if your system is so slow that the capacitors fully recharge between the time it discharges the caps and the time it checks the port again for their status? This loop is designed so that the joystick pot values will always be at least a one no matter how slow your system is. The other end of the problem cannot be as easily fixed, however there are a few suggestions that I can make to help your situation.
short
to long
. Long
integers provide a much greater range of values and will reduce the likelihood of timeouts
on a screaming fast system. The downside, because long integers have much larger
range of values, they can cause your system to run very poorly if used too much on joystick
pots that do not exist.
You may have also noticed that the software routines above disable the interrupts before running the counting loop. This is done to get a more accurate reading of the joystick pots. You typically don't want the timing loop interrupted while it is checking the status of the capacitors.
Send your questions, comments, or ideas here.