ARTICLE DETAIL

资讯详情

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

STM32H7通过FSMC驱动AD7606,采样率从100k翻倍到200k

STM32H7通过FSMC驱动AD7606,采样率从100k翻倍到200k 干数据采集这行当的老哥估计都绕不开一片芯片AD76068通道、16位、同步采样简直是电能质量分析和工业采集的常青树。我最早用STM32F103加SPI驱动它感觉还行心里还觉得挺美。直到某天项目要求把每通道采样率从100k往上提我盯着SPI那根时钟线才反应过来瓶颈根本不在ADC而在接口本身。后来把方案换成STM32H7的FSMCH7官方叫FMC但大家叫习惯了总线去怼AD7606采样率直接从100k飙到200k实打实翻了一倍。这篇文章就来好好聊一聊为什么FSMC能把AD7606喂饱而SPI会卡脖子以及具体怎么接线、怎么配时序、实测数据差多少。这内容适合谁看手上正用AD7606做高速采集对采样率还有要求的工程师准备从F103升级到H7但还没想好怎么驱动外挂ADC的同学以及对STM32H7的FMC外设不熟、想找一份完整驱动案例的朋友。这篇不是让你照着抄就完事而是把里面的时间账、时序余量、踩坑逻辑都讲透你拿去调自己的板子也能举一反三。1. 采样率瓶颈为什么SPI模式跑不满200kSPS1.1 AD7606的一条总线上要串行吐128位数据先看AD7606本身。这芯片是8路同步采样的SAR型ADC单通道最高能做到200kSPS也就是说每5微秒完成一次所有通道的转换。在并行模式下数据是16根数据线DB0~DB15一次全拉出来一次读操作就是16位读8个通道就是8次并行读。但在串行模式下不是每个通道一条线而是所有通道共用一根DOUT线或者类似模式一次转换结束后8个通道、每个通道16位的数据要一比特一比特地排在DOUT上往外吐总共128个SCLK周期。问题就在这128个周期上。假设你的SPI时钟能稳定跑到25MHz那128位就是128除以25MHz等于5.12微秒这还只是读取8个通道数据的纯总线时间。AD7606的转换时间典型值是4微秒也就是说一次完整的“转换读数据”周期至少是4微秒加5.12微秒约9.12微秒折算下来最高才109kSPS。你以为芯片能跑200k结果SPI接口直接把天花板压到了100k附近连120k都困难。有人会说那把SPI时钟提到50MHz呗128位读取时间不就降到2.56微秒了吗确实加上4微秒转换时间理论上能到152kSPS但这里有几个现实约束。一是AD7606的数据手册对串行时钟有明确要求不是你想多快就多快第二点是实际布线、光耦隔离、模块上可能存在的电平转换芯片都会吃掉时序余量50MHz的SPI在普通杜邦线连接下基本没法稳定跑第三即便你运气好跑到50MHz离200kSPS还是差着一大截。这就是SPI方案的天花板。1.2 并行读取和串行读取的时间账一算就明白我们把这笔账算得更细一点。AD7606要跑满200kSPS留给一次完整采样的时间只有5微秒。而ADC本身转换就要花掉4微秒这意味着从转换结束到下一轮转换开始你只有大约1微秒的时间把所有8通道数据搬走。如果是并行总线8次16位读操作每次读操作在FSMC/FMC控制下大约在几十纳秒级别。拿STM32H7的FMC来说AHB外设时钟在系统时钟480MHz时通常配到120MHz一个周期约8.3纳秒普通读时序配3到4个周期就几十纳秒。8次读全部完成也就200到300纳秒加上中断处理开销1微秒绰绰有余。如果是SPI串行想在1微秒内搬完128位SCLK需要跑到128MHz以上且不算协议、指令开销和转换时序这在AD7606上根本不可能。所以结论非常清晰想要完整享受AD7606的200kSPS采样率并行接口是唯一合理选择。STM32H7的FSMC/FMC方案天然适合干这件事因为它把并行外部设备直接映射进内存地址空间读数据就像读一个全局变量一样硬件自动产生读时序。想跑满采样率这条路比SPI靠谱得多。2. 用STM32H7的FSMC/FMC把AD7606变成“内存”2.1 为什么是FSMC而不是GPIO模拟并行口也许有人想既然瓶颈是并行读取那我用GPIO去模拟并行时序不就行了GPIO翻转速度再快在STM32H7上单个GPIO反转也就几十兆赫兹同时控制16根数据线加CS、RD、CONVST等信号软件开销极大而且时序抖动大稍有不慎就跑飞。你想读一次数据至少要拉低RD、延时、读16根IO、再拉高RD一次这么折腾读8个通道的总时间轻松超过1微秒勉强能跑但CPU占用率和中断响应压力都非常大。如果还要同时处理协议、日志、显示系统马上卡顿。FSMC/FMC就不一样它是硬件外设内部有专门的状态机帮你完成地址建立、片选拉低、读使能、数据采样、释放等操作。CPU只需要一条LDR指令在H7上就是一行C代码FMC就会自动产生对应的NOR/SRAM读时序然后外部设备的16位数据就被搬运到CPU寄存器里。你甚至可以配合DMA高速连续读取硬件自动化程度比GPIO模拟高了一个维度。另外FMC自带独立的地址空间映射比如NE1片选对应0x60000000起始地址。这意味着你的AD7606被“伪装”成一片外部SRAM地址一到片选自动拉低读写信号自动产生。后面如果系统里还要挂一个并口LCD或者外部RAMFMC可以同时管理多个Bank扩展性也远不是GPIO模拟能比的。2.2 H7的FMC时序配置CubeMX里的几个关键参数用STM32CubeMX配置FMC来驱动AD7606步骤并不复杂关键是理解每个时序参数到底在干嘛。先说接线我的做法是AD7606的DB0~DB15对应STM32H7的FMC数据线D0~D15RD引脚接FMC的NOE读使能CS引脚接FMC的NE1片选地址0x60000000CONVST接任意普通GPIO用于触发转换BUSY接一个普通GPIO输入用于检测转换是否结束。在CubeMX里打开FMC选NOR/SRAM Controller使能Bank1的NE1。配置要点如下配置项推荐值说明Memory typeSRAMFMC按SRAM时序读写外部设备Data width16 bitAD7606数据线为16位Write operationDisable不需要往AD7606写数据Address setup time1地址建立时间约8.3ns120MHzAddress hold time0地址保持时间可设0Data setup time1~2数据建立时间这是最关键参数Bus turnaround1总线周转留一点余量重点说Data setup time。AD7606从RD下降沿到数据出现在DB总线上的时间大约在10到20纳秒量级FMC的Data setup time可以理解成从NOE拉低到采样数据之间的时间。如果设得太短比如0FMC可能在数据还没稳定时就采样了读回来的值就会有一两个位的跳变。设成1约8.3ns或者2约16.6ns在大多数走线较短的板子上都能稳定设太长了也没必要会白白拉长读周期把高采样率优势吃掉。AHB时钟我这里按120MHz记一个周期8.333ns。如果Data setup设2整个读周期大概是地址建立1周期加数据建立2周期再加上其它固定开销总共约4个周期也就是33ns左右8次读不过264ns完全够用。CubeMX生成工程后要确认HAL的FMC初始化函数里时序参数被正确填入而不是被某个无效配置覆盖。2.3 读取代码LDR一条指令就是一次采集先定义一个宏指向FMC Bank1的起始地址#define AD7606_BASE 0x60000000U #define AD7606_DATA (*(volatile uint16_t *)AD7606_BASE)启动AD7606转换一般用CONVST引脚给一个上升沿。软件触发可以直接翻转GPIO如果要跑固定采样率就用定时器的PWM输出接到CONVST频率设定为200kHz精度还高。void AD7606_StartConv(void) { HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_RESET); }转换开始后BUSY引脚会拉高转换完成自动拉低。所以读取前必须等待BUSY变成低电平否则你读到的是上一次转换的旧数据uint16_t adc_raw[8]; void AD7606_ReadAll(uint16_t *buf) { // 等待转换完成 while (HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin) GPIO_PIN_SET) { } // 连续读8个通道FMC自动完成地址片选RD时序 for (uint8_t i 0; i 8; i) { buf[i] AD7606_DATA; } }这段代码编译后每次AD7606_DATA就是一次内存读取操作编译器通常会生成LDRH指令直接交给FMC产生外部读时序。实测在没有系统优化的情况下8次读取加循环开销也就几百纳秒。如果要进一步提升可以打开-O2优化或者借助DMA让FMC在读的同时CPU继续干别的活但绝大多数场景下CPU轮询读操作已经不是瓶颈了。3. 实测对比SPI vs FSMC采样率到底差多少3.1 两支方案的测试环境与测试方法为了让你对两个方案差异有直观感受我把它们在同一个板子上各跑了一遍。测试条件如下主控STM32H750VBT6ARM Cortex-M7系统主频480MHzAHB外设时钟120MHz被采集芯片AD76068通道16位并行接口信号源10kHz、满量程约2Vpp的正弦波接到CH1两个方案共用同一块AD7606核心模块只是接口方式不同采样率目标SPI方案按实际能稳定跑到的值FSMC方案设置200kSPS每次采集1024点导出原始数据后用Python做FFT分析。SPI方案我用SPI1主机模式SCLK配置到25MHz软件片选读取函数直接用HAL_SPI_Receive循环收FSMC方案则按上面FMC配置用定时器PWM触发CONVSTPWM频率设置为200kHzBUSY下降沿触发外部中断在中断服务函数里循环读取8次。3.2 实测数据采样率、总线占用、CPU开销实测下来数据整理成表就是这样测试项SPI方案FSMC/FMC方案接口时钟/时序SCLK 25MHzFMC读周期约33ns8通道读取时间约5.12us约0.3us含循环单次采样周期约9.2us约5.0us实际最高采样率约108kSPS200kSPS受ADC上限限制CPU占用读取阶段高HAL SPI软逻辑耗时明显极低几次LDR就完事配合DMA难度用SPI DMA但每次128位数据搬运有对齐烦恼可FMCDMA但轮询已很够用配置复杂度简单中等需要理解FMC时序这里最值得关注的是“单次采样周期”。SPI方案里9.2us这个周期被两部分吃掉了4us转换时间外加5.12us读取时间两者叠加导致采样率锁死在100k出头。FSMC方案里读取时间压缩到0.3us周期几乎等于AD7606的转换时间加上信号触发余量跑200kSPS毫无压力。换句话说直接把ADC的标称性能吃满了。3.3 波形与FFT结果怎么看把采集到的正弦波数据做FFTSPI方案在100kSPS采样率下10kHz信号大约落在10/100*1024约102个bin附近波形表面看起来挺正常但如果你看频谱底噪会发现有不少毛刺。这些毛刺一部分来自SPI读取时间过长造成的等效孔径抖动因为信号是连续的读取过程被拉长等效于每次采样的时间戳有微小的不确定度体现在频谱上就是噪声底抬高。FSMC方案跑200kSPS10kHz信号落在1024点FFT的第51个bin附近因为采样率翻倍同一个模拟信号在数字域里的“时间分辨率”提高了一倍频谱会更干净噪声底明显更低。当然也有FSMC时序本身带来的少量数字噪声但整体上SNR和THD都比SPI方案要好一截。如果你做的是电能质量谐波分析这个差距会直接体现在谐波插值精度上采样率翻倍的价值非常明显。4. 实操中踩过的坑和排查方法4.1 FMC时序余量不足读到乱码FSMC刚调通那会儿我兴冲冲配置了一组看起来非常“高效”的参数Address setup为0Data setup为1全往极限压。结果读数经常是低位乱跳今天读出来是0xFFF0下次可能变0xFFE8尤其在CH1和CH2之间切换时尤其明显十有八九是FMC的Data setup时间不够数据总线还没稳定就被采样了。这个问题的排查方法很笨但很有效把Data setup从0逐步往上加每加一个值连续读1000次看是否有数据跳变。如果Data setup到2就稳定了那就不要为了省那8纳秒又把参数调回去。高速总线求的是稳定不是极限。另外务必注意PCB走线长度16根数据线要尽量等长而且不要在中间串联过大的电阻否则信号上升沿变缓也会造成采样端数据建立时间不足。4.2 BUSY和CONVST的时序配合错误有个特别坑的细节是AD7606的BUSY引脚电平逻辑。转换过程中BUSY为高转换完成变成低。我第一次读数据的时候想着“等BUSY变高表示转换完毕”结果一上电就死等怎么都进不了下一步后来翻手册才发现反了。正确时序是先给CONVST上升沿启动转换此时BUSY拉高等待BUSY下降沿下降沿一到立刻开始并行读数据。这里面还有一个潜在的坑外部中断触发读取时如果没有做防抖FMC在读数据的瞬间可能因为BUSY下降沿和读操作竞争导致数据还是旧值。稳妥做法是BUSY下降沿中断触发后加几个nop延时再执行读取循环。这几十纳秒的延时基本不影响200kSPS的性能但能让数据可靠性大幅提升。4.3 数据错位、字节序和DMA的坑并行读取方式下8次读操作按顺序对应CH1到CH8。看起来简单但如果你在用DMA或者把地址当作数组指针就可能踩错位。AD7606的并行数据输出在CS和RD同时有效时每次读操作锁存当前通道连续读自然从通道1到通道8顺序输出前提是你的FSMC地址线没有另外控制通道选择逻辑。如果板子上用了地址线去切换通道虽然AD7606本身没有地址线但有些模块为了扩展会引入多路选择器那你读出的第一个数据不一定是CH1这时候要先用示波器确认通道顺序。字节序也不要忽略。FMC在16位数据宽度下读出来的就是端序对齐的uint16_t值。AD7606输出的是二进制补码数据所以把uint16_t强转成int16_t就能直接得到带符号结果不需要做字节交换。如果你把读取结果当成两个字节分开存那就要小心大小端问题。DMA读FMC还有一个非常容易翻车的点外设地址必须固定不动内存地址递增传输数据长度是16个字节8个uint16_t数据宽度设HalfWord。一旦把外设地址配置成递增模式DMA会去访问一堆不存在的地址读回来的数据全是乱的而且非常难查。4.4 其它系统集成需要留意的细节AD7606的逻辑电源VIO可以直接接3.3V这样DB0~DB15就能和STM32H7的3.3V GPIO电平兼容不需要额外电平转换。但很多市面上买的AD7606模块会兼容5V和3.3V中间加了转换电路这些电路延时大会吃掉FSMC的时序余量所以买模块时最好选那种跳线把VIO直接短到3.3V的版本能从源头减少很多问题。另外第一次上电时AD7606内部参考电压和模拟前端需要一点稳定时间。我的习惯是在初始化FMC和GPIO之后先做一次冗余转换启动一次CONVST读完8个通道后扔掉第二次读取的数据再投入使用。这个小动作花不了几微秒但能避免上电初期参考电压没稳定导致的头几次采样数据偏大或偏小。最后如果FMC读周期配得太快你还要留意STM32H7的IO输出速度和上下拉配置。FMC数据线D0~D15和NOE、NE1对应的GPIO都建议在HAL代码里把速度设为Very High并关闭FSMC相关引脚内部上拉。这个细节很多教程不会提但实际影响很大尤其在外部线缆较长的时候速度和上下拉配置不当会直接导致时序变形让你误以为参数配置错了。结尾我个人在实际项目里折腾完这两套方案体会最深的是做硬件系统要先算账再写代码。SPI和FSMC都不是玄学优劣全在时间预算上把转换时间、读取时间、采样周期这三笔账列清楚方案选择自然就明朗了。AD7606这种老牌并行ADC赶上STM32H7这种自带高速FMC的新控制器确实能被喂得饱饱的200kSPS跑起来稳得很。如果你刚拿到板子建议按我前面的顺序一步步来先固定FMC时序参数、把读数读稳再上定时器触发最后再碰DMA和中断优化。别急着抄最优配置把稳定性验证好再谈采样率翻倍。后续如果你想在这个框架上继续扩展还可以试试FMC配合DMA做循环采样或者把多片AD7606挂到FMC的NE1和NE2上做多板同步采集那又是另一片天地了。
返回列表