ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

手把手DIY多用途DDS信号发生器:AD9834+STM32软硬件实战

手把手DIY多用途DDS信号发生器:AD9834+STM32软硬件实战 上周调试一个压电麦克风的放大电路示波器夹住输出想测频率响应结果翻遍桌子只找到一块只能蹦出1kHz固定正弦波的破板子。那一刻我就下定决心必须做一台真正适合日常调试的Multipurpose Signal Generator。这台东西做出来之后帮我把一堆之前需要到处借设备才能干的事全干了输出正弦波、方波、三角波线性或对数扫频当简易频率计用还能给传感器激励和音频电路提供信号源。这篇文章不是复述数据手册而是把我从选型、搭电路、写固件到调通的完整过程写出来适合正在纠结“要不要自己DIY一台信号发生器”的人参考。做之前我也查过各大电商发现小台表级的DDS信号源动辄大几百上千块寄存器和旋钮还做得稀烂。自己动手做一台成本能压到两百元以内还能按自己的调试习惯定功能。更重要的是一台“多用途”信号发生器不该只是信号源它应该是一个能覆盖日常百分之八十调试场景的基础工具台。这篇文章里我会把每个关键器件的选型理由、电路设计思路、固件调度逻辑和实测数据全部摊开没有藏着掖着的东西。1. 设计目标一台工具级信号发生器到底要什么1.1 从一次糟糕的调试经历说起我那块压电麦克风放大板空载输出正常一接上后级电路就衰减得厉害。当时手头有示波器但缺一个能拉出频率响应的信号源。借来的信号发生器是台老古董只能输出10Hz到1MHz的固定正弦波扫频功能还是个机械电位器驱动转起来输出频率抖得人头皮发麻。没办法我只能一个频率一个频率地手动记录数据花了整整两个小时才画出一张粗糙的频响曲线。那段经历让我意识到日常调试真正需要的不是“一个正弦波源”而是能快速切换波形、扫频范围可设置、频率步进可编程、还能顺便监测输出的综合信号工具。正因为如此我决定把项目定位成“多用途信号发生器”而不是仿照别人的玩具做一个只会输出几个固定波形的板子。1.2 我先定下的五大硬指标在做任何电路设计之前我把需求列成了一张表。这个习惯建议每个人都有否则做到一半就会被细节带跑。指标项目标值制定理由输出频率范围0.1Hz ~ 10MHz正弦覆盖音频、传感器、开关电源、低频通信调试输出幅值0 ~ 10Vpp高阻负载0.5dB步进衰减可驱动大多数运放、供电电路0.5dB步进做频响测试够用波形种类正弦、三角、方波、脉冲支持扫频与调制日常调试最常见波形再多就是花架子附加功能频率计、输出静音、超时保护解决“有源无源”“负载保护”等实际需求交互方式OLED显示、旋转编码器、按键、USB串口脱离PC也能用接PC做批量扫描更方便这五项指标相互影响比如频率上限定了10MHz意味着DDS芯片时钟必须至少25MHz输出放大器的增益带宽积也至少要50MHz以上幅值到10Vpp意味着运放的供电至少要有±6V。这些都是在选型阶段要一起考虑的。1.3 方案选型DDS、MCU DAC 和纯模拟的三角博弈市面上常见的信号源方案基本可以归成三类。方案优点缺点适不适合本项目DDS专用芯片AD9834/AD9833频率控制精确扫频能力强内置正弦查找表输出幅值小需要外围放大和滤波非常适合MCU内置DAC 查表生成波形任意波形能力强元器件最少带宽受限频率不准确容易抖动做低速任意波形还行做信号源不够专业分立模拟555 运放 电容调谐极便宜电路透明频率漂移大调节困难扫频基本别想只能应付玩具级需求我最终选了AD9834作为核心理由有三它内部有完整的NCO数控振荡器频率字是28位的在50MHz参考时钟下频率分辨率可以做到0.186Hz第二它支持FSK和PSK调制扫频时只要通过SPI快速改写频率寄存器就能实现平滑扫频第三它自带SIGN BIT输出可以直接产生方波省掉一路比较器。主控用STM32F103C8T6性能对这类任务绰绰有余而且网上资料多后期想扩展任意波形也方便。2. 硬件链路DDS、运放和衰减器的关键连接2.1 频率源核心AD9834的NCO工作原理很多人第一次接触DDS时会被“相位累加器”这个概念唬住其实打个比方就懂了。NCO像一块快速转动的表盘表盘上有2^28个刻度每个时刻指针指向的位置就是相位表盘每转一圈就输出一个完整周期的波形。频率字就是“每个参考时钟周期里指针跳过的格数”。跳的格数越多转完一圈越快输出频率越高。所以频率和频率字的关系是fout (FREQREG × FMCLK) / 2^28反过来给一个目标频率就能算出要写入寄存器的值uint32_t freq_to_reg(double freq, double mclk) { return (uint32_t)((freq * 268435456.0) / mclk); // 2^28 268435456 }AD9834内部有两个28位频率寄存器FREQ0和FREQ1控制接口是3线SPI16位一帧。写频率寄存器时需要先写高14位再写低14位。我在固件里把这两次写操作封装成一个函数保证原子性避免扫频过程中寄存器被撕裂成“高字节是新值、低字节是旧值”的怪错误。这里要提一个比较容易踩的坑AD9834的参考时钟我一开始用了50MHz无源晶振结果输出波形有轻微抖动。后来换成50MHz有源晶振抖动立刻小了很多。原因是无源晶振的振荡波形上升沿不够陡DDS内部采样时相位噪声会变大。如果想再追求一点精度可以直接换成温补晶振TCXO几十块钱频率稳定性会好一个数量级。2.2 从0.6Vpp到10Vpp输出放大器的选型与补偿AD9834的IOUT引脚输出的是电流信号典型满量程电流是4mA在320欧外部电阻上转换出来的电压大约0.6Vpp左右。这个幅值对MCU或者耳机听个响没问题但作为信号源远远不够。我的方案是两级放大。第一级用AD8065做跨阻加同相混合结构把0.6Vpp放大到3Vpp左右第二级用OPA690做增益约3.3倍的末级放大器把信号推到10Vpp。为什么不用一级直接放大17倍因为单个运放在10MHz附近兼顾不了这么高的增益常见运放的开环增益滚降会把带宽压到可怜的几百kHz。分两级之后每一级只负责几倍的增益带宽压力小很多还方便在中间加入低通滤波器抑制DDS的镜像频谱。选运放时我对比过AD8065和OPA690参数AD8065OPA690增益带宽积145MHz500MHzG2压摆率180V/us1800V/us输出电流90mA190mA供电范围±12V±5V~±12V噪声较低稍高AD8065做前级主要看中它的JFET输入噪声低OPA690做末级是因为它能输出较大电流配合50欧负载时不容易塌陷。但OPA690在G2以上才稳定如果像我前面说的第二级增益是3.3倍完全没问题。两级中间我放了一个截止频率约12MHz的七阶椭圆低通滤波器。这个滤波器的作用是滤掉DDS输出里的阶梯杂散和镜像频率。如果省掉它信号源输出的“正弦波”用频谱仪看会有一堆间隔为参考时钟频率的毛刺做音频频响测试时容易把假的峰当成真的峰。2.3 衰减器与50欧输出阻抗匹配信号发生器最容易被新手忽略的就是输出阻抗。大多数仪器约定输出阻抗是50欧意思是信号源内部有一个50欧的源阻抗。当你用同轴电缆接到一个50欧的示波器或者被测系统时源阻抗和负载阻抗分压输出幅值会从显示值减半。这个特性不是bug反而是保证信号完整性的标准做法。我做衰减器的思路是用PE4312数字衰减器六位SPI控制0.5dB步进最大衰减31.5dB。把它放在第二级放大器之后、输出BNC之前这样既能控制输出幅值又能提供相对恒定的输出阻抗。为什么不直接用继电器切换电阻网络因为继电器体积大、响应慢而且触点抖动会造成毛刺PE4312是GaAs工艺响应几十纳秒控制方便唯一的缺点是贴片封装手工焊需要一点耐心。还有一个细节是输出端必须串一个50欧电阻到BNC中心针。这个电阻的作用是隔离输出电容和外部电缆的分布电容避免高频段出现振铃。我一开始为了省事没串结果5MHz以上的方波过冲严重串上之后立刻改善。2.4 电源设计摆脱地环路和纹波的双重夹击运放需要正负电源我最终使用了12V DC输入用TPS65130把电压转换成6V和-6V给运放供电再用LM1117-3.3生成数字部分的3.3V。这个方案的优点是TPS65130内部有开关稳压效率高而且正负两路输出可以独立调节我通过反馈电阻把两路都调整到了6.2V给运放留了一点余量。电源部分最头痛的问题是地环路。DDS的模拟地和数字地如果处理不好输出波形上会叠着一层高频开关噪声。我的做法是PCB上一整块底层地然后模拟地和数字地在芯片附近用0欧电阻单点连接所有开关电源的反馈采样点都尽量靠近负载侧。实测下来6V上的纹波大约15mVpp-6V上约20mVpp对信号源来说可以接受。如果后续想做成电池供电我建议用两节18650串联作为输入而不是单节18650升压。单节升压到大电流输出时纹波很难压下去容易污染信号链路。3. 多用途功能如何落地扫频、调制、脉冲与频率计3.1 扫频模式线性与对数步进设计扫频是信号发生器最核心的“多用途”功能之一。测滤波器频响、测扬声器阻抗、测放大器带宽都要靠它。AD9834的扫频原理很简单就是按固定时间间隔改频率寄存器。但步进怎么设计直接决定了扫出来曲线好不好看。我做线性扫频时频率从fstart到fstop总时长T秒步数N则每步间隔为T/N每步频增量为(fstop-fstart)/N。比如从100Hz扫到1MHz1秒扫完步数设成2000相当于每隔0.5ms改一次频率字。对SPI来说压力不大但对MCU的主循环就是考验。对数扫频更适合声学和滤波器测试因为人耳和很多滤波器都是按对数频率感知的。我实现的时候按“每十倍频程步数”来算比如每十倍频程200步。这样低频段不会漏细节高频段也不会因为步进过密导致扫得慢。计算步进频率的公式是double log_step total_duration / (decades * steps_per_decade); for (int i 0; i decade_count * steps_per_decade; i) { double f f_start * pow(10.0, (double)i / steps_per_decade); write_frequency(f); delay_us(log_step * 1000000.0); }实际测试中AD9834在100MHz SPI时钟下每次改频大约耗时2微秒扫频切换足够快。3.2 FSK和PSK调制AD9834的硬件特长AD9834内置了两个频率寄存器和两个相位寄存器通过FSELECT和PSELECT引脚可以快速切换。这意味着我不需要让MCU连续改写寄存器只需要用GPIO翻转两个引脚就能在两种频率或两种相位之间二选一。这个特性非常适合作FSK调制例如做简单无线遥控时把数据比特接在FSELECT上DDS的输出就是FSK信号。相位调制也是同理把数据比特接到PSELECT上输出在两种相位之间跳变。不过要注意的是AD9834的相位跳变是瞬间完成的如果跳变点在波形峰值附近输出会有较大的瞬态冲击外部设备可能受不了。所以我在固件里做了一种“过零点切换”策略先读取当前相位累加器的值等相位接近零点时再把PSELECT置位。这需要抓时序但实测能明显减少毛刺。3.3 幅度调制DAC加VGA的方案AD9834的短板是没有内置AM功能但“多用途”信号源不能缺幅度调制。我的做法是在DDS输出级之后加一级VGA压控增益放大器用STM32的DAC输出音频信号来控制放大增益。具体型号我用的是AD8331模拟增益范围-4.5dB到43.5dB足够做深度调制。控制电压每伏对应的增益变化大约是50dB/V所以我做了个DAC输出电压的线性映射调制深度在0到100%之间可调。如果不想加VGA也可以用模拟乘法器AD835把DDS输出和调制信号相乘。但AD835是四象限乘法器带宽高达250MHz价格比AD8331贵不少功耗也大。对于大多数音频和低频AM应用VGA方案更省电也更容易控制。3.4 脉冲输出比较器与PWM分工“多用途”信号发生器经常要输出脉冲方波比如测数字电路、触发示波器、驱动步进电机驱动芯片等。我的方案是两条路并行低频脉冲用STM32的定时器PWM直接输出频率最高做到1MHz占空比0~100%任意设置高频方波用AD9834的SIGN BIT输出再接74LVC1G14施密特缓冲器整形频率可以到25MHz但占空比固定50%。两条路的切换在固件里自动处理用户设定了频率和占空比之后如果概率低于1MHz且占空比可调就切换到PWM模式如果频率高于1MHz就强制使用DDS方波模式并忽略占空比设置。这样设计是为了避免用户在高频区设置了一个“看起来能输出”的占空比结果实际硬件根本做不到。3.5 频率计模式输入保护、整形与测频策略信号发生器带频率计功能是锦上添花但确实很实用。我在前面板上装了一个BNC输入口信号进来先经过BAV99限幅电路和1k电阻限流防止被测电路的高压打坏MCU引脚再用TLV3501高速比较器把任意波形整形成方波最后送入STM32的定时器捕获通道。测频策略有两种低频信号用周期法即在信号的上升沿启动定时器下一个上升沿停止把定时器计数换算成周期和频率高频信号用测频法即在一个固定闸门时间例如100ms内统计输入脉冲个数。我根据50Hz作为分界点自动切换两种算法实测从1Hz到30MHz都能可靠测频。TLV3501是高速比较器输入失调电压很小但比较器的输入端也要做好抗噪声滤波否则在信号幅值低于10mV时会乱跳。4. 固件与交互让信号发生器手不抖4.1 SPI驱动AD9834的坑时序、归属和撕裂AD9834的SPI时序看起来简单但实战中有几个坑。第一是时钟极性AD9834要求SPI模式2CPOL1CPHA0也就是时钟空闲为高电平数据在上升沿采样。如果按默认的SPI模式0去驱动数据会错位一整个字节。第二是CS引脚的电平保持时间AD9834的数据手册建议CS拉低后至少等待几十纳秒再发第一个时钟否则容易误触发。我在代码里直接用软件SPI不用STM32硬件SPI原因是AD9834的控制时序要求并不苛刻软件翻转GPIO完全够用而且写频率寄存器时更容易保证“高14位低14位”不被其他中断打断。写寄存器的时候如果两个频率寄存器都用到了要注意先切到目标频率寄存器再写否则方波或FSK会出现意想不到的跳频。我建议把所有寄存器写操作封装成下面这种结构void ad9834_write_freq(uint8_t reg, double freq) { uint32_t word freq_to_reg(freq, SYSTEM_CLOCK); ad9834_cs_low(); ad9834_write_16bit(AD9834_CMD_REG_SELECT | reg); ad9834_write_16bit((word 14) 0x3FFF); ad9834_write_16bit(word 0x3FFF); ad9834_cs_high(); }4.2 主循环任务调度OLED刷新不能拖累扫频STM32F103的主频是72MHz理论性能完全够跑“OLED刷新按键扫描SPI写DDS”这类任务但实际上如果处理不好调度扫频时会看到OLED刷新导致的频率步进卡顿。我的做法是用一个5ms定时器中断作为时间基准中断里只做标志位递增不干活。主循环则按优先级处理第一优先级扫频步进更新如果当前处于扫频模式。第二优先级旋转编码器和按键事件解析。第三优先级OLED刷新每50ms刷一次。这样一来扫频的时基由定时器保证OLED刷新慢一点也只是显示上的滞后不会影响信号质量。OLED驱动我用的是SSD1306的I2C模式写一帧全屏数据大约要30ms所以如果显示刷新频率太高确实会把主循环拖垮必须降频。4.3 输出瞬态保护上电、切频、切波形时的防冲击设计信号发生器接上电源时如果DDS寄存器里是上一次的初始值输出可能瞬间跳到某个不可控的幅值直接打在耳机或者扬声器上会“砰”的一声。我的处理方式是固件上电后先把AD9834的所有输出寄存器设置为无输出状态SLEEP位置1再初始化SPI。用户按下输出开启按钮后MCU先写一个频率和幅值都很小的初始值例如1kHz、1Vpp然后再设置用户期望的参数避免直接从0跳到10Vpp。切换波形时先关闭输出使能更新DDS寄存器再重新使能输出整个过程控制在几百微秒内。这些细节看起来琐碎但在实际测试中省掉第二步的话每次上电耳机都会爆一声非常烦人。4.4 校准幅值、频率和衰减三合一DDS输出的频率由晶振决定所以频率校准本质上就是校准晶振。如果板子上的晶振是50MHz标称值但实际为49.998MHz那输出频率就会偏低约40ppm。我在固件的系统参数里存了一个“晶振频率校准系数”用GPS驯服的频率计或者高精度示波器测一个10MHz输出然后把实际频率和设定频率的比值写到这个系数里。幅值校准分两步。第一步是在默认放大倍数下用真有效值万用表测量1kHz下的输出幅度把实际Vpp和理论Vpp的比写入“总增益校准”参数。第二步是衰减器校准每一档衰减的实际衰减量都有细微误差在出厂测试时逐档测量把每档的修正值写入EEPROM。这个工作虽然繁琐但做完之后这台信号源在1kHz~1MHz范围内的输出幅值误差能控制在±0.2dB以内基本达到了入门级台式仪器的水平。5. 实测数据与调试验收5.1 频率精度实测用实验室的GPS驯频频率计测量输出频率设定值和实测值的关系如下设定频率实测频率误差100.000 Hz99.999 Hz-0.001%1.000 kHz1.000 kHz0.001%100.000 kHz99.998 kHz-0.002%1.000 MHz0.9998 MHz-0.02%10.000 MHz9.996 MHz-0.04%误差在1MHz以下基本可以忽略10MHz处有一点下降主要原因是输出放大器的带宽限制和PCB走线电容造成的相位偏差不是DDS本身的问题。如果对10MHz精度要求高可以在固件里做频率响应补偿表。5.2 波形质量THD 和 FFT 看看就知道了在1kHz输出时用频谱仪观察二次谐波和三次谐波二次谐波大约-55dBc三次谐波约-48dBc总谐波失真THD大约0.35%。这个成绩对DIY信号发生器来说已经很不错。但工作在10MHz时波形会有一点正弦过冲原因是OPA690的增益余量随着频率升高在下降。我加了输出端的10欧串联电阻和33pF并联电容做局部补偿过冲从原来的15%降到了5%以内。5.3 输出阻抗与扫频实测消除“伪匹配”陷阱前面提到50欧源阻抗的匹配问题。我用这台信号源去测一个中心频率为10.7MHz的陶瓷滤波器时第一版板子因为没有串50欧输出电阻导致滤波器带外衰减比数据手册差了将近10dB。后来串了50欧电阻用50欧同轴电缆连接带通响应才和手册完全吻合。扫频实测方面我拿它测了一个二阶有源低通滤波器扫频范围为100Hz到100kHz对数扫频每十倍频程100步。得到的结果和用网络分析仪测到的曲线对比转折频率误差小于1%。能做到这一点主要归功于对数扫频步进足够密不会漏掉峰值。5.4 外壳、面板与成品化设计调通电路之后我给它做了个3D打印外壳。面板布局从左到右是OLED屏幕、旋转编码器、四个按键BNC输出口和BNC频率计输入口在侧面供电用DC12V插座。内部用一块铝片做了屏蔽隔板把电源部分和模拟信号链路隔开。虽然3D打印外壳的电磁屏蔽效果一般但把电源部分隔开后10MHz输出时的杂散比裸板时低了3dB左右。面板丝印要是嫌麻烦可以直接买透明贴纸打印。我第一批外壳偷懒没印丝印结果用了一个月之后想按第二个按键还得看屏幕猜实在不方便。6. 如果想再进一步升级方向与经验复盘6.1 如果重新做一遍我会改哪三处第一ADC采样输入端我只会加不会减最好是每个通道都能独立校准第二输出放大器我会换成更高速的电流反馈运放比如AD8009虽然贵一点但高频平坦度会更好第三底部电池仓我会预留两节18650的位置而不是用DC适配器。这三点改动分别对应“操作体验”“高频性能”和“便携性”属于那种用一段时间之后才会意识到的痛点。6.2 低成本升级路径任意波形、双通道和更高频率这台信号源的架构其实保留了很大的升级空间。如果想把DDS芯片换成AD9834的哥哥AD9959就能实现四通道同步输出并且支持幅度相位频率同时调制那就可以做像样的I/Q信号测试了。如果只想加任意波形功能保留现有硬件把MCU换成带DAC的STM32F373在低频段直接绕过DDS用DAC查表输出任意波形升级代价也很小。更简单的方案是加一个蓝牙串口模块用手机APP控制频率和幅值做现场调试时就不用低头按旋钮了。6.3 几个值得记住的调试技巧最后分享几个我做这个项目时反复用到的技巧。晶振附近的地过孔一定要打一圈否则开关噪声容易串进DDS的地平面PE4312这种高精度衰减器供电脚旁边必须放足够大的去耦电容我放了100nF加10uF两件套校准幅值时最好用50欧负载端接因为“10Vpp开路”和“10Vpp带50欧负载”完全是两码事。每一块板子的参考晶振温漂特性不一样所以EEPROM里要多存几组温度校准表方便冬天开机和夏天开机时结果一致。我从最初那个“只想赶紧测完滤波器”的念头走到今天这台能扫频、能调制、能当频率计用的多用途信号发生器中间踩了不少坑但每一坑都让电路和固件更皮实。如果你也想做一台别急着照抄某一份开源工程先把指标清单列清楚再决定用什么芯片和电源你会发现整个调试过程会顺很多。
返回列表