专栏首页根究FPGAFIR | 手把手教你写FIR低通滤波器代码

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

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代码,两种方式中都使用了有符号数转补码的函数,该函数将放大后的波形数据转换为对应的补码:

%下面函数重新新建一个脚本文件
%需要调用了如下函数,将有符号数转换成补码
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

%产生两个正弦信号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时读取数据失败。

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个输入数据相乘

最终系统函数如下所示:

//////////////////////////////////////////////////////////////////////////////////
// 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

本文分享自微信公众号 - 根究FPGA(gh_08b5d93f8fa5),作者:叫什么好呢啊

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-06-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【安富莱二代示波器教程】第10章 示波器设计—数字信号处理

    本章节为大家讲解二代示波器中用到的FFT和FIR。单纯从应用上来说,比较省事,调用API函数即可,从学习的角度来说,需要大家花点精力。

    armfly
  • 【DSP教程】第35章 FIR有限冲击响应滤波器设计

    FIR滤波器设计到的内容比较多,本章节主要经行了总结性的介绍,以帮助没有数字信号处理基础的读者能够有个整体的认识,有了这个整体的认识之后再去查阅相关资料可以到达...

    armfly
  • 手把手教系列之FIR滤波器设计

    【导读】:前面的文章介绍了移动平均滤波器、IIR滤波器、梳状滤波器,今天来谈谈FIR滤波器的设计实现。

    逸珺
  • FPGA仿真必备——Matlab生成.mif/.txt/.coe【Testbench】【Modelsim】【Matlab】

    .mif 和 .coe 是 FPGA 设计中常用的存储文件,用于 ROM、RAM 等存储器数据的加载,常见的还用在 DDS 信号发生器和 FIR 滤波器的设计中...

    FPGA探索者
  • matlab+vivado设计数字滤波器

    两个月在做数字信号处理方面的工作,也是从一个小白刚刚起步,这两天才把fir滤波器给跑通,写文记录下。希望大家欢迎,多多支持。这篇文章写得辛苦,仅仅Word文件就...

    数字积木
  • 【STM32F407的DSP教程】第41章 FIR滤波器的群延迟(重要)

    完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547

    armfly
  • 【STM32F429的DSP教程】第41章 FIR滤波器的群延迟(重要)

    完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547

    armfly
  • 【STM32H7的DSP教程】第41章 FIR滤波器的群延迟(重要)

    完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547

    armfly
  • matlab与FPGA数字滤波器设计(5)—— Verilog 串行 FIR 滤波器

    在 FPGA 实现 FIR 滤波器时,最常用的是直接型结构,简单方便,在实现直接型结构时,可以选择串行结构/并行结构/分布式结构。

    FPGA探索者
  • matlab与FPGA数字滤波器设计(6)—— Vivado 中使用 Verilog 实现并行 FIR 滤波器/截位操作

    在 FPGA 实现 FIR 滤波器时,最常用的是直接型结构,简单方便,在实现直接型结构时,可以选择串行结构/并行结构/分布式结构。

    FPGA探索者
  • P2P接口串行FIR设计

    配置接口使用寄存器组实现,掉电丢失,因此每次使用之前需要进行配置FIR参数,配置接口时序如下所示:

    月见樽
  • matlab与FPGA数字滤波器设计(3)—— Matlab 与 Vivado 联合仿真 FIR 滤波器

    本讲使用matlab产生待滤波信号,并编写testbench进行仿真分析,在Vivado中调用FIR滤波器的IP核进行滤波测试,下一讲使用两个DDS产生待滤波的...

    FPGA探索者
  • FIR数字滤波器设计(下)

    今天给大侠带来FIR数字滤波器设计,由于篇幅较长,分三篇。今天带来第三篇,FIR数字滤波器设计,包括窗函数法设计FIR滤波器、频率采样法设计FIR滤波器以及基于...

    FPGA技术江湖
  • FIR数字滤波器设计(下)

    今天给大侠带来FIR数字滤波器设计,由于篇幅较长,分三篇。今天带来第三篇,FIR数字滤波器设计,包括窗函数法设计FIR滤波器、频率采样法设计FIR滤波器以及基于...

    FPGA技术江湖
  • 基于Matlab中FDATool工具箱的滤波器设计及相关文件的生成

    大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。...

    FPGA技术江湖
  • FIR数字滤波器设计(上)

    今天给大侠带来FIR数字滤波器设计,由于篇幅较长,分三篇。今天带来第一篇,数字滤波器介绍,包括数字滤波器概述、分类以及设计指标。话不多说,上货。

    FPGA技术江湖
  • matlab与FPGA数字滤波器设计(4)—— Vivado DDS 与 FIR IP核设计 FIR 数字滤波器系统

    本讲使用两个 DDS 产生待滤波的信号和matlab产生带滤波信号,结合 FIR 滤波器搭建一个信号产生及滤波的系统,并编写 testbench 进行仿真分析,...

    FPGA探索者
  • FIR数字滤波器设计(上)

    今天给大侠带来FIR数字滤波器设计,由于篇幅较长,分三篇。今天带来第一篇,数字滤波器介绍,包括数字滤波器概述、分类以及设计指标。话不多说,上货。

    FPGA技术江湖
  • FPGA项目开发:基于FIR滤波器的带限白噪声的设计

    大侠好,最近本媛比较忙,没有时间更新。时间太长,怕大家忘了我,今天我来了,今天由“82年的程序媛”本媛给大侠带来基于FIR滤波器的带限白噪声的设计,后续本媛还会...

    FPGA技术江湖

扫码关注云+社区

领取腾讯云代金券