element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Achievement Levels
    • Benefits of Membership
    • Feedback and Support
    • Members Area
    • Personal Blogs
    • What's New on element14
  • Learn
    Learn
    • eBooks
    • Learning Center
    • Learning Groups
    • STEM Academy
    • Webinars, Training and Events
  • Technologies
    Technologies
    • 3D Printing
    • Experts & Guidance
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Arduino Projects
    • Design Challenges
    • element14 presents
    • Project14
    • Project Groups
    • Raspberry Pi Projects
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Or choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
Personal Blogs
  • Members
  • More
Personal Blogs
Legacy Personal Blogs Rotary Encoders - Part 5: Capturing Input on an FPGA
  • Blog
  • Documents
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 15 Jul 2015 10:08 AM Date Created
  • Views 1667 views
  • Likes 10 likes
  • Comments 4 comments
  • rotary_switch
  • spartan_6
  • rotary_encoder
  • papilio_pro
  • papilio
  • xilinx
  • fpga
Related
Recommended

Rotary Encoders - Part 5: Capturing Input on an FPGA

Jan Cumps
Jan Cumps
15 Jul 2015

I've removed the rotary encoder from the scroll button of a defect mouse. Let's see how we can use that in a microcontroller design.

In post 1, I wired up the electronics.

In part 2, I'm capturing the encoder's info on a Cypress PSoC4.

In the third blog, I've used an Arduino.

In number 4, I'm capturing the encoder's info with the eQEP module of a TI Hercules microcontroller.

This time, I'm testing it on on a Xilinx Spartan 6 FPGA.

 

 

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

 

The Code

 

Disclaimer: I haven't found consensus on how to call the logic for a FPGA.

I started from a project made  by Thomas Flummer on his hackmeister blog in 2010.

He started with a FPGA servo controller that was controlled with two buttons for left and right movement.

Later he added a quadrature encoder module to control the servo position.

And that's the project that I picked up here.

https://www.hackmeister.dk/2010/07/controlling-an-rc-servo-with-an-fpga/

https://www.hackmeister.dk/2010/07/using-a-quadrature-encoder-as-input-to-fpga/

 

I've adapted the code using info from the following blog post:

https://groups.google.com/d/msg/comp.lang.vhdl/nD5V5v_kfLw/at8iEDFsxYcJ

It uses a deprecated math library. I switched all the locations where STD_LOGIC_VECTOR is used in calculations to UNSIGNED.

Locations where the STD_LOGIC_VECTOR is used to contain bit values (e.g. QuadA_Delayed), I have kept as is.

(I'd like a guru to have a look at what I did with PulseCount in ServoDriver.vhd)

 

I've also replaced occurrences of

if Clk='1' and Clk'event then

by

if rising_edge(Clk) then

.

The code is using direct instantiation of entities, in stead of the architecture -> component -> entity -> instance approach.

Because of that, I had to add the "work." qualifier when instantiating modules. I may change that later.

 

johnbeetem advised me to ask michaelkellett for a VHDL review.

Go ahead Michael.

 

Disclaimer 2: The project doesn't deliver correct Servo signals. The PWM frequency isn't 50 Hz, and the pulse width isn't conform the servo PWM specs.

It doesn't matter. The only thing that I want to show here is that I can increase and decrease the duty cycle of the FPGA output with a rotary encoder.

 

image

 

The design has 3 modules:

  • The ServoDriver: a module that generates a PWM signal with a given duty cycle based upon position info that we pass to it.
  • The QuadratureDecoder: this module picks up movement of the rotary encoder and translates that into  a position.
  • The ServoUpDown: this top module glues the two other modules together. It connects the Encoder position with the Servo position.

 

edit after comments below: I've replaced all tabs in the source by spaces. That resolves the indentation differences between IDE and this blog.

QuadratureDecoder.vhd

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity QuadratureDecoder is
    Port ( QuadA : in  STD_LOGIC;
           QuadB : in  STD_LOGIC;
           Clk : in  STD_LOGIC;
           Position : out  unsigned (7 downto 0));
end QuadratureDecoder;

architecture Behavioral of QuadratureDecoder is

signal QuadA_Delayed: std_logic_vector(2 downto 0) := "000";
signal QuadB_Delayed: std_logic_vector(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 rising_edge(Clk) then
   -- 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 <= Count;
         else
            Count <= Count - 1;
            Position <= Count;
         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;

 

ServoDriver.vhd

----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date:    16:55:40 07/14/2015
-- Design Name:
-- Module Name:    ServoDriver - Behavioral
-- Project Name:
-- Target Devices:
-- Tool versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity ServoDriver is
    Port ( Clk : in  STD_LOGIC;
           Position : in unsigned (7 downto 0);
           Servo : out  STD_LOGIC);
end ServoDriver;

architecture Behavioral of ServoDriver is

constant ClockDiv: integer := 63;

signal ClockTick: std_logic := '0';
signal ClockCount: unsigned (6 downto 0) := "0000000";

signal PulseCount: unsigned (11 downto 0) := "000000000000";

begin

process (Clk)
begin
   if rising_edge(Clk) then
   -- if Clk='1' and Clk'event then
      if ClockCount = ClockDiv-2 then
         ClockTick <= '1';
      else
         ClockTick <= '0';
      end if;
      if ClockTick='1' then
         ClockCount <= "0000000";
      else
         ClockCount <= ClockCount + 1;
      end if;
   end if;
end process;

process (Clk)
begin
   if rising_edge(Clk) then
   -- if Clk='1' and Clk'event then
      if ClockTick='1' then
         PulseCount <= PulseCount + 1;
      end if;
      if PulseCount < ("0001" & Position) then
          Servo <= '1';
      else
         Servo <= '0';
      end if;
   end if;
end process;

end Behavioral;

 

ServoUpDown.vhd

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.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 : unsigned (7 downto 0);

begin

Servo1: entity work.ServoDriver port map (Clk, Position, Servo);
QuadDecoder1: entity work.QuadratureDecoder port map (QuadA, QuadB, Clk, Position);

end Behavioral;

 

 

 

Wiring the Hardware

 

Look back at my first post to check how to components are wired up.

Then connect the encoder with the Papilio Pro.

 

CONFIG PROHIBIT=P144;
CONFIG PROHIBIT=P69;
CONFIG PROHIBIT=P60;


NET Clk TNM_NET = Clk;
TIMESPEC TS_Clk = PERIOD Clk 31.25ns HIGH 50 %;


NET Clk            LOC="P94"  | IOSTANDARD=LVCMOS33 | PERIOD=31.25ns;             # CLK


NET Servo IOSTANDARD = LVCMOS33;
NET Servo SLEW = FAST;
NET Servo DRIVE = 8;
NET Servo LOC = P48; # A0


NET QuadA IOSTANDARD = LVCMOS33;
NET QuadA SLEW = FAST;
NET QuadA DRIVE = 8;
NET QuadA LOC = P51; # A1


NET QuadB IOSTANDARD = LVCMOS33;
NET QuadB SLEW = FAST;
NET QuadB DRIVE = 8;
NET QuadB LOC = P56; # A2

Ground to Ground, 3.3V to 3.3V,

PINA to A1, PINB to A2. Oscilloscope to A0.

 

image

 

Load the bitstream to the FPGA and rotate the encoder.

You'll see that the output signal has a constant frequency and that the duty cycle can be controlled by turning the encoder.

 

 

Content
Part 1: Electronics
Part 2: Capturing Input on Cypress PSoC4
Part 3: Capturing Input on an Arduino
Part 4: Capturing Input on a Texas Instruments Hercules LaunchPad with eQEP
Part 5: Capturing Input on an FPGA
XuLA2 FPGA - Rotary Encoder and VHDL
Real World Application: Hercules LaunchPad and GaN FETs: Control Big Power with a Flimsy Mouse Scroll Wheel
  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 2 years ago +3
    I'm training Pynq and Zynq design. I've tried to port the PWM module of this post to that architecture. Instead of taking the input for PWM duty cycle from a rotary encoder, I take it from the embedded…
  • michaelkellett
    michaelkellett over 8 years ago +2
    It's nice of you to invite me to comment - I'm kind of busy right now so I'll be brief. First let me say that I appreciate the public service you do in posting this stuff so the last thing I want to do…
  • Jan Cumps
    Jan Cumps over 8 years ago +2
    I finally managed to get that video uploaded. www.youtube.com/watch
  • Jan Cumps
    Jan Cumps over 2 years ago

    I'm training Pynq and Zynq design.

    I've tried to port the PWM module of this post to that architecture.

    Instead of taking the input for PWM duty cycle from a rotary encoder, I take it from the embedded ARM controller.

    Success:

     

     

     

    image

    I still have a lot to learn, but I managed to change the duty cycle from 6.5 to 15.5%, just like with the rotary encoder in the original post above

     

    image

     

    image

    • Cancel
    • Vote Up +3 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 8 years ago

    I finally managed to get that video uploaded.

     

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 8 years ago in reply to michaelkellett

    Thanks for the review and the advice.

    1) You don't reset in your code - the world is divided on this but I find it is often very useful to have a way of resetting the FPGA.

    I'll read up on that. I haven't done that before.

    2) You don't indent your if statements (may be an E14 problem)

    Yes, that's a copy-paste-prettyprint artifact. In the Xilinx ISE editor it's nicely indented.

    image

    replacing some bogus TABs with blanks solved that.

    Do you simulate your FPGA code before running it ?

    I haven't done that for this design - it worked right away. I'm doing simulation for other designs - while I'm studying hamsterworks.co.nz's tutorial  :

     

    This video is unavailable.
    You don't have permission to edit metadata of this video.

     

    Thanks!

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • michaelkellett
    michaelkellett over 8 years ago

    It's nice of you to invite me to comment  - I'm kind of busy right now so I'll be brief.

    First let me say that I appreciate the public service you do in posting this stuff so the last thing I want to do is nitpick about style.

    There are two things that stand out:

    1) You don't reset in your code - the world is divided on this but I find it is often very useful to have a way of resetting the FPGA.

    2) You don't indent your if statements (may be an E14 problem)

     

    I use a top level block diagram (and possibly lower level diagrams too) to instantiate and wire up entities (in AldecHDL). I find this makes complex designs much more understandable.

     

    If your concern re PulseCount is this line:

     

    if PulseCount < ("0001" & Position) then

     

    then I don't see any problem except that a comment might help others to understand it, and of course it imposes some restrictions on the timing of your waveforms which may well be an acceptable tradeoff for simplicity.

     

    Do you simulate your FPGA code before running it ?

     

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2023 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube