好久沒更了,百忙之中寫一篇so easy的代碼——基于FPGA的頻率計設(shè)計。
一、簡介
廢話不多說,下面是百度搜索關(guān)于頻率計的簡潔概念。
數(shù)字頻率計是一種基本的測量儀器,被廣泛應(yīng)用于航天、電子、測控等領(lǐng)域?;趥鹘y(tǒng)測頻原理的頻率計的測量精度將隨被測信號頻率的下降而降低,在使用中有較大的局限性,而等精度頻率計不但具有較高的測量精度,而且在整個頻率區(qū)域能保持恒定的測試精度。
二、設(shè)計方案
對于頻率計的設(shè)計,常用的方法有三種:測周法、測頻法以及等精度測量法。
(1)測周法
測周法的測量對象一般是頻率較低的被測信號,一般是選擇取被測時鐘的一個周期的上升沿或者下降沿都是可以的,下圖是選擇被測時鐘一個周期的上升沿。
?由波形圖中可以很快速的寫出測周法的代碼:
module cymometer
#(parameter SYS_CLK = 32'd100_000_000) // 基準時鐘頻率值
( //system clock
input sys_clk , // 基準時鐘信號
input rst_n , // 復(fù)位信號
//cymometer interface
input in_signal , // 被測時鐘信號
// output wire [31:0] fs_cnt_t/* synthesis syn_keep=1 */,
output reg [31:0] fre // 被測時鐘頻率輸出
);
//測周法(低頻)
reg [31:0] signal_cnt; //對被測信號高電平進行計數(shù)的計數(shù)器
//在測信號高電平期間進行計數(shù)
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
signal_cnt <= 0;
else if(low_flag)
signal_cnt <= signal_cnt + 1;
else if(low_flag == 1'b0)
signal_cnt <= signal_cnt;
end
reg [31:0] low_cnt;
reg low_flag;
always @(negedge in_signal or negedge rst_n )begin
if(!rst_n) begin
low_cnt <= 4'd0;
low_flag <= 1'b0;
end
else if(low_cnt < 4'd3) begin
low_cnt <= low_cnt + 1'b1;
low_flag <= 1'b0;
end
else if(low_cnt == 4'd3) begin
low_cnt <= low_cnt + 1'b1;
low_flag <= 1'b1;
end
else if(low_cnt == 4'd4) begin
low_cnt <= low_cnt;
low_flag <= 1'b0;
end
end
//低頻除法器
//在測信號低電平期輸出測量數(shù)據(jù)
always @(negedge low_flag or negedge rst_n )begin
if(!rst_n)
fre <= 0;
else
fre <= SYS_CLK / signal_cnt;
end
一直比較忙,所以仿真圖暫時不更新了,之前驗證過了沒啥問題,如果有問題需要的小伙伴可以自己調(diào)試哦。
(2)測頻法
一般是使用比被測信號高得多的標準信號,基準時鐘可以取一個固定值(如1S),然后被測時鐘以上升沿或者下降沿計數(shù),計數(shù)就為該被測時鐘的頻率,波形圖如下。
?代碼如下:
parameter CNT_2S = 32'd199_999_999;
//高頻
reg [31:0] cnt_high;
reg flag_1s;
always @(posedge sys_clk or negedge rst_n) begin
if(!rst_n) begin
cnt_high <= 32'd0;
flag_1s <= 1'd0;
end
else if(cnt_high < 32'd10) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd0;
end
else if(cnt_high == 32'd10) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd1;
end
else if(cnt_high < 32'd10 + SYS_CLK) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd1;
end
else if(cnt_high == 32'd10 + SYS_CLK) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd0;
end
else if(cnt_high == 32'd10 + SYS_CLK + CNT_2S) begin
cnt_high <= 32'd0;
flag_1s <= 1'd0;
end
end
reg [31:0] fre_high;
always @(posedge in_signal or negedge rst_n) begin
if(!rst_n)
fre_high <= 32'd0;
else if(flag_1s == 1'b1)
fre_high <= fre_high + 1'd1;
else if(flag_1s == 1'b0)
fre_high <= fre_high;
end
always@(negedge flag_1s or negedge rst_n) begin
if(!rst_n)
fre <= 32'd0;
else
fre <= fre_high;
end
(3)等精度測量法:
????????等精度測量法與前兩種方式不同,其最大的特點是,測量的實際門控時間不是一個固
定值,它與被測時鐘信號相關(guān),是被測時鐘信號周期的整數(shù)倍。在實際門控信號下,同時
對標準時鐘和被測時鐘信號的時鐘周期進行計數(shù),再通過公式計算得到被測信號的時鐘頻
率。
????????等精度測量法與前兩種方式不同,其最大的特點是,測量的實際門控時間不是一個固
定值,它與被測時鐘信號相關(guān),是被測時鐘信號周期的整數(shù)倍。在實際門控信號下,同時
對標準時鐘和被測時鐘信號的時鐘周期進行計數(shù),再通過公式計算得到被測信號的時鐘頻
率。
? ? ? ? 結(jié)合等精度測量原理和原理示意圖可得:被測時鐘信號的時鐘頻率 fx 的相對誤差與被測時鐘信號無關(guān);增大“軟件閘門”的有效范圍或者提高“標準時鐘信號”的時鐘頻率fs,可以減小誤差,提高測量精度。
????????了解了等精度測量原理之后,我們來說明一下被測時鐘信號的計算方法。首先我們先分別對實際閘門下被測時鐘信號和標準時鐘信號的時鐘周期進行計數(shù)。
? ? ? ? 我們設(shè)實際閘門被測時鐘的計數(shù)為x,被測信號時鐘周期為Tfx,他的時鐘頻率fx=1/Tfx,由此可以得公式:X * Tfx = X / fx = Tx(實際閘門)。
????????實際閘門下標準時鐘信號周期數(shù)為 Y,設(shè)被測信號時鐘周期為 Tfs,它的時鐘頻率 fs =1/Tfs,由此可得等式: Y * Tfs = Y / fs = Tx(實際閘門)。
????????其次,將兩等式結(jié)合得到只包含各自時鐘周期計數(shù)和時鐘頻率的等式: X / fx = Y / fs =Tx(實際閘門),等式變換, 得到被測時鐘信號時鐘頻率計算公式: fx = X * fs / Y。最后,將已知量標準時鐘信號時鐘頻率 fs 和測量量 X、 Y 帶入計算公式, 得到被測時鐘信號時鐘頻率 fx。
? ? ? ? 根據(jù)上面的分析,我們可以設(shè)計一個等精度的頻率計模塊。首先是進行波形圖的繪制:
?文章來源地址http://www.zghlxwxcb.cn/news/detail-662388.html
module frequency(
input sys_clk ,
input sys_rst_n ,
input clk_test ,
input clk_100M ,
output reg [33:0] freq
);
parameter CLK = 100_000_000;
parameter CNT_X = 28'd12_499_999,
CNT_5X = 28'd62_499_999,
CNT_6X = 28'd74_999_999;
wire gate_fx_neg; //被測閘門下降沿
wire gate_fs_neg; //基準閘門下降沿
wire locked;
assign locked = 1'b1;
reg [27:0] cnt; //
reg gate_soft; //軟件閘門
reg gate_a ; //實際閘門
reg gate_fx_reg; //被測閘門
reg gate_fs_reg; //基準閘門
reg [31:0] cnt_gate_fx; //被測閘門計數(shù)器
reg [31:0] cnt_gate_fs; //基準時鐘閘門計數(shù)器
reg [31:0] fx_cnt ; //被測頻率計數(shù)
reg [31:0] fs_cnt ; //標準頻率計數(shù)
reg cala_flag ; //計算標志位
reg [63:0] fre_cnt ; //頻率計數(shù)器
reg cala_flag_reg ; //計算標志寄存器
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt <= 28'd0;
else if(cnt == CNT_6X)
cnt <= 28'd0;
else
cnt <= cnt + 28'd1;
//軟件閘門
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
gate_soft <= 1'b0;
else if(cnt <= CNT_X || cnt > CNT_5X)
gate_soft <= 1'b0;
else
gate_soft <= 1'b1;
//實際閘門
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
gate_a <= 1'b0;
else
gate_a <= gate_soft;
//被測閘門
always@(posedge clk_test or negedge sys_rst_n)
if(!sys_rst_n)
gate_fx_reg <= 1'b0;
else
gate_fx_reg <= gate_a;
//被測計數(shù)器
always@(posedge clk_test or negedge sys_rst_n)
if(!sys_rst_n)
cnt_gate_fx <= 32'd0;
else if(gate_a == 1'b0)
cnt_gate_fx <= 32'd0;
else if(gate_a == 1'b1)
cnt_gate_fx <= cnt_gate_fx + 32'd1;
//取下降沿
assign gate_fx_neg = gate_fx_reg && ~gate_a;
//被測計數(shù)器
always@(posedge clk_test or negedge sys_rst_n)
if(!sys_rst_n)
fx_cnt <= 32'd0;
else if(gate_fx_neg == 1'b1)
fx_cnt <= cnt_gate_fx;
//基準閘門
always@(posedge clk_100M or negedge sys_rst_n)
if(!sys_rst_n)
gate_fs_reg <= 1'b0;
else
gate_fs_reg <= gate_a;
//基準閘門計數(shù)器
always@(posedge clk_100M or negedge sys_rst_n)
if(!sys_rst_n)
cnt_gate_fs <= 32'd0;
else if(gate_a == 1'b0)
cnt_gate_fs <= 32'd0;
else if(gate_a == 1'b1)
cnt_gate_fs <= cnt_gate_fs + 32'd1;
//取下降沿
assign gate_fs_neg = gate_fs_reg && ~gate_a;
//標準時鐘計數(shù)
always@(posedge clk_100M or negedge sys_rst_n)
if(!sys_rst_n)
fs_cnt <= 32'd0;
else if(gate_fs_neg == 1'b1)
fs_cnt <= cnt_gate_fs;
//頻率計數(shù)器
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
fre_cnt <= 64'd0;
else if(cala_flag == 1'b1)
fre_cnt <= CLK*fx_cnt/fs_cnt;
//計算標志位
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n) begin
cala_flag <= 1'b0;
cala_flag_reg <= 1'b0;
end
else if(cnt == CNT_6X) begin
cala_flag <= 1'b1;
cala_flag_reg <= cala_flag;
end
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
freq <= 34'd0;
else if(cala_flag_reg == 1'b1)
freq <= fre_cnt;
endmodule
仿真代碼:
`timescale 1ns / 1ns
module tb_freq();
reg sys_clk ;
reg sys_rst_n ;
reg clk_test ;
wire [33:0] freq ;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
clk_test = 1'b1;
#201
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
always #30 clk_test = ~clk_test;
defparam frequency_inst.CNT_X = 12_4,
frequency_inst.CNT_5X = 62_4,
frequency_inst.CNT_6X = 74_9;
frequency frequency_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_test (clk_test ),
.freq (freq )
);
endmodule
然后我們看一下仿真波形:
首先是被測時鐘的計數(shù)波形
?其次是標準時鐘計數(shù)波形
最后看一下計算的結(jié)果
????????計算的結(jié)果為100_000_000*167/1000=16_700_000,結(jié)果是沒有問題的,我們再看一下上板驗證的結(jié)果:
?????????這里測試時鐘設(shè)置的頻率為34.15926,PLL會保留小數(shù)點后三位也就是34.146,然后我們看一下上板之后的結(jié)果:
?結(jié)果跟我們設(shè)置的鎖相環(huán)結(jié)果是一致的,我們再來試一下高頻的測試:
?
?文章來源:http://www.zghlxwxcb.cn/news/detail-662388.html
????????鎖相環(huán)輸出的結(jié)果為109.091,而我們頻率計顯示的頻率為109.09,結(jié)果有一丟丟差異,就跟我們前面講的一樣,如果為了計算的精確,可以調(diào)節(jié)基準時鐘頻率或軟件閘門的保持時間對同一信號進行頻率測量。但相對于測頻和測周法來說,等精度的精度相度更加靈活和精準。
?
?
?
?
?
到了這里,關(guān)于基于FPGA的頻率計的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!