ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AD9361 FIR滤波器设计全解析:从系数生成到No-OS/Linux实操

AD9361 FIR滤波器设计全解析:从系数生成到No-OS/Linux实操 接手一块AD9361板卡最容易被忽略也最容易翻车的环节往往不是射频前端而是数字域里的FIR滤波器设计。AD9361作为射频捷变收发器片内集成了完整的数字下变频/上变频链路FIR就是这条链路里唯一可以由你完全掌控的“音色旋钮”它决定了通带多宽、带外抑制多狠、群延迟曲线长什么样直接关系到你BPSK、QPSK甚至更高阶调制信号的EVM和解调成功率。这篇文章主要围绕AD9361 FIR滤波器设计展开从链路结构、指标计算、系数生成到No-OS和Linux/Petalinux下的实操配置再到我实际调试中踩过的坑一次性讲清楚。适合正在用AD9361做定制带宽收发链路、或者被半带滤波和可编程FIR组合搞到头大的嵌入式射频工程师参考。1. 先搞清楚FIR在AD9361里的位置1.1 接收链路ADC出来的信号要过哪些滤波AD9361是零中频架构RF信号经过LNA、混频之后直接落到基带I/Q再经过可编程增益放大器后进入ADC采样。ADC出来之后可不是直接就能往DMA里丢的中间隔了一整条数字信号处理链。典型顺序是ADC输出 → HB3 → HB2 → HB1 → FIR → 可编程抽取 → 数字接口。这条链路可以理解成两级设计半带滤波器负责“粗拆”用固定系数把高频分量快速砍掉同时完成2倍抽取可编程FIR负责“精修”按照你项目需要的信道带宽、阻带衰减、通带平坦度去塑形。三个半带滤波器HB1/HB2/HB3每一级都是2倍抽取而且可以各自旁路。FIR则更灵活支持1/2/4倍抽取最大抽头数以官方手册为准我常用的配置里128 taps足够覆盖绝大多数应用。数据速率的关系是输出数据速率 ADC采样率 ÷ (HB3抽取比 × HB2抽取比 × HB1抽取比 × FIR抽取比)。比如ADC采样率61.44 MSPS三个半带全开FIR再抽2那输出就是61.44 ÷ (2×2×2×2) 3.84 MSPS。这个公式很重要你会反复用到。1.2 发射链路和接收镜像但方向相反发射链路是接收的镜像数字基带数据先经过插值然后送入FIR再经过HB1、HB2、HB3最后进入DAC。FIR在这里承担脉冲成型和预加重的作用半带滤波器负责把DAC输出端的镜频压下去。很多项目只盯着接收FIR忽略了发射FIR。实际上发射FIR设计得过宽DAC的镜频抑制会不够频谱模板测试很难过设计得过窄又限制了调制带宽和符号速率。而且发射和接收的抽取/插值倍率必须和整个时钟树匹配否则会出现“接收好好的发射出去别人收不到”这类诡异问题。1.3 为什么不能全靠模拟滤波器有人会问AD9361前端不是有模拟低通滤波器吗把模拟LPF阶数做高一点不就行了答案是工程上行不通。模拟滤波器阶数越高面积、功耗、器件失配都跟着上来阻带深度还受工艺和温度影响。今天调好的参数明天板子热了可能就飘了。数字FIR没有器件容差一组系数在-40℃到85℃表现完全一致调试时改系数不用换电容电阻重新烧个固件就搞定了。但有一点必须明确AD9361前端模拟LPF不能省它的作用是抗混叠、防止带外强信号把ADC顶到饱和。如果模拟域就饱和了数字域再狠也救不回来。这就是为什么你会看到AD9361参考设计里既有模拟LPF又有数字半带和FIR它们各管一段谁也替不了谁。2. FIR滤波器参数设计从指标到系数2.1 先定指标再选抽取倍数我见过不少新手上来就打开MATLAB设计一个“看起来不错”的低通滤波器然后往AD9361里一填结果链路就是不通。正确顺序是先定系统级指标再算滤波器参数最后生成系数。举个例子一个BPSK解调项目符号速率1 MspsADC采样率30.72 MSPS我们期望基带输出数据速率15.36 MSPS。这时FIR抽取倍率选2就合适三个半带滤波器也要一起参与抽取最终把速率从30.72降到15.36。BPSK信号主瓣宽度是2倍符号速率即2 MHz双边带宽考虑脉冲成型余量通带可以定在±0.7 MHz左右阻带起始点设在±1.5 MHz这样过渡带大约0.8 MHz。带外抑制要求60 dB以上通带纹波不要超过0.1 dB。这里有一个工程常识通带宽度和符号速率之间要留够余量尤其是BPSK这种恒包络但主瓣比较宽的调制方式FIR通带设成跟信号带宽一样星座点一定会发散。后面我会专门讲这个现象。2.2 阶数到底怎么算滤波器阶数取决于过渡带宽度和阻带衰减。过渡带越窄、阻带衰减越大需要的抽头数越多。工程上可以用凯泽窗近似公式估算N ≈ (Astop - 7.95) ÷ (2.285 × Δω)其中Δω是过渡带宽度归一化到2π的值。以上面的例子来算输入到FIR的数据速率是15.36 MSPS过渡带0.8 MHzΔω 2π × 0.8 ÷ 15.36 ≈ 0.327 rad/sample。Astop取60 dBN ≈ (60 - 7.95) ÷ (2.285 × 0.327) ≈ 69.6取整大概是70个抽头。工程上我不会卡着70去设计直接上128 taps把过渡带做陡一点、阻带衰减再压深一点反正AD9361的FIR资源足够。这里要提醒一句抽头数越多群延迟越大环路延迟也越大。做简单点对点通信无所谓但如果要做闭环或对时延敏感的系统阶数就要有所取舍。2.3 系数生成和归一化处理我一般用MATLAB的Filter Designer或者直接用等纹波设计函数生成系数。等纹波设计比窗函数设计在同等阶数下阻带衰减更均匀更适合AD9361这种可编程FIR。fs 15.36e6; Fpass 0.7e6; Fstop 1.5e6; Apass 0.1; % dB Astop 60; % dB d fdesign.lowpass(Fp,Fst,Ap,Ast, Fpass, Fstop, Apass, Astop, fs); Hd design(d, equiripple); b Hd.Numerator; % 归一化到0 dB直流增益 b b / sum(b); % 转16位有符号补码 b16 round(b * 2^15); b16(b16 32767) 32767; b16(b16 -32768) -32768;生成的b16就是可以直接填进AD9361寄存器或者No-OS配置数组的系数。注意这里的归一化是除以sum(b)让直流增益为1而不是除以max(abs(b))。两者区别在于除以max会把通带增益压到0 dB以下白白浪费动态范围除以sum能保留通带增益同时确保系数不溢出。另外AD9361的FIR系数格式是16位有符号定点数范围[-1, 1)写入时要用补码形式。用MATLAB导出时负数会显示成补码值比如-1对应0xFFFF这个在C数组和设备树里都要注意别拿原码直接填。3. 实操在AD9361上把FIR跑起来3.1 推荐用ADI官方Filter Design Wizard自己手算系数然后填寄存器当然可以但效率不高。ADI官方提供了一个叫Filter Design Wizard的工具专门用来给AD9361生成整条滤波链路配置包括时钟树、半带抽取组合、FIR抽取倍率、FIR系数一步到位。操作流程大概是先设置参考时钟频率和本振频率再设定需要的RX/TX数据速率工具会自动帮你搭配半带和FIR的抽取/插值倍率。然后在FIR参数页面填通带、阻带、纹波和衰减工具会实时显示系数长度和预估的群延迟。最后直接导出C头文件、寄存器脚本或者设备树片段非常方便。这个工具的最大好处是不会算错时钟链。手动算的话半带和FIR组合错了初始化时AD9361直接给你返回错误或者输出速率对不上查半天都查不到原因。3.2 No-OS工程里如何配置FIR如果你用的是ADI的No-OS驱动配置FIR很简单。打开ad9361_config.c修改这几个地方#define RX_FIR_DECIMATION 2 #define TX_FIR_INTERPOLATION 2 #define RX_FIR_NUM_TAPS 128 #define TX_FIR_NUM_TAPS 128 int16_t rx_fir_coeff[RX_FIR_NUM_TAPS] { 0x0001, 0xFFFE, 0x0002, // 示例系数实际用工具生成 }; int16_t tx_fir_coeff[TX_FIR_NUM_TAPS] { 0x0001, 0xFFFE, 0x0002, };然后在主流程里初始化顺序非常关键先ad9361_init做时钟链和基础配置再调用ad9361_set_rx_fir_config和ad9361_set_tx_fir_config下发系数最后设置采样率和增益。如果你的工程里顺序反了或者只配置了RX没配置TX初始化极大概率会失败。我给一个调试建议第一次跑通时把所有半带旁路、FIR旁路先把最原始的链路打通确认ADC/DAC和DMA没问题。之后再一级一级使能HB3、HB2、HB1最后再开FIR。这样做的好处是一旦哪一级把信号搞坏了频谱上一眼就能看出来。3.3 在Linux/Petalinux里配置FIR和设备树迁移很多实际项目不是在裸机上跑而是在嵌入式Linux里用ADI的驱动。这种情况下FIR系数的下放通常是靠设备树完成。AD9361的Linux驱动支持从设备树读取adi,rx-fir-config和adi,tx-fir-config属性里面就是16位补码系数数组。设备树片段大致长这个样子ad9361 { adi,rx-fir-config 0xFFF20000 0x00040002 0x00010003 ...; adi,rx-fir-decimation 2; adi,tx-fir-interpolation 2; };具体属性名和内核版本有关拿到一个新BSP先查Documentation/devicetree/bindings/iio/adc/adi,ad9361.yaml别凭记忆写。回到一个很多人问过的实际问题如何将AD9361原有设备树移到新建Petalinux工程里。我的做法分三步在旧工程里找出一份验证过的设备树源文件提取ad9361节点下的FIR相关属性原样复制到新工程的dtsi文件里。如果旧工程只有寄存器脚本没有设备树那就先跑No-OS把系数导出来再用ADI的Filter Design Wizard重新生成设备树片段。给新Petalinux工程编译前先确认meta-adi层的版本和内核版本匹配。不匹配的话驱动可能根本不读你填的属性名这时候去驱动源码里搜“fir-config”关键字看它实际读的属性名是什么。迁移完成后可以通过驱动暴露的调试节点检查系数有没有加载成功。拿libiio或者直接读/sys/kernel/debug/adi_axi-ad9361/下面的filter_fir_config节点打印出来的数值和设备树里填的一致才算真正生效。4. 常见问题和排查实录4.1 BPSK信号过FIR后星座发散这是我在实际项目中遇到最多的问题也是很多人卡住的第一关。现象是BPSK解调后星座点不是聚在1和-1两个点附近而是糊成一团EVM高得离谱。最常见原因是FIR通带设置太窄。假设符号速率1 Msps你把FIR通带设计成±0.5 MHz信号主瓣的能量被削掉一大块码间串扰自然大。BPSK符号速率1 Msps时主瓣零点就在±1 MHz通带至少要放到±0.7 MHz以上实际我经常放到±0.8 MHz配合发射端成型滤波接收端EVM会明显改善。另一个容易被忽略的原因FIR系数左右顺序写反了。数字滤波器的系数顺序决定了滤波器的相位响应写反了会出现群延迟异常、波形变形。检查方法很直接把I/Q两路分别抓下来对比两者波形是否一致不一致就查系数数组。如果发射和接收都经过FIR还要注意两级滤波的总带宽。发射FIR和接收FIR的级联等效带宽如果小于信号带宽星座照样发散。所以我会先把接收FIR通带放宽确认星座聚拢后再逐步收紧找到一个不牺牲EVM的最窄带宽。4.2 级联半带和FIR之后的增益与削波问题整条数字链路是级联的HB3、HB2、HB1每一级都有增益FIR也有自己的增益配置。很多人在配置完FIR后发现信号顶到削波或者信号太小动态范围不够十有八九是增益预算没算清楚。我建议调试时先把AGC关掉固定手动增益灌一个点频单音进去然后抓IQ看FFT。观察主峰幅度如果到0 dBFS附近就往下压一点增益留3到6 dB余量防止后面调制信号峰值过来时削波。FIR系数归一化到直流增益为1理论上不引入额外增益但半带滤波器各级之间可能存在bit growth预留余量最稳妥。注意AD9361内部有自动增益控制可以补偿链路增益变化但AGC生效需要时间。滤波链路本身如果发生削波不是AGC能救回来的。调FIR之前先保证手动增益下不削波再开AGC做系统联调。4.3 通带纹波对EVM的影响很多人设计FIR时只盯阻带衰减对通带平坦度不上心默认0.1 dB和1 dB没区别。实际上通带纹波直接变成幅度误差进入EVM预算。粗略估算一下0.1 dB纹波带来的幅度误差大约1.1%0.3 dB纹波大约3.4%对QPSK可能还能忍对256QAM这类高阶调制就是灾难。我一般把Apass设在0.05到0.1 dB之间。这样设计出来的FIR阶数会比1 dB纹波多十几个抽头但AD9361有128 taps预算这点开销完全可以接受。代价是过渡带可能稍微变宽需要靠阻带起始频率调整来平衡。调试的时候可以抓一个单音扫频信号看带内频谱的起伏是否和设计指标一致不一致就先查系数类型和量化是否到位。4.4 设备树迁移时最容易踩的坑新Petalinux工程include旧设备树最典型的问题有三类。第一类是属性名变了。内核版本升级后驱动从adi,fir-config改成adi,rx-fir-config和adi,tx-fir-config旧文件复制过来驱动完全不认链路初始化直接跳过FIR。别猜直接在驱动源码里搜“fir-config”看到底读哪个属性。第二类是系数数组长度对不上。驱动按rx_fir_num_taps指定的长度读取数组如果你的设备树数组比预期短后面会自动补零滤波器响应就完全变了。我遇到过有人把128 taps的系数只复制了前64个结果通带直接扭曲花了两天才排查出来。第三类是补码格式问题。设备树里16位有符号数可以写成负数比如-2写成0xFFFFFFFE也可以直接写0xFFFE取决于驱动解析方式。我之前从No-OS工程转换到设备树时就因为在负数表示上没对齐导致整个FIR频响不对。最稳的办法是用ADI官方工具导出设备树片段不要手工改二进制补码。4.5 顺手讲个容易混的概念LCL滤波器搜“滤波器设计”的时候很容易搜到LCL滤波器参数设计的内容。这里我特意说一下LCL滤波器是功率变换器输出侧的模拟滤波器用来滤PWM开关纹波设计时关注谐振频率、阻尼电阻、电感电流纹波和AD9361里的数字FIR完全不是一个领域。做射频板的人别拿LCL的思路来调数字FIR做电源的人也别把AD9361的FIR当成LCL来用。概念分清楚项目才不会跑偏。调试遇到问题的时候多想想是模拟域的问题还是数字域的问题。AD9361把模拟和数字封装在一起很容易让人混淆。我的经验是先用频谱仪看模拟中频或基带输出确定前端没问题再抓数字IQ数据在MATLAB里做FFT看频谱最后才下结论是不是FIR的问题。定位清楚再动手效率比盲目试系数高得多。最后分享一个我自己调AD9361 FIR的习惯。拿到新板卡我不会一上来就花一整天设计高性能滤波器而是先把所有数字滤波全部旁路用最粗的链路把数据跑通抓频谱确认信号链是通的然后一级一级使能HB3、HB2、HB1最后才把FIR打开。这样哪一级把信号搞坏了在频谱上一眼就能看出来。FIR要动最好一次只改一个参数改完看频谱和EVM再改下一个。这类板级调试的问题九成都是配置组合问题而不是系数本身算错了。希望这篇AD9361 FIR滤波器设计的经验能帮你少走点弯路。
返回列表