对于电动机的速度测量,我想在10ms的时间间隔内计算编码器输入的上升沿和下降沿的数量。
为此,我为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;
这应该每10毫秒使CLKSpeed
'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 ms在我的变量CountQEPA
中写入edgesPerTime
的当前值,然后重置计数器。
信号Speed
通过uart传输。不幸的是,每隔10ms重置一次CountQEPA
,我EdgesPerTime
的常数值为0。如果我删除代码中的重置行,我可以收到EdgesPerTime
的增加值,直到达到整数(2 ^ 16)的最大数,此时计数器重置为0.
VHDL中用于计算设定时间段内上升沿和下降沿的正确实现是什么?
任何帮助都非常复杂,因为我对vhdl仍然很陌生。
答案 0 :(得分:0)
您正在使用变量构建一个锁存器。您的综合工具推断CLKSpeed
的异步重置countQEPA
,并且由于您的锁存EdgesPerTime
在其写入启用countQEPA
被置位时锁存CLKSpeed
,您只能看到重置countQEPA
的{{1}}值。
您应该构建适当的同步逻辑:
0
我不是变量的忠实粉丝,但现在似乎综合工具变得更加聪明。如您所见,我制作了三个单独的流程来显示我们真正想要的内容。 CountQEPA和去抖现在是信号,因为它们需要从其他过程中读取。
我假设CLKSpeed与CLK(40 MHz)同步。如果不是,那么你应该考虑时钟域之间的握手。
您当然可以从这些单独的流程中制作单个流程。一般来说,我试图避免变量,因为它们可以很容易地用来描述不可综合的行为,就像你发现的那样。
我不确定您的编码器反弹多长时间,但如果您以40 MHz进行采样,则6位移位寄存器可能无法正常工作,因为只有125 ns去抖(您只使用低5位进行去抖动,注意到状态变化的最高位。)
说到你的去抖者。考虑以下情况:
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;
这可能不是你想要的。您应该将去抖信号与最后稳定状态进行比较,而不是假设state'delayed(5 * clk_period)是稳定状态。
正如我上面所说,我假设CLK是你的40 MHz时钟。您不需要快速采样编码器信号,因为正确的去抖动器会占用大量的移位寄存器位而没有任何附加值,因为您必须将该时钟分频至1 kHz,以便进行时隙计数。
相反,请考虑编码器信号的最大频率。您可能希望至少以该频率的两倍进行过采样,使该频率翻两倍才能安全。所以每个“位时间”有四个样本。
由于您期望1 kHz处有许多边缘,我认为您的编码器至少快两个数量级,即100 kHz。因此,将40 Mhz降至400 kHz,并在QEPA中进行调整以进行去抖动。对这些时隙测量采样400 kHz至1 kHz。
旁注:哪种电机编码只使用一位?你确定你没有处理正交编码器吗?