我试图计算来自直流电机的反馈信号的频率,以找出电机的速度(以转/秒为单位),或者至少计算其旋转速度。来自电机的反馈信号是方波,192个正边相当于一圈。
我使用50兆赫兹作为我的输入时钟信号。我试图设计一个采用50M-Hz时钟和电机反馈作为输入的模块,并输出电机的速率/速度。我已经挣扎了一段时间,因为Verilog不允许我在多个always块中使用一个变量。请帮我解决这个项目。谢谢!
答案 0 :(得分:0)
第一步是决定是否测量信号的频率或周期。正如Hida所指出的那样,通过在输入的每个上升沿之间计算50MHz脉冲,可以测量信号的周期。然后,您必须将常数除以此期间以获得RPM或RPS。
但RPM与脉冲信号的频率线性相关,而不是周期。因此,通过计算固定周期内的脉冲数,可以更容易地直接测量。
接下来,您必须决定测量系统所需的速度。如果计算一秒钟的脉冲数,你将获得RPS * 192,这是一个很好的精度,但每秒只能输出一次,可能太慢了。如果计算1/192秒,则直接获得RPS,但可能会失去精度。以下示例测量1/32秒,得到6 * RPS或RPM / 10。您应该选择一个测量周期以满足要求,并调整位宽和常数以适应。通过选择正确的采样周期,您甚至可以直接以所需的单位或至少方便的倍数获得输出。
将输入时钟从50MHz降低到您需要的任何采样率(周期)。在此示例中,使用1到1562450(即50000000/32)的计数器创建32Hz周期。或者使用从1562449到0的向下计数器。
跟踪输入信号以找到它的上升沿。如果它在一个50Mhz时钟上是低电平而在下一个时钟上是高电平,则它是一个脉冲的上升沿。由于输入信号相对于50MHz时钟来说是如此之慢,因此它被视为数据并且只是异步采样。注意:在实际设计中,您可能需要去抖动并注册输入信号,因为它与50MHz时钟异步。否则,任何采样的噪声或反弹都会向计数器添加错误脉冲。此示例假定已完成信号调节。
每次计数器达到零时,一个周期结束。注册当前边沿计数,使其在下一个周期内不会改变。然后将边沿计数重置为零,并在下一个周期再次开始计算边缘。
另外,您可以看到测试台和模块中有多个位置使用多个始终块中的信号(例如“计数器”)。您可以在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