完整工程请见:/download/qq_42838291/12573047
实物图及其测试效果,由于测试时没拍太多照片。高频部分的没有,只贴上低频的。
由于这里用到的多是集成的芯片和模块,所以画的比较简单。图中的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 defineparameter 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]; endelse if ((tx_cnt == 6'd9)&&(clk_cnt == BPS_CNT/2))begin //计数到停止位中间时,停止发送过程tx_flag <= 1'b0;//发送过程结束,标志位tx_flag拉低tx_data <= 8'd0;endelse begintx_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=433if (clk_cnt < BPS_CNT - 1) beginclk_cnt <= clk_cnt + 1'b1;tx_cnt <= tx_cnt;endelse //系统时钟计数超出一个波特率beginclk_cnt <= 16'd0;//对系统时钟计数达一个波特率周期后清零tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1endendelse //如果发送标志位拉低begin//发送过程结束clk_cnt <= 16'd0;tx_cnt <= 6'd0;endend//根据发送数据计数器来给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: ;endcaseelse uart_txd <= 1'b1; //空闲时发送端口为高电平endendmodule
打开单片机的工程
这里是单片机部分,串口接收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();//初始化OLEDOLED_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);}}
完整的工程在我的下载页面有。