ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AD9910 DDS波形发生器:原理、硬件设计与SPI配置实战

2026/9/7 13:45:22 拓冰建站 浏览量
AD9910 DDS波形发生器:原理、硬件设计与SPI配置实战 简介面向STM32与DDS开发者的AD9910波形发生器完整工程解决高频信号源设计中的波形生成、频率与幅度控制、扫频输出等需求。资源基于DDS技术覆盖1Hz~400MHz正弦波、1mV~650mV幅度调节默认500mV以及上下限频率、步进值和步进间隔可配置的扫频波并利用RAM调制模式实现4×(1~65536)ns采样间隔方波输出。压缩包共72个文件包含31个C源码与31个头文件、8个启动文件以及keil工程文件和开发文档源码与头文件对应驱动与应用程序启动文件覆盖不同STM32型号适合有STM32基础、希望快速移植或学习AD9910控制的开发者。已有2874人学习工程目录划分DDS库、APP与驱动模块并附带DDS开发文档便于理解寄存器配置和信号链设计可直接用于波形发生器类课程设计或仪器项目参考。1. 为什么是AD9910那届电赛把我逼到了DDS方案上做波形发生器最怕的不是焊板子而是方案选型。2015年那届电赛的多波形信号发生器题目出来以后很多人第一反应是正弦、方波、三角波嘛拿MCU配个DAC糊一个出来就行。真到做的时候才发现频率一上兆、幅度还要可调、波形还不能明显失真MCUDAC这条路立刻就走死了——10MHz的正弦波一个周期按20个点采样就是200MSPS的刷新率普通单片机根本没这个能力。我当时的方案核心是一颗ADI的AD9910。这颗DDS芯片内部系统时钟最高1GSPSDAC是14位的频率控制字32位满速下理论频率分辨率大约0.23Hz。这意味着只要给它一个干净的参考时钟就能稳定输出从极低频到上百MHz的正弦信号频率精度由数字逻辑保证完全不依赖模拟元件的精度。对题目里频率可设、步进可调、多波形切换这类要求DDS几乎是天然的最优解。1.1 三种常见方案的天花板动手之前我把技术路线认真比了一遍MCU直接驱动DAC实现最简单任意波形都能出但输出频率受限于MCU主频和DAC更新率上到MHz级别基本没戏只能做低频段。FPGADAC性能上限高但要自己写相位累加器和查找表逻辑还要处理高速并行数据开发周期长比赛场景下风险太大。专用DDS芯片把相位累加、查表、DAC全集成在一颗芯片里外部只要配寄存器和滤波成功率和性价比最高。专用DDS里还得再选。AD9833主频只有25MHz输出到12MHz以上波形质量明显下滑AD9851主频125MHz做中频信号源可以但10位DAC的杂散表现一般AD9959有四个通道很吸引人可它用的也是10位DAC单通道性能反而不如AD9910。AD9910价格贵一些但比赛里性能余量远比省那几十块钱重要。1.2 参数对比与选型结论方案/器件系统时钟DAC位数频率分辨率实际可用输出一句话评价MCUDAC几十MHz8~12—几百kHz适合低频任意波AD983325MHz100.1Hz左右10MHz入门DDS上限低AD9851125MHz100.04Hz30MHz左右中频正弦够用AD9959多通道10高中频多通道场景更合适AD99101GHz140.23Hz100MHz以上单通道高性能可玩性强选AD9910还有个隐藏原因它内置1024×32位的RAM支持RAM播放模式可以输出自定义波形数据。这意味着三角波、任意波都不用额外加高速DAC整套系统省掉一大块硬件后面章节会详细讲。2. DDS核心原理频率控制字到底怎么算选型定了以后我花了不少时间理解DDS的工作机制。如果只知道写个寄存器就有波形出来遇到问题会相当被动。2.1 相位累加器绕圈跑步的比喻DDS的核心是相位累加器。我习惯把它想象成一个人在圆形跑道上跑步一圈代表正弦波一个完整周期每个系统时钟沿他向前跨一步步长就是频率控制字FTW。步长越大跑完一圈需要的步数越少输出频率就越高。32位的FTW就是这个步长累加器每溢出一次就完成一个正弦周期。输出频率的公式极其简单fout FTW × fSYSCLK / 2^32反过来从目标频率求控制字FTW round(fout × 2^32 / fSYSCLK)2.2 实例计算1GHz系统时钟下的10MHz正弦我用的系统时钟是1GHz目标输出10MHz代入公式FTW 10^7 × 4294967296 / 10^9 42949672.96四舍五入后是42949673十六进制为0x028F5C29。把这个数拆成4个字节通过SPI写进FTW0寄存器再触发一次I/O_UPDATE输出端就能拿到频率误差不足1Hz的10MHz正弦波。这个换算在程序里用整数运算就能做完全不需要查表。做题目里的频率步进功能时比如要求1kHz步进每按一次按键算一次FTW写进芯片全是数字的事和模拟参数没有任何关系。频率分辨率fSYSCLK/2^32在1GHz下约0.233Hz这意味着程序里直接浮点计算后取整频率误差连1Hz都不到这种精度对模拟电路来说是不可想象的。2.3 别忘了sin(x)/x包络和镜像频率DDS看着美好但也有物理上的坑。DAC输出的本质是采样保持所以基频幅度随频率升高会呈sin(x)/x包络下降同时在fSYSCLK-fout、fSYSCLKfout这些位置出现镜像分量。如果不做后端滤波直接从DAC引脚看高频波形全是毛刺。这就是为什么AD9910输出级后面必须加一级重构低通滤波器。很多人第一次测试说芯片输出波形好脏十有八九是滤波器没加或者截止频率选得过高。滤波器设计放到硬件章节细说。3. 硬件设计电源、时钟、输出链路与PCB要点硬件部分我按电源、时钟、输出、PCB四条线来讲。这块没做好后面软件调得再欢都白搭。3.1 电源树与去耦纹波就是杂散AD9910的电源引脚分得比较细有数字3.3V、模拟3.3V、内核1.8V等几组。芯片内部虽然有稳压器可以把3.3V转成1.8V但PCB上仍然要把模拟电源和数字电源分开处理。我的电源树是这样的外部5V进来先经过一颗低噪声LDO得到3.3V再用磁珠分成AVDD和DVDD1.8V单独用另一颗LDO输出模拟域和数字域各走各的。每个电源引脚附近放三级去耦电容10uF大电容、100nF、1nF尽量靠近引脚放置。电源纹波会在DDS输出端直接变成杂散和相位噪声。我实测过同一块板子用开关电源供电比LDO供电时谐波差了接近10dB这种损失在后端很难补救所以电源这块千万别省。3.2 REFCLK与内部PLL50MHz变1GHzAD9910的REFCLK可以接外部有源晶振也可以利用内部振荡电路接无源晶振。实测下来有源晶振/TCXO的相位噪声表现更好。我用的是50MHz有源晶振通过内部PLL倍频20次把SYSCLK提到1GHz。倍频系数、电荷泵电流、VCO增益这些参数都在CFR2/CFR3里配置不同版本芯片的位定义略有差异建议以自己手头那颗芯片对应版本的手册为准。配好之后用示波器看SYNC_CLK引脚有没有时钟输出基本就能判断PLL是否正常工作。SYNC_CLK默认是SYSCLK/4有信号说明内核和PLL都在跑。如果这个脚没波形先别折腾SPI回头检查电源和时钟配置。3.3 DAC差分输出与重构滤波器DAC输出是差分电流形式满量程约20mA具体由DAC_RSET引脚对地的电阻设定典型值大约2.2kΩ对应20mA满量程。差分输出先经过一个1:1宽带变压器或巴伦合成单端再接低通滤波器。滤波器我用的是7阶椭圆低通截止频率按需要的最高基频来定。比如最高只做30MHz正弦低通截止设在4050MHz要上到100MHz就得换更高截止的滤波器或者做分段滤波。椭圆滤波器过渡带陡通带有纹波但在这个场景下那点纹波不影响使用。焊接完一定要用频谱仪扫一下通带特性别让一两个电感焊错导致高频段衰减巨大。3.4 方波怎么办外置高速比较器AD9910内部没有比较器想要方波不能指望它直接输出。我的做法是在滤波后的正弦信号后面接一颗高速比较器比如TI的TLV3501上升沿4.5ns把正弦过零整形成方波。比较器输入端加一点偏置和迟滞避免过零区域抖动输出端串一个几十欧的小电阻再往后级走既能抑制振铃也能防止长线反射。方波的频率和正弦完全一致软件上不用做任何额外处理。3.5 PCB布局的几条纪律底层整片铺地模拟地和数字地单点连接不要来回跨分割。REFCLK晶振和PLL相关引脚尽量远离数字走线最好用一小块局部地包住。DAC输出走线越短越好差分对要等长、并行走线。芯片底部散热焊盘要开窗并打过孔到地平面。AD9910全速工作时发热不小散热没做好会直接影响相位噪声和频率稳定性。4. SPI配置流程从上电到输出正弦波硬件焊好只是开始软件配置才是真正决定能不能出波的关键。4.1 SPI时序与寄存器写入函数AD9910的SPI支持三线或四线模式我用STM32硬件SPI跑三线模式主模式、8位数据、MSB先发。我习惯用模式0CPOL0、CPHA0如果发现数据对不上先用逻辑分析仪抓SCLK和SDIO的时序对照手册确认数据是不是在SCLK上升沿被锁存再决定要不要调CPHA。这个坑一旦踩进去很难定位因为指令字节和数据字节会一起错位。每个寄存器的操作分两段第一段是指令字节bit7是读写标志0写1读bit6到bit1是6位寄存器地址第二段是这个寄存器对应的数据字节字节数由寄存器宽度决定。比如FTW0是4字节POW0是2字节。核心写函数长这样void ad9910_write(uint8_t reg, const uint8_t *buf, uint8_t len) { uint8_t inst (reg 1) 0x7E; // 写指令 6位地址bit70 AD9910_CS_LOW(); spi_write_byte(inst); for (uint8_t i 0; i len; i) { spi_write_byte(buf[i]); } AD9910_CS_HIGH(); }读函数类似只是指令字节的bit7置1后面跟着读回对应长度的数据。读取功能在调试时非常有用写进去之后读一遍能立刻发现是SPI没通还是寄存器位配错。4.2 上电初始化序列我的初始化步骤固定如下电源稳定后延时至少10ms。给RESET引脚一个高电平脉冲完成主复位。配置CFR3/CFR2把PLL倍频系数、电荷泵电流、VCO增益设好使SYSCLK达到1GHz。配置CFR1选择单音模式后面做RAM模式时再改。写入FTW0、POW0、ASF设定频率、相位和幅度。给I/O_UPDATE一个上升沿。顺序不能乱尤其是复位必须先做。有人上电后直接写FTW芯片可能还在未定义状态写进去根本没反应。另外注意SPI速率不用拉到极限10MHz左右已经非常够用还不太容易受飞线干扰。4.3 I/O_UPDATE最容易被忽略的一颗暗雷第6步的I/O_UPDATE是无数人掉进去的坑。AD9910的大部分寄存器是双缓冲结构SPI写入的只是影子寄存器真正生效必须等I/O_UPDATE引脚来一个上升沿。如果只写不更新输出永远不变很多人会误以为芯片坏了反复复位、反复重写其实只是欠了一个脉冲。I/O_UPDATE可以用MCU的任意GPIO控制脉冲宽度只要大于一个SYNC_CLK周期我一般给几百纳秒非常宽裕。调试阶段我最喜欢把它封装成写完必带更新省得每次忘。5. 多波形扩展三角波、方波与RAM模式的灵活玩法正弦波搞定了接下来是比赛真正拉开差距的部分多种波形。5.1 方波比较器的实际调法方波的实现方案前面讲过软件上其实没什么事只要保证正弦幅度稳定比较器输出的方波频率就和正弦完全一致。唯一要注意的是比较器输入幅度不能太大否则可能损伤输入级。我会在比较器输入前放一个电阻分压把正弦幅度压到比较器允许的输入范围内。实测方波边沿5ns以内驱动下一级绰绰有余。5.2 三角波RAM模式的播放机制三角波我用的是AD9910的RAM模式。芯片内部有一块1024×32的RAM可以把三角波一个周期的采样点预先算好写进去然后设置为循环播放DAC输出就是三角波的阶梯近似经过低通滤波后非常平滑。播放频率的关系式是f_output fSYSCLK / (采样点数 × 地址步进间隔)1GHz系统时钟下如果三角波存满1024个点最高播放频率大约976kHz想要更高频率就得减少采样点数比如只用128个点最高能到7.8MHz左右。点数少了台阶感会变强但对比较器整形或者示波器显示来说依然够用。RAM模式的起始地址、结束地址、播放方向和步进间隔都写在RAM控制寄存器和Profile寄存器里位字段比较多容易配错。我的建议是先写一个10行以内的小程序把RAM模式只做一个固定频率的三角波跑通再去接复杂的交互逻辑。存储数据时注意一个32位字可以塞两个16位采样点高16位和低16位的组合方向不要搞反否则输出波形会完全错乱。5.3 低频段的补位方案RAM模式最大的短板是低频下不来。1024个点是上限SYSCLK又不能无限降低所以想要1kHz甚至更低的三角波只靠AD9910会比较吃力。我的处理办法是分频段几十kHz以上的三角波交给AD9910 RAM模式几十kHz以下以及任意波形比如扫频、调幅、模拟心电信号这类加分项直接用STM32内部的DAC加DMA查表输出。低速场景下MCUDAC的波形质量并不差而且代码更直观改动也灵活。至于正弦扫频这类更炫的功能AD9910还提供数字斜坡发生器DRG模式可以让频率在设定范围内自动线性扫完全不需要MCU频繁改FTW省下的CPU时间正好用来处理按键和显示。5.4 幅度调节数字与模拟的组合拳幅度控制有两条路。数字方式直接改ASF幅度缩放因子AD9910支持14位幅度调节精度高但它只改变DAC端的幅度没法覆盖后续放大链路的非线性区间。模拟方式是在滤波后加一级可编程增益放大器或者数控衰减器。比赛里我用ASF细调模拟PGA粗调的组合先把模拟链路增益设在合适的档位再用ASF做精细调节。实测下来从10mVpp到5Vpp都能稳定覆盖调节线性度比单一方案好很多。如果只靠ASF往死里压幅度输出会太小后面放大器噪声全部凸显出来波形看着就非常脏。6. 实测数据与调试避坑记录最后分享一些实测经验和排查链路这些东西datasheet里通常不会写。6.1 关键实测结果10MHz正弦输出在重构滤波器匹配良好、电源用LDO供电的前提下基波附近谐波可以压到比基波低50dB以上SFDR大概在60dBc以上对比赛应用足够了。如果把REFCLK换成高指标的TCXO相位噪声还能再低一些但如果只是普通有源晶振电源噪声就会成为瓶颈。三角波用RAM模式播放时低频端波形非常光滑高频端会有可见的阶梯但经过低通后肉眼分辨不出明显失真。方波从比较器出来上升沿5ns以内频率和正弦完全同步。整套系统连续跑上几个小时频率漂移主要来自晶振温度特性AD9910自身数字逻辑部分几乎没有漂移——这正是DDS方案比纯模拟方案最占优势的地方。6.2 高发坑位与排查链路坑一写寄存器无效。先检查I/O_UPDATE有没有给脉冲这是最高频的原因。其次检查SPI时序用逻辑分析仪抓波形和手册逐位对照。坑二读回全FF或全00。通常是CS没有正确拉低、线序接错或者SPI模式不对。先读一个固定的只读寄存器能读到正常值就说明通信链路是通的再去查具体寄存器配置。坑三输出波形频率完全不对。大概率是PLL没配置好或者SYSCLK不是预期值。看SYNC_CLK的实际频率如果和预期不符回去检查倍频系数和电荷泵电流设置。坑四高频输出幅度掉得厉害。查重构滤波器的截止频率和元件参数再用频谱仪扫一下通带。很多时候是电感焊错或者电容选错导致高频被吃掉换一颗对的元件立刻恢复。坑五发热严重导致波形漂移。AD9910全速工作发热不小散热焊盘必须处理好。如果裸板飞线调试热量散不出去输出频率和幅度都会缓慢漂移。我后来直接把散热焊盘压在完整地铜皮上问题就消失了。我的调试顺序固定成先最小系统出单音正弦确认SPI、PLL、输出、滤波四项全通再加比较器出方波最后上RAM模式。每完成一步就用示波器留截图后续出问题往回排查效率比直接堆功能高得多。这种从最小系统开始、一步一验证的习惯比任何技巧都更能保证项目按时跑通。本文还有配套的精品资源点击获取