設計一個FIR低通濾波器,階數為10,濾波器的通帶截止頻率是2Mhz,阻帶截止頻率是4Mhz。
Finite Impulse Response,有限衝擊響應。是數字信號處理系統中的最基本的元件。
假設FIR濾波器的階數為N,則它的濾波係數的為N+1,即抽頭數(TAP),濾波器的輸出卷積形式為:
x(x)為輸入信號,h(n)為濾波器的衝擊響應,即濾波器係數。Z變換表示為:
階數也是Z指數的值
過濾諧波的次數,階數越高,濾波效果就越好。
傅里葉變換的讓人拍案叫絕的妙處就在於,一個在時域上無論多複雜的信號,都可以分解展開成多個正弦信號的疊加,而這些多個信號在頻域上看過去,就是一系列衝擊信號的集合。更精彩的分析請看下面鏈接。
例如,下圖中的信號,這是它的時序波形
進行傅里葉變換後,這是它的頻域的信號。
如果將這兩個信號拆分開來,我們會發現,這是不同頻率的正弦波的疊加。
這樣子再往下看,我們就不難理解濾波器的原理。
假設有一個信號的波形如下圖
它的傅里葉變化如下,黑色線為原信號進行傅里葉變換後的波形,紅色寫為希望保留和濾除的頻率的分割線
在傅里葉空間上定義出紅線函數,將改函數與原信號的傅里葉變化相乘,得到如下波形
再將上圖信號做傅里葉逆變換,得到如下波形
以上就是低通濾波器。LowPass
LowPass,低通
HighPass,高通
BandPass,帶通
BandStop,反帶通
ByPass就是全通,也就是直接輸出,沒有濾波,
對於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
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]);
效果如圖
這裡效果不夠明顯,是因為我採用的階數太少,階數越大濾波效果越好。
subplot是將多個圖畫一個平面上的函數,m表示圖排成m列,n表示圖排成n列,p表示圖所在的位置
plot後加上axis([-1 1 0 1]),表示x軸和y軸的範圍。
繪製二維圖像
給x軸,y軸添加標籤。
產生一個帶有雜訊的正弦信號。
將上面生成的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的使用,不做介紹。
FPGA實現FIR濾波器