Using a quadrature encoder as input to FPGA

July 18, 2010 | 4 Minute Read

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.

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.

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.

#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