ARTICLE DETAIL

资讯详情

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

基于STM32F407与AD9833的幅频特性测试装置设计与实现

基于STM32F407与AD9833的幅频特性测试装置设计与实现 1. 幅频特性测试到底在测什么为什么值得用STM32F407加AD9833来做幅频特性测试这件事说白了就是给一个被测网络滤波器、放大器、谐振电路、传感器调理电路等喂一串不同频率的正弦波然后记录它在每个频点上的输出幅度和相位最后画出幅度随频率变化的曲线。这条曲线决定了这个网络能不能用、用在哪、带宽够不够、有没有谐振峰。传统做法是拿一台函数信号发生器和一台示波器手动一个频点一个频点地调、读、记测一个20Hz到200kHz的音频滤波器几十个点下来手都酸了还容易读错数。我这次做的这套装置核心思路就是用STM32F407当主控用AD9833这块DDS芯片产生可编程正弦激励信号再用STM32内部的ADC把被测网络的响应采回来通过串口或屏幕把幅频曲线输出出来。整套东西成本不到两百块精度对于教学实验和一般工程调试完全够用。关键词里提到的STM32F407、AD9833、ADC、DAC、幅频特性正好对应了这套装置的四个核心环节主控、信号源、采集、数据处理。为什么选STM32F407而不是F103这是第一个要讲清楚的问题。F407主频168MHz带FPU浮点运算单元做FFT或者计算幅值时不用软浮点速度快一个数量级它的ADC是12位、最高2.4Msps在ADCCLK36MHz时配合DMA可以做到不丢点连续采样定时器资源丰富TIM6可以专门用来触发DAC或作为采样时基。这些特性在测幅频特性时非常关键因为你要在宽频范围内保证采样率与被测频率的匹配否则会出现严重的频谱混叠。AD9833则是一块性价比极高的DDS芯片SPI接口输出正弦/三角/方波频率分辨率达到0.1Hz25MHz晶振时输出频率范围0到12.5MHz。它的输出幅度是固定的约0.6Vpp所以后面必须加一级可调增益的运放或者程控放大器才能适应不同被测网络的输入要求。这一点很多初次做的人会忽略直接拿AD9833的输出去驱动被测电路结果小信号被测网络根本推不动。这套装置适合谁适合电子类专业的学生做课程设计或毕设适合硬件工程师做滤波器调试的辅助工具也适合想入门STM32ADCDDS联合应用的开发者。它不追求实验室仪器的精度但胜在原理透明、可修改、可扩展你能清楚地知道每一个数据点是怎么来的。2. AD9833的SPI驱动与频率字计算从寄存器到实际输出2.1 AD9833的寄存器结构与写入时序AD9833内部有五个寄存器控制寄存器16位、频率寄存器0和1各两个16位共28位有效、相位寄存器0和1各12位有效。上电后默认输出是关闭的必须先把控制寄存器的B28位置1选择28位频率写入模式然后分两次写入频率寄存器的低14位和高14位。SPI的时序要注意AD9833的SCLK最高40MHz但FSYNC片选的下降沿到第一个SCLK上升沿之间需要至少40ns的建立时间。STM32F407的SPI1挂在APB2上84MHz分频后SCLK可以设到21MHz或10.5MHz都远低于40MHz上限所以时序上很宽裕。但有一个坑AD9833要求FSYNC拉低后数据在SCLK的下降沿被采样所以SPI的CPOL和CPHA要设成CPOL0、CPHA1即模式1或者CPOL1、CPHA0模式2具体看你的接线和手册时序图。我实测用模式2CPOL1CPHA0最稳。写入顺序不能乱先写频率低14位BIT13到BIT0再写频率高14位BIT13到BIT0每次写入时控制寄存器的B28位必须为1。如果B280则只能写14位频率分辨率会降到0.1Hz以下对于低频段测量不够用。2.2 频率字计算公式与实测偏差修正AD9833的输出频率公式是f_out (FREQREG × f_MCLK) / 2^28其中f_MCLK是外部晶振频率通常用25MHz。FREQREG是28位频率字范围0到2^28-1。比如要输出1kHzFREQREG 1000 × 2^28 / 25000000 10737.41824取整后写入10737实际输出频率为10737 × 25e6 / 2^28 999.9998Hz误差0.0002Hz完全可以忽略。但实测中我发现一个问题如果直接用25MHz晶振AD9833的输出频率在低频段比如10Hz以下会有轻微抖动原因是DDS的相位累加器在低频时每次累加值很小量化噪声相对明显。解决办法是在低频段改用频率寄存器1并配合相位寄存器做微调或者干脆在输出端加一个低通滤波器把高频杂散滤掉。我用的是一阶RC低通截止频率设在200kHz对音频段测量没有影响。另一个坑是AD9833的输出幅度。手册标称0.6Vpp但实测在1kHz时约0.58Vpp在100kHz时降到0.52Vpp高频段衰减明显。所以如果你的被测网络需要1Vpp以上的输入必须加一级运放。我用的是OPA2134同相放大3倍输出约1.8Vpp再经过一个10kΩ电位器分压就可以手动调节激励幅度了。2.3 用STM32CubeMX配置SPI的实操细节在CubeMX里配置SPI1时有几个参数容易设错Data Size设成8位还是16位AD9833是16位寄存器但SPI可以分两次8位发也可以一次16位发。我建议用16位因为AD9833的时序要求FSYNC在16个SCLK周期内保持低电平如果分两次8位发中间FSYNC会拉高导致写入失败。所以Data Size设16位MSB First。Baud Rate Prescaler设成4分频SPI时钟84MHz/421MHzSCLK周期约47.6ns满足AD9833的40ns最小脉宽要求。Clock Polarity和PhaseCPOLHighCPHA2Edge即模式2这样SCLK空闲为高数据在第二个边沿下降沿被采样与AD9833手册一致。NSS用软件控制在代码里手动拉低拉高GPIO不要用硬件NSS因为AD9833的FSYNC需要在整个16位数据传输期间保持低电平硬件NSS在每次8位传输后会拉高不适合。配置完后生成代码在main.c里加一个AD9833_WriteRegister函数void AD9833_WriteRegister(uint16_t data) { HAL_GPIO_WritePin(AD9833_FSYNC_GPIO_Port, AD9833_FSYNC_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, (uint8_t*)data, 1, 100); HAL_GPIO_WritePin(AD9833_FSYNC_GPIO_Port, AD9833_FSYNC_Pin, GPIO_PIN_SET); }注意HAL_SPI_Transmit的第三个参数是数据长度单位是“数据帧”的个数不是字节数。如果Data Size设16位这里写1就是发16位写2就是发32位。这个细节手册里写得比较隐晦我第一次用的时候在这里卡了半天。3. ADC采样与DMA搬运如何保证幅值测量不丢点、不混叠3.1 采样率与被测频率的匹配关系测幅频特性时ADC的采样率必须满足奈奎斯特准则即采样率至少是被测信号最高频率的2倍。但实际中为了准确测量幅值建议采样率至少是信号频率的10倍以上。比如测1kHz信号采样率设10kHz每个周期采10个点用峰值检测或RMS计算都能得到较准的幅值。STM32F407的ADC1和ADC2可以独立工作也可以做双重或三重交替采样。我这次用ADC1单通道配合TIM2触发采样率通过定时器周期控制。TIM2挂在APB1上84MHz设预分频器为84-1则计数频率1MHz自动重装载值设为100-1则更新频率10kHz即采样率10kHz。这个采样率对应被测频率上限约1kHz10倍关系如果要测更高频率需要动态调整TIM2的ARR值。这里有一个关键点ADC的采样时间必须足够长让采样保持电容充分充电。STM32F407的ADC采样时间可设3、15、28、56、84、112、144、480个ADC时钟周期。ADC时钟APB2/284MHz/242MHz但手册规定ADC时钟不得超过36MHz所以实际要设分频器为4ADC时钟21MHz。采样时间设84周期则采样时间84/21MHz4us对于1kHz信号周期1ms来说4us的孔径时间引起的幅值误差小于0.1%可以接受。3.2 DMA搬运的配置与双缓冲技巧用DMA搬运ADC数据是必须的否则CPU频繁中断会严重影响实时性。在CubeMX里配置DMA2 Stream0 Channel0方向Peripheral to Memory数据宽度Half Word16位模式Circular缓冲区大小设256。这样ADC每转换完一个点DMA自动搬到数组里CPU只需要在DMA传输完成一半或全部时去处理数据。但这里有一个坑如果DMA缓冲区设256采样率10kHz则每25.6ms触发一次半传输中断和全传输中断。在中断里做FFT或RMS计算时间可能不够。我的做法是设两个缓冲区用双缓冲模式DMA搬满缓冲区A时CPU处理缓冲区B搬满B时处理A。STM32F407的DMA支持双缓冲模式Double Buffer Mode在CubeMX里勾选“Use Double Buffer Mode”即可。这样CPU有充足的时间处理数据不会丢点。另一个细节是ADC的校准。STM32F407的ADC在上电后必须执行一次校准否则零点漂移可能达到几十个LSB。校准步骤先使能ADC然后设置ADC_CR2的CAL位等待CAL位被硬件清零校准完成。这个过程在HAL库里有现成的函数HAL_ADCEx_Calibration_Start()直接调用即可。我实测不校准的话输入接地时ADC读数在20到40之间跳动校准后降到0到3之间。3.3 幅值计算的三种方法对比采到数据后怎么算幅值常见有三种方法方法原理优点缺点适用场景峰值检测找最大值和最小值取差值计算简单速度快对噪声敏感需要滤波信噪比高的场合RMS计算平方和开根号抗噪声好物理意义明确需要浮点运算速度稍慢一般测量FFT频域分析能同时得到幅值和相位计算量大需要FPU需要相位信息时我这次用的是RMS法因为F407有FPU算256个点的平方和开根号只要几十微秒。具体公式Vrms sqrt( (1/N) × Σ(x[i]^2) )其中x[i]是ADC采样值减去直流偏置后的值。直流偏置怎么来在测量前先短接输入端采256个点取平均得到Voffset后续每个点减去Voffset再算RMS。如果要测相位那就必须用FFT或者过零检测。过零检测简单但精度受噪声影响大FFT精度高但需要至少256点。我建议如果只是画幅频曲线RMS法足够如果要画相频曲线用FFT配合STM32的DSP库arm_rfft_fast_f32可以做到1024点FFT在1ms内完成。4. 从DAC到被测网络激励信号调理与阻抗匹配的实战经验4.1 为什么AD9833的输出不能直接接被测网络AD9833的输出阻抗约200Ω输出幅度0.6Vpp带载能力很弱。如果直接接一个输入阻抗1kΩ的滤波器输出幅度会降到0.5Vpp以下而且波形失真。更严重的是如果被测网络有容性负载AD9833的输出级可能会振荡。所以必须加缓冲和放大。我的方案是AD9833输出→隔直电容1uF→OPA2134同相放大3倍→10kΩ电位器→被测网络。OPA2134的带宽是8MHz压摆率20V/us对于200kHz以内的信号完全够用。隔直电容的作用是去掉AD9833输出中的直流偏置约0.3V否则放大后直流偏置会达到0.9V可能让后级运放饱和。电位器的选择也有讲究。如果用10kΩ电位器当被测网络输入阻抗也是10kΩ时电位器分压比会随频率变化因为电位器的寄生电容导致高频段幅度不准。解决办法是用低阻值电位器1kΩ或者用程控增益放大器如PGA113。我手头只有10kΩ实测在100kHz以下误差小于2%100kHz以上误差增大到5%对于一般测量可以接受。4.2 被测网络的输入输出阻抗匹配幅频特性测试中被测网络的输入阻抗和输出阻抗会影响测量结果。如果被测网络输入阻抗低比如50Ω而激励源输出阻抗高比如200Ω则实际加到被测网络上的电压只有激励源开路电压的1/5测出来的幅频曲线会整体下移但形状不变。所以如果你只关心截止频率和带宽阻抗不匹配影响不大如果要测绝对增益就必须做阻抗匹配。输出端也一样。被测网络的输出阻抗如果很高比如10kΩ而ADC的输入阻抗是几十kΩ到几百kΩ则ADC会分走一部分电流导致测量值偏低。STM32F407的ADC输入阻抗在采样时约几kΩ取决于采样时间所以建议在被测网络输出和ADC之间加一级电压跟随器如OPA2134接成跟随器输入阻抗10^12Ω输出阻抗接近0完全消除负载效应。4.3 电源与接地容易被忽略的噪声来源这套装置里有两个噪声敏感点AD9833的模拟输出和ADC的模拟输入。如果电源纹波大或者数字地和模拟地混在一起测出来的幅频曲线会有毛刺尤其在低频段100Hz更明显。我的做法是AD9833和OPA2134用独立的LDO供电比如TPS7A4700数字部分STM32、屏幕用另一个LDOAMS1117-3.3。两个LDO的输入都来自同一个5V电源但输出地线在单点汇合。PCB布局时模拟部分和数字部分分开地平面用0Ω电阻或磁珠连接。实测这样处理后ADC读数的噪声从±10LSB降到±2LSB。还有一个细节AD9833的晶振要尽量靠近芯片走线短且包地。我用的是25MHz的有源晶振输出接AD9833的MCLK引脚中间串一个33Ω电阻做阻抗匹配。如果用手册推荐的無源晶振起振电容要选对否则可能不起振或者频率偏移。5. 上位机通信与数据可视化串口协议设计与曲线绘制5.1 自定义串口协议的帧格式STM32采完一个频点的数据后需要把频率和幅值发给上位机。我用的是简单的文本协议每帧格式$FREQ,AMP\r\n比如“$1000,0.85\r\n”表示频率1000Hz幅值0.85V。为什么用文本而不是二进制因为文本协议调试方便用串口助手就能看而且不容易因为字节对齐问题出错。缺点是传输效率低但幅频测试通常只有几十到几百个点每个点20字节总共几KB115200bps的串口几秒钟就传完了。如果要传相位格式改成“$FREQ,AMP,PHASE\r\n”。上位机用Python的pyserial库读取按逗号分割存到列表里最后用matplotlib画曲线。我写了一个简单的Python脚本50行代码支持实时显示和保存CSV。5.2 频点扫描策略线性还是对数测幅频特性时频点怎么选如果线性扫描比如从100Hz到100kHz步进100Hz那低频段点很密高频段点很稀画出来的曲线在高频段会失真。正确做法是对数扫描即每个频点按比例增加比如1.1倍频f[i1] f[i] × 1.1这样从100Hz到100kHz大约需要ln(1000)/ln(1.1) ≈ 72个点低频和高频的覆盖率一致。我实测用1.1倍频画出来的曲线在转折频率附近很平滑没有明显的折角。扫描速度也要考虑。每个频点需要等待信号稳定AD9833的频率切换时间约1us但被测网络的建立时间取决于其时间常数。对于RC滤波器时间常数τRC建立到1%需要5τ。如果R1kΩC1uFτ1ms5τ5ms。所以每个频点至少等5ms再采样。我设的是10ms72个点总共0.72秒加上串口传输时间整个扫描不到2秒体验很好。5.3 用Python做上位机可视化的实操代码上位机我用的是Python 3.8 pyserial matplotlib。核心代码如下import serial import matplotlib.pyplot as plt import matplotlib.animation as animation ser serial.Serial(COM3, 115200, timeout1) freqs, amps [], [] def update(frame): line ser.readline().decode(ascii, errorsignore).strip() if line.startswith($): parts line[1:].split(,) if len(parts) 2: freqs.append(float(parts[0])) amps.append(float(parts[1])) ax.clear() ax.semilogx(freqs, amps, b-) ax.set_xlabel(Frequency (Hz)) ax.set_ylabel(Amplitude (V)) ax.grid(True, whichboth) return ax, fig, ax plt.subplots() ani animation.FuncAnimation(fig, update, interval100) plt.show()注意semilogx用对数坐标这样低频和高频的细节都能看清。如果要用线性坐标把semilogx改成plot即可。实测这个脚本在Windows和Linux下都能跑只要串口号对。6. 实测中遇到的三个典型问题与排查过程6.1 问题一低频段幅值跳动大高频段反而稳定第一次测一个RC低通滤波器时发现100Hz以下幅值跳动±5%1kHz以上跳动只有±1%。一开始以为是ADC噪声但换了屏蔽线、加了滤波电容都没用。后来用示波器看AD9833的输出发现低频时波形有轻微抖动周期性地“胖瘦”变化。排查过程先怀疑AD9833的晶振不稳换了一个有源晶振问题依旧。然后怀疑电源纹波用示波器AC耦合看3.3V电源发现低频段有约10mVpp的纹波频率约50Hz。顺着电源线查发现是USB供电的5V来自电脑电脑电源的50Hz工频干扰通过USB线串进来了。换成电池供电18650锂电池LDO低频跳动降到±1%以内。这个坑的教训是测低频小信号时电源的工频干扰是主要噪声源。如果非要用USB供电至少在USB电源入口加一个π型滤波器10uF10Ω10uF或者用带屏蔽的USB线。6.2 问题二高频段幅值突然下降以为是滤波器特性测一个声称带宽1MHz的放大器时发现在500kHz以上幅值急剧下降-3dB点出现在300kHz远低于标称值。一开始以为是放大器的问题但用信号发生器直接测放大器-3dB点在900kHz正常。排查过程把AD9833的输出直接接示波器发现500kHz时输出幅度从0.6Vpp降到0.4Vpp1MHz时降到0.2Vpp。查AD9833手册发现输出幅度随频率下降是正常现象因为内部DAC的建立时间限制。解决办法是在AD9833输出加一级高频补偿用OPA2134接成同相放大反馈电阻并联一个小电容比如10pF提升高频增益。补偿后1MHz时输出幅度恢复到0.55Vpp-3dB点回到850kHz。这个坑的教训是DDS芯片的输出幅度不是恒定的高频段会衰减。选型时要看手册的“Output Amplitude vs Frequency”曲线或者实测确认。6.3 问题三串口传数据时偶尔丢帧导致曲线缺點在连续扫描时偶尔会发现上位机收到的数据少了一两个频点曲线出现断点。排查发现是STM32在发送串口数据时如果同时有ADC中断和DMA中断串口发送会被打断导致一帧数据只发了一半。解决办法给串口发送加一个环形缓冲区在中断里把数据放入缓冲区主循环里从缓冲区取出并发送。或者提高串口发送的优先级在CubeMX里把USART1的中断优先级设得比ADC和DMA高。我用的后者把USART1的抢占优先级设为0ADC设为1DMA设为2问题解决。这个坑的教训是多中断系统里通信中断的优先级要高于采集中断否则数据会丢。但也不能设得太高否则会影响采集的实时性。一般通信优先级比采集高一级即可。7. 精度提升的几个进阶思路如果你想让这套装置的精度再上一个台阶有几个方向可以尝试。第一个是用外部ADC替代STM32内部ADC。STM32F407的内部ADC是12位有效位数ENOB约10.5位信噪比约65dB。如果换成ADS125624位ENOB约22位幅值测量精度可以提升两个数量级。但ADS1256是SPI接口采样率最高30ksps适合低频段高精度测量。第二个是用DAC输出替代AD9833。STM32F407自带两个12位DAC配合DMA和定时器可以产生任意波形包括正弦波。用DAC的好处是频率和幅度都可以软件控制不需要外部芯片。但DAC的输出频率上限受限于采样率F407的DAC最高1Msps产生100kHz正弦波时每个周期只有10个点波形失真较大。所以DAC适合低频段10kHzAD9833适合高频段。第三个是加入相位测量。用双通道ADC同时采激励信号和响应信号做互相关或者FFT可以得到相位差。STM32F407有3个ADC可以同时采两路。但要注意两个ADC的采样时刻要对齐用双重ADC模式Dual ADC Mode可以做到严格同步。第四个是自动量程切换。在被测网络输出端加一个程控增益放大器如PGA113根据ADC读数自动切换增益保证ADC始终工作在半量程以上提高信噪比。PGA113的增益范围1到1000用SPI控制响应时间约1us完全跟得上扫描速度。8. 元件选型与PCB布局的几点个人体会AD9833的晶振我试过三种無源晶振两个22pF电容、有源晶振、以及直接从STM32的MCO引脚输出25MHz。無源晶振最便宜但起振时间约10ms而且频率精度受负载电容影响大。有源晶振最稳但贵几块钱。MCO输出的时钟抖动较大测高频时相位噪声明显。最终我选了有源晶振型号是ASE-25.000MHZ-LC-T精度±50ppm够用了。运放的选择上OPA2134是音频专用带宽8MHz压摆率20V/us对于200kHz信号够用。但如果要测1MHz以上建议换OPA2350带宽38MHz压摆率22V/us或者AD8065带宽145MHz压摆率180V/us。注意高速运放的电源去耦要更严格每个电源引脚都要加0.1uF和10uF电容否则会自激。PCB布局上模拟部分和数字部分要分开AD9833的模拟输出走线要短且远离数字信号线。ADC的输入走线要包地减少串扰。晶振下面不要走任何信号线地平面要完整。我第一版PCB没注意这些测出来的曲线在100kHz以上有周期性毛刺第二版重新布局后毛刺消失。最后说一个调试技巧在AD9833的输出端和ADC的输入端各留一个测试点比如一个过孔方便用示波器直接看波形。很多问题比如幅度不对、波形失真、频率偏移用示波器一看就清楚了比盲猜快得多。我在调试时至少省了一半时间。
返回列表