IIR滤波器的FPGA实现

IIR滤波器的FPGA实现

生产实习著
此篇实现的是直接1型7阶切比雪夫1型低通滤波器

[TOC]

IIR原理

IIR定义

IIR(Infinite Impulse Response)无限脉冲响应数字滤波器,也叫作递归滤波器.
由差分方程:
y(n) = \sum^M_{i=0}b_i x(n-i) - \sum^N_{i=1}a_i y(n-i)
进行Z变换:
Y(z)=\sum_{i=0}^{M} b_{i} z^{-i} X(z)-\sum_{i=1}^{N} a_{i} z^{-i} Y(z)
得到系统函数:
H(z)=\frac{Y(z)}{X(z)}=\frac{\sum_{i=0}^{M} b_{i} z^{-i}}{1+\sum_{i=1}^{N} a_{i} z^{-i}}
至于为什么叫IIR,和FIR有什么区别,大家做一下z逆变换就知道了

模拟滤波器设计

要提及到模拟滤波器设计的理由很简单,就是因为设计IIR的一般是先设计好相应的模拟滤波器,然后再利用双线性变换转换成数字滤波器,在这里我们介绍一个切比雪夫1型滤波器:
他的幅度平方函数长这个样:
G_{n}(\omega)=\left|H_{n}(j \omega)\right|=\frac{1}{\sqrt{1+\epsilon^{2} T_{n}^{2}\left(\frac{\omega}{\omega_{0}}\right)}}
\omega = \omega_0时有:
\left|H\left(\omega_{0}\right)\right|=\frac{1}{\sqrt{1+\epsilon^{2}}}
其中,T_{n}\left(\frac{\omega}{\omega_{0}}\right)是n阶切比雪夫多项式,定义为:
\begin{cases} T_{n}\left(\frac{\omega}{\omega_{0}}\right)=\cos \left(n \cdot \arccos \frac{\omega}{\omega_{0}}\right) ; 0 \leq \omega \leq \omega_{0}\\ T_{n}\left(\frac{\omega}{\omega_{0}}\right)=\cosh \left(n \cdot \operatorname{arccosh} \frac{\omega}{\omega_{0}}\right) ; \omega>\omega_{0} \end{cases}
当0<x<1时,T_{n}(x)在0和1之间变化 ;当x>1时,cos−是虚数,所以T_{n}(x)像双曲余弦一样单调地增加;∣Hn(w)∣对于0≤w/w0≤1呈现出在1和\frac1{2\epsilon^2}之间的波动;而对于w/w0〉1单调地减小。需要用三个参量来确定该滤波器:ε,w0和N。在典型的设计中,用容许的通带波纹来确定ε,而用希望的通带截止频率来确定w0。然后选择合适的阶次n,以便阻带的技术要求得到满足。

上述的 滤波器设计原理看不懂没关系,因为模拟滤波器设计理论已经非常成熟了,所以我们可以借助matlab来解决
详见:Matlab的IIR Filter Design

除此之外,常见的模拟滤波器还有巴特沃斯,切比雪夫2型,椭圆型滤波器等,此处不做详述

双线性变换

这是利用反正切的方法将s域投影到z域,理解思路有很多种,大家不妨多方查阅一下资料,这里直接给出转换公式:
H(z)=H_{a}\left.(s)\right|_{s=\frac{2}{T_{s}} \frac{1-z^{-1}}{1+z^{-1}}}

IIR系统结构

实际上从IIR的定义我们就可以直接得到直接1型:


a

我们不妨将ab调转过来,合并延时器,就可以得到直接2型:


b

不妨将系统函数定义式做一点变换,拆开每项系数:
H(z)=\frac{Y(z)}{X(z)}=\frac{\sum_{i=0}^{M} b_{i} z^{-i}}{1+\sum_{i=1}^{N} a_{i} z^{-i}}
to:
H(z)=\prod_{i=1}^{L} \frac{\gamma_{0 i}+\gamma_{1 i} z^{-1}+\gamma_{2 i} z^{-2}}{1+\beta_{1 i} z^{-1}+\beta_{2 i} z^{-2}}=A \prod_{i=1}^{L} \frac{1+\alpha_{1 i} z^{-1}+\alpha_{2 i} z^{-2}}{1+\beta_{1 i} z^{-1}+\beta_{2 i} z^{-2}}=A \prod_{i=1}^{L} H_{i}(z)
得到每一个子系统H_i(z),就可以用直接12型来表征子系统,然后再串起来,比如:

c


FPGA实现

这里实现的是直接1型7阶切比雪夫1型低通滤波器.思路很简单,直接将ab两路当成两个FIR滤波器就完事了

MATLAB 获取系数

获取系数有两种方法:
1. 利用函数cheby1,cheby2和butter等函数进行设计,建议回看我上面的matlab设计iir的帮助页面
2. 利用滤波器设计工具箱 filterDesigner
此处为了直观使用filterDesigner,设计完之后是这样的:


d

直接点Design Filter是设计出来的是直接2型的SOS(second-order sections)版也就是级联型.我们需要

  1. Edit-> Convert Structure 选Direct-Form I SOS
  2. Edit-> Convert to Single Section
  3. Ctrl +e (File->Export)导出系数

如果不慎生成了SOS的话,也可以在命令行中输入:

[Num,Den] = sos2tf(SOS,G);
进行转换,其中Num(numerator)为分子,Den(Denominator)为分母

然后对系数进行量化:

  Num = round(Num*2^9);      %最大值是2.8多,建议思考为什么是512
  Den = round(Den*2^9);

FPGA实现FIR滤波器

可以见我前面的FPGA/Verilog 设计FIR滤波器
我这里为了重定义系数和后续好改成SOS,做了一些小功夫和去除了累加器,自己写了个加法树.

module fir_7
#(parameter WIDTH=4'd12 , c0 = 12'b0 , c1 = 12'b0 , c2 = 12'b0 , c3 = 12'b0 , c4 = 12'b0 , c5=12'b0 , c6=12'b0 ,c7=12'b0 )
(
input rst_n ,
input clk   ,
input signed [WIDTH-1'b1:0] din,
output signed [2*WIDTH+2:0] dout
);
integer i=0;
integer j=0;

reg signed [WIDTH-1'b1:0] din_delay[7:0];
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        for(i=0;i<8;i =i+1)
            din_delay[i] <= 'b0;
        end
    else
        begin
        for(i=1;i<8;i =i+1)
            din_delay[i] <= din_delay[i-1];
        din_delay[0] <= din;
        end
end

reg signed [2*WIDTH-1'b1:0] din_mult[7:0];
always @ (posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        for(j=0;j<8;j =j+1)
            din_mult[j] <= 'b0;
        end
    else
        begin
        din_mult[0] <= din_delay[0]*c0;
        din_mult[1] <= din_delay[1]*c1;
        din_mult[2] <= din_delay[2]*c2;
        din_mult[3] <= din_delay[3]*c3;
        din_mult[4] <= din_delay[4]*c4;
        din_mult[5] <= din_delay[5]*c5;
        din_mult[6] <= din_delay[6]*c6;
        din_mult[7] <= din_delay[7]*c7;
        end
end

reg signed [2*WIDTH:0] din_mult1[3:0];
reg signed [2*WIDTH+1:0] din_mult2[1:0];
reg signed [2*WIDTH+2:0] din_mult3;

always @ (posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            din_mult1[0] <= 'b0;din_mult1[1] <= 'b0;din_mult1[2] <= 'b0;din_mult1[3] <= 'b0;
            din_mult2[0] <= 'b0;din_mult2[1] <= 'b0;din_mult3    <= 'b0;
        end
    else
        begin
        din_mult1[0] <= din_mult[0] + din_mult[1];
        din_mult1[1] <= din_mult[2] + din_mult[5];
        din_mult1[2] <= din_mult[3] + din_mult[6];
        din_mult1[3] <= din_mult[4] + din_mult[7];
        
        din_mult2[0] <= din_mult1[0] + din_mult1[1];
        din_mult2[1] <= din_mult1[2] + din_mult1[3];
        
        din_mult3    <= din_mult2[0] + din_mult2[1];
        end
end
assign dout = din_mult3;
        
endmodule

FPGA根据系统框图连线

这里比较简单,就给出个关键部分吧:

fir_7 
#(.WIDTH(4'd12),
 .c0(12'd1), .c1(12'd7), .c2(12'd21), .c3 (12'd36),
 .c4(12'd36), .c5(12'd21), .c6(12'd7) ,.c7(12'd1))
zero (
.rst_n(RST_N),
.clk(CLK_50M),
.din(Yin),
.dout(Yout)
);

fir_7 
#(.WIDTH(4'd12),
 .c0(12'd256) , .c1(-12'd510), .c2(12'd875), .c3 (-12'd971),
 .c4(12'd838), .c5(-12'd527), .c6(12'd231) , .c7(-12'd61))
poly (
.rst_n(RST_N),
.clk(CLK_50M),
.din(din),
.dout(Xout)
);

最终生成的框图是这样的:


e

MATLAB生成仿真数据

由于上几次的博客生成仿真数据的脚本不怎么好用,所以找了一个更好的jio本:

 %=============设置系统参数==============%
%f1=1e6;        %设置波形频率
f2=200e3;
f3=1000e3;
Fs=10e6;        %设置采样频率
L=4096;         %数据长度
N=12;           %数据位宽
%=============产生输入信号==============%
t=0:1/Fs:(1/Fs)*(L-1);
%y1=sin(2*pi*f1*t);
y2=0.5*sin(2*pi*f2*t);
y3=0.5*sin(2*pi*f3*t);
%y4=y1+y2+y3;
y4= y2+y3;
y_n=round(y4*(2^(N-2)-1));      %N比特量化;如果有n个信号相加,则设置(N-n)
%=================画图==================%
a=10;           %改变系数可以调整显示周期
stem(t,y_n);
axis([0 L/Fs/a -2^N 2^N]);      %显示
%=============写入外部文件==============%
fid=fopen('sinx.txt','w');    %把数据写入sin_data.txt文件中,如果没有就创建该文件  
for k=1:length(y_n)
    B_s=dec2bin(y_n(k)+((y_n(k))<0)*2^N,N);
    for j=1:N
        if B_s(j)=='1'
            tb=1;
        else
            tb=0;
        end
        fprintf(fid,'%d',tb);
    end
    fprintf(fid,'\r\n');
end
fprintf(fid,';');
fclose(fid);

写testbeach

这里只给出读取数据部分:

integer i;   //数组坐标
reg signed [11:0] stimulus[1:data_num];  //数组形式存储读出的数据
initial 
begin
    RST_N = 1'b1;
    #60 RST_N = 1'b0;
    #60 RST_N = 1'b1; 
    $readmemb("sinx.txt", stimulus);  //将txt文件中的数据存储在数组中
    i = 0;
    repeat(data_num*10) begin   //重复读取数组中的数据
        i = i + 1;
        din = stimulus[i%data_num]; 
        #PERIOD;         //每个时钟读取一次
    end
     $stop;
end     

仿真结果

g

后仿在1.2V 0度模型下最高运行速率180Mhz(板子最高200M)

结语

因为生产实习和电赛,最近不得不备一些这种小东西,会一直更新到7.26号,各位看官有兴趣的可以追踪一下.

想我尽早更新的方法之一

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容