这是我的第二篇博文,接上一篇,都是为了准备电赛,结果电赛没用上,特来分享。
这篇文章使用的方法同时也适用测量占空比。
使用器材:黑金的Artix7开发板和正点原子战舰精英板,EDA工具 Vivado2017.4,Keil 5
要求
能够准确测量两路同频信号的相位差,信号的频率从1Mhz-1hz,精度在0.1°。并且能够发送给stm32用LCD屏幕显示出来。(ps:为什么不直接用FPGA直接显示结果呢,因为算法比较复杂,工程量比较大,所以偷懒就只在FPGA上做了部分工作,计算交给了stm32。)
设计方案
输入信号为两路方波信号,对两路信号进行异或操作,就能得到能够表示两路信号相位差的信号,然后我们再分别对高电平和低电平持续时间进行计数,将两个计数值发给单片机进行计算。
(ps:这是网上大部分大佬使用的方法没错,但是不知是我的理解有问题还是咋的,我在实验的时候,它只能测量得到相位差小于180度的相位差,所以我就在前人的基础上进行了修改,做了一点小处理,使精度提高到了0.01°,并且能够测量0°到360°的相位差)
特殊处理
1.因为在使用两路信号异或的方法时,当两路信号的相位差越小或者频率越高时,计数值比较小,会带来一定的误差,所以我们使用pll时钟倍频,使用200Mhz的时钟。
2.在测量时,我们可以多对几个高电平和低电平计数,这样求得的结果也更加准确。
3.在判断相位差的时候,我使用了我工程代码所特殊的地方。这个在相位差测量模块中再提。
顶层模块
相比于频率计,相位差测量的代码比较简短,工程比较小,重点还是在计算的处理上。
顶层模块包含pll模块,相位差测量模块,和串口模块。
//将输入信号异或,然后求得占空比
//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
相位差测量模块
代码中我使用了一个flag_on信号,在flag_on信号的时候我们才对异或得到的XOR_OUT信号进行测量,然后我们发现,当相位差在0-180°时,flag_on信号的下降沿到来时,XOR_OUT信号刚好是高电平,当相位差在180°-360°时,XOR_OUT信号刚好是低电平。所以,我们在测量高电平和低电平的代码加入一个状态的判断模块。将状态位置于输出信号的最高位。
注:老铁们在实际验证的过程中可能会碰到相位差得到的结果相差180度的情况,这与你使用哪路信号作为基准信号有关系,对于这个问题,你们只需要修改我stm32工程中的部分代码就可以完成了。
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
tb仿真文件
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
约束文件
最好在我的xdc文件上修改端口,因为在产生bit文件的时候你们会碰到错误。
############## 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]
stm32相位差计算函数
首先判断状态位,然后进行计算,当两路信号相位差相差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;
}
//
完整工程我会放到百度云中,供大家一起学习交流:
链接:https://pan.baidu.com/s/1DjcD-lp2o4HKhFlG7VmULQ
提取码:jsfe
第二次写博客,排版和语法可能还有很多错误,希望大家包容,欢迎大家批评指正!!
也可以加我q:1391783671,以后一起学习交流FPGA.
有时间再完善优化。
参考文献
FPGA测两路信号相位差(https://blog.csdn.net/lt66ds/article/details/9749183) --it66ds