在 VHDL 中计算特定时间段内的边沿

Count Edges over specific Period of Time in VHDL

对于电动机的速度测量,我想在 10 毫秒的时间间隔内计算编码器输入的上升沿和下降沿的数量。

为此,我为我的 40 MHz 时钟实现了一个时钟分频器,如下所示:

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;

这应该使 CLKSpeed 每 10 毫秒为“1”。我将此块用作我的 Toplevel VHDL 模块中的一个组件。在下一个块中,我使用移位寄存器对来自我的编码器输入 (QEPA) 的边沿进行计数,以对输入信号进行去抖动。

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;

这应该每 10 毫秒将 CountQEPA 的当前值写入我的变量 edgesPerTime,然后重置计数器。 信号 Speed 通过 uart 传输。不幸的是,每 10 毫秒重置 CountQEPA,我收到 EdgesPerTime 的常数值 0。如果我删除代码中的重置行,我可以收到 EdgesPerTime 的递增值,直到达到最大整数 (2^16),此时计数器重置为 0。

VHDL 中计算设定时间段内上升沿和下降沿的正确实现是什么?

非常感谢任何帮助,因为我对 vhdl 还是很陌生。

您正在使用变量构建闩锁。您的综合工具为 countQEPA 推断出异步复位 CLKSpeed,并且由于您的锁存器 EdgesPerTime 在其写使能 CLKSpeed 被断言时锁存 countQEPA,您只能看到复位countQEPA 0 的值。

您应该构建适当的同步逻辑:

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;

我不是变量的忠实拥护者,但这些天综合工具似乎变得更加智能。如您所见,我创建了三个独立的流程来展示我们实际想要进行的操作。 CountQEPA 和 debounce 现在是信号,因为它们需要从其他进程读取。

我假设 CLKSpeed 与 CLK (40 MHz) 同步。如果不是,那么您应该考虑时钟域之间的握手。

您当然可以将这些单独的进程做成一个进程。一般来说,我尽量避免使用变量,因为它们可以很容易地用来描述不可综合的行为,就像你很难发现的那样。


我不确定您的编码器反弹了多长时间,但如果您以 40 MHz 采样,6 位移位寄存器可能无法工作,因为它只有 125 ns 的去抖(您仅将低 5 位用于去抖动,注意状态变化的最高位)。

说到你的去抖器。考虑以下情况:

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

这可能不是您想要的。您应该将去抖信号与上次稳定状态进行比较,而不是假设 state'delayed(5 * clk_period) 是稳定状态。


正如我上面所说,我假设 CLK 是您的 40 MHz 时钟。您不需要那么快地对编码器信号进行采样,因为合适的去抖动器会占用大量移位寄存器位,而不会给您带来任何附加值,因为无论如何您都必须将该时钟分频至 1 kHz 以进行时隙计数。

相反,请考虑编码器信号的最大频率。您可能希望至少以该频率的两倍对其进行过采样,为了安全起见将该频率提高四倍。所以每个 "bit time" 有四个样本。

由于您期望在 1 kHz 时有许多边沿,因此我假设您的编码器至少快两个数量级,即 100 kHz。因此,将 40 Mhz 缩小到 400 kHz,并在 QEPA 中移动以进行去抖动。将这些 400 kHz 采样到 1 kHz 以进行时隙测量。


旁注:什么样的电机编码只使用一个位?你确定你不是在处理正交编码器吗?