FIR滤波器的设计和仿真

 主要参照杜勇老师的《数字调制解调技术的MATLAB与FPGA实现》,在这里记录一下,并记录出现的问题

1、先根据系统所需要的FIR滤波器的参数在Matlab中设计出FIR的参数,保存到txt文件内。
程序如下:
  %E4_5_LpfDesign.m  
  %设计一个低通滤波器。采样频率fs=8MHz,过渡带fc=[1MHz 2MHz];
  %绘出滤波器第数量化前后的幅频响应图;将量化后的滤波器系数写入指定的txt文本文件中
  function h_pm=LpfDesign
  fs=12.5*10^6;          %采样频率
  qm=12;                 %滤波器系数量化位数
  fc=[2.5*10^6 3*10^6];  %过渡带
  mag=[1 0];             %窗函数的理想滤波器幅度
  %设置通带容限a1及阻带容限a2
  %通带衰减ap=-20*log10(1-a1)=0.915dB,阻带衰减为as=-20*log10(a2)=40dB
  a1=0.1;a2=0.01;
  dev=[a1 a2];
  %采用凯塞窗函数获取满足要求的最小滤波器阶数
  %因为凯塞窗具有可调选项,因此可以根据过渡带和波纹参数等计算出凯塞窗的β值和滤波器阶数
  %n:阶数     Wn:归一化频带边缘      beta:Kaiser窗beta参数   ftype:滤波器类型用于fir1函数
  [n,wn,beta,ftype] = kaiserord(fc,mag,dev,fs) 
 
  %采用firpm函数设计最优滤波器
  %因为用firpm函数(最优滤波器)和凯塞窗和海明窗滤波器比较,第一旁瓣电平比凯塞窗约低2.5db,且阻带波纹相同,而凯塞窗的阻带波纹却逐渐减少
  fpm=[0 fc(1)*2/fs fc(2)*2/fs 1];  %firpm函数的频段向量
  magpm=[1 1 0 0];                  %firpm函数的幅值向量 
  h_pm=firpm(n,fpm,magpm);          %设计最优滤波器
 
  %量化滤波系数
  a=abs(h_pm);
  b=max(a);
  c=h_pm/b;
  q_pm=round(h_pm/max(abs(h_pm))*(2^(qm-1)-1));
 
  %将生成的滤波器系数数据写入FPGA所需的txt文件中
  fid=fopen('E:\毕设\4、code\2、FIR\FIR滤波器_YH\MATLAB图例\lpf.txt','w');
  for k=1:length(h_pm)
       fprintf(fid,'%12.12f\r\n',h_pm);
       if(k~=length(h_pm))
          fprintf(fid,'\r\n');
       end
  end
  fclose(fid);
 
  %获取量化前后滤波器的幅频响应数据
  m_pm=20*log10(abs(fft(h_pm,1024)));   m_pm=m_pm-max(m_pm);
  q_pm=20*log10(abs(fft(q_pm,1024)));   q_pm=q_pm-max(q_pm);
 
  %设置幅频响应的横坐标单位为MHz
  x_f=[0:(fs/length(m_pm)):fs/2]/10^6;  
  %只显示正频率部分的幅频响应
  mf_pm=m_pm(1:length(x_f));
  mf_qm=q_pm(1:length(x_f));
 
  %绘制幅频响应曲线
  plot(x_f,mf_pm,'--',x_f,mf_qm,'-');
  xlabel('频率(MHz)');ylabel('幅度(dB)');
  legend('未量化','12位量化');
  grid;
FIR滤波器的设计和仿真

上边红色部分:需要注意的就是最后一个系数后面不能有回车,否则导入系数文件的时候又会被FIR Complier识别为一个新的系数。
 
2、产生测试激励信号文件
  clear all;clc;
  Fs=12500000;%设定采样频率
  f1=2500000;%信号频率 2.5MHZ
  f2=3100000;%信号频率 3.1MHZ
  N=12; %量化位数
  %t=0:0.00000008:0.000008; %0.00000008 相当于12.5MHZ采样一点 0.000008显示100点
  t=0:1/Fs:0.00016;
  s1=5*sin(2*pi*f1*t);
  s2=5*sin(2*pi*f2*t);
  s3=s1+s2;
  s=awgn(s3,15);
 
  %调用ELpfDesign函数设计的滤波器对信号进行滤波
  hn=LpfDesign;
  Filter_s=filter(hn,1,s);
 
  %求信号的幅频响应  fft(s,1024) 取s中1024个点,如果不足则零补齐,超过截断
  m_s=20*log(abs(fft(s,1024)))/log(10); m_s=m_s-max(m_s);
  %滤波后的幅频响应
  Fm_s=20*log(abs(fft(Filter_s,1024)))/log(10); Fm_s=Fm_s-max(Fm_s);
  %滤波器本身的幅频响应
  m_hn=20*log(abs(fft(hn,1024)))/log(10); m_hn=m_hn-max(m_hn);
 
  %设置幅频响应的横坐标单位为Hz
  x_f=[0:(Fs/length(m_s)):Fs/2];
  %只显示正频率部分的幅频响应
  mf_s=m_s(1:length(x_f));
  Fmf_s=Fm_s(1:length(x_f));
  Fm_hn=m_hn(1:length(x_f));
 
  subplot(2,3,1),plot(t,s1);
  axis([0 0.000008 -5 5]); %横坐标标度
  %xlabel('时间(s)','fontsize',8,'position',[0.000004, -5.2, 0]);
  xlabel('时间(s)');ylabel('幅度(v)');title('2.5MHZ正弦信号');
  grid;
  subplot(2,3,2),plot(t,s2);
  xlabel('时间(s)');ylabel('幅度(v)');title('3MHZ正弦信号');
  grid;
  subplot(2,3,3),plot(t,s);
  xlabel('时间(s)');ylabel('幅度(v)');title('2.5MHZ叠加3MHZ叠加噪声的信号');
  grid;
  %绘制幅频响应曲线
  subplot(234)
  plot(x_f,mf_s,'-.',x_f,Fmf_s,'-',x_f,Fm_hn,'--');
  xlabel('频率(Hz)');ylabel('幅度(dB)');title('Matlab仿真合成单频信号滤波前后的频谱');
  legend('输入信号频谱','输出信号频谱','滤波器响应');
  grid;
 
  %绘制滤波前后的时域波形
  subplot(235)
 
  %绘制时域波形
  %设置显示数据范围,设置横坐标单位ms
  t=0:1/Fs:80/Fs;t=t*10^6;
  t_s=s(1:length(t));
  t_filter_s=Filter_s(1:length(t));
 
  plot(t,t_s,'--',t,t_filter_s,'-');
  xlabel('时间(ms)');ylabel('幅度');title('FPGA仿真合成单频信号滤波前后的时域波形');
  legend('输入信号波形','输出信号波形');
  grid;
 
  subplot(236),plot(t,t_filter_s);
  xlabel('时间(s)');ylabel('幅度(v)');title('滤波后的信号');
  grid;
 
 
  %对仿真产生的合成单频信号进行量化处理
  s=s/max(abs(s));         %归一化处理
  Q_s=round(s*(2^(N-1)-1));%12比特量化
 
  %将生成的数据以二进制数据格式写入txt文件中
  fid=fopen('E:\毕设\4、code\2、FIR\FIR滤波器_YH\MATLAB图例\TestData0.txt','w');
  for i=1:length(Q_s)
      B_noise=dec2bin(Q_s(i)+(Q_s(i)<0)*2^N,N);
      for j=1:N
         if B_noise(j)=='1'
             tb=1;
         else
             tb=0;
         end
         fprintf(fid,'%d',tb); 
      end
      fprintf(fid,'\r\n');
  end
  fprintf(fid,';');
  fclose(fid);
  %画图
  %subplot(2,2,1),plot(t,s1);
  %axis([0 0.000008 -5 5]); %横坐标标度
  %xlabel('时间(s)','fontsize',8,'position',[0.000004, -5.2, 0]);
  %xlabel('时间(s)');ylabel('幅度(v)');title('2.5MHZ正弦信号');
  %subplot(2,2,2),plot(t,s2);
  %xlabel('时间(s)');ylabel('幅度(v)');title('3MHZ正弦信号');
  %subplot(2,2,3),plot(t,n);
  %xlabel('时间(s)');ylabel('幅度(v)');title('2.5MHZ叠加3MHZ叠加噪声的信号');
 
3、测试文件 
  `timescale 1 ns/ 1 ns
  module FirIPCore_vlg_tst();
  // constants                                           
  // general purpose registers
  //reg eachvec;
  // test vector input registers
  reg [11:0] Xin;
  reg clk,clk_data;
  reg reset_n;
  // wires                                               
  wire [24:0]  Yout;
  // assign statements (if any)                         
  FirIPCore i1 (
  // port map - connection between master ports and signals/registers   
      .Xin(Xin),
      .Yout(Yout),
      .clk(clk),
      .reset_n(reset_n)
  );
  parameter clk_period=20; //设置时钟信号周期(频率):50MHz
  parameter data_clk_period=clk_period*4; //设置数据时钟周期
  parameter clk_half_period=clk_period/2;
  parameter data_half_period=data_clk_period/2;
  parameter data_num=2000;  //仿真数据长度
  parameter time_sim=data_num*data_clk_period; //仿真时间
  initial
  begin
      //设置输入信号初值
      Xin=12'd10;
      //设置时钟信号初值
      clk=1;
      clk_data=1;
      //设置复位信号
      reset_n=0;
      #110 reset_n=1;
      //设置仿真时间
      #time_sim $finish;
  end
  //产生时钟信号
  always                                                 
      #clk_half_period clk=~clk;
  always                                                 
      #data_half_period clk_data=~clk_data;
  //从外部TX文件(E4_5_TestData.txt)读入数据作为测试激励
  integer Pattern;
  reg [11:0] stimulus[1:data_num];
  initial
  begin
     //文件必须放置在"工程目录\simulation\modelsim"路径下
      $readmemb("E4_5_TestData.txt",stimulus);
      Pattern=0;
      repeat(data_num)
          begin
              Pattern=Pattern+1;
              Xin=stimulus[Pattern];
              #data_clk_period;
          end
  end
  //将仿真数据Yout写入外部TXT文件中(E4_5_FpgaData.txt)
  integer file_out;
  initial
  begin
     //文件放置在"工程目录\simulation\modelsim"路径下                                                 
      file_out = $fopen("E4_5_FpgaData.txt");
      if(!file_out)
          begin
              $display("could not open file!");
              $finish;
          end
  end
  wire rst_write;
  wire signed [24:0] dout_s;
  assign dout_s = Yout;                  //将Yout转换成有符号数据
  assign rst_write = clk_data & (reset_n);    //产生写入时钟信号,复位状态时不写入数据
  always @(posedge rst_write )
      $fdisplay(file_out,"%d",dout_s);
  endmodule
4、仿真出现问题:
 1、生成IP核的时候提示:
  FIR滤波器的设计和仿真
  需要对32位软件**。
 2、点击Modelsim仿真:
  FIR滤波器的设计和仿真

  在生成FIR IP核的时候第二步没有点

  FIR滤波器的设计和仿真

  3、运行Modelsim时,出现 Library auk_dspip_lib not found

  FIR滤波器的设计和仿真

  查网说是一个bug,如果把IP核放在二级目录里,就会出现这个错误。所以需要把FIR IP核放到工程目录下。 

   4、运行Modelsim时,出现Module ‘XXX’ is not defined

   FIR滤波器的设计和仿真 

  出现这类错误就是因为在工程文件夹/simulation/modelsim文件夹下面缺少了必要的仿真文件.v(Verilog HDL)。
  之前一直是通过vsim -L altera_mf_ver work.fir123_vlg_tst来手动添加alter_mf库,但依然有问题,说明需要别的库。
  Verilog HDL语言的仿真库文件为220model.v,sgate.v和altera_mf.v;
  VHDL语言的仿真库文件为220pack.vhd、220model.vhd、altera_mf.vhd和altera_mf_components.vhd。  
  路径为C:\altera\13.0sp1\quartus\eda\sim_lib
  把需要的仿真文件复制到当前工程/simulation/modelsim文件夹下面,在modelsim界面点击compile,将生成的.vt文件和加入的.v文件一起编译就可以了,然后点击仿真就不会报错了。