crazybird

【原创】基于FPGA的测频系统+上位机

0
阅读(3490)

【1】本设计的要求:

  • 利用FPGA实现频率计,要求2秒内测出外部频率,并且2秒更新一次值;

  • 实现100MHz频率的测量;

  • 测量结果通过串口921600bps发送给PC;

  • 发送格式为:FFCB0176543210,其中FFCB01为帧头,76543210(即0x76543210)为频率值,数据在上位机中转换成十进制并用合适的单位(MHz、KHz、Hz)表示;

  • 完善上位机。

 

【2】现在就来说说我是如何实现上述条件的。对于第一个条件“2秒内测出频率并2秒更新一次”,很明显这是连续、实时的测量过程。跟我们经常看过的“一个占空比为50%、周期为4秒的方波,在其高电平时测试频率”完全不同,当然它也可以测出频率,但达不到连续测量的要求。为了实现该要求,我们得采取另一种思路,就是两个间隔2秒的脉冲之间进行频率的测量,如下图所示:

1

实现代码(时钟为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 <= 0; 
            end 
        else 
            begin 
            cnt1   <= 0;
            cnt_en <= 1; 
            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】最后,完成的上位机“测频系统”效果如下:

2

【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 
);

测试结果如下:

3

4

6

经过调试,该设计是符合条件的。

【8】虽然没有把该设计很详细的写出来,但是重要的是思路。哈哈,明天去公司报道,今晚就不再折腾了,早点睡觉,晚安*^_^*