赞
踩
设计一个15阶(长度为16)的具有线性相位低通FIR滤波器,采用布拉克曼窗函数设计,截止频率为500HZ,抽样频率为2000HZ;采用FPGA实现全串行FIR滤波器,系数的量化位数为12比特,输入数据位数为12比特,输出数据位数为29比特,系统时钟为16KHZ
设计思路:首先采用MATLAB根据要求设计出滤波器系数,并仿真出系数量化前后的幅频响应曲线;根据图4-17所示的结构采用Verilog HDL语言再FPGA中实现该滤波器;采用MATLAB仿真出具有白噪声特性的输入信号,以及由200HZ及800HZ单频信号合成的输入信号;将仿真的输入信号作为Verilog HDL测试输入信号,用MOdelSim仿真滤波器输出信号。采用MATLAB分析滤波器输出信号,比较输入输出信号的时域及频域图。
function Q12 = My_E4_7_fir8 %设置参数 N = 15; %N阶 长度为N+1 滤波器 fc = 500; %截止频率为500HZ fs = 2000; %抽样频率为2000HZ wn = fc/(fs/2); %归一化截止频率 ftype = 'low'; %滤波器类型 window = blackman(N+1)'; %布拉克曼窗函数 与滤波器长度一样 B = 12; %量化位数 %用firl函数设计低通滤波器 b = fir1(N,wn,ftype,window); Q12 = round(b/max(abs(b))*(2^(B-1)-1)); %12比特量化 %求其幅度响应 m_blcak = 20*log(abs(fft(b,1024)))/log(10); m12_black = 20*log(abs(fft(Q12,1024)))/log(10); %保证最大值相同(都为零),好进行比较 m_blcak = m_blcak - max(m_blcak); m12_black = m12_black - max(m12_black); %figure(2) %x_f = [0:fs/length(m_blcak):fs-fs/length(m_blcak)]; %plot(x_f,m_blcak,'-',x_f,m12_black,'--'); %xlabel('频率(Hz)');ylabel('幅度(dB)'); %legend('未量化','12bit量化'); %grid; %figure(3) %x_f_2 = [0:fs/length(m_blcak):fs/2]; %plot(x_f_2,m_blcak(1:length(x_f_2)),'-',x_f_2,m12_black(1:length(x_f_2)),'--'); %xlabel('频率(HZ)');ylabel('幅度(dB)'); %legend('未量化','12bit量化'); %grid;
采用MATLAB仿真滤波器测试数据、仿真测试数据经滤波器滤波后的输出数据,以便与FPGA实现后的结果进行比较,并判断FPGA实现的正确性。首先需要仿真生成12比特量化的、抽样频率为2000HZ的高斯噪声,以及两个频率分别为200HZ与800HZ信号的合成信号,并将仿真的数据转化成二进制数据写入文本文件中,供FPGA仿真程序读取;然后,仿真出量化后的数据经滤波器滤波后的输出结果。
%定义参数 fs = 2000; %抽样频率 f1 = 200; %信号1频率 f2 = 800; %信号2频率 N = 12; %量化位数为12 t = 0:1/fs:1; %数据采样时间长度 %生成信号 s1 = sin(2*pi*f1*t); %信号1 s2 = sin(2*pi*f2*t); %信号2 s = s1 + s2; %合成信号 %产生随机序列信号 noise = randn(1,length(t)); %生成均值为0,方差为1,大小为[1,length(t)]的高斯噪声 %归一化处理 s = s/max(abs(s)); noise = noise/max(abs(noise)); %12比特量化 s_12 = round(s*(2^(N-1)-1)); %-2^11——2^11 noise_12 = round(noise*(2^(N-1)-1)); %调用自己设计的滤波器对信号进行滤波 Q12=My_E4_7_fir8; Filter_noise=filter(Q12,1,noise_12); %由于是filter滤波器,所以b=Q12,a=1 Filter_s=filter(Q12,1,s_12); %求信号的幅频响应 m_s = 20*log(abs(fft(s,1024)))/log(10); m_s = m_s - max(m_s); m_noise = 20*log(abs(fft(noise,1024)))/log(10); m_noise = m_noise - max(m_noise); %求滤波后信号的幅频响应 m_filter_s = 20*log(abs(fft(Filter_s,1024)))/log(10); m_filter_s = m_filter_s - max(m_filter_s); m_filte_noise = 20*log(abs(fft(Filter_noise,1024)))/log(10); m_filte_noise = m_filte_noise - max(m_filte_noise); %求滤波器的幅频响应 m_Q12 = 20*log(abs(fft(Q12,1024)))/log(10); m_Q12 = m_Q12 - max(m_Q12); %设置幅频响应的横坐标单位为Hz x_f = 0:fs/length(m_s):fs/2; %只显示正频率部分的幅频响应 m_s = m_s(1:length(x_f)); m_Q12 = m_Q12(1:length(x_f)); m_noise = m_noise(1:length(x_f)); m_filter_s = m_filter_s(1:length(x_f)); m_filte_noise = m_filte_noise(1:length(x_f)); %画幅频响应曲线 figure(2) subplot(211) plot(x_f,m_s,'-.',x_f,m_filter_s,'-',x_f,m_Q12,'--'); xlabel('频率(HZ)');ylabel('幅度(dB)');title('Matlab仿真合成单频信号滤波前后的频谱'); legend('输入信号频谱','滤波后的频谱','滤波器频谱'); grid; subplot(212) plot(x_f,m_noise,'-.',x_f,m_filte_noise,'-',x_f,m_Q12,'--'); xlabel('频率(HZ)');ylabel('幅度(dB)');title('Matlab仿真白噪声信号滤波前后的频谱'); legend('输入信号频谱','滤波后的频谱','滤波器频谱'); grid; %将生成的数据以十进制数据格式写入txt文件中 fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Int_noise_12.txt','w'); fprintf(fid,'%8d\r\n',noise_12); fprintf(fid,';'); fclose(fid); fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Int_s_12.txt','w'); fprintf(fid,'%8d\r\n',s_12); fprintf(fid,';'); fclose(fid); %将生成的数据以二进制数据格式写入txt文件中 fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Bin_noise_12.txt','w'); for i=1:length(noise_12) B_noise=dec2bin(noise_12(i)+(noise_12(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); fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Bin_s_12.txt','w'); for i=1:length(s_12) B_s=dec2bin(s_12(i)+(s_12(i)<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);
不难发现,800HZ频率的信号几乎被滤掉了。
`
module My_FirlFullSerial( //滤波器系数为12位量化 输入数据也是12位 且都是有符号数 都是以补码形式输入(计算机中数都是以补码形式存在并进行加减) //使用到的是15阶FIR滤波器 长度为N=16 //由FIR滤波器计算公式 最终输出的数据位宽 > (16*2^12)*2^12 = 2^(4+12+12) = 2^28 //所以滤波后的数据保险起见可以为29位 input clk , //时钟信号 时钟频率为16KHZ input rst , //复位信号 input signed[11:0] Xin, //输入数据 数据输入频率为2KHZ output signed[28:0] Yout //滤波后的输出数据 ); //实例化有符号数乘法器IP核 mult reg signed [11:0] coe; //滤波器系数 wire signed [12:0] add_s; //输入数据中 对称数据的和 第n个和最后N-n个 wire signed [24:0] Mout; //两项乘积数的和 mult Umult( //乘法器IP核 .clock(clk), //时钟信号 .dataa(coe), .datab(add_s), .result(Mout) ); //实例化有符号数加法器IP核,对输入数据进行1比特符号位扩展,输出结果为13比特数据 //由于结果为13比特 ,所以把输入数据都扩展为了13位 reg signed [12:0] add_a; reg signed [12:0] add_b; adder Uadder( //加法器IP核 .dataa(add_a), .datab(add_b), .result(add_s) ); //时钟计数器 每计数八次为一次循环 reg [2:0] count; always @(posedge clk or negedge rst) if(!rst) count <= 3'd0; else count <= count + 1'd1; //给每一个数据寄存器赋值 共16个 reg [11:0] Xin_reg[15:0]; //输入数据寄存器 reg [3:0] i,j; always @(posedge clk or negedge rst) if(!rst) begin //复位时每个寄存器都清零 for(i=0;i<15;i=i+1) Xin_reg[i] <= 12'd0; end else if(count == 3'd7) begin //当一次循环结束时 开始加载下一个数据 并所有数据右移 for(j=0;j<15;j=j+1) Xin_reg[j+1] <= Xin_reg[j]; Xin_reg[0] <= Xin; end else ; //将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器 //需要注意的是,下面程序只用了了一个加法器及一个乘法器 //以8倍率的速率调用乘法器IP核,由于滤波器长度为16比特,系数具有一定的对称性 //故可在一个数据周期内完成所有8个滤波器系数与数据的乘法运算 //为了保证加法运算不溢出,输入、输出数据均扩展为13比特 always @(posedge clk or negedge rst) if(!rst) begin coe <= 12'd0; add_a <= 13'd0; add_b <= 13'd0; end else if(count == 3'd0) begin coe <= 12'd0; //第一个滤波器及最后一个滤波器系数的值 add_a <= {Xin_reg[0][11],Xin_reg[0]}; //第一个滤波器系数对应的输入值 并将12位的输入数据扩展为13位 add_b <= {Xin_reg[15][11],Xin_reg[15]}; //若最高位为1 则补1 最高位为零 则补零 总之就是补高位的值 end else if(count == 3'd1) begin coe <= 12'hffd; //c1 add_a <= {Xin_reg[1][11],Xin_reg[1]}; add_b <= {Xin_reg[14][11],Xin_reg[14]}; end else if(count == 3'd2) begin coe <= 12'h00f; add_a <= {Xin_reg[2][11],Xin_reg[2]}; add_b <= {Xin_reg[13][11],Xin_reg[13]}; end else if(count == 3'd3) begin coe <= 12'h02e; add_a <= {Xin_reg[3][11],Xin_reg[3]}; add_b <= {Xin_reg[12][11],Xin_reg[12]}; end else if(count == 3'd4) begin coe <= 12'hf8b; add_a <= {Xin_reg[4][11],Xin_reg[4]}; add_b <= {Xin_reg[11][11],Xin_reg[11]}; end else if(count == 3'd5) begin coe <= 12'hef9; add_a <= {Xin_reg[5][11],Xin_reg[5]}; add_b <= {Xin_reg[10][11],Xin_reg[10]}; end else if(count == 3'd6) begin coe <= 12'h24e; add_a <= {Xin_reg[6][11],Xin_reg[6]}; add_b <= {Xin_reg[9][11],Xin_reg[9]}; end else begin coe <= 12'h7ff; add_a <= {Xin_reg[7][11],Xin_reg[7]}; add_b <= {Xin_reg[8][11],Xin_reg[8]}; end //对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据 //考虑到乘法器及累加器的延时,需要在计数器count=2时对累计器清零,同时输出滤波器结果 //类似的延时长度一方面可通过精确计算获取,但更好的方法是通过行为仿真查看 reg signed [28:0] sum; reg signed [28:0] yout; always @(posedge clk or negedge rst) if(!rst) begin sum <= 29'd0; yout <= 29'd0; end else if(count == 2 ) begin //计数到2的时候 说明一次循环结束 yout <= sum; //将累加值保存到yout变量中 sum = 29'd0; //累加值重新清零 sum = sum + Mout; end else sum = sum + Mout; assign Yout = yout; endmodule
`timescale 1 ns/ 1 ns //设置仿真时间单位:ns module FirFullSerial_vlg_tst(); // constants // general purpose registers reg eachvec; // test vector input registers reg [11:0] Xin; reg clk; reg rst; wire clk_data; //数据时钟,速率为系统时钟clk的1/8; // wires wire [28:0] Yout; // assign statements (if any) FirFullSerial i1 ( // port map - connection between master ports and signals/registers .Xin(Xin), .Yout(Yout), .clk(clk), .rst(rst) ); parameter clk_period=62500; //设置时钟信号周期(频率):16kHz parameter clk_period_data=clk_period*8; parameter clk_half_period=clk_period/2; parameter clk_half_period_data=clk_half_period*8; parameter data_num=2000; //仿真数据长度 parameter time_sim=data_num*clk_period; //仿真时间 initial begin //设置时钟信号初值 clk=1; //clk_data=1; //设置复位信号 rst=1; #200000 rst=0; //设置仿真时间 #time_sim $finish; //设置输入信号初值 Xin=12'd10; end //产生时钟信号 always #clk_half_period clk=~clk; reg [2:0] cn_clk=3'd0; always @(posedge clk) cn_clk <= cn_clk + 3'd1; assign clk_data = cn_clk[2]; //从外部TX文件(SinIn.txt)读入数据作为测试激励 integer Pattern; reg [11:0] stimulus[1:data_num]; initial begin //文件必须放置在"工程目录\simulation\modelsim"路径下 //readmemb("E4_7_Bin_noise.txt",stimulus); $readmemb("E4_7_Bin_s.txt",stimulus); Pattern=0; repeat(data_num) begin @(posedge clk_data); Pattern=Pattern+1; Xin=stimulus[Pattern]; end end //将仿真数据dout写入外部TXT文件中(out.txt) integer file_out; initial begin //文件放置在"工程目录\simulation\modelsim"路径下 //file_out = fopen("E4_7_Noiseout.txt"); file_out = fopen("E4_7_Sout.txt"); if(!file_out) begin display("could not open file!"); finish; end end wire rst_write; wire signed [28:0] dout_s; assign dout_s = Yout; //将dout转换成有符号数据 assign rst_write = clk_data & (!rst);//产生写入时钟信号,复位状态时不写入数据 always @(posedge rst_write ) $fdisplay(file_out,"%d",dout_s); endmodule
看完书本这部分内容,几乎都自己重新写了一遍,算是大概了解了这个流程。虽然由于自身技术原因,利用FPGA仿真那部分操作出了点问题,现在还不怎么明白,所以5、6节我没加上去内容。总体来说还可以
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。