Every year the projects done in EE 552 get more complex and more interesting. Among the flashier
projects are ones that move. This can be done using standard DC or stepper motors if you don't need
to move something an exact amount. But if need to move or turn an exact amount repeatedly you need
a servomotor. You've all seen them before and you know what they do, they are generally used in model cars/boats/planes etc. but are great for use with digital hardware. They are neat little motors that turn a full 90 degrees and provide loads of torque, they are available at your local hobby shop for a tidy sum and are worth every penny.
There are three leads coming from the servo. Red and black are fairly obvious, power and ground respectively. Generally these model servos expect a 6 volt supply voltage, but they are more than happy to run on 5 volts, like from the regulator on your UP1 board. Now the other wire (usually white or yellow) is the control signal that runs from your controller into the servo, telling it what position you want it in. A pulse width modulated signal with a 60 Hz frequency is required to operate the servomotor correctly, there is a built in decoder that converts the pulse width into a position for it. The width of the pulse should range between 1 ms and 2 ms this will move the servo actuator linearly through the range of the servo (ie. 1 ms = 0 degrees, 1.5 ms = 45 degrees, 2 ms = 90 degrees, etc.) This pulse must be continuously applied since it does take a significant, relative to the system clock speed amount of time for the servo to rotate.
A note on noise: Do not be fooled by their size, these servos contain quite powerful DC motors and as such create a large amount of electromagnetic noise. Ordinarily one would not operate sensitive analog circuitry in close proximity to them, nor power said circuitry from the same source as the servos are powered from.
Shown below is an example of a VHDL function which takes in a 4-bit vector that represents the position you want the servo to rotate to. Sixteen different positions should be enough for most applications but this code is fully scalable to allow for a higher resolution. Currently with compiler optimizations enable this occupies a paltry 36 logic cells on the FLEX10K20.
Or if you would like to download it click here.
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; entity servo is generic ( -- controls clock divider frequency divisor : positive := 771; -- adjusts center position of the servo trim : positive := 18; -- servo_period controls the output peroid -- and should be adjusted if "divisor" is changed servo_period : positive := 272 ); port ( clk, reset : in std_logic; servo_pos : in std_logic_vector(3 downto 0); servo_out : buffer std_logic ); end servo; architecture behavioral of servo is signal slow_clk : std_logic; signal slow_clk_count : std_logic_vector(10 downto 0); signal servo_counter : std_logic_vector(8 downto 0); begin -- a simple clock divider -- divides by (divisor x 2) process (clk) begin if reset = '0' then slow_clk_count <= (others => '0'); slow_clk <= '0'; elsif clk'event and clk = '1' then if slow_clk_count >= divisor then slow_clk_count <= (others => '0'); slow_clk <= not slow_clk; else slow_clk_count <= slow_clk_count + 1; end if; end if; end process; -- this process controls the width of the pulse -- being sent to the servo, -- proportional to servo_pos process (slow_clk) begin if reset = '0' then servo_counter <= (others => '0'); elsif slow_clk'event and slow_clk = '1' then -- servo_period controls the period -- of the signal which is sent to -- the servo if servo_counter >= servo_period then servo_counter <= (others => '0'); else servo_counter <= servo_counter + 1; end if; -- depending how far into the period -- we are output a '1' or a '0' if servo_counter < (servo_pos + trim) then servo_out <= '1'; else servo_out <= '0'; end if; end if; end process; end behavioral;