Introduction
Driving a RC servo motor with a microcontroller is inefficient as for much of the time you are deleying and thus wasting time and processing power. You will also run into problems if you plan to run more than one as you either require individual drivers or slightly offset pulses.
This is where the benefits of using an FPGA (Field Programmable Gate Array) & Verilog come in. An FPGA gives you the ability to run a very large amount of servos simultaneously by simply duplicating the module below. The code below is written in Verilog however a VHDL translation is in progress.
For more information on RC Servos see Background Theroy - Servo Motors or Wikipedia.
Operation
Servos require a pule of width between 1ms and 2 ms, if we choose 8 bit resolution then we have 28 = 256 discreet values.
Each value therefore represents 1ms/256 = 3.9us. Since the clock speed of the FPGA is much faster than we require to divide the clock to generate a "tick" of period as close as possible to 3.9µs.
The main part of the code is a 12 bit counter than increments on each ClkTick and will rollover every 16ms.
To generate the actual servo pulse stream we simply set the pulse high when PulseCount is between 0 and 255 (The first 1ms), high from 256 upto the value of the 8 bit position value required, and then low upto 4096.
A positive edge on the Change line will result in the value of the DesiredPosition lines being set as the Actual Position.
Code
module ServoDriver(DesPosition, clk, change, ServoPWM, Position);
input [7:0] DesPosition; input clk, change; output ServoPWM; output [7:0] Position;
//============================================================================
parameter ClkDiv = 196; // 50000000/1000/256 = 195.3
reg [7:0] ClkCount; reg ClkTick; reg [11:0] PulseCount;
reg RCServo_pulse; reg [7:0] Position;
//////////////////////////////////////////////////////////////////////////// // divide the clock
always @(posedge clk) ClkTick <= (ClkCount==ClkDiv-2); always @(posedge clk) if(ClkTick) ClkCount <= 0; else ClkCount <= ClkCount + 1;
//////////////////////////////////////////////////////////////////////////// // Generate PWM Waveform
always @(posedge clk) if(ClkTick) PulseCount <= PulseCount + 1;
always @(posedge change) Position = DesPosition; always @(posedge clk) ServoPWM = (PulseCount < {4'b0001, Position});
endmodule
To Install
The ClkDiv parameter in the above code was calculated based upon a Clk of 50Mhz, to calculate the correct parameter for your clock speed, simply divide YOUR clock speed by (1000/256) and round to a whole number.
There is a slight error by doing this however it is negligible in most applications.
References
Original Verilog code from http://www.fpga4fun.com, modified by Michael Jones.
|