搜索
bottom↓
回复: 42

呜呜祖啦滤波器,初步设计数字信号处理,所做的一个题目,望高手指点

[复制链接]

出0入0汤圆

发表于 2010-8-7 13:54:19 | 显示全部楼层 |阅读模式
本人是FPGA的初学者,花了一个月时间来学习数字信号处理的知识,现在有一点点成效了,跟大家一起分享,对于初学者也许有帮助,但更希望高手们来指点,我想听取大家的建议进一步改善该方案。

以下是自己整理的一个文档,内容就是下面粘贴出来的那些和设计过程的一些感触而已,只是为了方便初学者,而整理的
点击此处下载 ourdev_573514.pdf(文件大小:337K) (原文件名:呜呜祖啦滤波器FPGA实现.pdf)


摘要:研究一种采用FPGA实现128阶FIR音频滤波器,在满足滤波要求的情况下,所耗资源最少;讨论窗函数的选择、滤波器的结构、系数的量化问题;重点在于如何去实现和如何去仿真验证,而不仅仅是理论讨论,涉及到MATLAB与Modelsim联合仿真验证。

1、引言
2010南非世界杯,球迷们的豪华盛宴,但遗憾的是南非球迷们在现场吹起了呜呜祖啦,这种声音不仅很刺耳,还覆盖掉了足球场上的一切声音,使得在呜呜祖啦滤波器FPGA实现.doc司迅速的推出了一款呜呜祖啦滤波器,但为什么转播的时候没有采用这款滤波器先滤除呜呜祖啦声音后再传送到电视机呢?一个很重要的原因是,这款滤波器是纯软件制作,速度无法达到直播所需的高速,而基于硬件实现的FPGA方案却能很好的满足这一要求,所以研究这种方案很既有吸引力。

2、MATLAB计算出滤波器系数
本设计采用的是有限脉冲响应滤波器(FIR),汉宁窗,高通,具体设计如下:
wp=0.17*pi; ws=0.12*pi; % 输入设计指标
deltaw=wp-ws; % 计算过渡带的宽度
N0=ceil(6.2*pi/deltaw); % 按汉宁窗窗计算滤波器长度N0
N=N0+mod(N0+1,2) % 为实现FIR类型I偶对称滤波器,应确保N为奇数
windows=(hanning(N))'; % 使用汉宁窗,并将列向量变为行向量
wc=(ws+wp)/2; % 截止频率取通阻带频率的平均值
hd=ideal_lp(pi,N)-ideal_lp(wc,N); % 建立理想高通滤波器
b=hd.*windows; % 求FIR系统函数系数
[db,mag,pha,grd,w]=freqz_m(b,1); % 求解频率特性
n=0:N-1; dw=2*pi/1000; % dw为频率分辨率,将0—2π 分成为1000份
Rp=-(min(db(wp/dw+1:501))) % 检验通带波动
As=-round(max(db(1:ws/dw+1))) % 检验最小阻带衰减

滤波器参数初步设定了之后,导入音频数据,实现滤波,进行快速傅里叶变换,观察滤波前与滤波后的频谱,试听滤波前与滤波后的音频,反复调整参数,直到达到所需效果,但有一点必须要考虑,那就是抽头系数N越大,滤波效果越好,但所耗资源越多。折合这两个因数,综合考虑,本设计决定采用125点对称抽头系数,125点既可以达到很好的效果,又可以不必消耗过多的资源,具体设计如下:
[y,fs,bits]=wavread('D:\2014.wav'); % 读入音频文件
Y=filter(b,1,y); % 实现数字滤波
t=(0:length(y)-1)/fs; % 计算数据时刻
subplot(3,2,1);plot(t,y); % 绘制原波形图
title('原信号波形图'); % 加标题
subplot(3,2,2);plot(t,Y); % 绘制滤波后波形图
title('滤波后波形图'); % 加标题
xf=fft(y); % 作傅里叶变换求原频谱
yf=fft(Y); % 作傅里叶变换求滤波后频谱
fm=3000*length(xf)/fs; % 确定绘频谱图的上限频率
f=(0:fm)*fs/length(xf); % 确定绘频谱图的频率刻度
subplot(3,2,3);plot(f,abs(xf(1:length(f)))); % 绘制原波形频谱图
title('原信号频谱图'); % 加标题
subplot(3,2,4);plot(f,abs(yf(1:length(f)))); % 绘制滤波后频谱图
title('滤波后信号频谱图'); % 加标题
subplot(3,2,5),plot(w/pi,db);
axis([0,1,-100,10]);
title('幅度频率响应');% 加标题
set(gca,'XTickMode','manual','XTick',...
    [0,ws/pi,wp/pi,1]);
set(gca,'YTickMode','manual','YTick',...
    [-100,-20,-3,0]);grid
sound(Y,fs,bits); % 播放音频
sound(y,fs,bits);
wavwrite(Y,fs,bits,'D:\4014.wav'); % 读出音频

以上的两段程序可以综合一起,做成一个high_pass_hanning.m文件。运行该程序可以得到125个具有对称性的抽头系数。(系数都是归一化了的,如-1.0023e-007、0.8550)以下是上面调用到的一个小函数:
function hd=ideal_lp(wc,N)
tao=(N-1)/2;
n=[0:(N-1)];
m=n-tao+eps;
hd=sin(wc*m)./(pi*m);
end

3、MATLAB对系数进行处理
FPGA无法对小数直接进行运算,故需对系数进行处理,转换为FPGA
能直接进行运算的正整数,负数用其补码表示,具体设计如下:
q = quantizer([24 23]); % 23代表截取小数点后面的位数,24代表转换之后的位数
b0=num2hex(q,b); % 有符号小数形式,转换为正整数形式,最高位为符号位

经这么一转换后,抽头系数就变成了正整数的形式了,以十六进制表示,位数为24位,最高位为符号位。(MATLAB的一些函数的用法,可以在命令窗口直接输入help+命令,如help num2hex,即可了解该函数的使用,MATLAB很人性化吧!)

4、Verilog HDL 实现24*8的乘法器
上面提到FPGA无法对小数直接进行运算,故转换成了正整数形式,但是我
们在进行运算的时候,时刻要警惕,我们进行运算的是小数,而不是整数,(它们的不同点就是:整数在高位可以补0,而小数则是在低位可以补0,如整数12’h010与8’h10相等,而小数12’h010与8’h01相等),其实就只是一点小小差别而已,至于运算都是一样的,这点得好好思考才行,我为这东西折腾了好几天才理解透。本设计采用的是加法器树乘法器,采用多级流水线技术,具体设计如下:
module signed_mult24_8 (
                      mul_a,
                      mul_b,
                      mul_out,
                      clk,
                      rst_n
                      );
                     
parameter   MUL_WIDTH_a  = 24;
parameter   MUL_WIDTH_b  = 8;
parameter   MUL_RESULT = 31;

input [MUL_WIDTH_a-1:0]   mul_a;
input [MUL_WIDTH_b-1:0]   mul_b;
input                   clk;
input                   rst_n;
output [MUL_RESULT-1:0]   mul_out;

reg [MUL_RESULT-1:0]   mul_out;
reg [MUL_RESULT-1:0]   mul_out_reg;
reg                    msb;
reg                    msb_reg_0;
reg                    msb_reg_1;
reg                    msb_reg_2;
reg [MUL_WIDTH_a-1:0]   mul_a_reg;
reg [MUL_WIDTH_b-1:0]   mul_b_reg;

reg [MUL_RESULT-2:0]   stored0;
reg [MUL_RESULT-2:0]   stored1;
reg [MUL_RESULT-2:0]   stored2;
reg [MUL_RESULT-2:0]   stored3;
reg [MUL_RESULT-2:0]   stored4;
reg [MUL_RESULT-2:0]   stored5;
reg [MUL_RESULT-2:0]   stored6;

reg [MUL_RESULT-2:0]   add0_0;
reg [MUL_RESULT-2:0]   add0_1;
reg [MUL_RESULT-2:0]   add0_2;
reg [MUL_RESULT-2:0]   add0_3;

reg [MUL_RESULT-2:0]   add1_0;
reg [MUL_RESULT-2:0]   add1_1;

reg [MUL_RESULT-2:0]   add2_0;

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      begin
         mul_a_reg <= 24'b0;
         mul_b_reg <= 8'b0;
      
         stored0 <= 30'b0;
         stored1 <= 30'b0;
         stored2 <= 30'b0;
         stored3 <= 30'b0;
         stored4 <= 30'b0;
         stored5 <= 30'b0;
         stored6 <= 30'b0;
      
         add0_0 <= 30'b0;
         add0_1 <= 30'b0;
         add0_2 <= 30'b0;
         add0_3 <= 30'b0;
   
         add1_0 <= 30'b0;
         add1_1 <= 30'b0;
      
         add2_0 <= 30'b0;
              
         msb       <= 1'b0;
         msb_reg_0 <= 1'b0;
         msb_reg_1 <= 1'b0;
         msb_reg_2 <= 1'b0;
         
         mul_out_reg <= 31'b0;
         mul_out     <= 31'b0;   
      
      end
   else
      begin           
         mul_a_reg <= (mul_a[23]==0)?  mul_a : {mul_a[23],~mul_a[22:0]+1'b1};
         mul_b_reg <= (mul_b[7]==0)?  mul_b : {mul_b[7],~mul_b[6:0]+1'b1};
               
         msb_reg_0 <= mul_a_reg[23] ^ mul_b_reg[7];
         msb_reg_1 <= msb_reg_0;
         msb_reg_2 <= msb_reg_1;
         msb <= msb_reg_2;
         
         stored0 <= mul_b_reg[0] ? {7'b0,mul_a_reg[22:0]}       : 30'b0;
         stored1 <= mul_b_reg[1] ? {6'b0,mul_a_reg[22:0],1'b0}  : 30'b0;
         stored2 <= mul_b_reg[2] ? {5'b0,mul_a_reg[22:0],2'b0}  : 30'b0;
         stored3 <= mul_b_reg[3] ? {4'b0,mul_a_reg[22:0],3'b0}  : 30'b0;
         stored4 <= mul_b_reg[4] ? {3'b0,mul_a_reg[22:0],4'b0}  : 30'b0;
         stored5 <= mul_b_reg[5] ? {2'b0,mul_a_reg[22:0],5'b0}  : 30'b0;
         stored6 <= mul_b_reg[6] ? {1'b0,mul_a_reg[22:0],6'b0}  : 30'b0;
               
         add0_0 <= stored0 + stored1;
         add0_1 <= stored2 + stored3;
         add0_2 <= stored4 + stored5;
         add0_3 <= stored6;
      
         add1_0 <= add0_0 + add0_1;
         add1_1 <= add0_2 + add0_3;
               
         add2_0 <= add1_0 + add1_1;

         mul_out_reg <= (add2_0==0)? 31'b0 : {msb,add2_0[29:0]};
         mul_out <= (mul_out_reg==0)? 31'b0 : (mul_out_reg[30]==0)? mul_out_reg :
                    {mul_out_reg[30],~mul_out_reg[29:0]+1'b1};

    end
end
endmodule

这里面有几个地方需要注意:1、每个二叉树后面需要使用一个流水寄存器,以至达到每个时钟周期完成一次运算。2、对于负数需把其补码还原为原码再进行运算,若运算结果为负数,需转换为补码再输出。3、对于输入的两个数,当一个为0,一个为负数时,需要特别处理,否则结果输出为-1,明显的出错了。4、符号位与数据位需要时钟同步,不然也会出错,因为符号位运算所需时钟周期较少,而数据运算所需周期较多。

5、采用改进的串行结构进行滤波器设计
串行结构所耗资源最少,只需一个乘法器和一些加法器,而速度很慢,据抽头系数个数而定;并行结构所耗资源最多,据抽头系数个数而定,但速度最快,一个时钟周期即可完成一次滤波;分布式结构,耗资源不是很多,而速度也不是很快,基于串行和并行之间,据输入数据的位数来决定。(以上说法不一定正确,但对于大多数情况都是这样的)本设计采用的是改进的串行结构(先加后乘),N=125,系数具有对称性,故只需63个时钟周期,即可完成一次滤波,耗资源很少,只需一个乘法器和62个加法器。本设计是针对音频信号进行滤波,音频信号频率很低,所以即使完成一次滤波需要63个时钟周期,但也能很好的满足要求,且能有效的减少所耗资源,具体设计如下:
module ser_fir (clk,rst_n,fir_in,fir_out );

parameter   FIR_TAP     = 125;
parameter   FIR_TAPHALF = 63;
parameter   IDATA_WIDTH = 8;
//parameter   PDATA_WIDTH = 9;
parameter   COEFF_WIDTH = 24;
parameter   OUT_WIDTH   = 31;

input       clk;
input       rst_n;
input [IDATA_WIDTH-1:0]   fir_in;
output [OUT_WIDTH-1:0]    fir_out;

reg [OUT_WIDTH-1:0]     fir_out;
reg [IDATA_WIDTH-1:0]   fir_in_reg;
reg [IDATA_WIDTH-1:0]   shift_buf[FIR_TAP-1:0];
reg [IDATA_WIDTH-1:0]   add[FIR_TAPHALF-1:0];

reg [COEFF_WIDTH-1:0]   cof[FIR_TAPHALF-1:0];
reg [COEFF_WIDTH-1:0]   cof_reg_maca;
reg [IDATA_WIDTH-1:0]   add_reg_macb;
wire [IDATA_WIDTH+COEFF_WIDTH-2:0]   result;
reg [OUT_WIDTH:0]     sum;
reg [5:0]               count;
integer                 i,j,k0,k1;   

always @ (posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      begin
         cof[0]        = 24'hffffff;
         cof[1]        = 24'hffffcd;
         cof[2]        = 24'hffff36;
         cof[3]        = 24'hfffe46;
         cof[4]        = 24'hfffd54;
         cof[5]        = 24'hfffcfe;
         cof[6]        = 24'hfffdee;
         cof[7]        = 24'h000095;
         cof[8]        = 24'h0004de;
         cof[9]        = 24'h000a03;
         cof[10]       = 24'h000e99;
         cof[11]       = 24'h0010cf;
         cof[12]       = 24'h000ef6;
         cof[13]       = 24'h000810;
         cof[14]       = 24'hfffc5d;
         cof[15]       = 24'hffed9d;

         cof[16]       = 24'hffdefc;
         cof[17]       = 24'hffd47f;
         cof[18]       = 24'hffd220;
         cof[19]       = 24'hffdaa9;
         cof[20]       = 24'hffeead;
         cof[21]       = 24'h000be4;
         cof[22]       = 24'h002d1d;
         cof[23]       = 24'h004b00;
         cof[24]       = 24'h005d7c;
         cof[25]       = 24'h005dab;
         cof[26]       = 24'h0047cb;
         cof[27]       = 24'h001cbe;
         cof[28]       = 24'hffe2b3;
         cof[29]       = 24'hffa492;
         cof[30]       = 24'hff7036;
         cof[31]       = 24'hff5398;

         cof[32]       = 24'hff5988;
         cof[33]       = 24'hff8699;
         cof[34]       = 24'hffd709;
         cof[35]       = 24'h003e4f;
         cof[36]       = 24'h00a893;
         cof[37]       = 24'h00fe1c;
         cof[38]       = 24'h01281a;
         cof[39]       = 24'h0115f7;
         cof[40]       = 24'h00c208;  
         cof[41]       = 24'h00347c;
         cof[42]       = 24'hff83b3;
         cof[43]       = 24'hfed17a;
         cof[44]       = 24'hfe456e;
         cof[45]       = 24'hfe0550;
         cof[46]       = 24'hfe2ca1;
         cof[47]       = 24'hfec52b;

         cof[48]       = 24'hffc216;
         cof[49]       = 24'h00fed6;
         cof[50]       = 24'h024291;
         cof[51]       = 24'h0347d1;
         cof[52]       = 24'h03c777;
         cof[53]       = 24'h03853a;
         cof[54]       = 24'h025b72;
         cof[55]       = 24'h004413;
         cof[56]       = 24'hfd5cef;
         cof[57]       = 24'hf9e642;
         cof[58]       = 24'hf63b5d;
         cof[59]       = 24'hf2c675;
         cof[60]       = 24'heff16e;
         cof[61]       = 24'hee161e;
         cof[62]       = 24'h6d70a3;
      end
end


always @ (posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      fir_in_reg <= 8'b0;
   else
      if ( count==6'd63 )
         fir_in_reg <= fir_in;
end

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      for ( i=0;i<=FIR_TAP-1;i=i+1 )
         shift_buf <= 8'b0;
   else
      if ( count==6'd63 )
         begin
            for ( j=0;j<FIR_TAP-1;j=j+1 )
               shift_buf[j+1] <= shift_buf[j];
            shift_buf[0] <= fir_in_reg;
         end
end

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      for ( k0=0;k0<=FIR_TAPHALF-1;k0=k0+1 )
         add[k0] <= 8'b0;            
   else
      if ( count==6'd63 )
        begin
         for( k1=0;k1<=FIR_TAPHALF-2;k1=k1+1 )
            add[k1] <= shift_buf[k1] + shift_buf[124-k1];   
         add[62] <= shift_buf[62];
        end
end

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      count <= 6'b0;
   else
      if ( count==6'd63 )
         count <= 6'b0;
      else
         count <= count + 1'b1;
end

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      begin
         cof_reg_maca <= 24'b0;
         add_reg_macb <= 8'b0;
      end
   else
      if ( count <= 6'd62 )
         begin
            cof_reg_maca <= cof[count];
            add_reg_macb <= add[count];
         end
      else if ( count == 6'd63 )
         begin
            cof_reg_maca <= 24'b0;
            add_reg_macb <= 8'b0;
         end
end

signed_mult24_8 mul_0 (
                     .mul_a(cof_reg_maca),
                     .mul_b(add_reg_macb),
                     .mul_out(result),
                     .clk(clk),
                     .rst_n(rst_n)
                     );

wire [OUT_WIDTH:0]   result_out = {result[30],result[30:0]};

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      sum <= 32'b0;
   else
      if ( count==6'b0)
         sum <= 32'b0;
      else
         sum <= sum + result_out;
end

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      fir_out <= 31'b0;
   else
      if ( count==6'b0 )
         fir_out <= sum[30:0];
end
endmodule

滤波器的核心程序简单吧!就那么百来行而已。但这里有一个需要注意的地方:在进行先加后乘的时候,由于抽头系数为奇数个,所以对第62个数据,需要特别处理,不需要与任何数相加,不然将会出错。

6、Modelsim对设计好的滤波器进行仿真验证
这里需要用Modelsim 进行仿真验证,而不用FPGA厂商提供的集成软件,Modelsim有很多优点,特别对于数字信号处理方面的仿真,我们可以通过写Testbench进行仿真。通过Testbench对记事本文件数据读入,处理后再读出到记事本文件。这样做的好处是,我们可以结合MATLAB一起进行仿真验证,首先是从MATLAB把未经滤波的音频数据读出到记事本文件,然后送给Modelsim作为数据输入源,经设计好了的滤波器滤波处理之后,把数据输出到另一个记事本文件,最后MATLAB把刚才处理过了的音频数据文件读出数据,进行快速傅里叶变换,观察其频谱,试听其音频,跟直接在MATLAB进行滤波处理的音频比较,这样便可以很清楚的看到自己设计的滤波器滤波效果与直接MATLAB滤波的效果的对比了,这时即可验证FPGA设计的正确性了,这种验证方法,对于数字信号处理非常的有效,所以极力推荐,具体设计如下:
`define auto_init
`timescale 1ns/1ns
`define INPUT_FILE "fir_in8.txt"
`define OUTPUT_FILE "fir_out31.txt"

module test_ser_fir ();

parameter   NOOFDATA = 40000;
parameter   FIR_TAP = 125;
parameter   FIR_TAPHALF = 63;
parameter   IDATA_WIDTH = 8;
//parameter   PDATA_WIDTH = 9;
parameter   COEFF_WIDTH = 24;
parameter   OUT_WIDTH = 31;

parameter   CLK_CYCLE = 20;
parameter   CLK_HCYCLE = 10;

reg   clk;
reg   rst_n;
reg [IDATA_WIDTH-1:0]   fir_in;
wire [OUT_WIDTH-1:0]   fir_out;

reg [IDATA_WIDTH-1:0]   memb [1:NOOFDATA];
reg [OUT_WIDTH-1:0]     membyte [0:NOOFDATA-1];

reg   write;
integer   handle;
reg [18:0]   count_w;
reg [5:0]    regcount;
reg [18:0]   k;
reg [18:0]   k0;

ser_fir dut ( clk,rst_n,fir_in,fir_out );

`ifdef auto_init
   initial
      begin
         $readmemb(`INPUT_FILE,memb);
         regcount = 0;
         count_w = 0;
         handle  = 0;
         clk     = 0;
         k      =0 ;
         k0     = 0;
        
         rst_n = 1'b0;
         #(10*CLK_CYCLE + CLK_HCYCLE) rst_n = 1'b1;
      end
`endif

always #CLK_HCYCLE clk = ~clk;

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      regcount <= 6'b0;
  else
     if ( regcount == 6'd63 )
        regcount <= 6'b0;
    else  
       regcount <= regcount + 1'b1;
end      

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      begin
         fir_in <= 10'b0;
         k <= 19'd1;
      end
   else
     if ( regcount==6'd63 )
      begin
         k <= k + 1'b1;        
         fir_in <= memb[k];
      end
end

always @ ( posedge clk or negedge rst_n )
begin
  if ( !rst_n )
     k0 <= 19'b0;
else
   if ( regcount==6'b0 )
     begin
        k0 <= k0 + 1'b1;
        membyte[k0] <= fir_out;
     end
end

always @ ( posedge clk or negedge rst_n )
begin
   if ( !rst_n )
      write <= 1'b0;
   else
      begin
         if ( k == NOOFDATA )
            write <= 1'b1;
      end
end

always @ ( posedge write )
begin
   handle = $fopen(`OUTPUT_FILE);
   $display("writing results to file...");
   for ( count_w=0;count_w<NOOFDATA;count_w=count_w+1 )
   begin
      $fdisplay(handle,"%d",membyte[count_w]);
      $display("%d",membyte[count_w]);
   end
   $fclose(handle);
end
endmodule

测试程序也很简单吧!非常短!但这里有几个需要注意的地方:1、记事本文件的地址是从1开始的,所以赋初值为1,而不是0;2、Testbench对记事本文件数据读取时,只能识别二进制数据,而写入记事本文件时则可写成十六进制、十进制、二进制等。MATLAB对记事本文件的操作,具体设计如下:
function var=txtfile_rt(filename,width,depth)
%       filename中的数是10进制的数,负数以补码形式表示, var为定点数
%       Created by maqingbiao
%       maqingbiao@foxmail.com
%       filename --the name of the file to be created,eg,"a.txt",string;
%       var ----the data to be writed to the file;
%       width --the word size of the data,width>=1,int;
%       depth --the number of the data to be writed,int;
fid=fopen(filename,'rt');
for i=1:depth
    var_d(i)=fscanf(fid,'%d',1);
end
fclose(fid);
q=quantizer([(width+1) width]);
var_h=dec2hex(var_d);
var=hex2num(q,var_h);
end

我发现在MATLAB把数据写入记事本的时候,不能识别二进制,所以就不能使用直接写入数据的方法,而是手工拷贝的,我要拷贝的是30秒钟的音频数据,数据总量有32万个之多,由于MATLAB显示的原因,一次不能显示完32万个,所以我分8次拷贝,具体设计如下:
q = quantizer([8 7]); % 7代表截取小数点后面的位数,8代表转换之后的位数
y=y(:,1); % y为音频数据,为二维的,左右声道,只需取其中一维处理即可
yy=num2hex(q,y); % 小数转十六进制
yyy=hex2dec(yy); %十六进制转十进制
y=yyy;
y1=y(1:40000); %取y的前4万个数据
y2=y(40001:80000);
y3=y(80001:120000);
y4=y(120001:160000);
y5=y(160001:200000);
y6=y(200001:240000);
y7=y(240001:280000);
y8=y(280001:320000);
y11=dec2bin(y1); %十进制转二进制
y12=dec2bin(y2);
y13=dec2bin(y3);
y14=dec2bin(y4);
y15=dec2bin(y5);
y16=dec2bin(y6);
y17=dec2bin(y7);
y18=dec2bin(y8);

MATLAB滤波与FPGA滤波效果对比,wuwuzula.m文件,具体设计如下:
wp=0.17*pi; ws=0.12*pi; % 输入设计指标
deltaw=wp-ws; % 计算过渡带的宽度
N0=ceil(6.2*pi/deltaw); % 按汉宁窗窗计算滤波器长度N0
N=N0+mod(N0+1,2) % 为实现FIR类型I偶对称滤波器,应确保N为奇数
windows=(hanning(N))'; % 使用汉宁窗,并将列向量变为行向量
wc=(ws+wp)/2; % 截止频率取通阻带频率的平均值
hd=ideal_lp(pi,N)-ideal_lp(wc,N); % 建立理想高通滤波器
b=hd.*windows; % 求FIR系统函数系数
[db,mag,pha,grd,w]=freqz_m(b,1); % 求解频率特性
n=0:N-1; dw=2*pi/1000; % dw为频率分辨率,将0—2π 分成为1000份
Rp=-(min(db(wp/dw+1:501))) % 检验通带波动
As=-round(max(db(1:ws/dw+1))) % 检验最小阻带衰减

[y,fs,bits]=wavread('D:\2014.wav'); % 读入音频文件
Y=filter(b,1,y); % 实现数字滤波
y11=y(:,1);
y11=y(1:40000);
y=y11;
y22=Y(:,1);
y22=y22(1:40000);
Y=y22;
YY=txtfile_rt('fir_out31.txt',30,40000);
t=(0:length(y)-1)/fs; % 计算数据时刻
xf=fft(y); % 作傅里叶变换求原频谱
yf=fft(Y); % 作傅里叶变换求MATLAB滤波后频谱
yyf=fft(YY); %作傅里叶变换求FPGA滤波后频谱
fm=3000*length(xf)/fs; % 确定绘频谱图的上限频率
f=(0:fm)*fs/length(xf); % 确定绘频谱图的频率刻度
subplot(2,2,1);plot(f,abs(xf(1:length(f)))); % 绘制原波形频谱图
title('原信号频谱图'); % 加标题
subplot(2,2,2);plot(f,abs(yf(1:length(f)))); % 绘制MATLAB滤波后频谱图
title('MATLAB滤波后信号频谱图'); % 加标题
subplot(2,2,3);plot(f,abs(yyf(1:length(f)))); % 绘制FPGA滤波后频谱图
title('FPGA滤波后信号频谱图'); % 加标题
subplot(2,2,4);plot(f,abs(yyf(1:length(f)))); % 绘制FPGA滤波后频谱图
title('FPGA滤波后信号频谱图'); % 加标题
运行该程序,得到结果如下,本设计滤波效果还可以吧!哈哈!

(原文件名:12.JPG)
以上的音频文件,是从互联网上下载一段2010世界杯视频下来,然后经音频提取软件,把这段视频中的音频提取下来,再经截断后得到的一小段含有呜呜祖啦声音的音频文件,需要注意的是音频文件需要转成.WAV格式,因为MATLAB只能识别这种格式的文件。
(设计到此结束)
参考文献:《基于Verilog HDL的数字系统应用设计》王钿、卓兴旺 编著。
         《数字信号处理的FPGA实现》第二版 刘凌 译。
         《数字信号处理实验》MATLAB版,刘舒帆、费诺、陆辉 编著

本人博客:http://www.cnblogs.com/maqingbiao/archive/2010/08/07/1794672.html

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入46汤圆

发表于 2010-8-7 13:55:53 | 显示全部楼层
不错!

出0入0汤圆

发表于 2010-8-7 13:56:07 | 显示全部楼层
虽然看不懂,但是还是顶!

出0入0汤圆

发表于 2010-8-7 15:13:46 | 显示全部楼层
人才啊。

出0入0汤圆

 楼主| 发表于 2010-8-7 15:16:57 | 显示全部楼层
其实我很菜鸟的    自己摸索了一个月才做出那么一个小小的东西来

出0入42汤圆

发表于 2010-8-7 15:40:51 | 显示全部楼层
matlab ,伤心事啊

出0入0汤圆

发表于 2010-8-7 16:04:35 | 显示全部楼层
你的sample下载连接在哪里?我想听听。另外,dsp芯片难道处理不了?

出0入0汤圆

发表于 2010-8-7 16:07:20 | 显示全部楼层
LZ人才也

出0入0汤圆

发表于 2010-8-7 16:22:49 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-7 17:04:41 | 显示全部楼层
DSP应该也能处理,延时可能稍大。FPGA应该可以在更小的延时下实现

出0入0汤圆

发表于 2010-8-7 17:11:32 | 显示全部楼层
参考  http://www.surfpoeten.de/tube/vuvuzela_filter
德文的,可以用翻译软件弄成英文来看

出0入0汤圆

发表于 2010-8-7 17:24:57 | 显示全部楼层
记号,备用!谢谢楼主!

出0入0汤圆

发表于 2010-8-7 17:55:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-7 20:49:30 | 显示全部楼层
回复【9楼】crom  
dsp应该也能处理,延时可能稍大。fpga应该可以在更小的延时下实现
-----------------------------------------------------------------------
STM32都能搞定
96kHz 24bit,如果改用IIR陷波,只需2阶即可

出0入0汤圆

发表于 2010-8-8 00:36:30 | 显示全部楼层
楼主能学以致用,学习

出0入0汤圆

发表于 2010-8-8 09:30:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-8 09:31:43 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-8 09:50:24 | 显示全部楼层
不顶不行!

出0入0汤圆

发表于 2010-8-8 11:49:59 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2010-8-8 12:42:17 | 显示全部楼层
回复【13楼】warmonkey
STM32都能搞定
96kHz 24bit,如果改用IIR陷波,只需2阶即可
-----------------------------------------------------------------------
也许STM32真的也能搞定   但应该不会有人能它做数字信号处理吧  至于多少阶能做到,这倒不是很要紧,阶数少点,过渡带宽点而已
我做这个东西  只是出于一种学习的目的  只要做一个滤波器了,再做其他滤波器,应该都不会觉得太难了的

出0入0汤圆

发表于 2010-8-15 21:32:22 | 显示全部楼层
回复【19楼】maqingbiao  
回复【13楼】warmonkey
stm32都能搞定
96khz 24bit,如果改用iir陷波,只需2阶即可
-----------------------------------------------------------------------
也许stm32真的也能搞定   但应该不会有人能它做数字信号处理吧  至于多少阶能做到,这倒不是很要紧,阶数少点,过渡带宽点而已
我做这个东西  只是出于一种学习的目的  只要做一个滤波器了,再做其他滤波器,应该都不会觉得太难了的  
-----------------------------------------------------------------------

不得不说lz还是做的很棒的,希望lz能够熟悉如何将滤波器实用化

出0入0汤圆

发表于 2010-8-16 10:32:49 | 显示全部楼层
mark 楼主你太有想法了.

出0入0汤圆

发表于 2010-8-16 10:47:52 | 显示全部楼层
从idea到realization,不错

出0入0汤圆

 楼主| 发表于 2010-8-16 12:55:04 | 显示全部楼层
呵呵  有了好想法   就要赋予行动   不然就没多大意义了

出0入0汤圆

发表于 2010-8-16 13:22:44 | 显示全部楼层
mark!!!!!

出0入0汤圆

发表于 2010-8-16 13:38:58 | 显示全部楼层
这个项目真的很有趣!

出0入0汤圆

发表于 2010-8-16 18:52:06 | 显示全部楼层
帅啊...

出0入0汤圆

发表于 2010-8-17 17:12:27 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-17 18:13:34 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-8-20 23:45:49 | 显示全部楼层
你这个延时多少呢

出0入0汤圆

 楼主| 发表于 2010-8-21 20:42:55 | 显示全部楼层
大概就是1/100Mhz*63 而已吧

出0入0汤圆

发表于 2010-8-21 21:19:42 | 显示全部楼层
有没有用自适应滤波试试

出0入0汤圆

发表于 2010-8-21 21:32:43 | 显示全部楼层
恩,很有参考价值

出0入0汤圆

发表于 2010-8-21 21:47:50 | 显示全部楼层
看楼主的图,似乎基本就是高通滤波,把低频段抹平?
这似乎不妥吧……低频段别人有用的声音也抹掉了啊……

出0入0汤圆

 楼主| 发表于 2010-8-21 23:18:17 | 显示全部楼层
回复【31楼】ggyyll8683
有没有用自适应滤波试试
-----------------------------------------------------------------------
正想尝试一下自适应滤波,但现在准备省的电子设计竞赛



回复【33楼】lileistone 三块石头
看楼主的图,似乎基本就是高通滤波,把低频段抹平?
这似乎不妥吧……低频段别人有用的声音也抹掉了啊……
-----------------------------------------------------------------------
确实是这样,人的声音有小部分已经被滤除掉,当时我也考虑到,但是音频信号属于低频,要做带阻,要做到过渡带很窄,就要阶数很高,这样耗资源很多,改善却很小,因为呜呜组啦声音几乎覆盖了0—800HZ。我用MATLAB仿真了很多次,给导师试听了效果,最后他建议我做成高通的滤波器了

出675入8汤圆

发表于 2010-11-1 20:08:40 | 显示全部楼层
楼主能否把MATLAB和FPGA代码都发上来参考下,在下目前也在接触数字信号处理的东西,很多的不解

出0入0汤圆

发表于 2010-11-1 21:09:15 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2010-11-4 22:06:48 | 显示全部楼层
回复【35楼】xml2028 收音机
楼主能否把MATLAB和FPGA代码都发上来参考下,在下目前也在接触数字信号处理的东西,很多的不解
-----------------------------------------------------------------------
点击此处下载 ourdev_595364DC3IAU.rar(文件大小:7.66M) (原文件名:ser_fir24_8.rar)

点击此处下载 ourdev_595363S5LFF2.rar(文件大小:1.06M) (原文件名:test_ser_fir24_8.rar)

出0入0汤圆

发表于 2010-12-1 11:01:56 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-28 11:47:18 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-30 23:56:03 | 显示全部楼层
在满足滤波要求的情况下,所耗资源最少;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

要达到这个目的,可以使用单bit乘法算法,省很多资源。
采用matlab是验证算法的正确性,而不是验证算法在特定硬件上是否最优。

出0入0汤圆

发表于 2016-8-4 23:12:38 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2016-11-22 18:07:02 | 显示全部楼层
强人啊                       
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 09:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表