VHDL rotary encoder interface

Discussion in 'Programmer's Corner' started by PvtVentura, May 11, 2009.

  1. PvtVentura

    Thread Starter New Member

    Feb 4, 2009
    1
    0
    Hi, i am trying to work with the rotary encoder interface of a FPGA using a spartan 3E kit and i don´t know how to start, every help i see in the web seems a bitter complex and i want to start with a few easy examples.

    Does anyone already worked with the rotary encoder? or have some pdf´s with some examples?

    Thanks
     
  2. DonQ

    Active Member

    May 6, 2009
    320
    11
    What you are probably working with is a "quadrature" encoder. If this isn't what you're using, it should be.

    The two main signals from a quadrature encoder are like square-wave versions of sine and cosine, offset from each others by 90°. When one is transitioning, the other is at a steady state. It becomes a matter of sensing the transitions, and determining the direction of rotation by the state of the other signal.

    Example: if channel A transitions low-to-high, and channel B is low, that can be interpreted as clockwise rotation. If channel A transitions high-to-low while B is low, it is turning counter-clockwise.

    A simple decoder stops here, buy you can decode each of the 4 transitions per cycle for increased resolution from the encoder. In this way, an encoder that says 200 pulses per revolution can give you 800 discrete positions.

    Once you decode the direction pulses, you keep track of them in a counter. There are some details about noise filtering the input and reading a multi-byte counter without incrementing during the read, but this should get you started.

    HP makes some discrete versions of this. It might be a good thing to do to see what features some of their chips have.
     
  3. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    5,435
    1,305
    The simplest way I found to decode quadrature encoders is this;

    (first detect the encoder has changed)
    then;
    if(newA == oldB) value++;
    else value--;

    I found it's generally preferable to detect encoder change first, by comparing new_read with last_read, then decode which direction it moved later.
     
  4. antiriad

    New Member

    Feb 15, 2011
    1
    0
    I wrote a bit of VHDL for generating inc/dec pulses from quadrature rotary encoders like the CTS Electrocomponents ones I bought from Digikey for a project I worked on recently. This debounce circuit and quadrature decoder combination is reliable and very noise/bounce immune.

    In the interest of full disclosure I have also shared this snippet on Techbites.

    Feel free to use this code, but please keep the author information in it:

    Low cost rotary encoders are a dial-of-choice when it comes to tactile user interfaces. Recently I wrote an article in my Altium blog on how to create your own peripherals for the Nanoboard-II or NB3000 FPGA development boards. The one I created had two rotary encoders on it, and I thought it would be worthwhile sharing the debounce circuit and VHDL I wrote to do the decoding of the quadrature output of the encoders (see below).



    If you want to know more about rotary encoders and how they work, check out the excellent page on Wikipedia about them. It's fairly easy to decode a rotary encoder signal - a quick web search will reveal countless snippets of AVR, PIC assembly or C code. None of the ones I found were able to make use of the full encoder resolution however - so I created my own in FPGA hardware using VHDL shown below. See the code comments for how it works.

    FPGA Encoder de-bounce

    [​IMG]

    Code ( (Unknown Language)):
    1. --------------------------------------------------------------------------------
    2. -- SubModule QDEC
    3. -- Created   30/09/2010 9:42:12 PM
    4. -- Ben Jordan, Altium Limited.
    5. --------------------------------------------------------------------------------
    6. Library IEEE;
    7. Use IEEE.Std_Logic_1164.all;
    8.  
    9. entity QDEC is port
    10.    (
    11.      CLK : in    std_logic;
    12.      A   : in    std_logic;
    13.      B   : in    std_logic;
    14.      FWD : out   std_logic;
    15.      REV : out   std_logic;
    16.      MOV : out   std_logic
    17.    );
    18. end QDEC;
    19. --------------------------------------------------------------------------------
    20.  
    21. --------------------------------------------------------------------------------
    22. --
    23. --  Typical rotary encoders output a quadrature encoded 2-bit signal as follows:
    24. --
    25. --  Clockwise motion:
    26. --               ____      ____      ____      ____      ____
    27. --  Signal A: __|    |____|    |____|    |____|    |____|     ...
    28. --             ____      ____      ____      ____      ____
    29. --  Signal B: |    |____|    |____|    |____|    |____|    |_ ...
    30. --
    31. --  Anti-Clockwise motion:
    32. --             ____      ____      ____      ____      ____
    33. --  Signal A: |    |____|    |____|    |____|    |____|    |_ ...
    34. --               ____      ____      ____      ____      ____
    35. --  Signal B: __|    |____|    |____|    |____|    |____|     ...
    36. --
    37. --  From this you can see that if there is a rising edge on A while B is high
    38. --  then this indicates a unit turn of the encoder in the clockwise direction.
    39. --  The converse is true for anti-clockwise rotation.
    40. --
    41. --  Most snippets of code (C or VHDL) you might discover only look at these two
    42. --  states - rising edge of A with B high, rising edge of B with A high - to
    43. --  determine a unit arc in either direction. However looking at the waveforms
    44. --  above you can see that is only one quater of the possible resolution.
    45. --
    46. --  This decoder in VHDL samples the signals using all four available edges of
    47. --  A and B. E.g. sample(B) on rising(A), sample(A) on falling(B), sample(B) on
    48. --  falling(A), and sample(A) on rising(B).
    49. --
    50. --  Using this method uses more FPGA flip flops, but not too many in the grand
    51. --  scheme of things. The net result is you get 80 counts per revolution versus
    52. --  the usual 20.
    53. --
    54. --------------------------------------------------------------------------------
    55.  
    56. architecture Structure of QDEC is
    57.  
    58. -- Signal Declarations
    59. signal code      : std_logic_vector(1 downto 0) := B"00";
    60. signal code_prev : std_logic_vector(1 downto 0) := B"00";
    61. signal MOV_del   : std_logic := '0';
    62.  
    63. begin
    64.     main:process(CLK) is
    65.     begin
    66.        if rising_edge(CLK) then
    67.           code_prev <= code;
    68.           code(0) <= A;
    69.           code(1) <= B;
    70.           MOV <= MOV_del;
    71.           if (code(0) = '1' and code_prev(0) = '0') then -- A rising edge
    72.               if (B='0') then -- forward
    73.                  FWD <= '1';
    74.                  MOV_del <= '1';
    75.                  REV <= '0';
    76.               elsif (B='1') then -- reverse
    77.                  FWD <= '0';
    78.                  MOV_del <= '1';
    79.                  REV <= '1';
    80.               end if;
    81.           elsif (code(1) = '1' and code_prev(1) = '0') then -- B rising edge
    82.               if (A='1') then -- forward
    83.                  FWD <= '1';
    84.                  MOV_del <= '1';
    85.                  REV <= '0';
    86.               elsif (A='0') then -- reverse
    87.                  FWD <= '0';
    88.                  MOV_del <= '1';
    89.                  REV <= '1';
    90.               end if;
    91.           elsif (code(0) = '0' and code_prev(0) = '1') then -- A falling edge
    92.               if (B='1') then -- forward
    93.                  FWD <= '1';
    94.                  MOV_del <= '1';
    95.                  REV <= '0';
    96.               elsif (B='0') then -- reverse
    97.                  FWD <= '0';
    98.                  MOV_del <= '1';
    99.                  REV <= '1';
    100.               end if;
    101.           elsif (code(1) = '0' and code_prev(1) = '1') then -- B falling edge
    102.               if (A='0') then -- forward
    103.                  FWD <= '1';
    104.                  MOV_del <= '1';
    105.                  REV <= '0';
    106.               elsif (A='1') then -- reverse
    107.                  FWD <= '0';
    108.                  MOV_del <= '1';
    109.                  REV <= '1';
    110.               end if;
    111.           else
    112.               FWD <= '0';
    113.               REV <= '0';
    114.               MOV_del <= '0';
    115.           end if;
    116.        end if;
    117.     end process;
    118. end Structure;
    119. --------------------------------------------------------------------------------
     
    Last edited: Feb 15, 2011
Loading...