基于FPGA的等精度频率计,频率测量范围0~25M正弦波(方波),相位测量范围0~100k

完整工程请见:https://download.csdn.net/download/qq_42838291/12573047
在这里插入图片描述
在这里插入图片描述
实物图及其测试效果,由于测试时没拍太多照片。高频部分的没有,只贴上低频的。
完整的FPGA工程+单片机显示程序在:
由于这里用到的多是集成的芯片和模块,所以画的比较简单。图中的CH1和CH2时信号源产生的两路正弦波或方波。方波可以直接输入测量,但是注意信号源要设置偏移量,将输出方波抬高到0上。因为信号源直接出来的方波含有负电压会导致测量不准,甚至损坏I/O口。正弦波要经过整形放大模块后才能进入FPGA。
进来的两路信号中,一路直接送进FPGA进行测频。异或门是将两路同频同幅的方波信号进行异或后再送入FPGA进行计数(测相)。D触发器是判断两路信号的超前滞后关系。
在这里插入图片描述
这里如果测量两路正弦波的频率和相位的话要先通过整形放大电路,将两路带负电压的正弦波整形成为单极性的方波。常用的电压比较器有LM311P或LM393之类的(延时在50~200ns之间),但是效果不尽人意,整形的波形不够干净。只能用来做测频用,测相会产生很大的误差。因为测相是直接对异或的方波进行计数,如果波形不好,计数也不会准确。附一张图:在这里插入图片描述
紫色是信号源产生的标准方波,下面是经过整形电路后的波形,测相要不得。
这里推荐使用高速电压比较器TLV3501(单通道)淘宝几块钱就有。也可以直接使用双通道TLV3502(淘宝十多块钱)。这是淘宝上比较便宜的价钱,可根据自己的心情选择购买。在这里插入图片描述
在这里插入图片描述
如果不想自己在做整形电路的话可以直接购买模块,淘宝上有单通道/双通道的TLV3501/3502模块。推荐一个单通道的,当时买来测试用的,单通道。每个35块,两块加起来还比双通道的便宜。在这里插入图片描述
注意一定要使用SMA线,不管你自己做电路还是买模块。即使是低频状态下,信号源输出到整形电路这一段距离都会受到比较大的干扰,突出的表现是频率测量准确,相位误差很大。购买时要注意如果SMA头是针头则要配合SMA内孔线。在这里插入图片描述
想自己做整形电路的朋友可以参考这张原理图(单通道),这是购买模块的商家的原理图。
在这里插入图片描述
打开FPGA工程
在这里插入图片描述
顶层模块是一些端口的声明,和几个模块的例化。

module Fre_measure(
                    input         clk,
						  input         rst_n,
						  input         clk_measure,     //频率测量输入引脚
						  input         Phase_Measure,   //相位测量输入引脚
						  input         uart_rx,					 
						  output        uart_tx
						);

wire[31:0]                        fre_cnt;			
wire[31:0]                        xiang_wei;
//parameter define
parameter  CLK_FREQ = 50000000;       //定义系统时钟频率
parameter  UART_BPS = 115200;         //定义串口波特率
wire pulse;
wire [31:0]pulse_high_reg,  pulse_low_reg;  //脉宽高低位寄存器

    Pulse_width_messure Pulse_width_messure//例化脉宽测量模块
(    
     .clk                              (clk),
	  .rst_n                            (rst_n),
	  .pulse_in                         (Phase_Measure),    //相位测量
	 
	  .pulse_high_reg                   (pulse_high_reg),//高电平宽度寄存器
	  .pulse_low_reg                    (pulse_low_reg)//低电平宽度寄存器
);

Time_1s time_1s( 
                  .clk                (clk             ),
						.rst_n              (rst_n           ),
						.flag_1s            (flag_1s         ),
						.xiang_wei          (xiang_wei       )
                );

dff1 dff_1(
                  .clk_measure        (clk_measure     ),
						.flag_1s            (flag_1s         ),
						.clk_match          (clk_match       )
			 );
			 
Fre_cnt fre_cnt1(
                  .clk_measure        (clk_measure     ),
						.rst_n              (rst_n           ),
						.clk_match          (clk_match       ),
						.fre_cnt            (fre_cnt         )
					  );

uart_send #(                          //串口发送模块
    .CLK_FREQ       (CLK_FREQ),       //设置系统时钟频率
    .UART_BPS       (UART_BPS))       //设置串口发送波特率
u_uart_send(                 
    .sys_clk        (clk),
    .sys_rst_n      (rst_n),
	 .pulse_low_reg  (pulse_low_reg),
	 .pulse_high_reg (pulse_high_reg),
    .uart_din_a     (fre_cnt),   //把接收到的数据放到发送模块再发送给PC
    .uart_txd       (uart_tx)
    );	
这是FPGA通用端口模拟串口发送测量得到的频率,占空比(相位测量用),还有一些模块由于篇幅有限没有贴出。可下载完整工程查看。

//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程          
always @(posedge sys_clk or negedge sys_rst_n) 
begin         
    if (!sys_rst_n) 
	 begin                                  
        tx_flag <= 1'b0;
        tx_data <= 8'd0;
		  state   <= 1'b0;
    end 
    else if (en_flag) 
	 begin                 //检测到发送使能上升沿   
             tx_flag <= 1'b1;  	 
              if(state < 13)	 	state <=  state + 4'd1;	
				else  
				  state <= 4'd1;    //进入发送过程,标志位tx_flag拉高    
				  if(state==4'd1)   tx_data <= check_fre[7:0];   //先发送一个校验数据帧
				  if(state==4'd2)   tx_data <= pulse_high_reg[7:0];     //寄存待发送的数据
			     if(state==4'd3)   tx_data <= pulse_high_reg[15:8]; 
				  if(state==4'd4)   tx_data <= pulse_high_reg[23:16]; 
				  if(state==4'd5)   tx_data <= pulse_high_reg[31:24]; 
				  
				  if(state==4'd6)   tx_data <= pulse_low_reg[7:0];     //寄存待发送的数据
			     if(state==4'd7)   tx_data <= pulse_low_reg[15:8]; 
				  if(state==4'd8)   tx_data <= pulse_low_reg[23:16]; 
				  if(state==4'd9)   tx_data <= pulse_low_reg[31:24]; 
				  
				  if(state==4'd10)   tx_data <= uart_din_a[7:0]; 
				  if(state==4'd11)   tx_data <= uart_din_a[15:8]; 
				  if(state==4'd12)   tx_data <= uart_din_a[23:16]; 
				  if(state==4'd13)   tx_data <= uart_din_a[31:24]; 
                 
        end
        else 
        if ((tx_cnt == 6'd9)&&(clk_cnt == BPS_CNT/2))
        begin                               //计数到停止位中间时,停止发送过程
            tx_flag <= 1'b0;                //发送过程结束,标志位tx_flag拉低
            tx_data <= 8'd0;
        end
        else 
		  begin
            tx_flag <= tx_flag;
            tx_data <= tx_data;
        end 
end

//进入发送过程后,启动系统时钟计数器与发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) 
	 begin                             
        clk_cnt <= 16'd0;                                  
        tx_cnt  <= 6'd0;
    end                                                      
    else if (tx_flag) 
	 begin                 //处于发送过程434-1=433
        if (clk_cnt < BPS_CNT - 1) 
		  begin
            clk_cnt <= clk_cnt + 1'b1;
            tx_cnt  <= tx_cnt;
        end
        else                                //系统时钟计数超出一个波特率
		  begin
            clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
            tx_cnt  <= tx_cnt + 1'b1;       //此时发送数据计数器加1
        end
    end
    else                                //如果发送标志位拉低
	 begin                              //发送过程结束
        clk_cnt <= 16'd0;
        tx_cnt  <= 6'd0;
    end
end

//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin        
    if (!sys_rst_n)  
	 uart_txd <= 1'b1;    //复位就是高电平,表示不传输数据时uart_txd为高电平
    else if (tx_flag)
        case(tx_cnt)                      //每个波特率时间内就传输一个数据,每位数据传输的时间是一个波特率
            6'd0: uart_txd <= 1'b0;         //起始位 
            6'd1: uart_txd <= tx_data[0];   //数据位最低位
            6'd2: uart_txd <= tx_data[1];
            6'd3: uart_txd <= tx_data[2];
            6'd4: uart_txd <= tx_data[3];
            6'd5: uart_txd <= tx_data[4];
            6'd6: uart_txd <= tx_data[5];
            6'd7: uart_txd <= tx_data[6];
            6'd8: uart_txd <= tx_data[7];   //数据位最高位
            6'd9: uart_txd <= 1'b1;         //停止位
            default: ;
        endcase
    else 
        uart_txd <= 1'b1;                   //空闲时发送端口为高电平
end

endmodule	          

打开单片机的工程
在这里插入图片描述

这里是单片机部分,串口接收FPGA送出的相关数据。FPGA先发送0XFF作为一个起始标志,当单片机接收到0Xff后开始接收后面的数据并放到寄存器中,当接收完毕后将接收到的数据进行移位。
void USART2_IRQHandler(void)
{
	 if(USART_GetITStatus(USART2,USART_IT_RXNE))
    {
	   res= USART_ReceiveData(USART2); 
	  
	  	  if(check_fre!=0xff)
	   check_fre = res;  
		  
	  if(check_fre==0xff)
	  {
		  if(time_uart==13)
		  time_uart=0;
		  
	      time_uart++;
		  
 		  if(time_uart==13) 
	      check_fre =0x00;
	  }  
	     USART_ClearFlag(USART2,USART_FLAG_TC); 	
  }
}
这里是单片机接收到数据后的处理过程。time_uart从2开始计算是因为第一个是起始标志0XFF。

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "usart2.h"
#include "Dtriggers.h"

u32 Freq,pulse_high_reg, pulse_low_reg;
float Duty_Cycle,Phase;

__align(4) u8 dtbuf[50];   								//打印缓存器  __align(4)


int main(void)
{
    u8 temp1,temp2,temp3,temp4;
    u8 pulse_high_reg_8, pulse_high_reg_16,pulse_high_reg_24, pulse_high_reg_32;
    u8 pulse_low_reg_8,  pulse_low_reg_16, pulse_low_reg_24, pulse_low_reg_32;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    delay_init();	    	 //延时函数初始化
    usart2_init(115200);
	Dtriggers_Init();
    OLED_Init();			//初始化OLED
    OLED_Clear();

    while(1)
    {

        if(time_uart==10)  temp1 = res;
        if(time_uart==11)  temp2 = res;
        if(time_uart==12)  temp3 = res;
        if(time_uart==13)
        {
            Freq = 0;
            temp4 = res;
            Freq = (temp4<<24)|(temp3<<16)|(temp2<<8)|temp1;
            time_uart = 0 ;
        }

        if(time_uart==2)  pulse_high_reg_8  = res;
        if(time_uart==3)  pulse_high_reg_16 = res;
        if(time_uart==4)  pulse_high_reg_24 = res;
        if(time_uart==5)
        {
            pulse_high_reg_32  = res;
            pulse_high_reg = 0;
            pulse_high_reg = (pulse_high_reg_32<<24)|(pulse_high_reg_24<<16)|(pulse_high_reg_16<<8)|pulse_high_reg_8;
        }

        if(time_uart==6)  pulse_low_reg_8  = res;
        if(time_uart==7)  pulse_low_reg_16 = res;
        if(time_uart==8)  pulse_low_reg_24   = res;
        if(time_uart==9)
        {
            pulse_low_reg_32  = res;
            pulse_low_reg = 0;
            pulse_low_reg = (pulse_low_reg_32<<24)|(pulse_low_reg_24<<16)|(pulse_low_reg_16<<8)|pulse_low_reg_8;
            Duty_Cycle = ((float)pulse_high_reg/((float)pulse_low_reg + (float)pulse_high_reg))*100;
            Phase = ((float)pulse_high_reg/((float)pulse_low_reg + (float)pulse_high_reg))*180;
        }
//        OLED_ShowString(0,0,"Frequency : ",16);
        OLED_ShowNum(0,4,Freq,13,16);
        OLED_ShowString(112,4,"Hz",16);

//        sprintf((char *)dtbuf,"Duty :  %0.1f",Duty_Cycle);
//        OLED_ShowString(0,4,dtbuf,16);
//		OLED_ShowString(112,4,"%",16);

//D触发器用于检测同频方波的超前滞后关系
//超前输出高电平,滞后输出低电平
		if(FLAG==1)
			sprintf((char *)dtbuf,"Phase:  %0.1f",Phase);
		else if(FLAG==0)
			sprintf((char *)dtbuf,"Phase:  %0.1f",360.-Phase);
        OLED_ShowString(0,6,dtbuf,16);
		OLED_ShowString(112,6,"*",16);

    }
}


完整的工程在我的下载页面有。