【1】本设计的要求:
- 利用FPGA实现频率计,要求2秒内测出外部频率,并且2秒更新一次值;
- 实现100MHz频率的测量;
- 测量结果通过串口921600bps发送给PC;
- 发送格式为:FFCB0176543210,其中FFCB01为帧头,76543210(即0x76543210)为频率值,数据在上位机中转换成十进制并用合适的单位(MHz、KHz、Hz)表示;
- 完善上位机。
【2】现在就来说说我是如何实现上述条件的。对于第一个条件“2秒内测出频率并2秒更新一次”,很明显这是连续、实时的测量过程。跟我们经常看过的“一个占空比为50%、周期为4秒的方波,在其高电平时测试频率”完全不同,当然它也可以测出频率,但达不到连续测量的要求。为了实现该要求,我们得采取另一种思路,就是两个间隔2秒的脉冲之间进行频率的测量,如下图所示:
实现代码(时钟为100MHz,clkin为待测频的信号):
//--------------------------------------- localparam Threshod_Delay = 29'd500000000; //2s reg [28:0] cnt1; reg cnt_en; always @(posedge clk_ref or negedge sys_rst_n) begin if(!sys_rst_n) begin cnt1 <= 0; cnt_en <= 0; end else begin if(cnt1 < Threshod_Delay) begin cnt1 <= cnt1 + 1'b1; cnt_en <= 1'b0; end else begin cnt1 <= 0; cnt_en <= 1'b1; end end end //--------------------------------------- reg clkin_buf1; reg clkin_buf2; always @(posedge clk_ref or negedge sys_rst_n) begin if(!sys_rst_n) begin clkin_buf1 <= 1; clkin_buf2 <= 1; end else begin clkin_buf1 <= clkin; clkin_buf2 <= clkin_buf1; end end wire pos_flag = clkin_buf1 && ~clkin_buf2; //--------------------------------------- reg [31:0] freqdata; reg [31:0] freout; reg flag1; always @(posedge clk_ref or negedge sys_rst_n) begin if(!sys_rst_n) begin freqdata <= 0; flag1 <= 0; freout <= 0; end else begin if(cnt_en) begin freout <= freqdata >> 1; flag1 <= cnt_en; freqdata <= 0; end else begin if(pos_flag) freqdata <= freqdata + 1'b1; else freqdata <= freqdata; freout <= freout; flag1 <= 0; end end end
在上面的设计中涉及到了一种很重要的思想即边沿检测。所谓边沿检测,就是检测外部输入信号或FPGA内部逻辑信号的跳变,即上升沿或下降沿的捕获。通过边沿检测实现使能时钟,避免了时钟满天飞,同时提高了时序电路的稳定性。边沿检测功能实现之前,我们必须对它在时序上的理解。当上一时刻为低电平,当前时刻为高电平时为上升沿;当上一时刻为高电平,当前时刻为低电平时为下降沿。
(1)上升沿的检测
reg DataInBuf1; reg DataInBuf2; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin DataInBuf1 <= 1'b1; DataInBuf2 <= 1'b1; end else begin DataInBuf1 <= DataIn; DataInBuf2 <= DataInBuf1; end end wire pos_flag = DataInBuf1 && ~DataInBuf2;
(2)下降沿的检测
reg DataInBuf1; reg DataInBuf2; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin DataInBuf1 <= 1'b0; DataInBuf2 <= 1'b0; end else begin DataInBuf1 <= DataIn; DataInBuf2 <= DataInBuf1; end end wire neg_flag = ~DataInBuf1 && DataInBuf2;
【3】对于第二个条件”实现100MHz频率的测量”,必须满足采样定理即采样率大于等于200MHz,而本设计的采样取250MHz。
【4】对于第三个条件“测量结果通过串口921600bps发送给PC”,首先要设计一个UART发送端,波特率为921600,然后将32位宽的测量结果并加上帧头FFCB01以字节为单位进行发送,其核心代码如下:
//--------------------------------------- reg [7:0] Din; reg [2:0] state; reg flag2; reg flag3; always @(posedge clk_ref or negedge sys_rst_n) begin if(!sys_rst_n) begin Din <= 0; state <= 0; flag2 <= 0; flag3 <= 0; end else begin if(flag1) flag2 <= 1; else begin if(flag && flag2) begin case(state) 3'd0 : begin Din <= 8'hFF; state <= 3'd1; end 3'd1 : begin Din <= 8'hCB; state <= 3'd2; end 3'd2 : begin Din <= 8'h01; state <= 3'd3; end 3'd3 : begin Din <= freout[31:24]; state <= 4; end 3'd4 : begin Din <= freout[23:16]; state <= 5; end 3'd5 : begin Din <= freout[15:8]; state <= 6; end 3'd6 : begin Din <= freout[7:0]; flag2 <= 0; state <= 0; end default : state <= 3'd0; endcase flag3 <= flag; end else begin Din <= Din; state <= state; flag3 <= 0; end end end end
【5】第四个条件“数据在上位机中转换成十进制并用合适的单位(MHz、KHz、Hz)表示”,该上位机使用VC++6.0实现,该条件部分实现代码如下:
// 计算频率值 switch(index) { case 0: if(bt == 0xff) index = 1; break; case 1: if(bt == 0xcb) index = 2; else index = 0; break; case 2: if(bt == 0x01) {index = 3;result = 0;} else index = 0; break; case 3: m_RevStr = m_RevStr + _T("FF CB 01 ") + strtemp; result += bt*16*16*16*16*16*16; index = 4; break; case 4: m_RevStr = m_RevStr + strtemp; result += bt*16*16*16*16; index = 5; break; case 5: m_RevStr = m_RevStr + strtemp; result += bt*16*16; index =6; break; case 6: m_RevStr = m_RevStr + strtemp; AddToInfOut(m_RevStr,TRUE,TRUE); result += bt; if(flag == TRUE) { if((1000000 <= result) && (result <= 2147483647)) { result /= 1000000; szFre.Format("%0.2lf",result); m_Frequst.SetWindowText(szFre+"MHz"); } else if((1000 <= result) && (result < 999999)) { result /= 1000; szFre.Format("%0.2lf",result); m_Frequst.SetWindowText(szFre+"KHz"); } else if((0 <= result) && (result<= 999)) { szFre.Format("%0.2lf",result); m_Frequst.SetWindowText(szFre+"Hz"); } } m_RevStr=""; index = 0; break; default: index = 0; break; }
【6】最后,完成的上位机“测频系统”效果如下:
【7】完成了测频电路和上位机的设计后,接下来就是调试了。由于我没有条件提供外部信号进行测频,于是采取了调用IP核PLL来产生待测信号(可通过按键进行选择),其例化代码如下:
system_ctrl_pll u_system_ctrl_pll ( //global clock .clk (clk ), .rst_n (rst_n ), //synced signal .clk_c0 (clk_ref ), //clock output,250MHz .clk_c1 (clkin1 ), //100MHz .clk_c2 (clkin2 ), //83.333333MHz .clk_c3 (clkin3 ), //66.666667MHz .clk_c4 (clkin4 ), //33.333333MHz .sys_rst_n(sys_rst_n) //system reset );
测试结果如下:
经过调试,该设计是符合条件的。
【8】虽然没有把该设计很详细的写出来,但是重要的是思路。哈哈,明天去公司报道,今晚就不再折腾了,早点睡觉,晚安*^_^*