ARTICLE DETAIL

资讯详情

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

STM32三重ADC交错采样:用定时器触发实现3倍采样率

STM32三重ADC交错采样:用定时器触发实现3倍采样率 测高速信号单颗ADC不够用的时候很多人第一反应是换更高采样率的芯片或者外挂独立ADC。但你手头可能只是一颗STM32F103或者F407那三重ADC交错采样就是一条不花钱提性能的路子。我大概三年前在一台电机控制器上用过这个方案最近又在调超声波回波采集踩了不少坑把能说的都整理一下。这篇文章会从原理拆起讲清楚为什么三个ADC串起来采样率就能翻三倍再给一套能直接抄的CubeMX配置和代码骨架最后是调试中会遇到的典型问题和排查思路。适合已经会基础ADC采集、想让采样率再往上走一档的工程师也适合刚接触DMA和定时器触发的同学参考。1. 三重ADC交错采样的核心原理1.1 为什么单ADC采样率会卡脖子讨论交错采样之前先把单颗ADC的瓶颈说透。STM32F103这类芯片ADC时钟最高14MHz12位分辨率下转换一次大约需要1us换算过来就是最大约1Msps。F407好一点ADC时钟能到36MHz同样的12位转换也就能跑到2.4Msps左右。问题在于实际项目里很多时候这个采样率就是不够。比如电机控制里看相电流的高频纹波或者超声回波信号处理信号本身有个几十kHz甚至上百kHz的频率分量。按奈奎斯特定理采样率至少要两倍于信号最高频率实际工程里往往要留5到10倍裕量。一旦信号频率到100kHz以上单ADC就开始吃力了。有人会问直接上连续采样模式让ADC自己不停转换不就行了连续模式确实能逼近单ADC的极限速率但你要在每次转换完成中断里搬数据中断频率上去之后CPU几乎干不了别的。很多刚接触ADC的人一开始按这个思路做最后发现采样率反而更低因为中断响应和数据处理把系统拖垮了。所以真正可靠的方案是DMA搬运加硬件触发然后再在这个基础上想办法提高等效采样率。1.2 交错采样的本质三个人轮流干活交错采样的思路其实特别朴素一个人干活太慢那就开三个窗口按顺序轮流接待。三个ADC各自按照自己的转换速度工作只要把它们的启动时刻错开让一个ADC在转换的时候另外两个ADC分别处于不同的转换阶段那么在时间轴上每隔三分之一转换周期就会有一个ADC输出新的采样结果。这里有个理解上的关键点很多人会搞错。交错采样并不是把一个ADC跑满三倍速度而是三个ADC并行工作每个ADC自己还是原来的转换节奏但最终合并出来的数据流采样密度变成了原来的三倍。打个比方单ADC的转换周期如果是3us那三个ADC交错之后时间轴上每us就有一个采样点等效采样率就是3倍。这个倍数直接等于参与交错的ADC数量。用文字示意一下时间关系触发时刻: t0 t1 t2 t3 ADC1: [转换] [转换] [转换] ADC2: [转换] [转换] [转换] ADC3: [转换] [转换] [转换] 采样点: t0, t0T/3, t02T/3, t0T, ...这里的T是单个ADC的转换周期T/3就是经过交错之后等效出来的采样间隔。数据合并之后采样率从1/T提升到了3/T效果非常直接。1.3 适用场景与性能边界三重ADC交错采样最适合的场景是信号频率比单ADC采样率低四五倍、但又不足以用单ADC安全采样的那些场合。典型的比如三相电机相电流同步采集、逆变器输出纹波监测、振动信号分析、音频和超声处理。这类应用往往需要同时在多个点高速采样三重ADC模式还能顺便解决多通道同步的问题。但也要说清楚边界。交错采样不会改变ADC本身的量化精度它提升的只是时间密度。另外触发信号的相位精度直接决定采样时刻准不准如果触发信号本身抖动大交错后数据的时间轴就会不均匀后面做FFT之类分析时会引入额外的噪声。这个问题后面讲实操的时候再展开。2. 实现方案对比芯片自带交错模式还是定时器多通道触发2.1 芯片内置三重模式的套路STM32的F1、F3、F4系列里都有多重ADC模式可以配置成规则同步、注入同步、交替触发等不同组合。部分系列还支持内置的交替/交错采样模式大致思路是把三个ADC配置成采样同一个通道然后通过寄存器设置让ADC1转换完成后自动触发ADC2ADC2完成后再触发ADC3形成一条链。这个方案的好处是寄存器配置相对集中触发链由硬件内部自动管理不需要自己设计相位错开逻辑。缺点是对我来说不够直接不同系列的配置位和触发行为有差异手册里的描述又比较绕调试的时候很难直观判断当前时刻到底是谁在采样。而且这种串联触发的方式实际能达到的采样时间间隔受限于ADC转换完成再触发下一级的时序相位间隔可调范围有限并不是所有项目都适合。还有一个隐藏问题内置三重模式下数据寄存器的排列格式比较特殊有的系列会把两个ADC的结果打包进同一个32位寄存器拆数据的时候要仔细对照手册。我身边有人在这个模式下被数据排列坑过波形一塌糊涂最后不得不换方案。2.2 定时器多通道触发方案我实际项目里长期用的是另一个思路同一个定时器用三个比较通道产生三个相位错开的比较事件分别作为三个ADC的外部触发源。定时器计数到比较值的那一瞬间OCxREF信号产生一个边沿ADC收到这个边沿就启动一次转换。这个方案的好处非常明显采样时刻完全由自己控制。想错开三分之一周期就把三个比较值放在周期的三分之一、三分之二、末尾。想调整相位改比较值就行。调试时用示波器看PWM输出或者手动改变比较值能直观看到采样点移动。这比黑盒式的内置交错模式好排查得多。另外用定时器还有一个天然优势很多应用本身就带着PWM输出。比如电机控制里TIM1或者TIM8做PWM驱动本来就要发PWM波现在只要把比较值安排在需要采样的时刻ADC和PWM天然对齐这在中心对齐PWM模式下做电流采样特别顺手可以精确控制采样点在PWM周期里的位置。2.3 两个方案怎么选我的建议是如果你只是想快速验证三重ADC能不能用项目对采样时刻精度要求不高可以试试芯片内置交错模式配置少代码量小。如果你要做的是电机控制、电源控制这类对采样时刻有明确要求的项目或者信号频率对相位误差敏感直接用定时器三通道触发不要犹豫。还有一点要多说一句不要试图用软件延时去做三路时间错开。软件延时的抖动太大最终合并的数据时间轴不均匀FFT之后全是杂散基本没法用。硬件定时器触发是底线。3. 实操基于CubeMX和HAL库的定时器三通道触发三重ADC3.1 CubeMX配置步骤下面这套配置我以STM32F4系列为例F1系列操作基本一致就是ADC时钟上限不同、DMA通道编号不同具体以你用的型号手册为准。先配置时钟树。F407的ADC时钟最高36MHz设成36MHz就好。F103则要压在14MHz以下。这个时钟直接决定单ADC最高采样率别为了省事往高了调超过上限ADC转换结果稳定性会明显下降。接着启用三个ADC。三个ADC都打开相同的通道。这里要注意一个问题同一个模拟输入信号要接到三个ADC上需要看你的芯片上哪几个ADC通道共用同一个引脚。比如有些引脚同时映射到ADC1和ADC2但不一定能同时映射到ADC3。如果找不到共用的引脚简单粗暴的做法是把信号分成三路走线直接接到三个ADC各自的引脚上。模拟输入阻抗比较高并联问题不大但走线尽量短地线要干净。每个ADC的配置要点分辨率三个ADC必须一致通常12位扫描模式关闭因为这里每个ADC只采一个通道连续转换模式关闭用外部触发单次转换外部触发源ADC1选TIM1_CH1ADC2选TIM1_CH2ADC3选TIM1_CH3具体对应关系要看你用的型号CubeMX下拉列表里会写清楚触发极性选上升沿触发采样周期三个ADC设置成相同值。这一点很重要如果采样保持时间不同采样时刻会有偏差等效采样间隔就不均匀DMA配置要单独说。三个ADC分别配一条DMA模式选Circular循环模式数据宽度Half Word内存地址自增。DMA的中断优先级建议配高一点后面讲问题的时候会解释为什么。F4系列注意把DMA配置到正确的StreamCubeMX会自动分配但你要确认这三个Stream不是同一个控制器下的同一通道避免总线冲突。定时器配置我以TIM1为例。TIM1是高级定时器适合做ADC触发。打开三个比较通道模式用PWM1模式即可。比较值先随便填后面代码里会再计算。这里生成的PWM输出虽然会占用引脚但实际ADC触发用的是内部比较事件引脚不接负载也没关系。排列好触发时刻采样率什么的不考虑先把结构跑通。3.2 定时器相位参数计算触发频率和相位差的计算是这部分的核心。设定时器计数时钟频率为f_cnt定时器重载值为ARR那么一个触发周期的时长T_trig是T_trig (ARR 1) / f_cnt一个触发周期里三个ADC各采一次所以等效采样率f_eq就是f_eq 3 / T_trig 3 * f_cnt / (ARR 1)举个例子F407主频168MHzTIM1挂在APB2上如果APB2定时器时钟是168MHz预分频设84-1计数时钟就是2MHz。想得到等效采样率300kHz也就是T_trig 10usARR就设20-1即计数器从0计到19共20个计数周期正好10us。三个比较值的相位位置按如下分布最均匀CC1 (ARR1) / 3 CC2 2 * (ARR1) / 3 CC3 ARR 1即下一个周期的起点实际写作脉冲值就是ARR如果ARR1不能被3整除优先保证前两个间隔接近T_trig/3最后一个间隔略有差异。高性能场景下计数时钟尽量高一点可以减小取整带来的量化误差。比如刚才的例子ARR20计数时钟2MHz三分之一周期的量化步长是0.5us。如果比较值取整后误差达到0.5us对300kHz信号来说相位误差就比较明显了。所以建议把计数时钟提高让ARR值尽量大取整误差占比变小。确定参数后在初始化代码里更新比较值htim1.Instance-CCR1 arr / 3; htim1.Instance-CCR2 arr * 2 / 3; htim1.Instance-CCR3 arr;注意ARR本身和比较值的精度CubeMX生成的初始化代码是在HAL_TIM_PWM_Start时装载的如果在运行中修改要重新设置CCR并确保更新事件触发。3.3 三路DMA数据交织合并三个ADC各自独立进行DMA传输结果分别存在三个缓冲区里。从时间轴上看同一轮三个缓冲区的数据顺序是ADC1先采样、ADC2次之、ADC3最后所以最终交织后的数据序列就是把三路数据按索引交错拼接。缓冲区定义和DMA启动#define SAMPLE_COUNT 2048 static uint16_t adc1_buf[SAMPLE_COUNT]; static uint16_t adc2_buf[SAMPLE_COUNT]; static uint16_t adc3_buf[SAMPLE_COUNT]; static uint32_t interleaved_buf[SAMPLE_COUNT * 3]; void start_acquisition(void) { HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc1_buf, SAMPLE_COUNT); HAL_ADC_Start_DMA(hadc2, (uint32_t *)adc2_buf, SAMPLE_COUNT); HAL_ADC_Start_DMA(hadc3, (uint32_t *)adc3_buf, SAMPLE_COUNT); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_3); }DMA传输完成后的回调里做交织void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC1) { for (int i 0; i SAMPLE_COUNT; i) { interleaved_buf[3 * i 0] adc1_buf[i]; interleaved_buf[3 * i 1] adc2_buf[i]; interleaved_buf[3 * i 2] adc3_buf[i]; } // 此时 interleaved_buf 里就是从低频到高频排列的完整时间序列 process_samples(interleaved_buf, SAMPLE_COUNT * 3); } }实际项目里如果采样频率很高DMA中断会非常频繁在回调里做大量数据处理不现实。通常的做法是把DMA配置成双缓冲或者环形缓冲在半传输和全传输两个中断点各处理一半数据数据处理放到主循环里做。F4的DMA支持双缓冲模式可以充分利用。3.4 采样率与转换时间的匹配关系这部分容易出问题。交错采样虽然把采样间隔压缩了但每个ADC本身完成一次转换仍然需要时间。如果触发间隔小于ADC转换时间ADC上一次转换还没结束新的触发又来了转换结果就会乱套。以F407为例ADC时钟36MHz12位分辨率采样周期设3个周期那么总转换时间是31215个ADC时钟周期约0.417us。三个ADC交错后等效采样间隔是0.139us等效采样率约7.2Msps。但单个ADC的实际转换周期是0.417us三个样本间隔如果靠定时器精确错开0.139us那么同一个ADC在启动一次转换之后要等3×0.1390.417us才会收到下一次触发刚好满足转换时间约束。所以参数设计的核心约束就是T_trig / 3 ADC单次转换时间即(ARR 1) / f_cnt / 3 T_conv算的时候记得给裕量DMA搬运和总线仲裁也要时间一忙起来ADC结果寄存器的读取延迟可能比理论值大留出10%到20%的余量比较稳。4. 常见问题与排查技巧实录4.1 波形频域出现大量毛刺触发时刻不均这是交错采样最典型的问题。三路数据合并后如果相位间隔不均匀波形看起来可能连续得很好但做FFT之后信号频率附近会出现明显的杂散分量。问题根源往往是定时器比较值取整误差太大或者三个ADC的触发源没有真正共用一个定时器。排查方法是先让信号接地用DMA采一段数据随机数分布应该均匀无规律再接一个已知频率的标准正弦波对比FFT结果。如果杂散分量高于正常水平把定时器计数时钟提高重新设比较值。另一个容易被忽略的原因是DMA搬运时有优先级冲突三个ADC的结果到达缓冲区的时间不是按采样顺序来的。解决办法是把DMA优先级逐一调高并查看三个DMA中断标志位确认优先级仲裁没有把某一路DMA饿死。4.2 采样值漂移周期性跳动电源和基准不稳ADC参考电压VREF如果不够干净采样结果会跟着漂。这个问题在单ADC的时候也存在但交错采样把采样率拉高后高频噪声更容易暴露出来。常规做法是在VREF引脚加RC滤波或者在VDDA和VSSA之间加磁珠和电容。另一个容易被忽视的是PCB布局ADC模拟输入走线尽量短远离数字信号线。特别是定时器PWM输出的IO如果走的不是内部触发而是外部引线数字信号的翻转换会耦合进模拟输入造成采样值周期性跳动。如果漂移幅度不大只是轻微波动可以先确认触发信号本身是否干净。用示波器看触发信号的上升沿如果有明显振铃可能是走线阻抗不匹配需要加端接电阻。4.3 数据乱序三路缓冲区和实际时序对不上刚把程序跑起来的时候先不要接真实信号直接把三个ADC输入接地或者接3.3V然后回读缓冲区。接地的那路缓冲区应该全部是接近0的值接高电平的那路应该是接近4095的值。这样能快速确认三路DMA数据是否和ADC对应关系一致。很多时候乱序是因为CubeMX里DMA通道配置跟代码里回调判断不匹配。比如ADC1的数据实际到了ADC2的缓冲区但回调里仍然按ADC1的索引去交织。遇到这种情况把回调里的判断换成检查具体DMA的传输完成回调或者直接用串口把三路缓冲区前几帧数据打印出来对照着看。4.4 DMA中途丢数据中断响应不及时高采样率下DMA缓冲到达一半时触发半传输中断如果中断里做的事情太多另一路DMA的传输完成事件可能被长时间挂起缓冲区数据来不及拷贝出现覆盖。解决思路是优先级。DMA中断优先级要比普通外设中断高同时中断回调里只做数据搬移不要做FFT、滤波这些耗时操作。如果还是丢数据就用DMA双缓冲模式一轮DMA在搬数据的时候另一轮缓冲的数据在主循环里慢慢处理让采样和计算彻底解耦。4.5 硬件触发源连接不上CubeMX里配置ADC的外部触发源时有时候会发现下拉列表里找不到想要的那个定时器比较事件。这大概率是定时器时钟没有使能或者定时器本身没有配置PWM模式。ADC的触发源映射依赖定时器的内部信号定时器不启动ADC永远等不到触发。另外有些型号上ADC1的外部触发源和ADC2、ADC3可选列表不一样不是所有比较通道都能配给所有ADC。查看数据手册里“ADC external trigger reference”表格确认当前型号支持哪几个。如果发现你要用的组合不可用只能换一个定时器或者换一路比较通道。5. 一些调试技巧和经验补充调试三重ADC交错采样我的建议是先低速再高速。最开始把定时器周期放到100us级别三路数据慢慢采用串口打印原始值确认接线、DMA、交织逻辑全部正确再缩短触发周期往高速走。不要一上来就奔着7Msps去不然问题混在一起很难定位。还有一个小技巧工程验证阶段可以用信号发生器输入一个正弦波用串口或者无线模块把交织后的数据传到上位机画波形图。这时候能很直观地看到相位对不对、间隔均不均匀。如果你做的是FFT分析建议在数据合并后加一个窗函数比如汉宁窗可以压掉一部分周期截断带来的频谱泄漏。最后说一句个人感受三重ADC交错采样这个方案配置本身不复杂真正花时间的是调试数据时序和排查噪声问题。遇到波形异常不要慌一步步把触发、DMA、数据排列、模拟前端分开验证问题其实都比较好定位。这套东西弄明白之后再做双ADC同步采样、中心对齐PWM采样点控制这类需求思路会顺很多。
返回列表