設計要求

設計一個FIR低通濾波器,階數為10,濾波器的通帶截止頻率是2Mhz,阻帶截止頻率是4Mhz。

基礎概念

FIR

Finite Impulse Response,有限衝擊響應。是數字信號處理系統中的最基本的元件。

假設FIR濾波器的階數為N,則它的濾波係數的為N+1,即抽頭數(TAP),濾波器的輸出卷積

形式為:

x(x)為輸入信號,h(n)為濾波器的衝擊響應,即濾波器係數。Z變換表示為:

階數也是Z指數的值

過濾諧波的次數,階數越高,濾波效果就越好。

傅里葉變換

傅里葉變換的讓人拍案叫絕的妙處就在於,一個在時域上無論多複雜的信號,都可以分解展開成多個正弦信號的疊加,而這些多個信號在頻域上看過去,就是一系列衝擊信號的集合。更精彩的分析請看下面鏈接。

Heinrich:傅里葉分析之掐死教程(完整版)更新於2014.06.06?

zhuanlan.zhihu.com
圖標

例如,下圖中的信號,這是它的時序波形

進行傅里葉變換後,這是它的頻域的信號。

如果將這兩個信號拆分開來,我們會發現,這是不同頻率的正弦波的疊加。

這樣子再往下看,我們就不難理解濾波器的原理。

濾波器原理

假設有一個信號的波形如下圖

它的傅里葉變化如下,黑色線為原信號進行傅里葉變換後的波形,紅色寫為希望保留和濾除的頻率的分割線

在傅里葉空間上定義出紅線函數,將改函數與原信號的傅里葉變化相乘,得到如下波形

再將上圖信號做傅里葉逆變換,得到如下波形

以上就是低通濾波器。LowPass

LowPass,低通

HighPass,高通

BandPass,帶通

BandStop,反帶通

ByPass就是全通,也就是直接輸出,沒有濾波,

Visual Signal Online Help?

www.ancad.com.tw

MATLAB FDA工具使用

對於FIR濾波器的數字電路實現,需要藉助輔助工具,來生成FIR濾波器的濾波係數,然後和輸入信號進行卷積就可以了,而且我們可以看出離散信號的卷積就是通過乘加的方式來實現的。

藉助MATLAB FDATool生成濾波係數

打開MATLAB,在命令行窗口輸入fdatool,回車進入Filter Designer界面。

Response Type設置濾波器類型,這裡是Lowpass

Design Method這裡設置FIR Equiripple

Filter Order設置濾波器階數,10

Fs採樣頻率50Mhz

Fpass通帶截止頻率2Mhz

Fstop阻帶截止頻率4Mhz

設置好後點擊Design Filter

File——Export

點擊Export

然後在命令行窗口輸入Num,回車得到濾波器係數

其他更多使用介紹請查閱MATLAB Filter Design官網文檔。

MATLAB實現

%產生兩個正弦信號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
y = (y/2) * 32768;%將信號放大32768倍
b = signed2unsigned(y,16); %轉換為無符號數輸入
fid = fopen(sinx.txt,wt); %將信號寫入一個.txt文件中
fprintf(fid,%16.0f
,b);
fclose(fid);
?
%下面函數重新新建一個腳本文件
%需要調用了如下函數,將有符號數轉換成無符號數
function b = signed2unsigned(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

  • 紅色為sin(x)函數
  • 綠色為sin(8x)函數
  • 藍色為生成的混合信號

x = 0 : 2*pi/63 :2*pi;
in = sin(x)+sin(8*x);
?
coeff =[0.1236 0.0764 0.0930 0.1063 0.1148 0.1178 0.1148 0.1063 0.0930 0.0764 0.1236];
?
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]);

效果如圖

這裡效果不夠明顯,是因為我採用的階數太少,階數越大濾波效果越好。

MATLAB函數

subplot(m, n, p)或subplot(m n p)

subplot是將多個圖畫一個平面上的函數,m表示圖排成m列,n表示圖排成n列,p表示圖所在的位置

axis([xmin xmax ymin ymax])函數

plot後加上axis([-1 1 0 1]),表示x軸和y軸的範圍。

plot(x, y)函數

繪製二維圖像

xlabel ylabel

給x軸,y軸添加標籤。

Verilog實現

產生一個帶有雜訊的正弦信號。

將上面生成的sinx.txt文件打開

在文件最後一個數據加上分號「;」,文件開頭加以下兩句:

memory_initialization_radix=10; memory_initialization_vector=

保存文件,並將文件格式改為.coe 格式。

MATLAB也可以在輸出數據時將頭文件加上。

調用Vivado ROM IP Core(不多說)

reg [5:0] addr;
?
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 0)begin
addr<=0;
end
else begin
addr <= addr + 1;
end
end
?
blk_mem_gen_0 blk_mem_gen_0_inst (
.clka(clk), // input wire clka
.addra(addr), // input wire [5 : 0] addra
.douta(douta) // output wire [15 : 0] douta
);

這樣直接輸出的就是疊加後的正弦波形了。

接下來是FIR_LPF模塊的設計

本設計分為三級流水線完成

  • 第一級,將輸入信號延時
  • 第二級,將輸入信號與濾波係數相乘
  • 第三級,將所有信號相加

0.1236 0.0764 0.0930 0.1063 0.1148 0.1178 0.1148 0.1063 0.0930 0.0764 0.1236
?
round(Num*512)
?
63 39 48 54 59 60 59 54 48 39 63

在MATLAB腳本窗口中直接輸入round(Num*512)將所有係數,擴大512倍並取整,好用Verilog描述實現,也就是所有係數都左移了9位。

//pipeline 1
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)begin
din_r1 <= 0;
din_r2 <= 0;
din_r3 <= 0;
din_r4 <= 0;
din_r5 <= 0;
din_r6 <= 0;
din_r7 <= 0;
din_r8 <= 0;
din_r9 <= 0;
din_r10 <= 0;
end
else begin
din_r1 <= din ;
din_r2 <= din_r1;
din_r3 <= din_r2;
din_r4 <= din_r3;
din_r5 <= din_r4;
din_r6 <= din_r5;
din_r7 <= din_r6;
din_r8 <= din_r7;
din_r9 <= din_r8;
din_r10 <= din_r9;
end
end
?
//pipeline2
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data1 <= 0;
else
mul_data1 <= din * COEFF1;
end
?
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data2 <= 0;
else
mul_data2 <= din_r1 * COEFF2;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data3 <= 0;
else
mul_data3 <= din_r2 * COEFF3;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data4 <= 0;
else
mul_data4 <= din_r3 * COEFF4;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data5 <= 0;
else
mul_data5 <= din_r4 * COEFF5;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data6 <= 0;
else
mul_data6 <= din_r5 * COEFF6;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data7 <= 0;
else
mul_data7 <= din_r6 * COEFF7;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data8 <= 0;
else
mul_data9 <= din_r7 * COEFF8;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data9 <= 0;
else
mul_data9 <= din_r8 * COEFF9;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data10 <= 0;
else
mul_data10 <= din_r9 * COEFF10;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)
mul_data11 <= 0;
else
mul_data11 <= din_r10 * COEFF11;
end
?
//pipeline3
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1b0)begin
dout_r <= 0;
end
else
dout_r <= mul_data1 + mul_data2 + mul_data3 + mul_data4 + mul_data5 + mul_data6 + mul_data7 + mul_data8 + mul_data9 + mul_data10 + mul_data11;
end

最終輸出右9位即可。

assign dout = $signed(dout_r) >>> 9;

模擬波形如下,

效果不是很好,這是因為階數太小了,不信我直接調用Xilinx的FIR Compile IP Core,使用MATLAB直接生成最小濾波階數。這應該都有好幾十階了。

生成的濾波係數導出coe文件,調用FIR Compile IP Core,濾波結果輸出,

本文記錄Verilog設計一個10階FIR濾波器,關於FIR Compile IP的使用,不做介紹。

Reference

FPGA實現FIR濾波器


推薦閱讀:
相关文章