概述
这篇博文记录一下博主在两年前大二时做过的一个小东西。在这个设计实例中,使用并行ADC采集脉冲信号,测量其频率、占空比、幅度、上升沿和下降沿时间,将测量结果通过串口发送至MSP430单片机,使用TFT做界面显示。另外FPGA还可以输出一路10%占空比的标准1MHz脉冲信号。
这是一些完成本文设计的参考文章:
1. ADC采集设计参考“FPGA基础设计(五):并行ADC与DAC” https://blog.csdn.net/FPGADesigner/article/details/80672231
2. 串口通信设计参考“FPGA基础设计(三):UART串口通信”https://blog.csdn.net/FPGADesigner/article/details/75201194
环境与设备
Quartus Prime 16.0
Altera FPGA开发板
AD9226信号采集板
MSP430开发板、高速比较器等其它硬件模块
系统设计
测量信号的频率、占空比、幅度都比较简单,对于FPGA而言可以轻而易举得达到很高的精度和分辨率。最难的设计应该是测量上升沿和下降沿的时间。
博主最初的构想是脉冲信号一路由ADC采集测量幅度和边沿时间,一路由高速比较器整形为FPGA I/O端口的电平标准,直接输入到FPGA中测量频率和占空比,硬件结构如下:
这也是FPGA+单片机的一种典型应用,由FPGA完成高速数据采集工作,由单片机完成一些低速的数据处理和显示工作,FPGA和单片机之间采用某种通信协议交互数据。本设计所使用的FPGA开发板端口电平为LVCMOS3.3,因此需要使用比较器将脉冲信号整形到3.3V,使FPGA可以正常识别出脉冲信号的0和1状态。
FPGA设计的RTL视图如下:
1.幅度测量:PLL产生系统所需的时钟,ADC采样频率设置为50MHz。AD9226采集到数据后,在一个固定的周期内判决信号的最大幅值(即每个周期内单独判断),达到实时更新的效果。
2.边沿时间测量:上升沿是指从信号稳态幅度的10%上升到90%所经过的时间间隔,下降沿反之。pos_tr模块根据测量的幅度,判定信号的边沿时间。
3.频率测量:根据频率的定义,由系统时钟分频出1Hz(周期1s)的信号,统计1s内输入信号上升沿的个数,即为频率,具体代码如下:
module count(
input clk,
input rst_n,
input fm_in,
output reg [31:0] fm_out
);
//1Hz分频,产生clk_1s,1s内计数值即为频率
reg [31:0]cnt;
reg clk_1s;
always @ (posedge clk or negedge rst_n)
if (!rst_n)
begin
cnt <= 32'h0;
clk_1s <= 1'b0;
end
else if (cnt == 32'd49_999_999)
begin
cnt <= 32'h0;
clk_1s <= ~clk_1s;
end
else cnt <= cnt + 1'b1;
//1S内计数
reg [31:0]fm_cnt;
always @ (posedge fm_in or negedge rst_n)
if (!rst_n) fm_cnt <= 32'd0;
else if (!clk_1s) fm_cnt <= 32'd0;
else fm_cnt <= fm_cnt + 1'b1;
//获取计数结果
always @ (negedge clk_1s or negedge rst_n)
if (!rst_n) fm_out <= 32'd0;
else fm_out <= fm_cnt;
endmodule
4.占空比测量:使用两级寄存器检测输入脉冲信号的上升沿,在两个上升沿之间分别在信号的高电平和低电平期间计数,利用这两个计数结果就可以计算得到信号的占空比。具体代码如下:
module dutycycle(
input clk,
input rst_n,
input wave_in,
output reg [31:0] pos_out, //输出高电平计数结果
output reg [31:0] neg_out //输出低电平计数结果
);
// ************************************************
// 输入脉冲上升沿检测
// ************************************************
reg syn1;
reg syn2;
always @ (posedge clk)
begin
syn1 <= wave_in;
syn2 <= syn1;
end
wire wave_pos = syn2 & (~syn1);
//wire wave_neg = (~syn2) & syn1;
// ************************************************
// 高电平低电平计数
// ************************************************
reg [31:0]cnt_pos; //高电平计数器
reg [31:0]cnt_neg; //低电平计数器
always @ (posedge clk or negedge rst_n)
if (!rst_n) begin
cnt_pos <= 32'd0;
cnt_neg <= 32'd0;
pos_out <= 32'd0;
neg_out <= 32'd0;
end
else if (wave_pos) begin
cnt_pos <= 32'd0;
cnt_neg <= 32'd0;
pos_out <= cnt_pos;
neg_out <= cnt_neg;
end
else if (syn1) cnt_pos <= cnt_pos + 1'b1;
else cnt_neg <= cnt_neg + 1'b1;
endmodule
所有的测量结果按一定的帧格式拼接,通过串口发送至MSP430单片机,在单片机中处理、显示即可。
博主当时在设计时遇到了一个问题:430无论如何都不能正确的接收到FPGA传来的数据。但是单独使用串口调试助手调试FPGA和430,结果又是正确的。费了一番功夫才发现是因为两边的通信格式不匹配:FPGA带了奇偶校验、430没有配置奇偶校验,这样MSP430在收到校验位时会错误的把它当作停止位,从而导致通信出错。而单独使用串口调试助手时又会容错这种错误。总之在通信问题上一定要注意双方协议的匹配。
输出10%占空比的标准1MHz脉冲信号很简单,利用一个计数器,在相应的计数状态改变输出端口的值即可。代码如下:
module pulse(
input clk,
input rst_n,
output reg pulse);
reg [7:0] cnt;
reg pulse_reg1;
always @ (posedge clk or negedge rst_n)
if (!rst_n) begin
cnt <= 'd0;
pulse_reg1 <= 1'b1;
end
else if (cnt == 'd18) begin
pulse_reg1 <= 1'b0;
cnt <= cnt + 1'b1;
end
else if (cnt == 'd199) begin
cnt <= 'd0;
pulse_reg1 <= 1'b1;
end
else begin
cnt <= cnt + 1'b1;
pulse_reg1 <= pulse_reg1;
end
always @ (posedge clk)
begin
pulse <= pulse_reg1;
end
endmodule
实物
搭建好的整体系统如下:
频率、占空比、幅度在实际测试中都表现不错,能达到很高的精度,但是边沿时间的测量就差强人意。因为50MHz的采样频率,在测量中时间分辨率只有20ns,由于代码设计的关系能达到的时间测量大概在80ns以上(因为要检测信号幅度的10%和90%),更不用说通常脉冲信号的边沿时间只有几ns甚至更低。
在网上查询资料后,我找到了一款型号为TDC-GP2的时间间隔测量芯片,分辨率可达50ps,可以用它来测量脉冲边沿时间。将系统设计框图修改为:
这样就可以完成边沿时间的测量,使整体系统功能更完整。