利用FIR滤波器设计低通滤波器
0 系统功能
假设输入信号为cos(2*pi*f1*t)+cos(2*pi*f2*t) 。其中f1=5MHz,f2=15MHz。
然后利用FIR滤波器滤掉f2频率分量。
1 FIR IP 核
1.1 参数设置
在IP Catalog中打开FIR Compiler,主界面如下。
配置界面一:
1、IP核支持两种FIR系数输入方式,以“Vector”的形式直接写入;或者以“COE File”的形式导入coe文件;
2、2处默认为1,表示有几路滤波通道,此处选择 1 路;
3、3处的数据是61,无法更改,表示的是从刚刚加载的 .coe 文件中读取到 61个滤波器系数;
4、Xilinx的FIR IP核支持多种滤波器结构,可以在“Filter Type”设置,本文选择传统的“Single Rate”结构。该IP核同样也支持系数重载。
除了单速率(Single Rate,即数据输出与输入速率相同)外,FIR Compiler还支持抽取(Decimation)和插值(Interpolation)应用于多速率信号处理系统。此外还支持希尔伯特变换(Hilbert)模式,可以在“Filter Type”中设置。
配置界面二:
IP核支持多通道数据输入,可以在Channel Specification这个Tab中设置输入数据的通道数。还可以在Hardware Oversampling Specification中设置过采样模式,即输入数据的频率可以高出FIR系统时钟的频率。
该页面还需要配置系统时钟和数据采样时钟,在上一讲中设置 matlab 滤波器参数时是125MHz采样频率,所以此处选择输入采样时钟为125MHz,为了方便起见,此处设置系统时钟为125MHz
配置界面三:
设置FIR系数的类型、量化方式、量化位宽(此值应该与MATLAB中的设置一致,否则频率响应是错的)和结构。本文由于是设计线性相位FIR,滤波器系数是对称的,因此选择为“Symmetric”,也可以选择为“Inferred”,软件会自动判断系数的结构。
其他配置界面保持默认。
1.2 例化模板
lowpass_fir your_instance_name (
.aclk(aclk), // input wire aclk
.s_axis_data_tvalid(s_axis_data_tvalid), // input wire s_axis_data_tvalid
.s_axis_data_tready(s_axis_data_tready), // output wire s_axis_data_tready
.s_axis_data_tdata(s_axis_data_tdata), // input wire [15 : 0] s_axis_data_tdata
.m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tdata(m_axis_data_tdata) // output wire [31 : 0] m_axis_data_tdata
);
参数解释说明:
注意点:
输入的数据是16位,输出的数据却为32位。但是工程要其求输出数据的位宽为16位,所以我们保留输出数据的高16位。
2 功能实现
2.1 MATLAB实现
2.1.1 程序
close all;
clear all;
clc;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 参数定义
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ADC_bit=16; % 采样位数
N=1024; % 采样点数
yinzi=10; % 缩减因子,避免出现 D 必须为小于 flintmax 的非负整数。
c=3e8; % 光速
f1=5e6; % 信号频率 5M
f2=15e6;
fs=125e6; % 采样频率
t=0:1/fs:(N-1)/fs;
st=cos(2*pi*f1*t)+cos(2*pi*f2*t); % 空间窄带远场平面波到达原点的解析信号
% ----幅度谱
P_st=fftshift(fft(st));
P_st=abs(P_st)/N;
f=(0:N-1)*fs/N-fs/2;
figure(1)
subplot(211);
plot(t,st);hold on;
subplot(212);
plot(f/1e6,P_st);grid on; % 在f和fs-f出现频点
title('FFT变换');
xlabel('频率/MHz');ylabel('幅度');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 导出波形数据
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 阵元1
fid=fopen('data_in.coe','w'); % 实部
[u_x1_real,x1_real]= coe_generate(fid,ADC_bit,N,yinzi,real(st) );
fprintf('x1_real信号的幅度值为%g\n',u_x1_real);
figure(2)
% plot(t,st);hold on;
plot(t,x1_real);hold on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 低通滤波器(10M以下的通过)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% LOW_PASS_FILTER Returns a discrete-time filter object.
% MATLAB Code
% Generated by MATLAB(R) 9.5 and Signal Processing Toolbox 8.1.
% Generated on: 26-Nov-2021 15:34:34
% Equiripple Lowpass filter designed using the FIRPM function.
% All frequency values are in MHz.
Fs = 125; % Sampling Frequency
N = 60; % Order
Fpass = 5; % Passband Frequency
Fstop = 10; % Stopband Frequency
Wpass = 1; % Passband Weight
Wstop = 1; % Stopband Weight
dens = 20; % Density Factor
% Calculate the coefficients using the FIRPM function.
b = firpm(N, [0 Fpass Fstop Fs/2]/(Fs/2), [1 1 0 0], [Wpass Wstop], ...
{dens});
y=conv(b,st);
L_filter=length(b);
delay_filter1=(L_filter-1)/2;
y1=y(delay_filter1+1:end-delay_filter1);
% ----幅度谱
P_y1=fftshift(fft(y1));
P_y1=abs(P_y1)/N;
figure(3)
subplot(211);
plot(t,y1);hold on;
subplot(212);
plot(f/1e6,P_y1);grid on;
xlabel('频率/MHz');ylabel('幅度');
子函数
function [u_yt,yt] = coe_generate(fid,ADC_bit,L,yinzi,st)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MATLAB生成coe文件
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 在.coe文件中
% 第一行为定义数据格式, 2代表 ROM 的数据格式为二进制。
% 从第 3 行开始到第最后一行,是这个 L(数据长度为1024)* ADC_bit(16bit) 大小 ROM 的初始化数据。
% 第一行到倒数第二行的数字后面用逗号,最后一行数字结束用分号。
% y=st/max(abs(st)); % 归一化
% 千万不能归一化
y=st/yinzi;
yt=round(y*(2^(ADC_bit-1)-1)); % 12bit量化 round()表示取整
YT_FFT=fft(yt,L); % 傅里叶变换
P_YT=abs(YT_FFT)/L; % 幅度谱
u_yt=max(P_YT); % 得到波束1接收到的信号的幅度值
% fid=fopen('data0.coe','w');
fprintf(fid,'Memory_Initialization_Radix = 2;\r\n'); % 二进制
fprintf(fid,'Memory_Initialization_Vector = \r\n');
for p=1:L
B_s=dec2bin(yt(p)+(yt(p)<0)*2^ADC_bit,ADC_bit);
for q=1:ADC_bit % 12位,依次判断这12位的数值
if B_s(q)=='1'
data=1;
else
data=0;
end
fprintf(fid,'%d',data);
end
% 下面if语句的目的
% 每行数字后面用逗号(,),最后一行数字结束用分号(;)
if (p<L)
fprintf(fid,',\r\n');
else
fprintf(fid,';\r\n'); % 分号(;) 结束标志位
end
end
fclose(fid);
end
2.1.2 仿真结果
2.2 FPGA实现
2.2.1 程序结构
2.2.2 FIR IP的系数生成
我们利用MATLAB来生成滤波器的系数,也就是利用MATLAB来设计一个低通滤波器。
在输入框输入fdatool,按照以下参数进行设计
然后导出为函数,如下所示:
function Hd = low_pass_filter
%LOW_PASS_FILTER Returns a discrete-time filter object.
% MATLAB Code
% Generated by MATLAB(R) 9.5 and Signal Processing Toolbox 8.1.
% Generated on: 26-Nov-2021 17:23:51
% Equiripple Lowpass filter designed using the FIRPM function.
% All frequency values are in MHz.
Fs = 125; % Sampling Frequency
N = 60; % Order
Fpass = 5; % Passband Frequency
Fstop = 10; % Stopband Frequency
Wpass = 1; % Passband Weight
Wstop = 1; % Stopband Weight
dens = 20; % Density Factor
% Calculate the coefficients using the FIRPM function.
b = firpm(N, [0 Fpass Fstop Fs/2]/(Fs/2), [1 1 0 0], [Wpass Wstop], ...
{dens});
Hd = dfilt.dffir(b);
% [EOF]
生成FIR滤波器的系数的程序
close all;
clear all;
clc;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 参数定义
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Quantify_bit=16; % 希尔伯特滤波器量化位数 16位
fs=65e6; % 采样频率
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 低通滤波器(10M以下的通过)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%LOW_PASS_FILTER Returns a discrete-time filter object.
% MATLAB Code
% Generated by MATLAB(R) 9.5 and Signal Processing Toolbox 8.1.
% Generated on: 26-Nov-2021 15:34:34
% Equiripple Lowpass filter designed using the FIRPM function.
% All frequency values are in MHz.
Fs = 125; % Sampling Frequency
N = 60; % Order
Fpass = 5; % Passband Frequency
Fstop = 10; % Stopband Frequency
Wpass = 1; % Passband Weight
Wstop = 1; % Stopband Weight
dens = 20; % Density Factor
% Calculate the coefficients using the FIRPM function.
b = firpm(N, [0 Fpass Fstop Fs/2]/(Fs/2), [1 1 0 0], [Wpass Wstop], ...
{dens});
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MATLAB生成coe文件
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 在.coe文件中
% 第一行为定义数据格式, 2代表 ROM 的数据格式为二进制。
% 从第 3 行开始yong到第最后一行,是这个 L(数据长度为1024)* ADC_bit(16bit) 大小 ROM 的初始化数据。
% 第一行到倒数第二行的数字后面用逗号,最后一行数字结束用分号。
yt=round(b*(2^(Quantify_bit-1)-1)); % 12bit量化
fid=fopen('lowpass_filter.coe','w'); % w表示write
fprintf(fid,'Radix = 10;\r\n'); % 二进制
fprintf(fid,'coefdata = \r\n');
L=length(b);
for p=1:L
fprintf(fid,'%d',yt(p)); % 写入数据
% 下面if语句的目的
% 每行数字后面用逗号(,),最后一行数字结束用分号(;)
if (p<L)
fprintf(fid,' \r\n');
else
fprintf(fid,';\r\n'); % 分号(;) 结束标志位
end
end
fclose(fid);
figure(1)
plot(b);
figure(2)
plot(yt);
figure(3)
plot(yt./b );
2.2.3 verilog程序
(1)top模块
`timescale 1ns / 1ps
// 程序功能:
//假设输入信号为cos(2*pi*f1*t)+cos(2*pi*f2*t) 。其中f1=5MHz,f2=15MHz。
//然后利用FIR滤波器滤掉f2频率分量。
module top(
input clk ,
input rst_n ,
output [31:0] data_out ,
output [15:0] data_out_16bit
);
//----- 1、产生长度为1024的正弦波
//--- cos(2*pi*f1*t)+cos(2*pi*f2*t) .f1=5MHz,f2=15MHz
wire [15:0] rom_data; //ROM读出数据 每个数据有16bit
reg [9:0] rom_addr; //ROM输入地址 1024个数据,需要2^10个地址
//产生ROM地址读取数据
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
rom_addr <= 10'd0;
else
rom_addr <= rom_addr+1'b1;
end
//实例化ROM
rom_data_in rom_data_in_inst
(
.clka (clk ), //inoput clka
.addra (rom_addr ), //input [9:0] addra 1024个数据,需要2^10个地址
.douta (rom_data ) //output [15:0] douta
);
//---------2、低通滤波器 (滤掉f2,保留f1)
v_fir_filter v_fir_filter_inst(
// input
.clk (clk ),
.data_in (rom_data ),
//output
.data_out(data_out )
);
assign data_out_16bit=data_out[31:16];
endmodule
(2)v_fir_filter模块
`timescale 1ns / 1ps
//
// Company: 低通滤波器的设计
//
module v_fir_filter(
input clk ,
input [15:0] data_in ,
output [31:0] data_out
);
lowpass_fir lowpass_fir_inst (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(1'b1), // input wire s_axis_data_tvalid
.s_axis_data_tready(), // output wire s_axis_data_tready
.s_axis_data_tdata(data_in), // input wire [15 : 0] s_axis_data_tdata
.m_axis_data_tvalid(), // output wire m_axis_data_tvalid
.m_axis_data_tdata(data_out) // output wire [31 : 0] m_axis_data_tdata
);
endmodule
(3)testbench
`timescale 1ns / 1ps
//
module sim_mytop;
//input
reg clk;
reg rst_n;
//output
wire [31:0] data_out;
wire [15:0] data_out_16bit;
//-----------例化top模块----------//
top top_inst(
//input
.clk (clk ),
.rst_n (rst_n ),
//output
.data_out (data_out ),
.data_out_16bit (data_out_16bit )
);
initial
begin
clk=0;
rst_n=0;
#5
rst_n=1;
end
always #5 clk=~clk;//10ns 100M
endmodule