前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FIR | 手把手教你写FIR低通滤波器代码

FIR | 手把手教你写FIR低通滤波器代码

作者头像
根究FPGA
发布2021-07-20 11:24:54
2.8K1
发布2021-07-20 11:24:54
举报
文章被收录于专栏:根究FPGA根究FPGA

FIR滤波器

FIR滤波器,全称为Finite Impulse Response,即有限脉冲响应。滤波器的系统函数为:

当分母中所有ak=0时,即不存在反馈支路,输出数据仅取决于输入,这也就是Finite的由来,在上式的系统函数H(z)中,每个z代表一个延迟delay。

那么根据公式H(z)即可很清楚的了解,FIR简单理解就是对输入进行多级存储,之后对不同级别的缓存*系数b,即可实现信号序列的低通、带通、高通等操作!

FIR低通滤波器的verilog实现

上文中介绍了FIR的工作原理,本部分就手写一个FIR低通滤波器,需要准备的原始材料很简单,

1、输入波形数据

2、滤波器系数b。

获取输入波形数据:

波形生成的方式有很多很多种,起码弯路就走过不少,目前见过的生成方式有两种。第一种为时间段生成法,通过生成一个时间段内的数据,通过循环读取该时间段的数据获取生成波形;第二种方式为周期函数法,通过对0~2pi内的函数值进行读取,在本文中分享两种方式的matlab代码,两种方式中都使用了有符号数转补码的函数,该函数将放大后的波形数据转换为对应的补码:

代码语言:javascript
复制
%下面函数重新新建一个脚本文件
%需要调用了如下函数,将有符号数转换成补码
function b = signed2unsigned(a,wl);  %a为输入数据,wl为位宽
%This function covert an signed integer number into an unsinged integer
%number. a is the input vector while wl means the width of input number;
%Example: a = [-2,-1,0,1];
%signed2unsigned(a,3); THEN return [2,3,0,1]
k = 2^(wl)*(a<0);
b = k + a;
b = fix(b+0.5)
for i = 1:length(a)
   if (b(i) == 65536)
       b(i) = 0;
   else ;
   end
end
end

第一种方式,获取一个周期内的波形数据,之后进行循环读取,这种方式我是从公众号《硅农》中学到的(非常优质的公众号,欢迎大家关注!),以采样频率50Mhz,通带截止频率2Mhz,阻带截止频率4Mhz为例,其波形生成函数如下所示,接下来的操作我就不赘述了,大佬公众号里讲的很明白了:

https://mp.weixin.qq.com/s/UpSJIFBk0RBvoQEmbIvjVg

代码语言:javascript
复制
%产生两个正弦信号sin(x)和sin(8x)叠加后的信号,取64个点,将信号放大,
%转换成无符号数据,存入ROM中作为信号源
% clear all
clc
x = 0 : 2*pi/63 :2*pi;
y = sin(x)+sin(8*x);
plot(x,sin(x),'r') %红色为sin(x)函数
hold on
plot(x,sin(8*x),'g')   %绿色为sin(8x)函数
hold on
plot(x,y,'b') %蓝色为生成的混合信号
grid
hold on;
y = (y/2) * 32768;%将信号放大32768倍 2^15
b = signed2unsigned(y,16);  %有转换为补码输入
fid = fopen('sinx.txt','wt'); %将信号写入一个.txt文件中
fprintf(fid,'%16.0f\n',b);
fclose(fid);

in = sin(x)+sin(8*x);

coeff =[ 0.123642574470134
        0.0764392722067448
        0.0929760805141543
         0.106348480961932
         0.114819991146986
         0.117786188756463
         0.114819991146986
         0.106348480961932
        0.0929760805141543
        0.0764392722067448
         0.123642574470134];

out =conv(in,coeff);%卷积滤波

subplot(2,1,1);
plot(in);
xlabel('滤波前');
axis([0,100,-2,2]);
subplot(2,1,2);
plot(out);
xlabel('滤波后');
axis([0,100,-1,1]);

%下面函数重新新建一个脚本文件
%需要调用了如下函数,将有符号数转换成补码
function b = signed2unsigned(a,wl);  %a为输入数据,wl为位宽
%This function covert an signed integer number into an unsinged integer
%number. a is the input vector while wl means the width of input number;
%Example: a = [-2,-1,0,1];
%signed2unsigned(a,3); THEN return [2,3,0,1]
k = 2^(wl)*(a<0);
b = k + a;
b = fix(b+0.5)
for i = 1:length(a)
   if (b(i) == 65536)
       b(i) = 0;
   else ;
   end
end
end 

第二种方式是我采用的方式,以采样频率200Khz,通带截止频率10Khz,阻带截止频率30Khz为例,其波形生成函数如下,获取10Khz与45Khz两个波形在0~0.1s时间段内的数据,之后将前1024个点存入到RAM中(bug记录RAM中的数据要为2的N次方,在写入数据个数为400时读取数据失败。

代码语言:javascript
复制
clear;
clc;
%采样速率200kHz,所以步进值为1/20000,共1s数据
fs=1/2e5;
x=0:2*pi*fs:0.1;  %0.1s
%将10kHz与45kHz正弦波叠加
f0=10e3;
f1=45e3;
y=sin(f0.*x)+sin(f1.*x); 
y1=floor(y/2.*(2^15));  %15bit signed ADC
y2= signed2unsigned(y1,16);  %有转换为补码输入 signed 15bit-->16bit unsigned
fid=fopen('./sin_mix.txt','w');
fprintf(fid,'memory_initialization_radix=10;\r\n');
fprintf(fid,'memory_initialization_vector=\r\n');

for i=1:1024
       fprintf(fid,'%16.0f\r\n',y2(i));
%    fprintf(fid,'%4d',yn(i));    
end
fclose(fid);
plot(x,y1)

%下面函数重新新建一个脚本文件
%需要调用了如下函数,将有符号数转换成补码
function b = signed2unsigned(a,wl);  %a为输入数据,wl为位宽
%This function covert an signed integer number into an unsinged integer
%number. a is the input vector while wl means the width of input number;
%Example: a = [-2,-1,0,1];
%signed2unsigned(a,3); THEN return [2,3,0,1]
k = 2^(wl)*(a<0);
b = k + a;
b = fix(b+0.5)
for i = 1:length(a)
   if (b(i) == 65536)
       b(i) = 0;
   else ;
   end
end
end 

获取该波形文件后将其存到single Port ROM中,之后进行读取测试,观察是否可以读出数据(Waveform Style : analog ; Radix: signed decimal):

接下来的就是懒人操作了,因为FIR IP里面的很多设置开始是真的不理解,所以才从结构的角度开始考虑,反正一个“z”就是一级缓存,一个coef系数就是一个乘数,那么就根据下图FIR结构展开,在读取上述波形数据时,一共11个coe系数,10级缓存,共11个输入数据相乘

最终系统函数如下所示:

代码语言:javascript
复制
//////////////////////////////////////////////////////////////////////////////////
// Engineer: XS 
// Create Date: 2021/06/23 10:46:17
// Design Name: 根究FPGA
// Module Name: FIR_top 
//////////////////////////////////////////////////////////////////////////////////
module FIR_top2(
input clk,
input rst_n, 
output signed [15:0] douta,
output signed [15:0] dout
);
reg [9:0]addr=0;
    
always@(posedge clk or negedge rst_n)
if(~rst_n)
 addr<=9'd0;
else 
 addr<=addr+1'b1;

blk_mem_gen_0 u0 (
  .clka(clk),    // input wire clka
  .addra(addr),  // input wire [5 : 0] addra
  .douta(douta)  // output wire [15 : 0] douta
);

reg signed[15:0]z_pipeline[0:10];
parameter signed coef_0=-45,
         coef_1=19,
         coef_2=50,
         coef_3=90,
         coef_4=122,
         coef_5=135, 
         coef_6=122,
         coef_7=90,
         coef_8=50,
         coef_9=19,
        coef_10=-45;        
/*
11个coe系数与10级缓存,共11个输入数据相乘
*/
wire [24:0] buf0=z_pipeline[0]*coef_0;
wire [24:0] buf1=z_pipeline[1]*coef_1;
wire [24:0] buf2=z_pipeline[2]*coef_2;
wire [24:0] buf3=z_pipeline[3]*coef_3;
wire [24:0] buf4=z_pipeline[4]*coef_4;
wire [24:0] buf5=z_pipeline[5]*coef_5;
wire [24:0] buf6=z_pipeline[6]*coef_6;
wire [24:0] buf7=z_pipeline[7]*coef_7;
wire [24:0] buf8=z_pipeline[8]*coef_8;
wire [24:0] buf9=z_pipeline[9]*coef_9;
wire [24:0] buf10=z_pipeline[10]*coef_10; 

always@(posedge clk or negedge rst_n)
if(~rst_n) begin
  z_pipeline[0]<=0;
  z_pipeline[1]<=0;
  z_pipeline[2]<=0;
  z_pipeline[3]<=0;
  z_pipeline[4]<=0;
  z_pipeline[5]<=0;
  z_pipeline[6]<=0;
  z_pipeline[7]<=0;
  z_pipeline[8]<=0;
  z_pipeline[9]<=0;
  z_pipeline[10]<=0; 
 end 
else begin 
  z_pipeline[0]<=douta;
  z_pipeline[1]<=z_pipeline[0];
  z_pipeline[2]<=z_pipeline[1];
  z_pipeline[3]<=z_pipeline[2];
  z_pipeline[4]<=z_pipeline[3];
  z_pipeline[5]<=z_pipeline[4];
  z_pipeline[6]<=z_pipeline[5];
  z_pipeline[7]<=z_pipeline[6];
  z_pipeline[8]<=z_pipeline[7];
  z_pipeline[9]<=z_pipeline[8];
  z_pipeline[10]<=z_pipeline[9]; 
 end 
wire signed [31:0] sum=buf0+buf1+buf2+buf3+buf4+buf5+buf6+buf7+buf8+buf9+buf10;
assign  dout = sum >>> 9;
endmodule

仿真结果如下所示,如果想效果更好的话,可以增加coef系数个数,这样的话就不太适合直接结构法(本文仅是从理解原理的角度的出发):

两种方式的运行环境为vivado2017.4,若需获取工程请于后台回复:FIR01

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 根究FPGA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档