基于FPGA的测频系统+上位机

【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 <= 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】最后,完成的上位机“测频系统”效果如下:

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

 
原文地址:https://www.cnblogs.com/CrazyBirdLin/p/4319623.html