1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
|
<html>
<head>
<link href="../lg.css" rel="stylesheet" type="text/css" media="screen, projection" />
<title>Simple Joystick control of a servo motor with RTAI/Linux LG #101</title>
<style type="text/css" media="screen, projection">
<!--
.articlecontent {
position:absolute;
top:143px;
}
-->
</style>
</head>
<body>
<img src="../gx/2003/newlogo-blank-200-gold2.jpg" id="logo" alt="Linux Gazette"/>
<p id="fun">...making Linux just a little more fun!</p>
<div class="content articlecontent">
<div id="previousnexttop">
<A HREF="levkovich.html" ><-- prev</A> | <A HREF="sunil.html" >next --></A>
</div>
<h1>Simple Joystick control of a servo motor with RTAI/Linux</h1>
<p id="by"><b>By <A HREF="../authors/pramode.html">Pramode C.E</A></b></p>
<p>
<p>
The analog joystick which plugs onto the PC game port
is a cool little device - you don't need to be a hardware
wizard to learn how it works, and you can make it do
fun and absolutely useless stuff like turn a stepper or
servo motor. This article describes an experiment which
I did with a joystick and a Futaba S2003 servo motor, both
controlled by the real time operating environment for
Linux called <a href="http://www.aero.polimi.it/~rtai">RTAI</a>. Readers who
are not familiar with RTAI might like to refer to the
<a href="../issue95/pramode.html">introductory article</a> I had
written earlier before continuing.
<h2>The Joystick</h2>
<p>
If you are game-crazy, you are sure to have used
one. As far as electronics is concerned, it is a very
primitive device - so is the game port which it
plugs into. You will find adequate hardware
descriptions <a href="http://documents.epanorama.net/documents/joystick/pc_joystick.html">here</a>. The <a href="http://www.tldp.org/HOWTO/IO-Port-Programming.html">
Linux IO-Port Programming Mini-HOWTO</a> also provides
sufficient information to get started with hacking the joystick.
<h2>Reading the buttons</h2>
<p>
Your first joystick programming assignment should
be reading the state of the buttons. For this, you
have to know the ISA port address which the gameport
uses. Loading the standard Linux joystick driver
(you will have to modprobe three modules - joydev,
ns558 and analog) and doing `cat /dev/ioports' on
my ASUS A7N266VM motherboard showed this to be 0x200.
<p>
The state of two of my joystick buttons is encoded
in bits D4 and D5 of the 8 bit value returned by
an `inb' on 0x200 (D0 is least significant bit and D7
is most significant bit). If the value is 1, the
button is in the `released' state and if it is 0, the
button is in the `pressed' state. Here is a small
program which tests this out:
<p>
<a href="misc/pramode/button.txt">Listing 1</a>
<pre>
#include <asm/io.h>
#define JS_PORT 0x200
main()
{
iopl(3);
while(1)
printf("%x\n", (inb(JS_PORT) >> 4)&1);
}
</pre>
<h2>Reading the X and Y positions</h2>
<p>
Moving the joystick results in a potentiometer
turning - the potentiometer is connected to the game port,
which contains a 555 timer based circuit. A simple
`outb' to 0x200 (the value written doesn't matter)
will result in the circuit getting `reset' - now
a read (ie, an `inb') from 0x200 will yield a bit
pattern whose D0 and D1 bits are 1's. Keep on reading
- after a short time these bits become zero. Measure
the time it takes for the bits to become zero. Take
the measurements with the joystick at the extreme left,
middle and extreme right endpoints of the X axis as
well as the top, middle and bottom points of the Y axis
as part of a `calibration' process. Now, whatever be
the position of the joystick along the X-Y axes,
measuring the time it takes
for the D0 (X axis) and D1 (Y axis) bits to become zero's after a
`reset' (note, we `reset' by writing something to 0x200)
should help us find it out (assuming that time varies linearly
with distance between the middle and left/top as well as middle and
right/bottom endpoints - which I really haven't verified).
<p>
Here is a program which measures the time it takes for the X-axis
bit to become zero after a reset. It uses the `time stamp counter'
which is a 64 bit counter available on all machines with, I believe,
a Pentium and above CPU. If you have a 1GHz CPU, the timer gets
incremented at a rate of 1,000,000,000 per second. My Athlon XP CPU
runs at a clock speed of 1462.904 MHz (read from /proc/cpuinfo) -
with this information, it is easy to compute the time elapsed
between any two points in your program. The time stamp counter
(TSC) can be read using a macro called `rdtsc' defined in the
file /usr/src/linux/include/asm/msr.h.
<p>
<a href="misc/pramode/xmeasure.txt">Listing 2</a>
<pre>
#include <asm/io.h>
#include <asm/msr.h>
#define JS_PORT 0x200
#define CPU_HZ 1462904000
void trigger(void)
{
outb(0x0, JS_PORT);
}
main()
{
unsigned int low1, high1, low2, high2;
iopl(3);
trigger();
rdtsc(low1, high1);
while(inb(JS_PORT) & 1);
rdtsc(low2, high2);
printf("low1=%u, high1=%u, low2=%u, high2=%u\n", low1, high1, low2, high2);
printf("low2 - low1 = %u\n", low2 - low1);
printf("in ms = %f\n", (((double)(low2-low1))/CPU_HZ)*1000);
}
</pre>
<p>
The program should be compiled like this:
<pre>
cc -I/usr/src/linux/include -O2 xmeasure.c
</pre>
I got readings of 0.0262ms, 0.68ms and 1.60ms for the
left, middle and right positions.
<p>
One trouble with this crude form of analog-to-digital
conversion is that you have to sit in a loop waiting for the
bits to drop to zero - this burns up CPU cycles. A better
design would have been for the joystick hardware itself to perform
the A-D conversion and send the resulting numbers to the PC - thus
avoiding lots of software overhead.
<h2>Using a periodic RTAI task to sense joystick position</h2>
<p>
My experiment is this: I have a servo motor connected to the parallel
port. The servo is not capable of rotating the full 360 degrees - it
describes an arc of about 180 degrees. When I turn the joystick left, the
servo also moves to the left end of the arc. When the joystick is in the
`middle' position, the servo positions itself near the centre of the 180
degree arc and when the joystick moves towards the right end, the servo
also moves towards the right end of the arc. Note that I try to sense only
three joystick positions - left, middle and right.
<p>
<img src="misc/pramode/servosmall.jpg">
<p>
The picture above shows two servo motors - the one at the bottom serving to
rotate the platform resting on it - it is this servo which I will be moving
with the joystick.
<p>
The idea is simple. A periodic task (period .33 milliseconds) monitors
the joystick. At the first activation of this task (lets say at time 0), we
`trigger' the game port (by writing to it) and assume that joystick position
is `LEFT'. The next activation of the task would be at 0.33 milliseconds. If
reading from the game port tells us that the X axis bit (D0) is still set, we
assume that the joystick is in the `MIDDLE' position. The next activation of
the task would be at 0.66 milliseconds - but we are not interested in checking
the X axis bit at this point - we will take it that the joystick is in the
`MIDDLE' position if the bit stays high for a period between 0.33 and 0.99
milliseconds (note that the `bit-high' times obtained experimentally were
0.026, 0.68 and 1.60 milliseconds respectively for extreme left, middle and
extreme right positions). The next activation would be at 0.99 milliseconds -
if bit D0 still stays high, we assume that the joystick is in the `RIGHT'
position. Only at this point are we sure of the actual position of the
joystick - we shall set a global variable, `joystick_position' to LEFT,
RIGHT or MIDDLE.
<p>
Now we come to the
<a href="http://www.seattlerobotics.org/guide/servos.html">servo motor control</a> part - which is
fairly simple.
A hobby servo motor requires a `control pulse' on its white wire. The
total on-off time of the pulse should be around 20 milliseconds - it
is the 'on' time which actually controls the servo's position. My servo
moves to one end of a 180 degree arc for an 'on' time of about 0.5 millisecond
and moves to the other end for an 'on' time of about 2.2 seconds. A separate
RTAI task controls the generation of this signal. A global array called
`on_time' maintains the three 'on'-time values which would move the servo
to the left, middle and right points of its arc. The servo task makes
pin 3 of the parallel port (to which the servo's control wire is connected)
high for a period of on_time[LEFT] if the current joystick position is
`LEFT' - similarly for MIDDLE and RIGHT also. The 'off' time of the control
pulse is stored in a variable `off_time' and is computed in such a way that
the total 'on' plus 'off' time is around 20 milliseconds.
<p>
<a href="misc/pramode/control.txt">[Listing 3]</a>
<pre>
static void
pwm_servo(int t)
{
/* Servo is controlled by
* signal on pin 3 of LPT1
*/
while(1) {
outb(2, LPT1_BASE); /* Pin 3 high */
rt_sleep(on_time[joystick_position]);
outb(~2, LPT1_BASE);
rt_sleep(off_time);
}
}
</pre>
<h2>Conclusion</h2>
<p>
It has been fun playing with the joystick. I would like to know
if there is a good method to monitor the joystick position continuously
without loading RTAI too much (by increasing the timer frequency
or resorting to busy loops) - let me know if you come across anything
interesting. I can be contacted via my home page at <a href="http://pramode.net">
pramode.net</a>.
</p>
<!-- *** BEGIN author bio *** -->
<P>
<P>
<!-- *** BEGIN bio *** -->
<P>
<img ALIGN="LEFT" ALT="[BIO]" SRC="../gx/2002/note.png">
<em>
I am an instructor working for IC Software in Kerala, India. I would have loved
becoming an organic chemist, but I do the second best thing possible, which is
play with Linux and teach programming!
</em>
<br CLEAR="all">
<!-- *** END bio *** -->
<!-- *** END author bio *** -->
<div id="articlefooter">
<p>
Copyright © 2004, Pramode C.E. Copying license
<a href="http://linuxgazette.net/copying.html">http://linuxgazette.net/copying.html</a>
</p>
<p>
Published in Issue 101 of Linux Gazette, April 2004
</p>
</div>
<div id="previousnextbottom">
<A HREF="levkovich.html" ><-- prev</A> | <A HREF="sunil.html" >next --></A>
</div>
</div>
<div id="navigation">
<a href="../index.html">Home</a>
<a href="../faq/index.html">FAQ</a>
<a href="../lg_index.html">Site Map</a>
<a href="../mirrors.html">Mirrors</a>
<a href="../mirrors.html">Translations</a>
<a href="../search.html">Search</a>
<a href="../archives.html">Archives</a>
<a href="../authors/index.html">Authors</a>
<a href="../contact.html">Contact Us</a>
</div>
<div id="breadcrumbs">
<a href="../index.html">Home</a> >
<a href="index.html">April 2004 (#101)</a> >
Article
</div>
<img src="../gx/2003/sit3-shine.7-2.gif" id="tux" alt="Tux"/>
</body>
</html>
|