☰
AD9361信号链中FIR滤波器设计与系数加载全流程解析
2026/10/5 5:04:41 网站建设 项目流程

1. 先说清楚:AD9361信号链里FIR滤波器到底卡在哪

做射频收发项目的人对AD9361应该不陌生,这颗芯片在软件无线电、无人机图传、专网通信里出现频率非常高。但很多人一开始接触FIR滤波器设计时,容易犯一个方向性错误:拿着MATLAB里那套通用的低通滤波器设计方法,套到AD9361上,结果导出的系数要么写不进去,要么写进去之后信号链增益和带宽全乱了。

要先纠正这个认知,AD9361不是一颗带普通DSP加速器的通用处理器,它内部的可配置FIR滤波器是收发信号链里固定的一环。你绕不开它,但也别把它当成万能的滤波器来用。它跟你在FPGA里随便写的几百阶FIR不是一回事,它受限于AD9361内部数据通路的结构、抽取/插值比例、系数数目上限和位宽。换句话说,你在设计FIR之前,得先搞清楚它在这颗芯片里到底承担什么角色。

AD9361的接收链路大概是这样:射频信号进来,经过LNA混频到基带,再过模拟低通滤波器(BB LPF),然后ADC采样,之后进入数字域的三级处理——半带滤波器(HB1、HB2、HB3)、可配置FIR滤波器,最后以你设定的基带采样率把数据交给FPGA或者处理器。发射链路正好反过来:数据从FPGA进来,先过可配置FIR,再过半带插值滤波器,然后DAC输出,再过模拟低通,最后混频到射频。所以这个可配置FIR是数字域和模拟域之间的最后一道"整形闸门",它的好坏直接影响带外抑制、邻道泄漏和灵敏度。

有一个关键点很多初学者会忽略:AD9361的FIR设计并不是你随便提出一个"通带、阻带、阶数"就能满足,它必须满足芯片内部数据通路对参数的限制。最典型的就是滤波器系数数量必须在16到128之间,而且为了保持线性相位特性,系数个数必须是偶数;当你用MATLAB fdatool或者Filter Designer导出系数时,默认很可能给你生成一个奇数阶滤波器,这时候就必须做截断或补零处理,否则AD9361的API函数直接会返回错误。

所以,这篇文章我会从头到尾走一遍在AD9361上做FIR滤波器设计的完整流程,包括指标怎么定、系数怎么设计、怎么量化、怎么用API写进去、联调时有哪些坑,以及跟设备树/Petalinux工程相关的一些实操经验。

1.1 可配置FIR在收发链路中的位置与作用

AD9361的可配置FIR在数据手册里被称为"128-tap programmable FIR filter",但注意,这个"128-tap"是上限,不是说你设计时就一定要用满。相反,实际工程中我更建议从需求出发,能用64阶解决的不要用128阶,因为阶数越大,系数数量越多,占用的配置时间和内部RAM资源也越多,而且对带外抑制的提升并不是线性的。

这个FIR在接收链路中主要干两件事。第一件事是最终的信道选择,也就是把你想要的信号频段留住,把邻道、镜像、带外噪声都压下去。前面那些半带滤波器虽然也能滤,但半带滤波器的过渡带比较宽,而且它们的系数是固定的,你动不了;FIR是唯一一个你可以完全自定义幅度响应的数字滤波器。第二件事是补偿前面模拟滤波器的幅频跌落。AD9361的BB LPF是模拟滤波器,它的通带边缘会有一定滚降,高速率信号在经过它之后,高频分量会被削掉一部分,这时候FIR可以设计成一个有轻微高频提升的幅频特性,把整个链路的幅频响应拉平。

发射链路中,FIR的任务是成型滤波。拿BPSK或者QPSK这类PSK调制来说,基带信号的码元波形如果不做成型,直接射频出去会占用很宽的频谱,邻道泄漏严重。所以你会看到很多AD9361相关的调制解调例程里,都会在FPGA里做根升余弦(RRC)脉冲成型,或者把成型滤波的系数扔到AD9361的发射FIR里。实际上,AD9361的Tx FIR完全可以直接承担脉冲成型的工作,让FPGA端省掉一块逻辑资源,只做基带符号映射就行。

很多人会问:既然FIR这么好,能不能把模拟LPF完全干掉,全靠FIR?答案是不能。AD9361的模拟LPF专门用来抗混叠,如果模拟LPF的截止频率设得太高,带外强干扰会在ADC采样时混叠进基带,到那时FIR理论上压不住混叠进来的信号,因为混叠信号在数字域和有用信号重叠了。所以正确思路是:模拟LPF负责粗滤,FIR负责精滤。

1.2 AD9361 FIR的三个硬性约束,设计前必须知道

这块内容我觉得值得单独拿出来讲,因为我在实际项目中见过太多人在这里卡壳。

第一个约束是系数个数。AD9361的FIR系数个数N必须满足:N是16到128之间的偶数,而且N除以2之后必须是整数,也就是N/2是8到64之间的整数。为什么要求除以2?因为内部实现时利用了线性相位FIR的系数对称性,只需要存储一半的系数。所以你在设计之前就得把目标阶数往这个区间上靠,别用65阶、73阶这种奇数。

第二个约束是增益归一化。AD9361内部FIR的系数是以定点格式存储的,每一个系数可以用一个6位小数定点数表示,范围在-1到+1之间,而且所有系数的绝对值之和必须小于等于某个上限。这个约束直接导致了一个后果:你在MATLAB里设计的浮点系数,直接转定点之后,滤波器的通带增益往往不是1,可能是0.7或者1.3。如果不去管它,信号链路整体增益就会偏大或偏小,接收端可能会出现削波,发射端会导致调制质量下降。所以系数写入之前,必须做一次归一化处理,把系数缩放一个合适倍数,确保没有溢出,然后再在软件层面做增益补偿。

第三个约束是抽取/插值因子与FIR输入采样率的匹配。AD9361允许的最大FIR输入采样率不是随便定的,它跟外部FPGA接口的时钟、半带滤波器的级联配置强相关。以接收为例,MAX_FIR_RATE为250MSPS,但这个值基于BBPLL几倍频、半带滤波器如何配置会变。你要先确认当前配置下的实际FIR输入速率是多少,再去设计滤波器通带,否则标称通带跟实际响应会差出不少。

提示:设计FIR之前,先用ADI官方的AD936x Filter Guide工具(一个Excel表格)把整个信号链的速率配置跑一遍,它会直接告诉你当前配置下FIR支持的最大阶数、输入输出速率,以及模拟滤波器推荐带宽。这比你自己对着数据手册慢慢算靠谱得多。

2. 动手前先算账:采样率、通带、阶数的确定方法

FIR设计的起点不是滤波器本身,而是系统指标。我从接收链路和发射链路分开说,因为这两者的设计目标完全不同。

接收链路中,FIR的定位是信道选择和灵敏度保障。你要先问自己几个问题:基带信号带宽是多少?相邻信道信号有多强?系统对带外抑制的要求是60dB还是80dB?这些问题决定了FIR的过渡带宽度和阻带衰减需求。举个例子,一个带宽为10MHz的BPSK信号,符号速率为10Mbps,理论上基带主瓣宽度就是10MHz(单边5MHz),如果信道间隔是20MHz,那FIR就可以把过渡带设计在5MHz到10MHz之间,阻带从10MHz开始压。

发射链路中,FIR的核心指标是EVM和ACPR。EVM(误差矢量幅度)要求滤波器群延迟特性好、通带纹波小;ACPR(邻道功率比)要求滤波器阻带衰减足够大。这里有一个常见的矛盾:为了ACPR好看,把FIR阶数加到128,但阶数一高,通带边缘变陡,群延迟波动变大,EVM反而变差。所以发射链路FIR通常不是"越多阶越好",而是刚好够用就行。

2.1 从协议标准反推滤波器指标

真正专业的做法是先从协议标准里把频谱模板(Spectral Mask)拉出来,然后反推滤波器指标,而不是拍脑袋定一个"40dB衰减应该够了吧"。

以专网通信中常见的BPSK调制为例。假设符号速率Rs=10Mbps,基带信号用矩形脉冲的话,频谱是sinc形状,主瓣宽度为Rs(即10MHz),第一旁瓣只比主瓣低13dB左右。如果不做成型滤波,频谱根本没法看。所以发射端至少要用RRC成型,滚降因子α=0.35的话,信号双边带宽就变成(1+α)×Rs=13.5MHz。此时你去看系统的频谱模板,通常要求带外(超过13.5MHz的部分在某个偏移处)衰减到-50dBc以下。那么FIR的设计目标就很明确了:通带截止频率6.75MHz,阻带起始频率可能从9MHz或者10MHz开始,阻带衰减至少55dB。

有了通带、阻带、衰减三个数字,接下来就可以查表或者用经验公式估算阶数。

2.2 用MATLAB设计系数时的参数坑

MATLAB的Filter Designer(fdatool在旧版本里叫这个)是设计系数的主力工具,但直接用它的默认导出会踩坑,我列几个最常见的。

第一,采样率别填错。你设计的是数字滤波器,MATLAB里的Fs必须填当前信号链中FIR输入端的实际采样率,不是基带符号速率,也不是FPGA接口的等效速率。比如接收链路经过半带滤波器后,送给FIR的采样率是61.44MSPS,那设计时Fs就填61.44e6,通带截止频率按Hz填,不要按归一化频率填,这能少干很多换算的活。

第二,输出类型要选Fixed-point。如果你直接用double类型导出系数再转定点,会丢失一些精度信息。正确做法是直接在MATLAB里把滤波器结构设置成Direct-Form FIR,运算精度设置成定点,输出小数位数设为跟AD9361的系数格式匹配。AD9361的系数格式是6位小数定点,所以你可以把系数量化的小数位设为6bit,看量化后的幅频响应和浮点差多少。如果阻带衰减从60dB掉到了48dB,那说明你的阶数在定点实现下不够,需要增加阶数或者调整过渡带。

第三,记得强制把阶数凑成偶数。在Filter Designer里,当你设置"Minimum Order"时,算出来的阶数很可能是奇数。这时我建议回到规格参数里,把阶数手动改成满足16~128且除以2为整数的值,然后再让工具重新优化。别直接拿奇数阶结果截掉一阶,那会破坏线性相位结构,通带纹波特性会变的比较奇怪。

2.3 量化效应:为什么系数位宽变化后滤波器响应会变形

这个问题实践中经常被忽视。AD9361内部FIR的系数是12位存储(其中1位符号位),有效数据位10位左右。跟FPGA里的浮点FIR比起来,这个精度确实不高,所以系数量化误差对阻带性能的影响需要认真评估。

我举个实际例子。设计一个64阶低通FIR,通带10MHz,阻带起始12MHz,浮点系数下阻带衰减可以到62dB。但同一组系数量化到6位小数定点后,阻带衰减可能只到50dB,噪声基底抬高了12dB左右。这种差异在接收机灵敏度测试时非常致命,因为邻道干扰被滤不干净,最后整机灵敏度可能直接掉出规格。

处理办法有三个方向。第一,增加阶数留余量,把阻带衰减指标往高了设计,比如要求50dB的系统,设计时按65dB做,这样量化损失后仍然够用。第二,用MATLAB的定点工具箱做系数优化,让量化误差在带内分布更均匀。第三,在系统层面加一段数字补偿,比如在FPGA里再级联一个短阶的补偿滤波器。但这会增加FPGA逻辑资源,一般只在设计余量实在不够时才用。

3. 从fdatool到寄存器:FIR系数加载的完整流程

系数设计好了之后,接下来的工作是把浮点系数从MATLAB搬到AD9361的寄存器里。这个过程有标准流程,但每步都藏着细节。我按实际操作顺序拆开讲。

3.1 生成系数文件的正确姿势

当你在MATLAB Filter Designer里完成了滤波器设计,并且通过了定点量化验证后,导出系数时建议导出成.fcf格式,这是ADI官方工具链支持的一种系数文本格式,本质就是一行一个系数值的文本文件。但请注意,这个值仍然是十进制浮点格式,并不是AD9361能直接用的二进制补码格式,中间需要经过转换。

切换到Linux或者Windows下使用ADI的libad9361-iio库时,加载FIR系数有两种方式:一种是通过IIO属性接口动态写入,另一种是在设备树里预置。对原型验证阶段,IIO属性接口更灵活,因为你可以在运行时随意换滤波器系数,不需要重新编译内核。而产品化阶段,通常把系数固化到设备树或者生成配置头文件,保证启动即加载。

我建议在MATLAB导出后,先做一个系数预处理脚本,它负责三件事:

  • 把浮点系数归一化到-1到1的范围;
  • 按照AD9361的定点格式做量化(用6位小数位做舍入);
  • 把量化后的系数转换成带符号十进制整数,方便和最终写入寄存器的值核对。

做完这三步,系数文件里每一行就是一个可直接写入寄存器的整数值。

3.2 no-OS驱动下写FIR系数的API调用细节

如果你用的不是Linux IIO,而是裸机或者RTOS下的no-OS驱动,写FIR系数的函数是ad9361_set_tx_fir_config和ad9361_set_rx_fir_config。这两个函数都会接收一个struct ad9361_rf_phy指针和一个struct ad9361_fir_config结构体。

ad9361_fir_config里的几个关键字段值得注意:

字段名作用易错点
num_taps滤波器阶数必须为偶数,且16到128之间;之前没注意都是在这里返回-1
taps系数数组指针数组里存的是量化后的定点整数值,不是浮点小数
gain滤波器数字增益建议设成-6dB或-12dB,给信号留headroom
tx_clk_gain/rx_clk_gain时钟路径增益与半带滤波器的速率配置联动,改差了频谱会异常
decimation/interpolation抽取/插值因子FIR的decimation必须同时满足半带滤波器的级联要求

写入顺序也有讲究。必须先把FIR配置成bypass状态,然后再写系数,最后再切换回enable状态。如果你直接把系数写入一个正在生效的滤波器,芯片内部的系数加载指针会和数据流发生竞争,偶尔会出现"半个滤波器"的诡异现象——具体表现是幅度响应完全不对,但寄存器读出来又好像是对的。这个我在调试时遇到过两次,现象非常迷惑,最后加了bypass流程就再也没出现过。

4. 实测验证与信号链联调

系数写进去之后,很多人以为大功告成,其实真正的麻烦刚开始。滤波器是数字信号处理器件,但它工作在一条完整的模拟-数字混合信号链里,跟模拟前端的匹配、跟FPGA接口时序的配合、跟增益控制策略的联动,任何一环有问题,滤波器的实际效果都会偏离设计值。

4.1 用BPSK信号验证滤波效果

验证FIR最直观的方式,是给它灌一段已知的BPSK信号,然后在频谱仪上观察输出。我建议按三个步骤做。

第一步,把AD9361的发射端配置成单音信号,频率放在通带中心,此时看频谱仪,确认信号功率和预期一致;再把单音频率推到通带边缘,观察幅度是否跌落,这一步验证的是通带平坦度。第二步,关闭单音,改成发射BPSK调制信号,此时你能明显看到频谱主瓣和旁瓣的形状。正常情况下,经过FIR成型之后,旁瓣应该被压得很干净,与不加FIR时那种宽大的sinc频谱形成鲜明对比。第三步,把频谱仪的RBW设成窄带(比如100kHz),观察噪声基底,这里能看到FIR对带外噪声的实际抑制能力。

有一个指标记得同时记录:BPSK解调之后的EVM。如果EVM在3%以内,说明FIR通带纹波和群延迟都在合理范围内;如果超过5%,重点检查FIR的通带纹波是否偏大,或者FIR增益设置是否导致信号削波。EVM异常的排查我建议优先看时域波形,直接抓AD9361输出的I/Q采样数据,在MATLAB里做星座图,波形上看削波非常明显。

4.2 通带纹波与增益设置的互动问题

再讲一个我踩过不少次坑的环节。AD9361的Rx信号链路里,AGC(自动增益控制)会把信号幅度维护在目标电平附近。当FIR的增益设置不当,比如FIR总体增益为1.5(也就是约3.5dB提升),那么信号经过FIR后幅度会明显增长,AGC检测到信号变大后会回调LNA和VGA增益。你说巧不巧,AGC又在模拟端,它把增益压下来之后,模拟噪声也跟着一起压,但FIR带来的纹波不会因为AGC回调而消失,结果就是:整体增益正常了,但信号链的噪声系数变差了,灵敏度掉一截。

所以我建议FIR的数字增益不要设成正数,尽量设成-6dB或者-12dB,给模拟端AGC留出足够的调整空间。接受-6dB的数字增益损失,然后通过增加LNA/BB LPF后面的VGA增益来补偿,这样信噪比损失远小于FIR正向增益导致的AGC误判。

注意:FIR里的gain字段是在数字域做乘加之后的缩放,它影响的是送入后级基带处理器的数据幅度,但不影响模拟前的噪声匹配。只要模拟链路增益的分配是合理的,数字域损失几个dB完全可以通过基带算法里的自动增益控制补回来。

4.3 设备树/Petalinux工程里预置FIR系数的处理

再来看设备树和Petalinux工程里的问题。别看AD9361是射频芯片,它跟嵌入式处理器之间的接口配置同样重要。Linux下如果用ADI的IIO驱动,FIR系数可以通过设备树节点的adi,fir-config-no-os这类属性预置。但新的Petalinux工程结构里,设备树改了之后,固件重新编译不一定能把新的coefficient数据打包进驱动加载路径,经常遇到的现象是:设备树里明明写了FIR系数,启动之后用IIO工具读回系数,发现还是默认值。

我排查这个问题的经验是,先确认两件事:第一,你改的是不是被编译进内核设备树二进制(DTB)里的设备树源文件,因为Petalinux工程里很可能有多份dtsi,有些被include,有些是残留;第二,IIO驱动里的固件预置逻辑有些版本只处理adi,fir-config属性,有些版本还需要配合adi,fir-taps-index这类索引字段。如果不匹配,驱动会静默跳过系数加载。

另外,设备树里写的系数值必须是量化后的整数值,而且格式是数组元素从低到高排列。如果你从MATLAB导出的系数是浮点小数,直接拷进去根本不起作用。这里有一个快速的核对方法:启动Linux后在/sys/bus/iio/devices/iio:deviceX目录下找到相位相关的属性节点,把读回的值跟你的量化系数文件逐行对比。如果对不上,说明加载路径有问题,从设备树和驱动的版本兼容性入手查;如果对上了,那问题大概率是在上层应用里,你读数据的DMA通道可能配置错了。

5. 从滤波器设计到系统优化的几个进阶思路

FIR滤波器设计到这里算是通了,但从"能跑通"到"性能调优",中间还隔着一段距离。我在最后分享几个从工程实践中总结出来的进阶方向。

第一,在FPGA端做滤波器辅助。AD9361的FIR是带限滤波器,它不擅长做匹配滤波。如果你做的是BPSK/QPSK接收机,匹配滤波(RRC匹配)放在FPGA里更合理,输入的采样率正好是符号速率的整数倍,匹配滤波的输出直接就是最佳采样点。AD9361的FIR只需要做到"别让邻道干扰进ADC",剩下的匹配交给FPGA,这样每一级各司其职,性能更容易做高。

第二,用多相结构来节省功耗。如果你在FPGA里自己写FIR,别忘了AD9361输出的是并行的I/Q数据,采样率可能是几十MSPS甚至上百MSPS。直接用一个高速的普通FIR结构实现,占用的DSP Slice会很多。但因为你只需要在符号速率这个点上做判决,所以可以把FIR设计成多相结构,每一相只在特定的时间相位上做乘法累加,这样DSP资源直接除以抽取倍数。这个思路跟"匹配滤波降速处理"是同一个道理。

第三,调FIR之前先调好BB LPF。AD9361的BB LPF截止频率和FIR通带是联动的。我的经验是:先根据信号带宽设置BB LPF,让它比信号带宽高30%左右,比如10MHz信号,BB LPF设13MHz或14MHz;然后观察FIR输出端的频谱,如果带外有凸起,再回头调BB LPF,而不是反复改FIR系数。因为模拟滤波器的滚降不是理想砖墙,FIR很难完全补偿它留下的"尾巴"。

第四,别忘了温度特性。AD9361的模拟LPF和混频器增益会随温度漂移,FIR是数字的不会漂,但信号链整体的电平匹配会漂。量产品在-40℃到+85℃时,最好各跑一轮FIR输出功率的标定,确认AGC能包住漂移。我在项目验收时遇到过低温下接收灵敏度掉3dB的情况,最后查出来就是BB LPF的截止频率在低温下往低走了一段,导致信号高频分量被削掉,EVM恶化。后来把BB LPF带宽调宽10%,问题就解决了。

最后说说我对AD9361 FIR设计整体节奏的体会。不要一上来就精雕细琢滤波器系数,先把整个链路用默认滤波器调通,用频谱仪确认信号链本身没问题,再去优化FIR。等到优化FIR时,每次只动一个参数,改一次测一次,记录实测响应和仿真响应的差异。到这一步,你对这颗芯片的信号链理解就真正成型了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询