做多通道数据采集的人,大概率绕不开AD7606这颗芯片:8通道同步采样、16位分辨率、支持±5V和±10V双极性输入,外加片上滤波和过采样,放在工业现场非常能打。但很多人真正上手后会卡在同一个地方——采样率上不去。我见过不少工程师用STM32的SPI去读AD7606,忙活半天只能跑六七十kSPS,最后怀疑是芯片标称参数有问题。其实问题不在ADC,而在接口选型。这篇文章把两种常见姿势放在一起做了一组实测对比:一种是最传统的SPI串行读取,另一种是把AD7606挂到STM32H7的FSMC(H7官方叫法FMC)并行总线上。先放结论:SCLK=25MHz时,SPI方案实测约95kSPS;FSMC并行方案实测逼近200kSPS,恰好翻倍还多。
这篇文章不打算只给结论,更想把背后的原理、接线、CubeMX配置、代码和踩坑都摊开讲。如果你正在选型、或者手里的AD7606迟迟跑不到理想采样率,下面这些内容应该能帮上忙。
1. 项目概述:为什么AD7606值得把接口方案抠到极致
1.1 这颗ADC的定位与典型应用场景
AD7606是ADI推出的一款8通道同步采样ADC,内置16位逐次逼近型转换核,每个通道都有独立的采样保持器,因此8个通道可以真正做到同一时刻采样,而不是像通道轮询那样存在相位差。这对于电力系统的三相电压电流分析、电机驱动器的相电流采集、振动监测、声学阵列等场景非常关键。
另一个让它受欢迎的地方是前端模拟电路非常省心。芯片内部集成了输入缓冲放大器、一阶抗混叠滤波器、双极性电源基准等,单5V供电就能处理±10V的工业信号,不需要额外配运放和负电源。开发板级别的模块也很多,几十块钱就能买到现成板子,焊上排针就能用。
但回到真实项目里,很多人只用到了它一半的性能。200kSPS的最大吞吐率写在手册上,实际系统却常常因为读取接口太慢,只能工作在80~100kSPS,甚至更低。这里的关键在于:AD7606的ADC转换核心很快,慢的是我们往外倒数据的方式。
1.2 采样率瓶颈到底卡在哪里
先搞清楚AD7606的工作节奏。一次完整的采样周期可以拆成两段:
- 转换阶段:CONVST引脚收到上升沿后,内部ADC启动转换,BUSY引脚拉高。转换完成后BUSY拉低,8个通道的结果被锁存到输出寄存器。这个阶段典型耗时约4us。
- 读取阶段:CPU需要把8个通道的结果从芯片搬进内存。如果使用并行接口,每读一个16位结果只需几十纳秒;如果使用SPI串行接口,则至少需要128个SCLK时钟(16位×8通道),而且所有数据都从一根线串行移出。
注意一个关键约束:读取阶段必须在下一轮转换开始前完成,否则旧数据会被新结果覆盖。手册给出的最大吞吐率200kSPS,换算下来采样周期是5us。扣除4us转换时间,留给读取的时间只有约1us。SPI方案在1us内要倒完128个bit,需要SCLK不低于128MHz,这在AD7606上根本不现实。而FSMC并行方案读取8个16位数据只需要约300ns,完全落在窗口以内。
所以,瓶颈从来不是ADC的转换速度,而是数据回传通道的带宽。
2. 两种驱动方案的设计思路与原理
2.1 方案A:SPI串行读取,最常用但也最受限
SPI方案之所以普及,是因为几乎所有MCU都带SPI外设,引脚少、接线简单。AD7606进入串行模式后,将SER/PAR引脚拉高,数据就从DOUTA引脚串行移出,连接SCLK和MISO即可,CS由普通GPIO控制。
很多人默认SPI传输不会影响采样率,但在AD7606这种8通道同步采样场景里,SPI的串行本质成了硬伤。一次采样周期要移出128个bit,假设SCLK为25MHz,仅传输时间就是128 / 25MHz = 5.12us,再加上4us转换时间,理论周期9.12us,约合109kSPS。如果SCLK只有10MHz,传输时间变成12.8us,理论采样率直接跌到59kSPS。何况实际代码里还有中断进出、DMA配置、轮询等待的开销,实测值会比理论值更低。
当然,SPI方案也不是没有优点。它的接线只有四五根,驱动代码简单,排查问题方便,而且采样率要是只需要80kSPS以下,SPI完全能胜任。
2.2 方案B:FSMC(FMC)并行映射,把数据读取变成内存读
FSMC全称是Flexible Static Memory Controller,在STM32H7上官方叫FMC,但大部分工程师仍习惯叫FSMC。它本身是给NOR Flash、SRAM、PSRAM这类并行存储器设计的控制器,核心能力是:把外部设备映射到MCU的寻址空间里,CPU往某个地址读写,FSMC硬件会自动产生对应的片选、读、写时序信号。
AD7606的并行接口刚好可以伪装成一块16位宽的SRAM:D0~D15接数据总线,CS接片选,RD接读使能,BUSY接普通GPIO。这样一来,CPU读取AD7606某个通道的数据,实际上就是执行一条对映射地址的16位读指令。FSMC硬件会在一个总线周期内自动完成地址建立、片选拉低、读脉冲、数据锁存、总线释放,不需要软件模拟任何时序。
数据读取速度的提升是代差级别的。假设FMC外设时钟为200MHz(STM32H7典型配置),每个总线周期约5ns,配置得当的情况下一次16位读访问约30~50ns,8通道统统拿完也不过300ns左右。相比SPI的微秒级传输时间,这部分开销几乎可以忽略。正因如此,AD7606在FSMC方案里可以跑到标称的200kSPS上限。
2.3 两种方案的对比逻辑
用一张表快速总结两种方案在AD7606场景下的差异:
| 对比项 | SPI串行方案 | FSMC并行方案 |
|---|---|---|
| 数据线数量 | 3~4根(SCLK、MISO、CS、BUSY) | 20根左右(D0~D15、NOE、NE、BUSY、CONVST等) |
| 读取8通道耗时 | 128个SCLK,约5.12us@25MHz | 约0.3us@FMC 200MHz |
| 理论最高采样率 | 约109kSPS@25MHz SCLK | 受AD7606限制,约200kSPS |
| CPU开销 | 轮询较高,DMA可优化 | 等效内存读,开销低 |
| 引脚占用 | 少 | 多 |
| 驱动代码量 | 简单 | 中,需配FMC时序 |
这样看就很清楚了:如果你需要高采样率,FSMC是必然选择;如果引脚紧张且采样率要求不高,SPI更现实。
3. 硬件连接与CubeMX配置要点
3.1 FSMC方案的引脚规划与连接清单
下面以STM32H743/H750配合常见AD7606模块为例,给出信号级连接。不同封装的具体引脚号请以自己板子的原理图和数据手册为准,但信号对应关系是一致的。
| AD7606信号 | STM32H7 FMC信号 | 说明 |
|---|---|---|
| DB0~DB15 | FMC D0~D15 | 16位并行数据总线 |
| CS | FMC NE1 | Bank1片选,映射区域起始地址0x60000000 |
| RD | FMC NOE | 读使能,对应AD7606的RD低脉冲 |
| BUSY | 任意GPIO(建议带EXTI) | 转换完成信号,下降沿表示可读数 |
| RESET | 任意GPIO | 芯片复位 |
| CONVST A/B | 任意GPIO或PWM | 启动转换,上升沿有效 |
| RANGE | GPIO或固定电平 | 选择±5V或±10V量程 |
| OS0~OS2 | 固定低电平 | 关闭过采样,保证最高采样率 |
| PAR/SER | 固定低电平 | 并行模式 |
有个很多人忽略的细节:FMC的地址线A0~A2在FSMC方案里其实可以不用接。AD7606没有地址线,它是通过RD脉冲来推进内部通道指针的,每次RD下降沿自动切换到下一通道。因此连续对映射区域读8次,就会依次得到通道1到通道8的数据,地址线接不接都不影响通道顺序。
3.2 CubeMX中FMC参数设置与计算
在CubeMX里配置STM32H7的FMC时,有些新手会卡在时序参数上。我的做法是直接把FMC的NOR/SRAM Controller打开,选择Bank1的NE1、16位数据宽度、Memory Type选SRAM,然后在时序配置里给出一组安全起点:
- AddressSetupTime = 1
- AddressHoldTime = 1
- DataSetupTime = 6
- BusTurnAroundDuration = 1
这些参数的单位是FMC外设时钟周期。H7主频400MHz时,FMC外设时钟一般配置为200MHz,一个周期5ns。按DataSetupTime=6计算,NOE低电平持续时间约30ns,满足AD7606手册要求的约20ns最小读脉冲宽度,留有余量但不拖沓。8通道读8次,总读取时间约 (1+6+1) * 5ns * 8 = 320ns,完全落在1us读取窗口之内。
调试时建议用示波器抓NOE引脚,确认低电平宽度是否符合预期。如果数据读出来偶尔错位或全零,优先把DataSetupTime往上加;如果对采样周期很敏感,再逐步往下压。
3.3 SPI方案的CubeMX配置要点
SPI方案配置很简单:选一个SPI外设,主模式,时钟极性CPOL=0、时钟相位CPHA=1(对应AD7606手册里的串行时序:空闲时SCLK为低,数据在SCLK下降沿切换、上升沿稳定采样),数据帧宽度16位,MSB先行,速率按25MHz左右配置。CS用普通GPIO控制,BUSY接GPIO或EXTI。
需要注意区分:SPI16位帧和8通道数据之间没有直接映射关系。每次CS拉低后,连续接收8个16位帧,这8帧才依次对应通道1到通道8。这跟并行的连续读8次逻辑是类似的,只不过SPI是一根线串行吐出来。
4. 驱动代码实现:FSMC与SPI对比
4.1 FSMC方案:查询式读取
CubeMX配置好FMC之后,驱动代码的核心其实非常短。下面是我在实际项目里用的一段简化逻辑:
#define AD7606_BASE 0x60000000UL volatile uint16_t *ad7606 = (volatile uint16_t *)AD7606_BASE; // 启动一次转换:CONVST先低后高,上升沿触发 void AD7606_StartConv(void) { HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_RESET); delay_ns(100); HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_SET); } // 等待转换完成,读取8通道数据 void AD7606_ReadAllChannels(uint16_t *buf) { // BUSY高电平期间表示转换进行中,等待低电平 while (HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin) == GPIO_PIN_SET) ; // 连续读8次,每次触发一次FMC总线读周期 for (int i = 0; i < 8; i++) { buf[i] = ad7606[i]; } }两个关键点。第一,ad7606[i]虽然地址在递增,但AD7606并不关心地址线,它只是把每次FMC读产生的RD脉冲当作通道推进信号。第二,读取必须放在BUSY拉低之后,转换过程中读数据拿到的是无效电平。
实测时,这段代码在200kSPS触发频率下完全能稳定跑,没有丢数据。
4.2 FSMC方案进阶:利用DMA减轻CPU负担
如果不想让CPU在每次转换完成后都去执行8次读操作,可以把FMC的内存映射地址直接作为DMA源地址,让DMA自动搬8个半字(16字节)进内存缓冲区。核心操作类似:
__attribute__((section(".RAM_D1"))) uint16_t adcBuf[8]; HAL_DMA_Start_IT(&hdma_fmc, (uint32_t)AD7606_BASE, (uint32_t)adcBuf, 8);这里的hdma_fmc需要在CubeMX里把DMA请求源连接到FMC。当BUSY下降沿触发外部中断时,在中断服务函数里调一次上面的DMA启动即可。完成中断里做数据处理。
STM32H7还可以进一步利用DMAMUX的双缓冲特性,让DMA在装满一块缓冲区后自动切换另一块,配合BUSY中断完成“转换-搬运-处理”流水线。这个配置在H7系列上比较常用,具体实现时注意缓冲区必须放在DMA能访问的RAM区域,也就是D1域或D2域,别放到默认的DTCM里去踩坑。
4.3 SPI方案实现:HAL库+DMA
SPI方案的代码也不复杂,但性能差距在逻辑设计阶段就已经注定了。轮询版本:
void AD7606_SPI_ReadAllChannel(uint16_t *buf) { // 等待转换完成 while (HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin) == GPIO_PIN_SET) ; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); // SPI配置为16位帧,size=8表示连续接收8个半字 HAL_SPI_Receive(&hspi1, (uint8_t *)buf, 8, HAL_MAX_DELAY); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }如果编译环境支持,也可以把SPI配置为RXONLY模式,让SPI在接收时自动产生SCLK时钟,不需要额外的Dummy发送,效率会稍高一点。
SPI+DMA版本只是在初始化里把HAL_SPI_Receive_DMA挂上,同时处理好CS的拉低和拉高。真正限制采样率的不是DMA有没有用,而是128个SCLK本身需要的时间,所以就算DMA把CPU释放了,物理上的传输时间仍跑不掉。
4.4 采样触发方式:定时器PWM更精确
还有一个值得讲的优化:启动转换不要用软件翻转GPIO,而是用定时器的PWM输出直接驱动CONVST引脚。比如用定时器输出一个50%占空比的方波,频率就是采样率。PWM的上升沿触发AD7606开始转换,硬件自动完成,不占CPU,也不会因为中断延迟导致采样间隔抖动。
我用定时器输出200kHz PWM,FMC方案在这个频率下表现稳定;同样的PWM频率,SPI方案因为一帧传输就要5us+,再加上转换4us,已经完全来不及,读回来的数据会出现明显的乱码或错位,这正好说明为什么SPI无法跑满速。
5. 实测对比:数据、采样率、CPU占用
5.1 测试环境
测试现场的条件如下:
- MCU:STM32H750VBT6,主频400MHz,FMC外设时钟200MHz
- ADC:市售AD7606模块,RANGE设为±5V
- 信号源:函数发生器输出1kHz正弦波,同时接到8个通道
- 上位机:通过串口将原始码值发送到PC,用Python解析并绘制波形
- 触发方式:定时器PWM分别配置为100kSPS、200kSPS两种频率
为了统计实际采样率,我在BUSY引脚上接了一个逻辑分析仪,测量两次采样完成之间的时间间隔。
5.2 采样率实测结果
| 方案配置 | 理论极限 | 实测平均采样率 | CPU开销评价 |
|---|---|---|---|
| SPI轮询,SCLK=10MHz | 约59kSPS | 约52kSPS | 高,CPU大部分时间在等SPI |
| SPI+DMA,SCLK=25MHz | 约109kSPS | 约95kSPS | 中,DMA释放了部分CPU |
| FMC查询式 | 约200kSPS | 约196kSPS | 低,每次读8个半字仅几十周期 |
| FMC+DMA | 约200kSPS | 约198kSPS | 极低,CPU只响应BUSY中断和DMA完成中断 |
这组数据能说明两件事:第一,SPI方案在25MHz SCLK下确实只能逼近100kSPS,距离200kSPS差一倍;第二,FSMC方案的实测结果非常接近AD7606的标称上限,说明瓶颈已经是芯片本身。
5.3 波形还原与数据精度
在1kHz正弦波输入下,两种方案还原出来的波形肉眼几乎看不出差别。把数据切到20kHz正弦波,用SPI方案采集到约95kSPS时,波形每个周期只有4~5个采样点,看起来就是锯齿状;FSMC方案以200kSPS采同样的信号,每个周期有10个采样点,波形还原度明显更好。
数据精度方面,我分别用两种方案连续采集1秒,统计每通道码值的方差。结果FSMC方案的噪声底略好于SPI方案,这主要是读取完成后数据在总线上保持时间更短,受干扰概率更小。实际上两者都符合AD7606在16位下的典型表现,但FSMC方案在反复连续采集时数据一致性更稳定。
5.4 CPU占用对比的直观感受
用SPI轮询时,CPU几乎被卡死在读取函数里,主循环里的其他任务都会受影响。SPI+DMA好一些,但每5us左右一次的中断频率还是不低。到了FSMC方案,200kSPS下每次读取8个16位数据也就是几十个周期的开销,CPU占用极低,这为后续在同样的MCU上跑控制算法或频谱分析留出了充足余量。
6. 常见问题与排查技巧实录
6.1 FSMC读回来的数据全是0或0xFFFF
这个坑我遇到不止一次,原因集中在几个方向:
- 芯片处于复位状态或RESET引脚被拉低,检查RESET和上电时序。
- FMC时序过短,尤其是DataSetupTime太小,导致RD脉冲宽度不满足AD7606最小读写时间。把DataSetupTime往上加,实测从6加到8基本就能解决。
- 数据总线接线错误,检查D0~D15是否一一对应,这里最容易出现跨越排线时的错位。
- PAR/SER引脚没有正确拉低,芯片仍处于串行模式,并行总线不输出有效数据。
排查建议是先用示波器抓NOE和NE1信号,确认FMC确实在访问映射地址时产生了预期的脉冲。
6.2 SPI数据错位或通道顺序对不上
如果SPI读回来的数据有值但通道顺序错乱,或者偶发地某几个通道数据跳变,先检查CS拉低后到第一个SCLK上升沿之间是否有足够的建立时间。AD7606在CS有效后需要一点时间把第一个通道的数据放到输出线上,如果SPI时钟太快,第一个bit会被吃掉。
我的做法是CS拉低后加一个短暂延时,或者把SPI的时钟极性和相位重新核对一遍。CPOL=0、CPHA=1是多数模块默认能跑通的组合,但如果你的板子和我的不一样,以数据手册的时序图为准。
6.3 采样率上不去或周期不稳定
这个问题的根源往往是触发方式。如果用软件翻转GPIO来启动转换,中断响应时间会带来几微秒的抖动,采样率高了之后波形会明显变形。解决办法就是用定时器PWM硬件触发CONVST,把采样周期稳定性交给硬件保证。
另一个容易踩的点是:读取阶段必须严格遵守“BUSY下降沿之后、下一次CONVST之前”读完。在SPI方案里,如果采样率配置得过高,很可能上次数据还没传完,下一次转换已经触发,读出来的结果自然乱套。SPI方案的安全工作区间建议控制在90kSPS以下。
6.4 数据整体偏移或噪声偏大
排除接线问题后,优先检查模拟输入端的参考地和电源去耦。AD7606虽然集成度很高,但AGND和DGND的布局、REF引脚的电容、电源去耦电容离芯片远近,都会影响实际噪声。工业现场应用最好在模块输入端加RC滤波,特别是长线传输过来的信号。
如果只追求极致的16位精度,建议给AD7606的供电使用低噪声LDO,避免开关电源直接供电造成的纹波耦合。
6.5 # FSMC与DMA搬运时的地址陷阱
使用FMC+DMA时有个容易踩的细节:DMA的源地址如果不是连续的,会直接影响搬运结果。AD7606这里虽然看起来是连续读同一个映射区域,但每次读操作本身会触发芯片内部地址指针自动加一,所以DMA按内存地址递增连续读8次,恰好对应8个通道,这个逻辑是成立的。
不过要注意DMA缓冲区必须放在DMA可访问的RAM区域。STM32H7的DMA默认访问不了DTCM,直接把数组定义在默认内存段很可能导致DMA传输无响应,需要把缓冲区显式放到.RAM_D1段。
7. 选型建议与经验总结
回到实际项目里,SPI和FSMC并不是非得二选一的关系,而是取决于系统需求。
如果你的系统采样率不超过80kSPS,引脚又特别紧张,比如还需要同时挂LCD、SD卡、SDRAM等外设,SPI方案完全够用,开发调试成本也低。如果你要榨干AD7606的性能,或者后续有做频谱分析、电力谐波分析这类需要200kSPS满速运行的项目,FSMC并行方案是目前最合理的做法。它虽然占用引脚多,但换来的是读取速度提升一个数量级、CPU开销大幅下降,代码也简洁直观。
我个人的建议是:新项目如果主控选STM32H7,且AD7606是主角,直接上FSMC方案。前期接线麻烦一点、CubeMX时序多调几次,但后期做功能扩展时省下的CPU资源和时间成本远超当时投入。
这次测试也让我有个体会:很多所谓的“芯片性能不够”,最后查下来其实是接口方案或者时序配置拖了后腿。多花一点时间研究芯片手册里的并行接口时序,比在代码层面反复优化SPI轮询逻辑要有效得多。