the 1 MHz clock, or once every microsecond.
As you can see, the pwm_out <= (window_32ms <
(servo_vector + minpulsewidth)); Verilog statement uses
the 32.768 ms clock, the servo_vector value, and the
minimumpulsewidth constant to create a complete PWM
signal. The proof is in the pudding. One slice of servo driver
pie is represented in Screenshot 1, which is the 1.5 ms
pulse our Verilog code generated. I captured the servo
rotor-centering pulse for you with a CleverScope.
Screenshot 2 is another CleverScope shot of the complete
32.768 ms servo control pulse window, including the 1.5
ms servo position pulse.
With the XC2C64A code, we’ve put together so far, we
can change the position of the hobby servo rotor by simply
changing the value of the servo_vector parameter. Valid
servo_vector values range from zero to 1000 decimal, with
a zero value producing a 1 ms servo position pulse and a
value of 1000 decimal pushing the hobby servo to its full
opposite extent with a 2 ms pulse. We have easily taken
control of one hobby servo using only our 64A CPLD. Why
not two hobby servos??
One XC2C64A. Two Servos.
You already understand what it takes to move a hobby
servo rotor with an XC2C64A CPLD. So, let’s not waste
any time getting some double rotor code down and
running. First, let’s add that second PWM output to our
rcservo module:
module rcservo(
input clk_1mhz,
output reg pwm_out1,
output reg pwm_out2
);
We’re going to take a different turn here. Instead
of loading the servo rotor position manually via the
servo_vector parameter, we’re going to instantiate a pair
of servo_vector registers to hold our desired servo rotor
positions:
parameter
minpulsewidth = 1000;
reg
reg
reg
reg
[14:0] window_32ms;
[9:0] servo_vector1;
[9:0] servo_vector2;
servo_direction;
Recall that our servo_vector values can range from
zero to 1000 decimal. That means we need 10-bits to
hold the maximum servo_vector values. The single bit
servo_direction register should give you a clue as to where
that different turn will take us.
Nothing has changed about the way we generate the
servo control pulse window:
The CPLD Servo Driver
//*******************************************************
//* GENERATE SERVO CONTROL PULSE WINDOW
//*******************************************************
always @(posedge clk_1mhz)
window_32ms <= window_32ms + 1;
We will build upon our single servo code to add the
second PWM output pulse. We laid the ground work for
the extra PWM output earlier in the rcservo module:
//*******************************************************
//* GENERATE SERVO CONTROL PULSES
//*******************************************************
always @(posedge clk_1mhz)
begin
pwm_out1 <= (window_32ms <
(servo_vector1 + minpulsewidth));
pwm_out2 <= (window_32ms <
(servo_vector2 + minpulsewidth));
end
Notice that we replaced the servo_vector constant with
our servo_vector register variables.
Up to now, we’ve been seeing double. Let’s drive down
that road we made the different turn onto. I don’t think
you’ll have any problem following the idea behind this
Verilog code snippet:
//*******************************************************
//* DETERMINE SERVO ROTOR DIRECTION
//*******************************************************
always @(posedge clk_1mhz)
begin
if(window_32ms == 0)
begin
if(servo_vector1 == 0 || servo_vector1 == 1000)
servo_direction = ~servo_direction;
if(servo_direction)
begin
servo_vector1 <= servo_vector1 + 1;
servo_vector2 <= servo_vector2 - 1;
end
else
begin
servo_vector1 <= servo_vector1 - 1;
servo_vector2 <= servo_vector2 + 1;
end
end
end
endmodule
Everything we do must synchronize to the beginning of
the servo control pulse window. So, we must always check
to see if the servo control pulse window counter register is
at zero as this is the synchronization point we must adhere
to. The idea is that if we have a new servo vector value to
enter, it must be entered at the beginning of a new servo
control pulse window.
The servo_vector values of zero and 1000 decimal force
SERVO 08.2008 41