This time, instead of using buttons to control the servo, we will use a quadrature encoder (I got it from Sparkfun, sku: COM-09117, but you should be able to use more or less any quadrature encoder).
The QuadratureDecoder entity
This entity reads the signals from the quadrature encoder and maintains a counter, that is incremented or decremented depending on which way the knob is rotated.
The VHDL below is more or less a translation of the Verilog example at fpga4fun.com, where you can also find a description of the signals from the quadrature encoder.
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 | library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity QuadratureDecoder is Port ( QuadA : in STD_LOGIC; QuadB : in STD_LOGIC; Clk : in STD_LOGIC; Position : out STD_LOGIC_VECTOR (7 downto 0)); end QuadratureDecoder; architecture Behavioral of QuadratureDecoder is signal QuadA_Delayed: unsigned(2 downto 0) := "000"; signal QuadB_Delayed: unsigned(2 downto 0) := "000"; signal Count_Enable: STD_LOGIC; signal Count_Direction: STD_LOGIC; signal Count: unsigned(7 downto 0) := "00000000"; begin process (Clk) begin if Clk='1' and Clk'event then QuadA_Delayed <= (QuadA_Delayed(1), QuadA_Delayed(0), QuadA); QuadB_Delayed <= (QuadB_Delayed(1), QuadB_Delayed(0), QuadB); if Count_Enable='1' then if Count_Direction='1' then Count <= Count + 1; Position <= conv_std_logic_vector(Count, 8); else Count <= Count - 1; Position <= conv_std_logic_vector(Count, 8); end if; end if; end if; end process; Count_Enable <= QuadA_Delayed(1) xor QuadA_Delayed(2) xor QuadB_Delayed(1) xor QuadB_Delayed(2); Count_Direction <= QuadA_Delayed(1) xor QuadB_Delayed(2); end Behavioral; |
Modified version of the top module
This is the same top module as used with the previous post, but with the button controller exchanged with the above quadrature decoder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ServoUpDown is Port ( Clk : in STD_LOGIC; QuadA : in STD_LOGIC; QuadB : in STD_LOGIC; Servo : out STD_LOGIC); end ServoUpDown; architecture Behavioral of ServoUpDown is signal Position : std_logic_vector (7 downto 0); begin Servo1: entity ServoDriver port map (Clk, Position, Servo); QuadDecoder1: entity QuadratureDecoder port map (QuadA, QuadB, Clk, Position); end Behavioral; |
The modified UCF file for the AVNET Xilinx® Spartan®-3A Evaluation Kit
Here I just removed the two buttons and added the two inputs from the quadrature encoder. To keep it simple on the hardware side, I enabled pull-up on both of those I/O’s, and connected the shared pin from the quadrature encoder to GND on the board. The servo is connected with negative to GND, positive to +5v and signal to D10.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #Created by Constraints Editor (xc3s400a-ft256-4) - 2010/06/17 NET "Clk" TNM_NET = "Clk"; TIMESPEC TS_Clk = PERIOD "Clk" 62.5 ns HIGH 50 %; # PlanAhead Generated physical constraints NET "Clk" LOC = C10; NET "Servo" LOC = D10; NET "QuadA" LOC = C5; NET "QuadB" LOC = C6; # PlanAhead Generated IO constraints NET "Clk" IOSTANDARD = LVCMOS33; NET "Servo" IOSTANDARD = LVCMOS33; NET "QuadA" IOSTANDARD = LVCMOS33; NET "QuadB" IOSTANDARD = LVCMOS33; # PlanAhead Generated IO constraints NET "QuadA" PULLUP; NET "QuadB" PULLUP; |
Below is a video showing the use of the quadrature encoder to control the servo position
Related posts:
- Controlling an RC Servo with an FPGA This time, we will look at how to control a...
- Learning to program FPGA’s About two months ago a couple of guys from Labitat...