element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • 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
  • Settings
Radio Control
  • Challenges & Projects
  • Project14
  • Radio Control
  • More
  • Cancel
Radio Control
Blog FPGA: a quadrature oscillator with rotary encoder control
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Radio Control to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 20 Oct 2021 6:48 PM Date Created
  • Views 5915 views
  • Likes 1 like
  • Comments 15 comments
  • summer_of_fpgas
  • radiocontrolch
  • fpga
Related
Recommended

FPGA: a quadrature oscillator with rotary encoder control

Jan Cumps
Jan Cumps
20 Oct 2021

This design is a pure VHDL quadrature signal generator.

With variable frequency. The frequency can be controlled with a scroll wheel.

The purpose of this design is to build an alternative local oscilator for Shabaz' Software Defined Radio (SDR) Experiment Board.

 

image

 

I tried to make everything as standard as possible, so that it can be used on many FPGA makes and sizes.

I'm working on a Xilinx Spartan 6, with a 12 MHz clock (Xess XuLA2 kit).

 

image

 

The design has 4 inputs

  • rotary encoder pins A and B
  • clock  input
  • reset

and 5 outputs:

  • quadrature signals 0 -> 3
  • the variable clock based on the input clock and the encoder setting

 

image

 

The design is broken up in 3 components:

  • decoder to translate the movements of a scroll wheel into a numeric value.
  • variable clock that will stretch the input clock based on the value coming out of the decoder
  • quadrature signal generator will create 4 90° phase-shifted signals from that variable clock.

A wrapper glues the components together.

 

All this has been used before for my Pynq blog series and this Spartan 6 article: XuLA2 FPGA - Rotary Encoder and VHDL.

 

image

 

Because all the code has been documented before, I'm just putting the source here.

Check the link earlier in this post for explanations.

 

quad_decode_quad_osc.vhd

This code should be portable.

The external pins are defined in wrapper entity quad_decode_quad_osc.

 

-- ==============================================

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

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

architecture Behavioral of quadrature_decoder is

signal QuadA_Delayed: std_logic_vector(2 downto 0) := (others=>'0');
signal QuadB_Delayed: std_logic_vector(2 downto 0) := (others=>'0');

signal Count_Enable: STD_LOGIC;
signal Count_Direction: STD_LOGIC;

signal Count: unsigned(Position'length-1 downto 0) := (others=>'0');

begin

process (Clk, nReset)
begin
   if (nReset = '0') then
      Count <= (others=>'0');
      QuadA_Delayed <= (others=>'0');
      QuadB_Delayed <= (others=>'0');
   elsif rising_edge(Clk) 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;

-- ==============================================

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

entity variable_clock is
    Port ( 
      clk_i    : in  STD_LOGIC;
      resetn_i : in  STD_LOGIC;
      ticks_i  : in  unsigned (7 downto 0);
      clk_o    : out STD_LOGIC
    );
end variable_clock;

architecture Behavioral of variable_clock is
  signal counter_s: natural range 0 to 2**ticks_i'length-1 := 0;
  signal clk_out_s: STD_LOGIC := '0';
begin

process (clk_i, resetn_i)

  procedure perform_reset is -- this procedure contains the reset action
  begin
      counter_s <= 0;
      clk_out_s <= '0';
  end procedure;

  procedure perform_action is -- this procedure contains the reset action
  begin
      if counter_s >= ticks_i then -- >= because the value can change
        counter_s <= 0;
        clk_out_s <= not clk_out_s;
      else
        counter_s <= counter_s + 1;
      end if;
  end procedure;

begin
  if rising_edge(clk_i) then
    if resetn_i = '0' then
      perform_reset;
    else
      perform_action;
    end if;
    clk_o <= clk_out_s;
  end if;
end process;  
  
end Behavioral;

-- ==============================================

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

entity quadrature_oscillator is
  Port (
    clk_i     : in std_logic;
    resetn_i  : in std_logic;
    outputs_o : out std_logic_vector (3 downto 0)
   );
end quadrature_oscillator;

architecture Behavioral of quadrature_oscillator is
  signal pattern_s: std_logic_vector (6 downto 0) := "0011001";
  signal counter_s: natural range 0 to 3 := 0;

begin

process (clk_i, resetn_i)

  procedure perform_reset is -- this procedure contains the reset action
  begin
    counter_s <= 0;
  end procedure;

  procedure perform_action is -- this procedure contains the reset action
  begin
    if counter_s = 3 then 
      counter_s <= 0;
    else
      counter_s <= counter_s + 1;
    end if;
  end procedure;

begin
  if rising_edge(clk_i) then
    if resetn_i = '0' then
      perform_reset;
    else
      perform_action;
    end if;
    outputs_o(0) <= pattern_s(counter_s + 3); 
    outputs_o(1) <= pattern_s(counter_s + 2); 
    outputs_o(2) <= pattern_s(counter_s + 1); 
    outputs_o(3) <= pattern_s(counter_s + 0);
  end if;
end process;

end Behavioral;

-- ==============================================

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

entity quad_decode_quad_osc is
    Port ( QuadA_in : in  STD_LOGIC;
           QuadB_in : in  STD_LOGIC;
           clk_in : in  STD_LOGIC;
           nReset_in : in  STD_LOGIC;
           oscillator_quad_out : out  STD_LOGIC_VECTOR (3 downto 0);
           clk_out : out  STD_LOGIC);
end quad_decode_quad_osc;

architecture Behavioral of quad_decode_quad_osc is

signal position_s : unsigned (7 downto 0);
signal clk_s : STD_LOGIC;

component quadrature_decoder is
    Port ( QuadA : in  STD_LOGIC;
           QuadB : in  STD_LOGIC;
           Clk : in  STD_LOGIC;
           nReset : in STD_LOGIC;
           Position : out  unsigned (7 downto 0));
end component quadrature_decoder;

component variable_clock is
    Port ( 
      clk_i    : in  STD_LOGIC;
      resetn_i : in  STD_LOGIC;
      ticks_i  : in  unsigned (7 downto 0);
      clk_o    : out STD_LOGIC
    );
end component variable_clock;

component quadrature_oscillator is
  Port (
    clk_i     : in std_logic;
    resetn_i  : in std_logic;
    outputs_o : out std_logic_vector (3 downto 0)
   );
end component quadrature_oscillator;

begin

quadrature_decoder_i: component quadrature_decoder
     port map (
   QuadA => QuadA_in,
   QuadB => QuadB_in,
   Clk => clk_in,
   nReset => nReset_in,
   Position (7 downto 0) => position_s (7 downto 0)
    );

variable_clock_i: component variable_clock
     port map (
   clk_i => clk_in,
   resetn_i => nReset_in,
   ticks_i (7 downto 0) => position_s (7 downto 0),
   clk_o => clk_s
    );

quadrature_oscillator_i: component quadrature_oscillator
     port map (
   clk_i => clk_s,
   resetn_i => nReset_in,
   outputs_o (3 downto 0) => oscillator_quad_out (3 downto 0)
    );

  clk_out <= clk_s;  

end Behavioral;

 

constraints.ucf

The external pins defined in quad_decode_quad_osc are mapped here.

I've mapped them for my specific board. Adapt for your FPGA/kit.

 

net clk_in                      loc=a9; # 12 MHz clock to FPGA.
net nReset_in                   loc=k15;


# PM1 connections for rotary encoder module.
net QuadA_in                    loc=r15;
net QuadB_in                    loc=m15;


# PM1 connections for the pwm outputs
net oscillator_quad_out[0]      loc=r7;
net oscillator_quad_out[1]      loc=r16;
net oscillator_quad_out[2]      loc=m16;
net oscillator_quad_out[3]      loc=k16;
net clk_out                     loc=t7;

 

Untested. I'll update if there are errors.

 

image

 

If used for Shabaz' SDR, pins [0]and [2] should go to the JFETS for one opamp. Pins [1] and [3] to the JFETS for the other. Because they are the 2 differential pairs.

  • Sign in to reply
  • jc2048
    jc2048 over 3 years ago in reply to dougw

    Looking quickly at the family datasheet, the PSOC 5 has a PLL. Do you know if that's programmable on the fly? If so, the PSOC 5 might be able to manage a frequency synthesizer for the tuning.

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

    Those built-in primitives are a great time saver. I was just exploring decoder logic for fun.

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

    Jan Cumps  wrote:

     

    Yes, but wasn't that with a dedicated decoder primitive?

    Ah no. my project was with the primitive. You built it from logic blocks.

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

    Yes, but wasn't that with a dedicated decoder primitive?

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

    If that is all, it should be no problem for a PSoC 4. It could also do the quadrature decoding of a rotary encoder.

    • Cancel
    • Vote Up 0 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 © 2025 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