基于FPGA的数字插值滤波器仿真

一、插值原理

       由数字信号处理方面的知识我们了解到,对于数字信号的插值,在时域上看,就是将信号的采样率 Fs 变成原来的 L 倍,其中 L 便是插值倍率。最简单的插值就是在信号中间补零,如图所示

       下面的信号就是由上面的信号补零而来的,可以看见原来相邻的数字信号之间补了一个零,这就是最简单的信号插值。

       但是问题又出现了,我们想的是插值以后可以让波形更细腻,但是单纯补零好像并没有达到这个要求,那我们为什么还要这么做呢?

补零前后时域表达式如下,

 v(n)是补完零后的信号,这时再将其傅里叶变换,得到频域表达式如下

 可以见得插值前后信号的频域关系如下

由此可见,在时域 补零,实际上是将原来的频谱压缩,原来频率分量为\pi的经过插值后变到了\frac{\pi }{L}的位置上,如下图

       因此如果想实现真正的插值,就需要将镜像频率剔除,就是上图蓝色的部分,这也是时域补零带来的失真。

       这时就需要将补零后的信号通过滤波器滤除,由分析可知,插值滤波器仅需一个低通滤波器就可以实现。

PS:实际上在时域波形来看,补零后的零点和下一个实际信号点的突变就是一个高频分量,因此通过滤除高频分量就可以实现真正的插值。

二、高效滤波器结构

        由上述分析,仅需要低通滤波器就可以实现,卷积公式如下图:

       如果按照卷积公式直接进行运算,将会有很多0值参与乘法运算,这样会浪费资源与时间,为解决这个问题,将上式改写为

       由上式可以看到,这种高效结构将原来带零点的v(n)直接改写成补零前的x(n),这样就节省了无用的计算时间。

       利用此结构对信号进行25倍插值,设置100个滤波器系数,结构如下图

 

三、软硬件仿真

MATLAB仿真

       利用MATLAB对插值滤波器进行仿真,设置输入信号长度为256,进行25倍插值,滤波器阶数为99。

% 采样序列为input_seq,长度为256
input_seq = xn; % 输入的采样序列

% 25倍插值后的序列长度
interp_length = 256 * 25;

% 进行插零值
zero_padded_seq = zeros(1, interp_length);
zero_padded_seq(1:25:end) = input_seq;

% 设计低通滤波器
filter_order = 99; % 滤波器阶数
cutoff_freq = 0.04; % 截止频率
filter_coeff = fir1(filter_order, cutoff_freq);


% 应用低通滤波器
filtered_seq = conv(zero_padded_seq, filter_coeff, 'same');
%filtered_seq = conv(zero_padded_seq, fix_x, 'same');
% 输出插值后的序列
output_seq = 25*filtered_seq;

        结果如图

       可以看到滤波器实现了想要的结果,对正弦波数据进行了插值。

FPGA仿真

       通过MATLAB验证,实现了信号的插值,接下来将滤波器系数转入coe文件。

% 放大2的16次幂
x = filter_coeff * 20;
qpath = quantizer('fixed','round','saturate',[20,0]);
fix_x = quantize(qpath,x);

% 转换为16进制有符号数
% x_hex = dec2hex(typecast(int16(x), 'uint16')); % 转换为无符号整型,并转换为16进制字符串
% x_hex = reshape(x_hex, [], 4); % 将16进制字符串按4个字符一组划分
% fix_x = flipud(fix_x); % 翻转顺序,以满足coe文件的顺序要求
% fix_x = cellstr(fix_x); % 转换为cell数组
% fix_x = strcat(fix_x, {';'}); % 添加分号

% 存储coe文件

% 2. 将输入数组转换为16进制字符串
hex_string = dec2hex(fix_x)
% 3. 将16进制字符串写入coe文件
filename = ' G:\BaiduNetdiskWorkspace\English_path\Interpolation\Interpolation_fir_99order.coe'; % 文件名

fileID = fopen(filename, 'w');
fprintf(fileID, 'memory_initialization_radix=16;\nmemory_initialization_vector=\n');
for i = 1:size(hex_string, 1)
    fprintf(fileID, '%s,\n', hex_string(i,:));
end
fclose(fileID);

再将正弦波序列也存为coe文件,再存入FPGA中ROM IP核中(实际上应该用RAM,因为可以随时可以改,要不然滤波器只有一种截止频率,或者不使用低通滤波器进行插值滤波,本设计仅针对低通滤波器)

       顶层结构由三个单元组成,分别为正弦波ROM IP核,地址控制模块,以及运算模块组成,其中运算模块是依照高效滤波结构进行设计,调用四个乘法IP核,并且通过控制滤波器系数的地址,来获取参与运算的滤波器系数,并将其输入至乘法器,最终将四路乘法器输出相加,得到插值滤波结果。

四、仿真结果

       首先是寄存器地址,以上四路代表了四组滤波器系数寄存器的地址,可见是由上面分析的每组的系数是分别从h[0]、h[25]、h[50]、h[75]开始,到h[24]、h[49]、h[74]、h[99]结束一轮卷积。

       可以看到红色的信号是输入信号,是每隔25个时钟输入,意味着信号的采样率是2MSPS,而25倍插值后就是50MSPS。四路信号经过延时,依次落后一拍,与理论相符。(至于滤波后数变大,有一个原因是在MATLAB导出滤波器系数的时候,对滤波器系数进行了放大后再定点化,这样做的原因是滤波器系数都是小数,如果不放大直接定点化,则会出现全零或全一的结果,因此放大再定点是可以解决这个问题的)

       红色的为插值前的信号,绿色的为插值后的信号,可以看到实现了25倍插值。上图为数字,下图为模拟波形

        最终在硬件中实现了25倍插值滤波器。

完整代码可见github:

https://github.com/ZFXS55/Interpolation-filter-based-on-the-FPGA

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值