Search code examples
vhdlcounterresetencoder

Count Edges over specific Period of Time in VHDL


For speed measurement of an electric motor I would like to count the amount of rising and falling edges of an encoder-input in a time-interval of 10ms.

To do this I have implementet a clock divider for my 40 MHz Clock as follows:

entity SpeedCLK is
Port ( CLK          : in  STD_LOGIC;
       CLKspeed     : out  STD_LOGIC);
end SpeedCLK;
architecture Behavioral of SpeedCLK is
signal CounterCLK   : NATURAL range 0 to 400001;
begin

SpeedCounter : process
begin
wait until rising_edge(CLK);
CounterCLK <= CounterCLK + 1;
if CounterCLK < 400000 then
    CLKspeed        <= '0';
else
    CLKspeed        <= '1';
    CounterCLK      <= 0;
end if;
end process SpeedCounter;

end Behavioral;

This should make CLKSpeed '1' every 10 ms. I use this block as a component in my Toplevel VHDL-Module. In the next Block I count the Edges from my encoderinput(QEPA) with a shift register to debounce the Input Signal.

entity SpeedMeasure is
Port (
    QEPA        :   in      STD_LOGIC;
    CLK     :   in      STD_LOGIC;
    CLKSpeed    :   in      STD_LOGIC;
    Speed       :   OUT INTEGER -- in rpm
    );
end SpeedMeasure;

architecture Behavioral of SpeedMeasure is

begin
Edges : process(CLKSpeed, CLK)

variable detect     : STD_LOGIC_VECTOR(5 downto 0) := "000000";
variable    EdgesPerTime    : INTEGER := 0;
variable    CountQEPA       : INTEGER := 0;

begin
if CLKSpeed = '1' then
    EdgesPerTime := countQEPA;
    countQEPA := 0;
ELSIF (CLK'EVENT AND CLK = '1') THEN
    detect(5 downto 1) := detect(4 downto 0);
    detect(0) := QEPA;
if (detect = "011111") OR (detect = "100000") then 
countQEPA := countQEPA + 1;
    else
        countQEPA := countQEPA;
    end if;
end if; 
Speed <= EdgesPerTime;
end process Edges;


end Behavioral;

This should write the current value of CountQEPA in my variable edgesPerTime every 10 ms and reset the Counter afterwards. The signal Speed gets transmitted via uart. Unfortunatly with the reset of CountQEPA every 10ms I receive a constant value of 0 for EdgesPerTime. If I remove the reset line in the code, I can receive an increasing value for EdgesPerTime until the largest number for Integer (2^16) is reached, at which Point the Counter resets to 0.

What is the correct implementation in VHDL to count rising and falling edges in a set period of time?

Any help is greatly apreciated as I am still very new to vhdl.


Solution

  • You're building a latch using a variable. Your synthesis tool inferred an asynchronous reset CLKSpeed for countQEPA and since your latch EdgesPerTime latches countQEPA when its write enable CLKSpeed is asserted, you only see the reset countQEPA value of 0.

    You should build proper synchronous logic:

    signal CountQEPA : integer                      := 0;
    signal detect    : std_logic_vector(5 downto 0) := (others => '0');
    begin
    
    edge_cnt_p : process (
        CLK
        )
    begin
    
        if (rising_edge(CLK)) then
    
            -- synchronous reset
            if (CLKSpeed = '1') then
    
                countQEPA <= 0;
    
            elsif ((detect = "011111") or (detect = "100000")) then
    
                countQEPA <= countQEPA + 1;
    
            end if;
    
        end if;
    
    end process edge_cnt_p;
    
    edge_debounce_p : process (
        CLK
        )
    begin
    
        if (rising_edge(CLK)) then
    
            detect <= detect(detect'left downto 1) & QEPA;
    
        end if;
    
    end process edge_debounce_p;
    
    count_register_p : process (
        CLK
        )
    begin
    
        if (rising_edge(CLK)) then
    
            -- synchronous write enable
            if (CLKSpeed = '1') then
    
                Speed <= countQEPA;
    
            end if;
    
        end if;
    
    end process count_register_p;
    

    I'm not a big fan of variables, but it seems synthesis tools got a lot smarter these days. As you can see, I made three separate processes to show what we actually want going on. CountQEPA and debounce are signals now, because they need to be readable from other processes.

    I assume CLKSpeed is synchronous to CLK (40 MHz). If it isn't, then you should think about handshaking between the clock domains.

    You can of course make a single process out of these individual processes. In general, I try to avoid variables, because they can easily be used to describe behavior that is not synthesizable, like you found out the hard way.


    I'm not sure how long your encoder bounces, but if you do sample at 40 MHz, a 6 bit shift register will likely not work as that's only a 125 ns debounce (you use the lower 5 bits only for debouncing, the top bit for noticing state changes).

    Speaking of your debouncer. Consider the following case:

    debounce QEPA
     111111   1
     111111   0 <-- glitch/bounce on encoder line; 25 ns wide
     111110   1
     111101   1
     111011   1
     110111   1
     101111   1
     011111   1 <-- fake edge detected
    

    That's probably not what you want. You should compare your debounced signal to the last stable state instead of assuming state'delayed(5 * clk_period) being a stable state.


    As I said above, I'm assuming CLK is your 40 MHz clock. You needn't sample your encoder signal that fast, because a proper debouncer would take a large number of shift register bits at no added value to you, because you have to divide that clock down to 1 kHz anyway for your time-slot counting.

    Instead, take into account the maximum frequency of your encoder signal. You would want to oversample that at least by double that frequency, quadruple that frequency to be safe. So you have four samples per "bit time".

    Since you're expecting many edges at 1 kHz, I assume your encoder is at least two orders of magnitude fast, i.e. 100 kHz. So scale 40 Mhz down to 400 kHz and shift in QEPA for debouncing. Sample these 400 kHz down to 1 kHz for your time slot measurements.


    On a side note: What kind of motor encoding only uses a single bit? Are you sure you're not dealing with a quadrature encoder?