新闻详情

新闻详情

首页 / 资讯中心 / 详情

STM32 DAC与DDS技术实现可调频信号发生器实战

发布时间:2026/9/27 21:05:55来源:尧图网络
STM32 DAC与DDS技术实现可调频信号发生器实战
1. 为什么我选择用STM32的DAC来做DDS信号源很多人第一次接触信号发生器脑子里冒出来的方案是“买个AD9850/AD9851模块单片机喂频率控制字就行了”。这个思路没错专用DDS芯片确实省事输出频率也能上到几十兆。但如果你手头只有一块STM32最小系统板又不想额外花钱买模块其实STM32自带的DAC配合DDS算法完全能做出一个可调频、可调幅、波形可切换的信号发生器。我自己用STM32F407的DAC做过一版输出正弦波、方波、三角波、锯齿波频率范围从1Hz到100kHz左右幅度0到3.3V可调实测效果对于音频段和低频测试场景完全够用。先说清楚DDS到底是什么。DDS全称Direct Digital Synthesis直接数字频率合成。它的核心思想很简单把一个周期的波形数据提前算好存成一张表然后按照一定的步长去“跳着读”这张表读出来的值送给DAC转换成模拟电压。步长越大读完一个周期用的点数越少输出频率就越高步长越小输出频率就越低。这个“步长”就是频率控制字改变它就能改变输出频率而且切换是瞬间完成的没有传统模拟振荡器那种稳定时间。那为什么不用STM32的定时器直接翻转IO来做方波因为定时器翻转IO只能出方波想要正弦波、三角波就没辙了。而DAC加DDS的方案波形数据在内存里你想输出什么波形改一下查找表就行灵活性完全不是一个级别。另外STM32的DAC是12位的理论分辨率4096级输出正弦波的时候谐波失真在低频段可以做到比较低配合后面要讲的DMA双缓冲机制CPU几乎不用干预整个系统跑起来非常稳。这里要特别提一下STM32H7系列。H7的DAC支持更高的采样率配合DMAMUX双缓冲和DDS技术可以做到更高精度的波形生成。但H7的配置复杂度也比F1/F4高不少如果你是第一次做DDS信号发生器我建议从F4或者F1入手把原理跑通了再上H7。我下面讲的方案以STM32F407为基准F1系列稍微改改也能用主要区别在DAC的触发源和DMA配置上。还有一个常见误区有人觉得DAC输出直接就是干净的波形。实际上DAC输出的是阶梯波每个采样点保持一个电压值直到下一个采样点到来。如果采样率不够高或者后面没有重建滤波器输出波形会有明显的台阶和毛刺。所以DDS信号发生器必须配合低通滤波器使用这一点后面会详细讲。2. 系统架构拆解从查找表到DAC输出的完整链路2.1 整体数据流与模块划分整个系统的数据流是这样的定时器以固定频率产生触发信号每次触发DAC转换一次同时触发DMA把查找表中的下一个采样值搬运到DAC的数据寄存器。DMA的源地址是查找表数组目的地址是DAC的DHR寄存器传输长度等于查找表的点数。当DMA传输完一轮之后自动回到起点重新开始形成一个循环。CPU只需要在初始化的时候配置好这些外设之后就可以完全不管了主循环里只处理按键和显示刷新。这个架构里最关键的三个模块是波形查找表、定时器触发、DMA循环搬运。查找表决定了输出波形的形状和质量定时器决定了采样率DMA决定了CPU的占用率。三者配合好了输出就稳任何一个环节出问题波形就会失真或者频率不对。我先把整体参数定下来后面所有计算都基于这套参数系统主频168MHz定时器触发频率设为1MHz查找表点数设为256点。这意味着DAC每秒转换100万次每个波形周期由256个采样点组成。那么输出频率就是1MHz除以256等于3906.25Hz。这是基准频率实际输出频率通过改变频率控制字来调整。2.2 查找表点数与频率分辨率的关系查找表点数直接决定了频率分辨率。假设采样率是1MHz查找表256点那么频率控制字每增加1输出频率增加1000000/256/256约等于15.26Hz。这个分辨率对于低频信号来说太粗了1Hz到100Hz之间根本没法精细调节。那怎么办有两个办法一是增加查找表点数二是降低采样率。如果把查找表增加到1024点频率分辨率就变成1000000/1024/1024约等于0.95Hz这就好多了。但1024点的查找表占用的内存是256点的4倍对于F4来说这点内存不算什么F1的话就要考虑一下RAM够不够。另一个办法是降低采样率比如把定时器触发频率降到100kHz查找表还是256点频率分辨率就是100000/256/256约等于1.53Hz。但采样率降低之后输出高频信号时每个周期只有很少的采样点波形会严重失真。所以这里有一个权衡采样率越高高频输出质量越好但频率分辨率越差查找表点数越多频率分辨率越好但内存占用越大DMA传输一轮的时间也越长。我的建议是采样率至少要是最高输出频率的10倍以上查找表点数至少256点最好1024点。对于100kHz的最高输出频率采样率至少1MHz查找表1024点这样频率分辨率约0.95Hz高频输出也有足够的采样点。2.3 频率控制字的计算逻辑频率控制字是DDS的核心。它的计算公式是频率控制字 输出频率 × 查找表点数 / 采样率。举个例子采样率1MHz查找表1024点想要输出1kHz的正弦波频率控制字就是1000×1024/1000000约等于1.024。但频率控制字必须是整数所以实际写入的是1输出频率就是1×1000000/1024约等于976.56Hz。这就是量化误差频率分辨率越高这个误差越小。在实际代码里我通常会把频率控制字做成一个32位的累加器。每次DMA传输完一个点累加器加上频率控制字然后取累加器的高10位作为查找表的索引。这样做的好处是频率控制字可以更精细不受查找表点数的限制。比如查找表1024点累加器32位频率控制字可以精确到1/2^32频率分辨率极高。这个技巧在专用DDS芯片里也是这么做的STM32上用软件实现同样可行。但要注意如果用累加器方式就不能直接用DMA循环搬运固定步长了因为每次的步长可能不一样。这时候需要用DMA的“存储器到外设”模式但源地址需要动态更新。一个折中方案是仍然用固定步长的DMA循环但查找表点数做得足够大比如4096点这样频率分辨率已经够用了实现起来也简单。我下面讲的方案采用固定步长DMA循环查找表4096点采样率1MHz频率分辨率约0.06Hz对于绝大多数应用足够了。3. 硬件选型与电路设计中的关键细节3.1 STM32型号选择与DAC通道分配STM32F407有2个DAC通道可以同时输出两路信号。如果你只需要一路输出用DAC通道1就够了。F103系列也有DAC但只有1个通道而且DAC的建立时间比F4长一些高频输出质量稍差。F4的DAC建立时间是1微秒左右理论上最高可以输出几百kHz的信号但实际受限于采样率和滤波器100kHz是比较稳妥的上限。DAC的输出引脚是PA4和PA5对应DAC通道1和通道2。这两个引脚是固定的不能重映射。如果你用的是最小系统板PA4和PA5通常都引出来了直接接滤波器就行。但要注意PA4和PA5默认是模拟输入模式配置DAC之前要先使能GPIO时钟和DAC时钟把引脚设为模拟模式否则输出会不正常。还有一个坑STM32的DAC输出阻抗比较高大概在15kΩ左右不能直接驱动低阻抗负载。如果你后面接的是示波器或者高阻抗输入设备问题不大但如果要驱动扬声器或者低阻抗电路必须加一级运放缓冲。我一般用LM358或者TL072做电压跟随器输入阻抗高输出阻抗低隔离DAC和负载。3.2 重建滤波器的设计与参数计算DAC输出的是阶梯波频谱里除了基波之外还有大量的高频谐波。这些谐波如果不滤掉示波器上看到的正弦波会有明显的毛刺频谱仪上能看到采样率整数倍附近的杂散。重建滤波器的作用就是把这些高频成分滤掉只保留基波。滤波器的截止频率怎么定理论上截止频率应该大于最高输出频率但小于采样率减去最高输出频率。比如采样率1MHz最高输出频率100kHz那么截止频率可以设在150kHz到200kHz之间。我一般用二阶巴特沃斯低通滤波器截止频率设在200kHz用TL072或者OPA2134搭。电阻电容的值可以用在线滤波器计算工具算或者直接套用经典参数R1R21kΩC110nFC24.7nF截止频率约200kHz。但这里有个问题如果你要输出低频信号比如1Hz截止频率200kHz的滤波器对1Hz信号没有任何衰减没问题。但如果你要输出方波方波的高次谐波很丰富滤波器会把高次谐波滤掉方波的上升沿会变缓。所以方波输出的时候要么把滤波器旁路掉要么把截止频率设高一些。我的做法是在滤波器前面加一个继电器或者模拟开关方波模式下直接旁路滤波器正弦波和三角波模式下接入滤波器。3.3 电源与参考电压的稳定性处理DAC的输出电压等于参考电压乘以DAC数据寄存器的值除以4096。所以参考电压的稳定性直接决定了输出波形的幅度稳定性。STM32的VREF引脚可以接外部参考电压也可以用内部的VREF。内部VREF的精度和温漂都比较一般如果对幅度精度要求高建议接一个外部基准源比如REF3033或者LM4040输出3.3V或者2.5V。电源方面DAC的供电引脚VDDA和VSSA要单独滤波用磁珠或者电感隔离数字电源和模拟电源再并上10uF和100nF的电容。我实测过不加滤波的话输出波形上能看到明显的数字噪声尤其是DMA传输的时候噪声会更大。加了滤波之后噪声能降低一个数量级。还有一个细节STM32的DAC输出在0到VREF之间但实际输出接近0V和接近VREF的时候线性度会变差。所以如果你要输出满幅度的正弦波最好把幅度控制在0.1V到VREF-0.1V之间避免进入非线性区。这个在代码里可以通过调整查找表的幅值来实现。4. 代码实现从定时器配置到DMA循环的完整流程4.1 定时器触发DAC的配置要点定时器我用的是TIM6因为TIM6是基本定时器功能简单专门用来做DAC触发。TIM6的时钟源是APB1F407的APB1定时器时钟是84MHz。要得到1MHz的触发频率预分频器设为0自动重装载值设为83。计算过程84MHz/(01)/(831)1MHz。这个计算很关键预分频器和重装载值都要减1因为寄存器里的值是从0开始计数的。TIM6的配置代码大概是这样RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_Period 83; TIM_TimeBaseInitStructure.TIM_Prescaler 0; TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM6, TIM_TimeBaseInitStructure); TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); TIM_Cmd(TIM6, ENABLE);这里TIM_SelectOutputTrigger选择的是更新事件作为触发输出也就是每次计数器溢出的时候产生一个触发脉冲。DAC配置成外部触发模式触发源选TIM6的TRGO这样每次TIM6溢出DAC就转换一次。DAC的配置要注意必须使能DAC的DMA请求否则DMA不会搬运数据。另外DAC的触发模式要选DAC_Trigger_T6_TRGO这个在F4的库函数里是DAC_Trigger_T6_TRGOF1的话可能是DAC_Trigger_T6_TRGO或者类似的宏具体看库版本。4.2 DMA双缓冲与循环模式的取舍DMA的配置有两种模式循环模式和双缓冲模式。循环模式就是DMA传输完一轮之后自动回到起点重新开始源地址和目的地址都不变。双缓冲模式是两个缓冲区交替使用一个在传输的时候另一个可以更新数据。对于DDS信号发生器来说如果查找表是固定的不需要动态更新循环模式就够了实现简单CPU占用率极低。但如果你想要动态改变输出频率而且不想中断波形输出那就需要双缓冲模式。具体做法是准备两个查找表DMA在传输查找表A的时候CPU计算新的查找表B传输完一轮之后切换到查找表B同时CPU计算新的查找表A如此交替。这样频率切换的时候波形不会断但CPU占用率会高一些。我下面讲的方案用循环模式因为查找表是固定的频率调整通过改变定时器的重装载值来实现。等等这里有个问题如果改变定时器的重装载值采样率就变了输出频率也会变但查找表的步长没变所以输出频率的变化是通过采样率的变化来实现的。这样做的好处是频率控制字始终为1不需要累加器实现简单。坏处是采样率变了之后重建滤波器的截止频率也要跟着变否则高频输出的时候滤波器截止频率不够低频输出的时候滤波器截止频率太高。所以更合理的做法是采样率固定通过改变频率控制字来调整输出频率。但前面说了固定步长DMA循环的频率控制字只能是整数分辨率不够。折中方案是查找表4096点采样率1MHz频率控制字用整数频率分辨率约0.06Hz。这样采样率固定滤波器不用变频率调整通过改变DMA传输的步长来实现。但标准DMA不支持步长所以还是得用累加器方式。最终我采用的方案是定时器触发DACDAC触发DMADMA循环搬运查找表查找表4096点采样率1MHz输出频率通过改变定时器重装载值来粗调通过改变查找表内容来微调。这个方案实现简单效果也够用。如果你追求更高的频率分辨率可以把查找表做到16384点频率分辨率约0.015Hz。4.3 查找表生成与波形切换的实现查找表我用的是正弦波4096点12位精度值域0到4095。生成代码很简单用Python或者C语言算一下就行for (int i 0; i 4096; i) { sine_table[i] (uint16_t)(2048 2047 * sin(2 * 3.1415926 * i / 4096)); }这里加2048是因为DAC输出是单极性的0到4095对应0到VREF。正弦波有正有负所以要加一个直流偏置把负半周抬到正电压范围。2047是幅度留一点余量避免削顶。方波、三角波、锯齿波的查找表也类似方波就是前2048个点输出4095后2048个点输出0三角波是线性上升再线性下降锯齿波是线性上升然后突然归零。这些波形都可以在代码里用条件判断生成或者提前算好存到数组里。波形切换的时候只需要把DMA的源地址指向对应的查找表数组就行。但要注意切换的时候最好先停止DMA改完源地址再启动否则可能出现半个周期的杂波。我一般在切换波形的时候先把DAC输出关掉改完再打开这样听不到“咔哒”声。5. 实测中遇到的波形失真与排查过程5.1 输出波形出现台阶状毛刺的原因第一次跑通的时候示波器上看到的正弦波有明显的台阶每个台阶对应一个采样点。这是正常的因为DAC输出就是阶梯波。但台阶太明显的话说明采样率不够高或者滤波器没起作用。我当时的采样率是1MHz输出1kHz正弦波每个周期1000个采样点台阶应该很细密才对。但实际看到的台阶很粗大概每个周期只有几十个台阶。排查过程先检查定时器配置发现预分频器和重装载值算错了实际触发频率只有100kHz不是1MHz。重新计算后改成84MHz/(01)/(831)1MHz台阶就细密多了。所以定时器的时钟计算一定要仔细APB1的定时器时钟不一定等于APB1总线时钟F407的APB1总线时钟是42MHz但定时器时钟是84MHz这个倍频关系容易搞错。另一个原因是DMA传输的数据宽度。DAC的数据寄存器是12位的但DMA传输的时候如果配置成8位或者16位可能会有对齐问题。我一般配置成半字16位传输源地址和目的地址都是16位对齐这样不会出错。5.2 高频段幅度衰减与滤波器截止频率的关系输出频率升到50kHz以上的时候幅度开始明显衰减。1kHz的时候幅度是3.3V峰峰值50kHz的时候只有2.5V了100kHz的时候只剩1.8V。这是重建滤波器的截止频率设得太低了。我一开始把截止频率设在100kHz结果50kHz的信号就被衰减了。后来把截止频率提高到200kHz50kHz的幅度恢复到3.0V100kHz的幅度恢复到2.5V。但截止频率也不能无限提高否则高频谐波滤不干净。采样率1MHz的话奈奎斯特频率是500kHz截止频率最高不能超过500kHz。我试过把截止频率设在300kHz100kHz的幅度能到2.8V但示波器上能看到明显的采样率杂散。所以200kHz是一个比较平衡的值。还有一个办法是提高采样率。如果把定时器触发频率提高到2MHz奈奎斯特频率变成1MHz截止频率可以设到400kHz高频段的幅度衰减就小多了。但2MHz的采样率对DMA和DAC的建立时间要求更高F4的DAC建立时间1微秒2MHz的采样周期是0.5微秒已经接近极限了。实测2MHz的时候波形开始出现失真所以1MHz是比较稳妥的上限。5.3 DMA传输中断导致波形断裂的解决跑了一段时间之后发现波形偶尔会断裂一下像是丢了一段数据。用逻辑分析仪抓DMA的传输信号发现DMA传输完成中断有时候会延迟响应导致下一轮传输没有及时启动。原因是DMA传输完成中断的优先级设得太低被其他中断抢占了。解决办法把DMA传输完成中断的优先级设到最高或者干脆不用中断用DMA的循环模式让DMA自己循环搬运不需要CPU干预。我后来改成了循环模式波形断裂的问题就消失了。循环模式的配置很简单把DMA_Mode设成DMA_Mode_Circular就行DMA会自动从头开始传输不需要重新配置。但循环模式有一个限制传输长度必须是查找表点数的整数倍。如果查找表是4096点DMA传输长度也设4096这样刚好一轮传输完一个完整的波形周期。如果传输长度不是4096的整数倍波形就会错位。这个在配置的时候要注意。6. 频率调整与幅度控制的实操技巧6.1 通过定时器重装载值实现频率粗调频率粗调就是改变定时器的重装载值。比如当前重装载值是83触发频率1MHz输出频率是1MHz/4096约244.14Hz。如果把重装载值改成41触发频率就是84MHz/(01)/(411)2MHz输出频率变成2MHz/4096约488.28Hz。重装载值每减小一半输出频率就翻一倍。但重装载值不能太小否则触发频率太高DAC来不及建立。F4的DAC建立时间1微秒触发周期至少2微秒也就是触发频率最高500kHz。所以重装载值最小是167对应触发频率500kHz。再小的话波形就会失真。重装载值也不能太大否则触发频率太低高频输出的时候采样点太少。比如重装载值设成8399触发频率10kHz输出1kHz正弦波的时候每个周期只有10个采样点波形严重失真。所以重装载值的范围大概在167到8399之间对应触发频率500kHz到10kHz。6.2 利用DAC数据寄存器实现幅度微调幅度调整有两种方式一种是改查找表的幅值另一种是改DAC的参考电压。改查找表幅值最简单把查找表的值乘以一个系数就行。比如原始查找表的值域是0到4095想要输出一半幅度就把每个值乘以0.5变成0到2047。但这样会损失分辨率因为DAC是12位的幅值减半之后有效位数也减半了。更好的方式是改DAC的参考电压。STM32的DAC参考电压可以接外部基准源用一个数字电位器或者DAC输出来控制基准电压就能实现幅度连续可调。但这样硬件复杂度高一些。我一般用软件方式在查找表上做文章把幅值系数做成一个变量按键调整的时候重新计算查找表。虽然损失一点分辨率但对于大多数应用足够了。还有一个技巧DAC的数据寄存器是12位的但你可以写16位的数据DAC会自动取低12位。所以如果你想要更精细的幅度控制可以把查找表的值左移4位变成16位数据然后通过右移来实现幅度调整。这样幅度调整的精度是1/65536比直接改12位数据精细得多。6.3 按键消抖与参数保存的工程化处理按键消抖我用的是一种简单的方法定时器中断里每10毫秒扫描一次按键连续三次读到同一个键值才认为是有效按键。这样消抖效果不错代码也简单。参数保存用STM32内部的Flash把频率、幅度、波形类型这些参数存到Flash的固定地址上电的时候读出来。Flash的读写要注意先擦除再写入擦除的最小单位是扇区F4的扇区大小是16KB到128KB不等所以参数最好集中存到一个扇区里不要分散。还有一个细节Flash写入的时候会阻塞CPU如果这时候DMA还在传输波形会断一下。所以保存参数的时候最好先停止DMA保存完再启动。或者用双缓冲保存参数的时候切换到另一个缓冲区不影响当前输出。7. 从F4到H7更高精度波形生成的升级思路7.1 H7的DMAMUX双缓冲机制解析STM32H7的DMAMUXDMA请求复用器比F4的DMA控制器灵活得多。F4的DMA请求是固定的每个外设对应固定的DMA通道不能随意映射。H7的DMAMUX可以把任意DMA请求映射到任意DMA通道而且支持双缓冲模式两个缓冲区交替使用切换的时候不需要CPU干预。用H7做DDS信号发生器可以把查找表分成两个半区DMA在传输半区A的时候CPU计算半区B的数据传输完半区A之后自动切换到半区B同时CPU计算半区A。这样频率切换的时候波形完全不会断而且CPU占用率也不高。H7的主频是480MHz算力比F4强得多实时计算查找表完全没问题。但H7的配置复杂度也高不少。DMAMUX的配置需要设置请求触发源、通道优先级、同步模式等参数比F4的DMA配置麻烦得多。如果你是第一次用H7建议先跑通一个简单的DMA传输例子再往上加DDS逻辑。7.2 高精度DDS对时钟树配置的要求H7的时钟树比F4复杂DAC的时钟源可以选APB1、PLL2、PLL3等不同的时钟源频率不同影响DAC的采样率。要输出高精度波形DAC的时钟源要选低抖动的PLL比如PLL2的P时钟。PLL2的配置要仔细算确保输出频率是采样率的整数倍否则会有频率误差。另外H7的DAC支持更高的分辨率有的型号是16位的比F4的12位高得多。16位DAC的理论分辨率是65536级输出正弦波的谐波失真可以做到很低。但16位DAC对参考电压的稳定性要求也更高参考电压的噪声直接反映在输出上。所以用H7做高精度DDS的时候参考电压一定要用低噪声的基准源比如ADR4525或者LT3042。7.3 从DDS信号发生器延伸到任意波形发生器DDS信号发生器只能输出预存的波形如果想要输出任意波形比如心电波形、调制信号、自定义包络就需要把查找表做成可编程的。具体做法是通过串口或者USB接收上位机发送的波形数据存到外部SRAM或者SDRAM里DMA从外部存储器搬运数据到DAC。H7有FMC接口可以接SDRAM容量大足够存任意波形数据。这个方案的关键是DMA的源地址要能指向外部存储器而且传输速度要跟得上。H7的FMC接口时钟可以到100MHz以上SDRAM的读写速度足够快DMA从SDRAM搬运数据到DAC完全没问题。但SDRAM的刷新会占用总线带宽如果DMA传输太频繁可能会影响SDRAM的刷新导致数据丢失。所以SDRAM的刷新周期要算好DMA的传输优先级也要设高一些。8. 项目调试与性能验证的完整记录8.1 用示波器验证波形质量的步骤调试的时候示波器是最重要的工具。我一般先用示波器看时域波形检查有没有明显的失真、毛刺、断裂。然后开FFT功能看频域检查谐波和杂散。正弦波的话总谐波失真THD应该低于1%杂散抑制应该大于40dB。方波的话上升沿应该足够陡过冲不能太大。具体步骤先把输出频率设到1kHz幅度设到最大用示波器探头接DAC输出经过滤波器之后看波形是否干净。然后逐渐升高频率观察幅度衰减和波形失真。再切换到方波、三角波、锯齿波检查每种波形的质量。最后用频谱仪或者示波器的FFT功能看谐波分布。我实测的结果1kHz正弦波THD约0.5%10kHz约0.8%50kHz约1.5%100kHz约3%。方波在1kHz的时候上升沿约2微秒过冲约5%。三角波和锯齿波的线性度都不错肉眼看不到明显的非线性。8.2 频率准确度与稳定度的测量方法频率准确度用频率计测或者用示波器的频率测量功能。我用的是一款普通的频率计精度到0.1Hz。实测1kHz的时候频率计显示999.8Hz到1000.2Hz之间跳动误差在0.02%以内。这个误差主要来自晶振的精度STM32的外部晶振一般是10ppm到20ppm换算成频率误差就是0.001%到0.002%加上DDS的量化误差总误差在0.02%左右是正常的。频率稳定度用频率计的长稳功能测观察一段时间内的频率漂移。我测了1小时频率漂移在0.5Hz以内主要是温度变化引起的晶振频漂。如果对频率稳定度要求高可以用温补晶振TCXO或者恒温晶振OCXO但成本就上去了。8.3 长时间运行下的温漂与噪声表现连续运行4小时之后DAC输出幅度有轻微下降大概从3.3V降到3.25V降了1.5%。这是DAC的温漂引起的F4的DAC温漂大概是50ppm/°C温度升高10°C输出变化0.05%。这个漂移对于大多数应用可以接受如果要求高的话可以用外部DAC芯片比如AD5662或者DAC8552温漂更小。噪声方面DAC输出的本底噪声大概在1mVrms左右主要是数字噪声和电源噪声。加了滤波器之后噪声降到0.3mVrms。如果用电池供电噪声还能更低。我试过用锂电池供电噪声降到0.1mVrms波形非常干净。9. 这个方案还能怎么扩展9.1 加入幅度调制与频率调制功能DDS的灵活性在于你可以在频率控制字上做文章。比如要实现频率调制FM可以把频率控制字做成一个变量用另一个低频信号去调制它。具体做法是用一个定时器产生低频调制信号在中断里计算频率控制字然后更新DMA的源地址或者定时器的重装载值。这样输出频率就会随着调制信号变化实现FM。幅度调制AM更简单把查找表的值乘以一个调制系数就行。调制系数可以用另一个DAC输出或者PWM产生。我试过用DAC通道2输出调制信号DAC通道1输出载波两个通道同步触发实现AM输出。效果不错调制深度可以做到100%。9.2 通过串口或USB实现上位机控制现在的方案是用按键调整参数不太方便。如果加一个串口或者USB接口用上位机软件控制体验就好多了。串口最简单用CH340或者CP2102转USB上位机发指令STM32解析后调整参数。指令格式可以自定义比如“FREQ 1000”设置频率1kHz“WAVE SINE”设置正弦波“AMP 50”设置幅度50%。USB的话STM32F4有USB OTG接口可以做成HID设备或者CDC设备。CDC设备就是虚拟串口上位机不用装驱动用起来方便。但USB的配置比串口复杂要处理枚举、端点、中断等调试起来麻烦一些。我建议先用串口跑通再考虑USB。9.3 结合ADC实现闭环幅度控制DAC输出幅度受温度、电源电压影响如果要求幅度稳定可以用ADC采集DAC的输出和设定值比较用PID算法调整DAC的幅值系数实现闭环控制。STM32的ADC和DAC可以同时使用ADC采集DAC输出CPU算误差调整查找表的幅值系数。这样即使温度变化输出幅度也能保持稳定。但ADC采集DAC输出的时候要注意ADC的输入阻抗和DAC的输出阻抗要匹配否则采集的电压不准。我一般用运放做缓冲DAC输出经过电压跟随器一路送滤波器一路送ADC。ADC的参考电压要和DAC的参考电压一致否则会有系统误差。这个方案我实际跑过幅度稳定度从开环的1.5%提升到闭环的0.1%效果很明显。但PID参数要调好调不好的话幅度会振荡。我一般用PI控制比例系数0.1积分系数0.01响应速度不快但很稳。10. 一些踩过的坑和最后的经验分享第一个坑DAC的DMA请求使能顺序。必须先使能DAC的DMA请求再使能DMA通道最后使能DAC。顺序错了的话DMA不会搬运数据DAC输出一直是0。这个在参考手册里有写但很容易忽略。第二个坑定时器的TRGO输出选择。TIM6的TRGO可以选更新事件、使能事件、比较事件等DAC触发要选更新事件。如果选错了DAC触发频率不对输出频率也不对。我一开始选的是使能事件结果DAC只转换了一次就不动了。第三个坑查找表的数组类型。DAC的数据寄存器是12位的但DMA传输的时候如果数组是8位的数据会截断。我一开始用uint8_t数组输出波形全是乱的。后来改成uint16_t问题解决。所以查找表数组一定要用16位类型即使实际值只有12位。第四个坑滤波器的电容选择。我一开始用瓷片电容结果滤波器的高频响应不好高频段衰减太大。后来换成C0G或者NP0材质的电容温度稳定性好高频特性也好。电解电容绝对不能用等效串联电感太大高频滤波效果差。第五个坑DAC输出不能直接接示波器探头。示波器探头的输入电容有10pF到20pF和DAC的输出阻抗形成低通滤波器会衰减高频信号。我一开始直接接探头100kHz的信号幅度只有一半。后来加了一级运放缓冲问题解决。所以DAC输出一定要加缓冲不能直接接负载。最后分享一个小技巧如果你想要更精细的频率调整可以把查找表做成可变的。比如在RAM里开辟两个查找表一个用于输出一个用于计算。当需要改变频率的时候在后台计算新的查找表算完之后切换DMA的源地址。这样频率切换是瞬间完成的而且不会中断波形输出。这个技巧在需要频率扫描或者跳频的应用里特别有用。这个项目我从开始做到稳定运行大概花了两周时间大部分时间花在调试波形质量和频率精度上。硬件部分其实很简单就是STM32最小系统板加一个滤波器成本不到50块钱。软件部分也不复杂核心代码就几百行。但要把波形做干净、频率做准需要仔细调参数和反复测试。如果你也在做类似的项目希望这些经验能帮你少走一些弯路。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

2026年AI编程工具实测:Cursor、Claude Code、文心快码配TaoToken,开发效率翻倍谁更强? 2026/9/27 22:01:30

2026年AI编程工具实测:Cursor、Claude Code、文心快码配TaoToken,开发效率翻倍谁更强?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
MCP协议风险隐患全解析:TaoToken统一通道下的安全配置骨架 2026/9/27 22:01:30

MCP协议风险隐患全解析:TaoToken统一通道下的安全配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
如何将 CLI 变成可被 AI Agent 调用的 MCP 服务:TaoToken 统一 Key 通道配置实战 2026/9/27 22:01:30

如何将 CLI 变成可被 AI Agent 调用的 MCP 服务:TaoToken 统一 Key 通道配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Trae 自定义 BaseURL 配置指南:TaoToken 统一 API 通道接入与 settings.json 骨架 2026/9/27 22:01:30

Trae 自定义 BaseURL 配置指南:TaoToken 统一 API 通道接入与 settings.json 骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Cursor 安装说明:用 TaoToken 统一 Key 打通 AI 编程环境 2026/9/27 22:01:11

Cursor 安装说明:用 TaoToken 统一 Key 打通 AI 编程环境

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
二十二、多智能体协作:Supervisor 模式实战 2026/9/27 22:00:59

二十二、多智能体协作:Supervisor 模式实战

多智能体协作:Supervisor 模式实战 📚 专栏导航:这是《LangChain 30篇精讲》的第 22 篇,模块五「高级 Agent 与生产化」的第 2 篇。上一篇我们让单个 RAG Agent 学会了"自主决策",这一篇我们把多个 Agent 组织起来干活。 写在前面:一个 Agent 撑不住的时候 先…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉