2048 as this is the half-way point for a 12-bit ADC. One of
my desires is to control servos with the joysticks, so a little
extra effort will be required to make this work cleanly.
The first thing to do is nullify the neutral position
values; the autocal() method does this:
pub autocal | idx, raw
repeat idx from 0 to 3
raw := adc.read(idx, adc#SE) >> 4
ofs[idx] := JOY_CNTR - raw
There are actually two things happening here: the
channel is read (in single-ended mode) and then shifted
right by four bits to reduce the ADC resolution down to
eight bits; the pots we’re using are not precision units and
12 bits is excessive. By reducing the resolution, we can
nearly eliminate the LSB “bouncing” of the input readings.
The second step is to save the offset from expected center
(128 for eight bits) in an array that can be applied to
future readings.
So, let’s say that we want to control four servos with
the headers on the board: The final step would be to
convert the joystick channel reading to the range required
by the servo driver. This value will usually be expressed in
microseconds, and 1000 to 2000 is the standard range.
The joy2servo() method takes an eight-bit joystick reading
and, using algebra, converts it to a standard servo position
value:
jpos will fall between 15 (JOY_MIN) and 240 (JOY_MAX).
The next step converts jpos to a percentage. For
example, if the joystick is in the neutral position (jpos =
128), then delta will be 50%. Since we’re dealing with
integers, the percentage is multiplied by 1,000 to prevent
rounding errors.
The final step translates the input delta to a servo
position value, as defined by the minimum and maximum
servo position constants. As you can see, the calculated
position delta is applied to the span of the servo range and
then divided by 1,000 to get to the value within the span;
this is added to the minimum value for the range to get the
proper servo position value. Figure 4 shows the output from
jm_joystick_servo_demo.spin. As you can see, the calibration
has taken care of getting the neutral position where it needs
to be, and the center value for a typical servo is properly
calculated. The program includes a mini servo driver
which allows us to control four servos from the joysticks.
PULSE POSITION MAGIC
pub joy2servo(jpos) | delta, spos
jpos := JOY_MIN #> jpos <# JOY_MAX
delta := (jpos - JOY_MIN) 1000 {
} / (JOY_MAX - JOY_MIN)
spos := ((SVO_MAX - SVO_MIN) {
} delta / 1000) + SVO_MIN
return spos
The first step ensures that the input value in jpos is
legal, that is, it fits within the eight-bit range we should
expect from the joystick. With the variations in the pots, I
decided to truncate the readings by 15 on each end, so
■ FIGURE 3. Reading Joysticks.
A couple years ago, I was visiting my friend Dan at a
Hollywood special effects shop, and he showed me a box
of VEX transmitters that his company had purchased from
All Electronics when the price was really low (about $30 —
and you can still find these units on eBay). Dan was gutting
the joysticks from the transmitters for use in animatronics
controls, but for many projects, there’s no need to do this.
Most RC transmitters — including the one from VEX —
have a trainer connection that emits a pulse stream that
defines the position data of the joysticks and buttons.
Figure 5 shows the stream from the six-channel VEX
transmitter when it’s connected to a microcontroller. The
actual output of the VEX is open-collector, so the input to
the micro needs a pull-up.
In order to provide the greatest flexibility, I used the
circuit shown in Figure 6 on the project board. Two
jumpers let me configure the three-pin PPM port; I can
provide power to the device (e.g., a VEX receiver) and
configure the input as pulled up or pulled down as
required. The purpose of the 2.2K series resistor is to limit
the current into the
Propeller I/O pin for
those devices that
provide a driven
output (5V max).
The VEX stream
begins with a nine
millisecond sync
pulse and is followed
by six channel pulses.
All of the pulses are
separated by an idle
period of about 390
microseconds. The
value for a channel is
■ FIGURE 4.
Joystick to Servo.
16
September 2010