【问题标题】:How can I calculate the frequency of an input signal using Verilog?如何使用 Verilog 计算输入信号的频率?
【发布时间】:2016-06-26 03:12:34
【问题描述】:

我正在尝试计算来自直流电机的反馈信号的频率,以找出电机的速度(以转/秒为单位),或者至少是它的旋转速度。电机的反馈信号为方波,192个上升沿相当于转一圈。

我使用 50 兆赫兹作为输入时钟信号。我试图设计一个模块,将 50M-Hz 时钟和来自电机的反馈作为我的输入,并输出电机的速率/速度。我一直在苦苦挣扎,因为 Verilog 不允许我在多个 always 块中使用一个变量。请帮我完成这个项目。谢谢!

【问题讨论】:

  • 该反馈方波的频率是多少,或者该方波的两个姿势之间的最短时间周期是多少?
  • 您希望如何输出数据?转速?我建议您每 1/192 周期输出 50Mhz 时钟的数量。然后你只需要一个计数器,它会从零开始为每个反馈姿势计数。另外:请发布您到目前为止所做的事情,以便我们了解您是如何解决这个问题的。

标签: verilog system-verilog


【解决方案1】:

第一步是决定是测量信号的频率还是周期。正如 Hida 指出的那样,可以通过计算输入的每个上升沿之间的 50MHz 脉冲来测量信号的周期。然后,您必须将常数除以该周期才能获得 RPM 或 RPS。

但 RPM 与脉冲信号的频率成线性关系,而不是与周期相关。所以更容易通过计算固定周期内的脉冲数来直接测量。

接下来,您必须决定测量系统所需的速度。如果你计算一整秒的脉冲数,你会得到 RPS * 192,这是一个很好的精度,但每秒只给出一次输出,可能太慢了。如果您计算 1/192 秒,您将直接获得 RPS,但可能会降低精度。下面的示例测量 1/32 秒,即 6*RPS 或 RPM/10。您应该选择适合要求的测量周期并调整位宽和常数以适合。通过选择正确的采样周期,您甚至可以直接以所需单位或至少方便的倍数获得输出。

  1. 将输入时钟从 50MHz 分频到您需要的任何采样率(周期)。在此示例中,使用从 1 到 1562450(即 50000000 / 32)的计数器创建 32Hz 周期。或者使用从 1562449 到 0 的递减计数器。

  2. 跟踪输入信号以找到它的上升沿。如果它在一个 50Mhz 时钟上为低电平,而在下一个时钟上为高电平,则它是一个脉冲的上升沿。由于输入信号相对于 50MHz 时钟非常慢,因此它被视为数据并且只是异步采样。注意:在实际设计中,您可能需要对输入信号进行去抖动和注册,因为它与 50MHz 时钟异步。否则,采样到的任何噪声或反弹都会向计数器添加错误脉冲。此示例假设信号调理已经完成。

  3. 每次计数器达到零时,一个周期结束。注册当前的边沿计数,使其在下一个周期内不会改变。然后将边沿计数重置为零,并为下一个周期重新开始边沿计数。

顺便说一句,您可以看到测试台和模块中的某些地方在多个 always 块中使用信号(例如“计数器”)。您可以在http://www.iverilog.com 或使用 Verilog 模拟器玩这个。

CAVEAT:这只是一个非常简单的例子。生产代码将明确所有位宽,将线分配给周期结束信号,而不是复制逻辑。时钟计数器非常宽,可能应该预先缩放,以及许多其他细节。但这在模拟器中运行,并且应该在 FPGA @50Mhz 中合成并正常工作。


module rpm_counter(
    input clk, // 50Mhz
    input signal, // 0 to 1Mhz signal
    output [15:0]rpmout // RPM/10 e.g. 300 for 3000RPM
);

// counter == 0 @ 32 Hz
reg [20:0]counter = 0;
always @(posedge clk) begin
    if(counter == 0)
        counter <= 1562500 - 1; // 3-2-1-0 is period of 4, not 3
    else
        counter <= counter - 1;
end

// simple flip/flip to store last value of signal
reg last = 0;
always @(posedge clk) begin
    last <= signal;
end

// when counter == 0, register current count and reset edge counter
// otherwise increment edge counter for each rising edge of the pulse
reg [15:0]edges = 0;
reg [15:0]outreg = 0;
always @(posedge clk) begin
    if(counter == 0) begin
        outreg <= edges;
        edges <= 0;
    end
    // detect edge, was low and now high
    else if(~last & signal)
        edges <= edges + 1;
end

assign rpmout = outreg;

endmodule

module cheesy_testbench;

wire [15:0]out;
initial
  begin
    #100000000;
    $display("0x%x is %d RPM", out, out*10);
    $finish;
  end

reg clk50 = 1;
always
  #10 clk50 = ~clk50; // 10+10 = 20ns = 50Mhz

reg sig = 1;
always
  #45208 sig = ~sig; // 45208ns*2 period @3456RPM

rpm_counter UUT (
  .clk(clk50),
  .signal(sig),
  .rpmout(out)
);

endmodule

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-10
    • 2017-10-25
    • 2021-04-07
    • 1970-01-01
    • 2021-10-26
    相关资源
    最近更新 更多