基于FPGA和STM32的相位差测量(含源码)

1. 前言

这是我的第二篇博文,接上一篇,都是为了准备电赛,结果电赛没用上,特来分享。
大家需要结合自身实际进行开发,本工程仅供参考。

这篇文章使用的方法同时也适用测量占空比(0°-360°精确到0.1°含源代码)。

环境

  • FPGA:黑金的Artix7开发板
  • STM32: 正点原子战舰精英板
  • EDA工具: Vivado2017.4 和 Keil 5

参考博客:FPGA测两路信号相位差-it66ds


2. 正文

2.1 设计要求

能够准确测量两路同频信号的相位差,信号的频率从1Mhz-1hz,精度在0.1°。并且能够发送给stm32用LCD屏幕显示出来。

(ps:为什么不直接用FPGA直接显示结果呢,因为算法比较复杂,工程量比较大,所以偷懒就只在FPGA上做了部分工作,计算交给了stm32。)


2.2 设计方案

输入信号为两路方波信号,对两路信号进行异或操作,就能得到能够表示两路信号相位差的信号,然后我们再分别对高电平和低电平持续时间进行计数,将两个计数值发给单片机进行计算。

(ps:这是网上大部分大佬使用的方法没错,但是不知是我的理解有问题还是咋的,我在实验的时候,它只能测量得到相位差小于180度的相位差,所以我就在前人的基础上进行了修改,做了一点小处理,使精度提高到了0.01°,并且能够测量0°到360°的相位差)

特殊处理

  1. 因为在使用两路信号异或的方法时,当两路信号的相位差越小或者频率越高时,计数值比较小,会带来一定的误差,所以我们使用pll时钟倍频,使用200Mhz的时钟。
  2. 在测量时,我们可以多对几个高电平和低电平计数,这样求得的结果也更加准确。
  3. 在判断相位差的时候,我使用了我工程代码所特殊的地方。这个在相位差测量模块中再提。

相位差测量方法


2.3 FPGA设计

2.3.1 顶层模块

  • 相比于频率计,相位差测量的代码比较简短,工程比较小,重点还是在计算的处理上。
  • 顶层模块包含pll模块,相位差测量模块,和串口模块。

工程结构图

RTL图如下图所示:

注:在看一个工程时,我最先会去看RTL图。这样能够最快最方便的了解整个设计。
原理图


设计代码:

//将输入信号异或,然后求得占空比
//state=1 说明相位差在180以内,state=0 说明相位差在180-360度,算法不同
//输出信号为64位,第一位为状态state 63-32为高电平计数值,32-1为低电平计数值
//时钟200Mhz
//编辑xdc文件可以修改与pc还是stm32通信
//测量的频率范围是1hz-1Mhz最高待测
//相位精度0.01度

module phase_top(
    input clk,
    input rst_n,
    input in_signal1,
    input in_signal2,
    input uart_rx,
    //output XOR_OUT, //tb调试使用
    //output [63:0]out_date_wire,
    //output clk_wire,
    //output flag_on_wire,
    //output state_wire,
    output uart_tx
    );

    wire clk_200mhz;
    wire [63:0] out_date;
    //assign out_date_wire=out_date;  //tb调试用
    //assign clk_wire=clk_200mhz;

 	  
    //调用pll产生200mHz时钟
    pll_test pll_200mHz(
        .sys_clk(clk),              //system clock 50Mhz on board
        .rst_n(rst_n),              //reset ,low active
        .clk_out(clk_200mhz)  
    );


    Phase_measure #(
        .times(8'd3) //设置计数周期,这里设置的是对三个信号周期进行计数
    )phase_measure(    
        .clk(clk_200mhz),
        .rst_n(rst_n),
	    .in_signal1(in_signal1),
	    .in_signal2(in_signal2),
	    //.XOR_OUT(XOR_OUT), //tb调试用
        //.flag_on_wire(flag_on_wire),
        //.state_wire(state_wire),
        .out_date(out_date)  //高位为高电平计数值,低位为低电平计数值   out_date={Pon+Poff}
    );


    uart_phase uart_phase1(
        .clk(clk),
        .rst_n(rst_n),
        .uart_rx(uart_rx),
        .phase_cnt(out_date), 
        .uart_tx(uart_tx)
    );


endmodule

2.3.2 相位差测量模块

  • 代码中我使用了一个flag_on信号,在flag_on信号的时候我们才对异或得到的XOR_OUT信号进行测量,然后我们发现,当相位差在0-180°时,flag_on信号的下降沿到来时,XOR_OUT信号刚好是高电平,当相位差在180°-360°时,XOR_OUT信号刚好是低电平。

  • 所以,我们在测量高电平和低电平的代码加入一个状态的判断模块。将状态位置于输出信号的最高位。

注:老铁们在实际验证的过程中可能会碰到相位差得到的结果相差180度的情况,这与你使用哪路信号作为基准信号有关系,对于这个问题,你们只需要修改我stm32工程中的部分代码就可以完成了。

STM32端代码:
stm32代码
FPGA端代码:
相位差范围的判断


设计代码:

module	Phase_measure#(
    parameter times = 8'd3
    )(
    input clk,
    input rst_n,
	input in_signal1,
	input in_signal2,
	//output  XOR_OUT, //tb调试用
    //output flag_on_wire,
    //output state_wire,
    output [63:0]out_date
    );
        
 //异或XOR信号
 wire  XOR_OUT0;
 assign	XOR_OUT0 = in_signal1^in_signal2;       //新状态


 //assign  XOR_OUT = XOR_OUT0;  //tb调试用
 //assign flag_on_wire=flag_on;
 //assign state_wire=state;



 //计算高低电平宽度
 reg[31:0]	    Pon_reg,Poff_reg;
 reg[31:0]      Pon,Poff;          //用于储存pon_reg,poff_reg结果
 reg[7:0]       on_cnt;             //用来对XOR_OUT0高电平计数,设置times可以增加计数值
 reg            flag_on=1'b0;       //flag_on=1时对XOR_OUT0计数
 reg            state;              //state=1,前180度,state=0,后180度

 


 //计数times个信号源的时间  不好修改,提高高频测量精度可以修改times的值
 always @(posedge in_signal1)
    begin
        if(! rst_n)
            begin
                on_cnt<=8'd0;
            end
        else if(on_cnt<=times-2)                    //times个信号1次
                on_cnt <= on_cnt+1'b1;
        else
            begin
                on_cnt<=8'd0;
                flag_on<=~flag_on;
            end        
    end

 //判断相位差范围
 always @(negedge flag_on)
    begin
        if(XOR_OUT0)
            state <=1'b1;           //状态1相位差在180内
        else
            state <=1'b0;           //状态0相位差在180外
    end


 always @ (posedge clk)
 begin
	if(flag_on)
        begin
            if(XOR_OUT0)                    //高电平计数
                Pon_reg  <= Pon_reg  + 1'b1;
	        else                            //低电平计数
                Poff_reg <= Poff_reg + 1'b1;
        end
    else
        begin
		        Pon_reg  <= 32'd0;
		        Poff_reg <= 32'd0;
        end     
 end

 always @(negedge flag_on)
		begin
                Pon  <= Pon_reg;        //保存计数器值
                Poff <= Poff_reg;       //保存计数器值
		end

 assign out_date={state,Pon[30:0],Poff[31:0]};    					//将状态和计数值发送给stm32处理

endmodule		


2.3.3 Testbench

  • tb文件中我使用了一个小技巧,就是引入了一个enable信号。因为仅仅通过延时,你是做不到使两路信号的有相位差。当enable信号为1以后,我们再输入signal2信号。

(ps:你们将我在各个模块中的注释的部分取消就能验证我刚刚在相位差测量模块中说的方法了。)

源代码:

module phase_tb(
    );

    reg clk;
    reg rst_n;
    reg in_signal1;
    reg in_signal2;
    reg enable;

    wire [63:0]out_date_wire;
    //wire clk_wire;
    wire XOR_OUT;
    wire flag_on_wire;
    wire state_wire;
    wire uart_tx;


    parameter T = 2000>>1;  //周期是1000ns ,延时500ns

    phase_top phase_top1(
    .clk(clk),
    .rst_n(rst_n),
    .in_signal1(in_signal1),
    .in_signal2(in_signal2),
    .uart_rx(),
    .XOR_OUT(XOR_OUT), //tb调试使用

    .out_date_wire(out_date_wire),
    //.clk_wire(clk_wire),
    .flag_on_wire(flag_on_wire),
    .state_wire(state_wire),
    .uart_tx(uart_tx)
    );

    initial 
     begin
		// Initialize Inputs
		clk = 0;
        in_signal1=0;
        in_signal2=0;
        enable=0;
        rst_n=0;

        #5
        rst_n=1;
        #500
        enable=1;

    end

    always #5 clk=~clk;
    always @(posedge clk)
    begin
    #T in_signal1=1;
    #T in_signal1=0;
    end

    always @(posedge clk)
    begin
        if(enable==1)
        begin
        #T in_signal2=1;
        #T in_signal2=0; 
        end
        else
        in_signal2=0;
    end    
   
endmodule


2.3.4 约束

  • 最好在我的xdc文件上修改端口,因为在产生bit文件的时候你们会碰到错误。
  • 输入信号最好加一个IBUF


############## NET - IOSTANDARD ##################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
#############SPI Configurate Setting##################
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] 
set_property CONFIG_MODE SPIx4 [current_design] 
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] 

create_clock -period 20 [get_ports clk]


set_property PACKAGE_PIN Y18 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

set_property PACKAGE_PIN E17 [get_ports in_signal1]
set_property PACKAGE_PIN F16 [get_ports in_signal2]
set_property IOSTANDARD LVCMOS33 [get_ports in_signal1]
set_property IOSTANDARD LVCMOS33 [get_ports in_signal2]

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets in_signal1_IBUF]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets in_signal2_IBUF]

set_property PACKAGE_PIN F20 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]

set_property PACKAGE_PIN E14 [get_ports uart_rx]
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx]

set_property PACKAGE_PIN E13 [get_ports uart_tx]
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx]

2.4 STM32设计

3.1 相位差计算函数

  • 首先判断标志位,然后进行计算,当两路信号相位差相差0°-180°和180°到360°计算的公式是不同的,大家可以自己在纸上画一下,就能知道差别了。

double on_cnt=0;
//u8 buff_on[32];
double off_cnt=0;
//u8 buff_off[32];
int state;
double phase_res;
int int_data;
int dec_data;


void phase_do(u8 buff_fre[68])
{	
	int i;
	//根据引脚确定状态,提前试一下
	if( buff_fre[0]=='1')	state=0;		//0-180度
	if( buff_fre[0]=='0')	state=1;		//180-360度	
	for(i=1;i<64;i++)
			{
				if(i<32)
				{
				on_cnt=on_cnt 	+ ( (double)((buff_fre[i]-48) << (31-i))); //移位,得到实际的数值
//				buff_on[i-1]=buff_fre[i];//显示收到的数据
				}
				else
				{
				off_cnt=off_cnt +	( (double)((buff_fre[i]-48) << (63-i)));
//				buff_off[i-32]=buff_fre[i];//显示收到的数据
				}	
			}
	if(state)
	{
		phase_res=(on_cnt*180)/(on_cnt+off_cnt); 				// 当相位小于180度时,state=1,phase=on*180/(on+off)
	}
	else
	{
		phase_res=(off_cnt*180)/(on_cnt+off_cnt) + 180;		// 当相位大于180度时,state=0,phase=off*180/(on+off) +180
	}
	phase_res= phase_res+0.005; //四舍五入
	phase_res= phase_res*1000;
	int_data=phase_res/1000;
	dec_data=(phase_res-int_data*1000)/10;
}
//




3. 后言

需要源码的评论区留言,我私发!(除了周末,我几乎每天都会登录)

觉得有帮助的朋友们记得点个赞哦!


  • 16
    点赞
  • 91
    收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 8

打赏作者

Bigbee_i

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值