6
\$\begingroup\$

I am working on generating a pulse train to control a motor that accepts a pulse train as an input. Each pulse corresponds to a pre-set movement increment; I can set one pulse equal to 1/1000 degree (or whatever), and if I send it 20 pulses, the motor will move 20/1000 degree.

The software that performs all the heavy lifting and determines where the motor needs to be commanded to go at any given time is programmed in LabVIEW. This program then sends position and speed commands (as 32-bit integers) to an FPGA, which I would like to use to generate a series of pulses to tell the motor how far and how fast to move. I have a simple pulse generator that just puts out the required number of pulses at the FPGA's clock speed (see diagram below). How can I control the speed of these pulses in my FPGA?

I am using an Altera FPGA programmed in Quartus II v9.0.

schematic

simulate this circuit – Schematic created using CircuitLab

Note the inverting terminal for a = b? on the comparator. The FPGA will then output the values of pulse and sign to tell my motor how far to turn and in what direction. Inputs to the FPGA are the integer number of pulses we want to generate, ref[31..00], and a boolean write flag, writeF. Multiple motors are controlled by one program, thus the need to specify when the data on the bus ref[31..00] is for a particular motor. The most significant bit of the reference value will control the direction of movement, thus err31 is used as the input to the updown terminal.

As you can see, the counter is counting the number of pulses generated, using pulse as its clock input, but pulse is only being generated at the FPGA's clock speed. Given an additional input to my FPGA to control pulse rate, can I make the pulse rate variable?

EDIT: I changed my circuit so that the system clock is going in to the clock input of my counter, and my pulse output is being used as the clock enable (clock_en) signal to this counter. Previously I had my pulse output plugged straight in to my clock input, which is potentially bad. I will post my findings here when I have implemented suggestions.

VHDL Variable Counter Solution

I am trying to implement David Kessner's approach using VHDL. Basically I am creating a counter that can increment by numbers other than 1, and using the rollover of this counter to determine when I should generate a pulse. The code looks like this so far:

--****************************************************************************
-- Load required libraries
--****************************************************************************
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

--****************************************************************************
-- Define the inputs, outputs, and parameters
--****************************************************************************
entity var_count is

    generic(N: integer :=32);               -- for generic counter size
    port(
            inc_i       : in    std_logic_vector(N-1 downto 0);
            load_i      : in    std_logic;
            clk_i       : in    std_logic;
            clear_i     : in    std_logic;
            clk_en_i    : in    std_logic;
            count_en_i  : in    std_logic;
            msb_o       : out   std_logic
        );

end var_count;

--****************************************************************************
-- Define the behavior of the counter
--****************************************************************************
architecture behavior of var_count is

    -- Define our count variable. No need to initialize in VHDL.
    signal count : unsigned(N-1 downto 0) := to_unsigned(0, N);
    signal incr  : unsigned(N-1 downto 0) := to_unsigned(0, N);

begin   
    -- Define our clock process
    clk_proc : process(clk_i, clear_i, load_i)
    begin
        -- Asynchronous clear
        if clear_i = '1' then
            count <= to_unsigned(0, N);
        end if;

        -- Asynchronous load
        if load_i = '1' then
            incr <= unsigned(inc_i);
        end if;

        -- Define processes synch'd with clock.
        if rising_edge(clk_i) and clk_en_i = '1' then
            if count_en_i = '1' then            -- increment the counter
                -- count <= count + unsigned(inc_i);
                count <= count + incr;
            end if;
        end if;     
    end process clk_proc;

    -- Output the MSB for the sake of generating a nice easy square wave.
    msb_o <= count(count'left);

end behavior;

I intend to either output the MSB directly, or to take the MSB from the this counter (msb_o(k)), pass it through a single-bit D-Q flip flop so that I also have msb_o(k-1), and output a pulse every time my variable counter rolls over by executing:

PULSE = ~msb_o(k) * msb_o(k-1)

where ~ denotes logical NOT, and * denotes logical AND. This is the first VHDL program I have written, and I wrote it largely using this, this, and this. Does anybody have any recommendations as to how I could improve my code? Unfortunately I am not getting any pulses out of my FPGA still.

EDIT: Updated the VHDL code to the current implementation (2013-08-12). Also adding this free book to the list of references.

EDIT 2: Updated my code to the (final) working version.

\$\endgroup\$

4 Answers 4

6
\$\begingroup\$

What you want to do is called a Numerically Controlled "Oscillator", or NCO. It works like this...

Create a counter that can increment by values other than 1. The inputs to this counter are the master clock, and a value to count by (din). For each clock edge, count <= count + din. The number of bits in din is the same as the number of bits in the counter. The actual count value can be used for many useful things, but what you want to do is super simple.

You want to detect every time the counter rolls over, and output a pulse to your motor when that happens. Do this by taking the most significant bit of the counter and running it through a single flip-flop to delay it by one clock. Now you have two signals that I'll call MSB, and MSB_Previous. You know if the counter has rolled over because MSB=0 and MSB_Prev=1. When that condition is true, send a pulse to the motor.

To set the pulse frequency, the formula is this: pulse_rate = main_clk_freq * inc_value/2^n_bits

Where inc_value is the value that the counter is being incremented by and n_bits is the number of bits in the counter.

An important thing to note is that adding bits to the counter does not change the range of the output frequency-- that is always 0 Hz to half of main_clk_freq. But it does change the accuracy that you can generate the desired frequency. Odds are high that you won't need 32-bits for this counter, and that maybe just 10 to 16 bits will be enough.

This method of generating pulses is nice because it is super easy, the logic is small and fast, and it can often generate frequencies more accurately and with better flexibility than the type of counter+comparator design that you have in your question.

The reason why the logic is smaller is not only because you can get by with a smaller counter, but you do not have to compare the entire output of the counter. You only need the top bit. Also, comparing two large numbers in an FPGA usually requires a lot of LUTs. Comparing two 32-bit numbers would require 21 4-Input LUTs and 3 logic levels, where as the NCO design requires 1 LUT, 2 Flip-Flops, and only 1 logic level. (I'm ignoring the counter, since it is basically the same for both designs.) The NCO approach is much smaller, much faster, much simpler, and yields better results.

Update: An alternative approach to making the rollover detector is to simply send out the MSB of the counter to the motor. If you do this, the signal going to the motor will always be a 50/50 duty cycle. Choosing the best approach depends on what kind of pulse your motor needs.

Update: Here is a VHDL code snippet for doing the NCO.

signal count :std_logic_vector (15 downto 0) := (others=>'0);
signal inc   :std_logic_vector (15 downto 0) := (others=>'0);
signal pulse :std_logic := '0';

. . .

process (clk)
begin
  if rising_edge(clk) then
    count <= count + inc;
  end if;
end process;

pulse <= count(count'high);
\$\endgroup\$
6
  • \$\begingroup\$ This makes a lot of sense, and definitely sounds easier than the 42-piece screwdriver approach that I have been trying to implement. I will see if I can get this up and running and post on here with what I find. Thanks! And thank you for the level of detail that you gave in your answer. \$\endgroup\$
    – Engineero
    Commented Aug 2, 2013 at 13:35
  • \$\begingroup\$ Baring a built-in function that can count in increments other than one, which I do not think I have in Quartus II v9.0, how would I go about building one of these in FPGA code? I am not very familiar with the actual line code used to work with FPGAs (VHDL or whatever?). \$\endgroup\$
    – Engineero
    Commented Aug 3, 2013 at 19:20
  • 1
    \$\begingroup\$ @engineero, you need to learn VHDL or Verilog. Otherwise you will always be frustrated developing FPGAs with schematics. Writing the code that I outlined above takes, literally, 3 minutes to write. 5 if you are careful. But for schematic entry, use an n-bit adder and an n-bit d-flip-flop. \$\endgroup\$
    – user3624
    Commented Aug 3, 2013 at 22:56
  • \$\begingroup\$ Thanks! I have written a VHDL code to do this (I think) but it does not seem to automatically roll over. When I output the MSB, it just goes high and stays high. I put a case into the code (updating right after I submit this) to roll over when the count gets to 2^N - inc, but I get kind of random frequency and duty cycle square waves, which does not make sense to me. Any further suggestions would be greatly appreciated! \$\endgroup\$
    – Engineero
    Commented Aug 12, 2013 at 14:58
  • 1
    \$\begingroup\$ @Engineero I have no idea why your code does not automagically roll over. You actually have to write code specifically to NOT have it roll over. I update my answer to show some example code (see, it's super simple!). \$\endgroup\$
    – user3624
    Commented Aug 12, 2013 at 15:05
3
\$\begingroup\$

I assume this is roughly the functional behavior you want to implement:

procedure generate_pulse_train( signal write : in std_logic; 
                                signal pulse : out std_logic;
                                constant t_pulse : time; 
                                signal n_pulses : in natural) is
begin
     wait until write = '1';

     for n in 0 to n_pulses-1 loop
         pulse <= '1';
         wait for t_pulse/2;
         pulse <= '0';
         wait for t_pulse/2;
     end loop;
end procedure;

Unless you have very stringent requirements for the timing of the pulse clock I would recommend a simple state machine to generate the pulse train instead of gating the clock. To alter the frequency of the clock during run-time you would have to use a configurable PLL, but if the pulse clock frequency is equal or less than the system clock you can just use that to generate the pulse clock.

An example of such a state machine could for instance be:

pr_fsm : process(clk) is
begin
if rising_edge(clk) then
   case state is
       when IDLE =>
           scaler <= pulse_period/2 ;
           pulse_counter <= n_pulses;
           pulse  <= '0';

           if writeF = '1' then
               state <= ACTIVE;
           end if; 

       when ACTIVE =>
           if scaler = 0 then
               scaler <= pulse_length;

               pulse <= not pulse;

               if pulse_counter = 0 then
                   state <= IDLE;
               else
                   pulse_counter <= pulse_counter - 1;
               end if;

           else
               scaler <= scaler - 1;
           end if;
   end case;

end if;
end process pr_fsm;
\$\endgroup\$
2
  • \$\begingroup\$ Thank you. I am unfamiliar with state machines on FPGAs, although I have programmed state machines in other languages. Could you elaborate on how I would use a state machine to do this? Also, for your code, would t_pulse and n_pulses just be integers (number of clock cycles and number of pulses respectively)? \$\endgroup\$
    – Engineero
    Commented Aug 1, 2013 at 13:40
  • \$\begingroup\$ Thanks for the detailed answer. The only problem is that I need to be able to adjust the pulse frequency on the fly, which I can do using a counter with variable increment and outputting the MSB of the count. I really appreciate your help! \$\endgroup\$
    – Engineero
    Commented Aug 13, 2013 at 17:46
2
\$\begingroup\$

You've gated your clock which is a very bad thing to do unless you know what you are doing. The same clock signal needs to go to all the clock inputs of your circuits.

You need to use your pulse signal as a clock enable to your counter - that way all is synchronous and your counter still only counts when pulse is high.

Your pulse generator can operate at other speeds, below the main clock speed by having another counter which outputs a single clock wide pulse each time it wraps around. By increasing the terminal count of this counter, you get longer between pulses, and slower movements of your motor.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Ooo, that sounds like something I could do. And I already updated the pulse that I generate to go to my clock enable pin after tackling a similar problem with the encoder counting logic on my board. I will update my schematic accordingly and try to use a counter to control my pulse period. Thanks! \$\endgroup\$
    – Engineero
    Commented Aug 1, 2013 at 14:32
1
\$\begingroup\$

There are a number of ways of producing a frequency which is an adjustable (possibly fractional) submultiple of an input clock. One popular approach for allowing any fraction from 1/2^N to 1-(1/2^N) is to have a programmable N-bit register to select the desired frequency, and an N-bit register called the phase accumulator. Every clock cycle, add the frequency value to the phase accumulator and output a pulse if there is a carry. If the values are 16 bits and the frequency value is 573, then there will be 573 more-or-less-equally-spaced output pulses every 65,536 input pulses. Changes to the requested frequency will "smoothly" affect the output.

An alternative approach which may reduce the amount of circuitry required, especially if one chip will have to handle many independently-programmable output frequencies, is to have a counter with circuitry on each bit to generate a pulse each time a particular bit is going to change from a zero to a one (the output controlled to the LSB of the counter will pulse on half the cycles, the next output will pulse on half the cycles where the LSB one didn't, and each output in turn will pulse on half the cycles where none of the lower ones do. If one ANDs the output from the MSB of the "desired frequency" register the the LSB of the aforementioned circuit, and the remaining bits of the frequency register in descending sequence with bits from the aforementioned circuit in ascending sequence, then every time the counter wraps the output will have sent the appropriate number of pulses. Note that when using this approach, the output will have more jitter than with the phase-accumulator approach, but the amount of circuitry required for each output may be reduced. Note also that changes to the programmed frequency should be synchronized with times the timer wraps, or else they may cause disruption in the output.

Yet another approach, which is something of a hybrid between the first two, is to have a counter designed so that a certain input will make it "stall" for a cycle, and wire a circuit similar to the second one above to that stall circuit. If one has an 8-bit counter, the value programmed into the frequency register will cause it to stall between one and 255 times every 256 times the counter advances, thus causing the wrap rate of the counter to be adjustable from 256 to 511. If one uses an 8-way multiplexer, one may thus have a divide ratio of K/2^N where K is 256-511 and N is zero to seven. Note that lower-order "taps" from the counter will have a lot of jitter, but the higher-order ones will be much cleaner.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.