ZYNQ学习笔记(六):数字信号处理—CIC插值滤波器的FPGA实现


前言

前段时间参加比赛,在实现作品的过程中,我们需要对数字化基带信号进行高倍率的上采样,以增加其采样率,从而扩展频率范围。上采样后的基带信号将与DDS生成的高频载波信号进行乘法运算,从而实现直接数字上变频。本文将首先介绍如何通过CIC插值滤波器在数字域内实现数字信号的采样率提升。

一、CIC滤波器

1.1 基本原理

CIC (Cascaded Integrator-Comb) 滤波器是一种常用的数字信号处理滤波器,主要用于降采样(decimation)和升采样(interpolation)操作,一般用于数字下变频(DDC)和数字上变频(DUC)系统,因此其又可以按功能分为CIC抽取滤波器与CIC插值滤波器。其结构分别为下图所示:
CIC抽取滤波器结构

CIC内插器滤波器结构,其结构与CIC抽取滤波器类似,只不过梳状器和积分器的顺序进行了变化。图中,上采样速率因子R,该速率在梳状器和积分器之间变化,即每个输入采样值间插入R-1个0值,也就是采样率速率扩大倍数。

1、梳状器是一种有限脉冲响应(FIR)滤波器,通过延迟和差分操作来处理输入信号。梳状器可以看作是一个高通滤波器,它的主要功能是去除直流和低频分量。

2、插值模块通过在原始信号样本之间插入零点(零插值)来增加信号的采样率,将信号的采样率提高到所需的水平。插值操作会引入高频分量(由于零插值引入的尖锐变化),这些高频分量将在后续的积分器中进行平滑处理。

3、积分器是一种无限脉冲响应(IIR)滤波器,通过累加信号样本的和来实现信号的平滑。积分器可以看作是一个低通滤波器,其作用是平滑信号,去除插值过程中引入的高频分量,使得插值后的信号更加平滑和连续,恢复其原始频谱特性。

这种流程的设计确保了信号在经过插值处理后能够保持其原有的频谱特性和信号质量,还有效地提高信号的采样率。因此CIC滤波器的这种结构使得它在硬件实现上非常高效,适用于需要进行高采样率转换的应用场景。

此外,从上图中我们可以看出,CIC 插值器与抽取器的信号处理顺序是不一样的,其实造成这两种处理顺序是基于信号插值和抽取的不同需求和特性~

CIC插值器中,信号处理的顺序是:梳状器~插值模块 ~积分器。CIC内插滤波器的目标是增加采样率,同时保持和恢复信号的频谱特性。具体步骤如下:高通滤波(梳状滤波器):通过差分运算突出信号的高频成分。插值:在现有样本之间插入零值,增加采样率。这一步会引入频谱混叠。低通滤波(积分器):通过累加操作平滑信号,去除插值引入的高频混叠成分,并恢复信号的频谱特性。

CIC抽取器中,信号处理的顺序是:==积分器~抽取模块 梳状器==:是因为采样定理(Nyquist-Shannon采样定理)告诉我们,采样频率必须至少是信号中最高频率的两倍,才能无失真地重建原始信号。如果采样频率低于奈奎斯特频率,高频成分就会混叠到低频范围内,产生别名效应。因此我们抽取样本降低采样率之前需要先经过低通滤波器 即积分器~抽取模块 ~梳状器。

1.2 CIC滤波器位宽确定

上文图中R抽取或插值系数,M差分延迟,N滤波器级数、Bin输入数据的位宽、Bout不发生溢出的位宽值。 计算公式如下:
  
1、抽取滤波器,为了获得完全的精度,CIC抽取器实现在内部为每个积分器和微分器级使用Bout位;即积分器~抽取模块 ~梳状器三者之间的输入输出位宽均为Bout位,这在输出端不会引入量化误差。

2、插值滤波器,与抽取滤波器不同的是,全精度CIC抽取输出位宽Bout可以表示为如下图所示:

但是滤波器的输出(梳状器的输出)可以选择为“全精度或有限精度(带截断或舍入)”,以适应特定于应用的输出宽度。使用有限精度(比如积分器的输出位宽是高xx位,截掉部分低位)不会影响中间过程的结果,只有末级输出被缩放,如果需要的话,四舍五入以提供所选的输出宽度。即当输出宽度等于Bout宽度时,输出全精度结果,输出的幅度反映滤波器增益。当输出宽度设置为小于最Bout宽度时,输出将被截断,增益相应减小。

此外,M差分延迟的意思是CIC滤波器中的差分延迟参数,它定义了梳状滤波器部分中两个信号之间的延迟。简单来说,当M = 1时,梳状滤波器计算的是相邻样本之间的差值。这种配置最常见,当M = 2时,梳状滤波器计算的是当前样本与两个采样周期前的样本的差值。M = 2一般用于更严格的高频噪声抑制需求,或者当输入信号的频率特性需要更大的差分延迟来优化时使用。

更多详细的关于CIC滤波器知识这里就不展开细说了,可以参考底部的文章链接~

二、 多级CIC内插滤波器的FPGA实现

2.1各模块设计

首先确定Bout的大小、实验设计的插值滤波器插值系数 R = 4,M = 1,N = 4,Bin = 16bit;根据计算公式得Bout=22bit。即利用一个4阶的CIC插值滤波器来实现信号的4倍采样率提升。
梳状器模块:

高通滤波原理:假设输入信号包含一个高频成分和一个低频成分,用傅里叶变换分析可以表示为:
在这里插入图片描述
由于高频成分的周期较短,它在相邻两个时刻的变化较大,因此差分后高频成分会较大程度地保留;而低频成分的周期较长,它在相邻两个时刻的变化较小,因此差分后低频成分会显著减小。

// 梳状器(由clk2驱动)
always @(posedge clk_in or posedge reset) begin
    if (reset) begin
        for (i = 0; i < STAGES; i = i + 1) begin
            comb[i] <= 0;
            combd[i] <= 0;
        end
    end 
    else begin
        // 梳状器操作
        comb[0] <= {{(INDATA_WIDTH-DATA_WIDTH){data_in[DATA_WIDTH-1]}}, data_in};
        for (i = 1; i < STAGES; i = i + 1) begin
            combd[i-1] <= comb[i-1];
            comb[i] <= comb[i-1] - combd[i-1];
        end
        combd[STAGES-1] <= comb[STAGES-1];
    end
end

插值器模块:

// 插值器(clk1驱动)
always @(posedge clk_out or posedge reset) begin
    if (reset) begin
        interpolation <= 0;
        cont <= 0;
    end 
    else begin
        cont <= cont + 1;
        if (cont == Ntimer-1) begin // N倍插值
            interpolation <= comb[STAGES-1];
            cont <= 0;
        end
        else
            interpolation <= 0;
    end
end

积分器模块:

低通滤波原理:
积分器的操作可以看作是对信号进行累加,即对当前输入信号与前一时刻的信号进行加法操作,从而累积所有之前的输入信号值。数学上,积分器的输出可以表示为:

积分器通过累加操作,使得输出信号在时间上是平滑的。这是因为积分器的输出不仅取决于当前输入信号,还取决于之前所有时刻的输入信号。因此,输入信号中的高频变化会被平滑掉,输出信号变得更加平稳。在频域上,积分操作相当于一个低通滤波器。积分器的频率响应函数可以表示为:

该函数在低频时具有较高的增益,而在高频时增益逐渐减小,从而实现低通滤波效果。

// 积分器逻辑(由clk1驱动)
always @(posedge clk_out or posedge reset) begin
    if (reset) begin
        for (i = 0; i < STAGES; i = i + 1) begin
            integrator[i] <= 0;
        end
        output_buffer <= 0;
    end 
    else begin
        integrator[0] <= interpolation;
        for (i = 1; i < STAGES; i = i + 1) begin // 积分器
            integrator[i] <= integrator[i] + integrator[i-1];
        end
        output_buffer <= integrator[STAGES-1];
    end
end

2.2 CIC模块整体设计

这里不再详细解释,我添加了必要的注释~

module clc
(
     clk_in,  // clk1 25MHZ,梳妆滤波器时钟
     clk_out, // clk2(4倍于clk1)内插与积分滤波器时钟
     reset,   // 复位信号
     data_in, // 输入数据
     data_out, // 输出数据
    comb_out, // 梳状滤波器输出
    interpolation_out // 插值输出
 
);
parameter STAGES = 4; // 滤波器阶数
parameter DATA_WIDTH = 16; // 数据宽度
parameter INDATA_WIDTH = 22; // 中间数据宽度
parameter Ntimer = 4; // 插值倍数

    input clk_in; // 输入数据时钟
    input clk_out;// 输出数据时钟(Ntimer倍于输入数据时钟)
    input reset;  // 复位信号
    input signed [DATA_WIDTH-1:0] data_in; // 输入数据
    output reg signed [DATA_WIDTH-1:0] data_out;// 输出数据
    output reg signed [DATA_WIDTH-1:0] comb_out; // 梳状滤波器输出
    output reg signed [DATA_WIDTH-1:0] interpolation_out ;// 插值输出
    


// 积分器的寄存器
reg signed [INDATA_WIDTH-1:0] integrator [0:STAGES-1]; 
// 梳状器的寄存器
reg signed [INDATA_WIDTH-1:0] comb [0:STAGES-1]; 
reg signed [INDATA_WIDTH-1:0] combd [0:STAGES-1]; 
// 插值的寄存器
reg signed [INDATA_WIDTH-1:0] interpolation = 0;
reg [7:0] cont;
// 输出缓冲
reg signed [INDATA_WIDTH-1:0] output_buffer = 0;
integer i;

// 将输出缓冲的值映射到输出端口
always @(posedge clk_out) begin
    data_out <= output_buffer[INDATA_WIDTH-1:INDATA_WIDTH-DATA_WIDTH]; // 调整以适应实际的位宽和动态范围
end

// 将梳状滤波器输出值映射到输出端口
always @(posedge clk_in) begin
    comb_out <= comb[STAGES-1][INDATA_WIDTH-1:INDATA_WIDTH-DATA_WIDTH]; // 调整以适应实际的位宽和动态范围
end

// 将插值后的信号映射到输出端口
always @(posedge clk_out) begin
    interpolation_out <= interpolation[INDATA_WIDTH-1:INDATA_WIDTH-DATA_WIDTH]; // 调整以适应实际的位宽和动态范围
end

// 梳状器(由输入时钟驱动)
always @(posedge clk_in or posedge reset) begin
    if (reset) begin
        for (i = 0; i < STAGES; i = i + 1) begin
            comb[i] <= 0;
            combd[i] <= 0;
        end
    end 
    else begin
        // 梳状器操作
        comb[0] <= {{(INDATA_WIDTH-DATA_WIDTH){data_in[DATA_WIDTH-1]}}, data_in};
        for (i = 1; i < STAGES; i = i + 1) begin
            combd[i-1] <= comb[i-1];
            comb[i] <= comb[i-1] - combd[i-1];
        end
        combd[STAGES-1] <= comb[STAGES-1];
    end
end

// 插值器(输出时钟驱动)
always @(posedge clk_out or posedge reset) begin
    if (reset) begin
        interpolation <= 0;
        cont <= 0;
    end 
    else begin
        cont <= cont + 1;
        if (cont == Ntimer-1) begin // 4倍插值,两个样本点之间插3个0。
            interpolation <= comb[STAGES-1];
            cont <= 0;
        end
        else
            interpolation <= 0;
    end
end

// 积分器逻辑(由输出时钟驱动)
always @(posedge clk_out or posedge reset) begin
    if (reset) begin
        for (i = 0; i < STAGES; i = i + 1) begin
            integrator[i] <= 0;
        end
        output_buffer <= 0;
    end 
    else begin
        integrator[0] <= interpolation;
        for (i = 1; i < STAGES; i = i + 1) begin // 积分器
            integrator[i] <= integrator[i] + integrator[i-1];
        end
        output_buffer <= integrator[STAGES-1];
    end
end

endmodule

2.3 CIC整体测试BD设计


系统时钟sys_clk:50MHZ,clk_out1:25MHZ用于clk1(梳状滤波器模块时钟),以及DDS\VIO;
clk_out2:100MHZ,用于clk2(内插与积分滤波器模块时钟),clk_out3:200MHZ。
VIO来控制DDS频率字,生成一个16位、1MHZ、25MHZ采样率的正弦信号。

工程下载链接:CIC插值滤波器的FPGA实现

三、 实验结果与分析

首先利用ILA1(clk100MHZ)观察插值结果:

由此可见两个样本点之间插入了3个0,满足预设情况~
其次利用ILA2(clk200MHZ)观测采样率更改结果:

由此可见,通过clc输入数据与输出数据的对比,我们可以清晰的看到数据采样率已经从25MHZ变为了100MHZ~ 满足我们的预设情况~
最终结果如图所示:信号依次为dds输出、梳状滤波器模块输出、插值模块输出、最终的积分模块输出~
我们把ILA1观测的积分模块(CIC最终的输出信号)输出信号存为.CSV文件,导入MATLAB分析

close all;
clc;
clear all;

% 从CSV文件加载信号数据
data = readmatrix('iladata1.csv'); 

x = data(:, 4);  % 提取信号向量
fs = 100000000; % 设置采样频率为100MHz

% 绘制信号的时域图像
figure;
time_vector = (0:length(x)-1) / fs; % 生成时间轴向量
plot(time_vector, x);
title('DDS信号波形');
xlabel('Time (s)');
ylabel('Amplitude');
xlim([0 max(time_vector)]); % 设置x轴范围
grid on;

% 调用 spectrum_plot 函数来绘制频谱图
tt_str = '频谱图'; % 设置图表标题
spectrum_plot(x, fs, tt_str);

% spectrum_plot 函数定义
function spectrum_plot(x, fs, tt_str)
  vec_win = kaiser(length(x), 10);
  y = fftshift(fft(x .* vec_win));
  y_dB = 20*log10(abs(y));
  N_f = length(y);
  f_idx = (0:N_f-1).' / N_f * fs - (fs / 2);
  figure;
  plot(f_idx / 1E6, y_dB); % 将频率转换为MHz
  xlabel('MHz');
  ylabel('Magnitude (dB)');
  title(tt_str);
  grid on; 
end


综合以上验证信息来看,不论是插值、采样率还是信号频谱都正确,由此可见CIC模块已经完全符合我们的预期~

总结

实验并不复杂,但是也是花了些心思,至此我们学会了如何通过CIC插值滤波器在数字域内实现数字信号的采样率提升。道阻且长,还在路上
参考链接:1、数字信号处理(四)CIC IP核滤波器详解(一)
2、ZYNQ实验–CIC插值滤波器实验

  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空lg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值