ARTICLE DETAIL

资讯详情

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

STM32 FSMC驱动AD7606实现8通道同步采样实战

STM32 FSMC驱动AD7606实现8通道同步采样实战 1. 为什么8通道同步采样是个硬骨头做电力监测、电机控制或者多路传感器采集的朋友大概率都遇到过这个场景手头有8路模拟信号需要同时采集而且对相位一致性有要求。比如三相电压电流的谐波分析如果各路之间存在采样时间差算出来的功率、相位角全是错的。这时候用STM32自带的ADC轮询或者扫描模式基本就是给自己挖坑。STM32的片上ADC在规则组扫描模式下多通道之间是分时复用的通道切换需要建立时间采样保持电容充放电也需要时间。12位精度下通道间的时间偏差轻松跑到微秒级。对于50Hz工频信号1微秒对应0.018度的相位误差看着不大但谐波分析里高次谐波的相位误差会被放大17次谐波下这个误差就接近0.3度了。更别说片上ADC的INL和DNL指标做精密测量根本不够看。AD7606这颗芯片就是冲着这个痛点来的。8个通道各自独立的ADC核心真正的同步采样所有通道在同一时刻锁存信号。16位分辨率内置模拟输入钳位保护输入范围可选±5V或±10V片上还有二阶抗混叠滤波器。最关键的是它把8路采样、保持、转换、数据输出这一整套流程都集成好了STM32只需要负责读取数据和配置寄存器。我手上这个项目用的是AD7606的并行接口模式配合STM32F407的FSMC总线读取。选并行而不是SPI原因很简单8通道16位数据一次转换就是128位SPI就算跑到10MHz读一遍也要十几微秒而并行总线一个读周期几十纳秒就搞定。对于需要高采样率的应用并行接口的吞吐优势非常明显。这篇文章会从硬件连接到FSMC配置从AD7606的时序控制到数据读取的代码实现把整个链路讲透。适合正在做电力监测、振动分析、多路数据采集的嵌入式开发者也适合想了解高速并行总线与ADC配合的STM32玩家。代码部分基于HAL库但底层逻辑对标准库同样适用。2. AD7606的引脚与时序别被数据手册吓到2.1 关键引脚的功能梳理AD7606有64个引脚但真正需要关注的没几个。先把核心控制引脚列出来引脚名方向功能说明CONVST A/B输入转换启动A和B可以短接在一起同时触发BUSY输出转换进行中为高下降沿表示转换完成RESET输入复位芯片高电平有效CS输入片选低有效RD输入读使能低有效FRSTDATA输出指示第一个通道数据就绪OS0/OS1/OS2输入过采样倍率选择RANGE输入输入范围选择0为±5V1为±10VDB0-DB15双向并行数据总线PAR/SER/BYTE SEL输入接口模式选择低为并行CONVST A和CONVST B分别控制前4通道和后4通道的采样保持器。如果只用一个信号同时驱动这两个引脚8个通道就是严格同步的。我见过有人只接了CONVST A结果后4个通道根本没启动转换读出来全是0排查了半天以为是时序问题。BUSY信号是整个流程的节拍器。CONVST上升沿之后BUSY会拉高表示内部在转换。转换时间跟过采样设置有关无过采样时大约4微秒8倍过采样时接近20微秒。BUSY下降沿之后数据就可以读了。2.2 并行读取的时序窗口并行模式下读数据核心是CS和RD的配合。数据手册里的时序图看着复杂实际用起来就记住几个关键参数RD低电平持续时间最小25纳秒CS有效到RD有效之间的建立时间最小0纳秒RD上升沿之后数据保持时间最小5纳秒STM32的FSMC在模式A或者模式D下都能满足这个时序。我用的是模式A读操作时序里地址建立时间和数据保持时间都可以独立配置。FSMC的时钟是HCLKF407跑168MHz时一个HCLK周期约5.95纳秒。把地址建立时间设为2个HCLK数据保持设为3个HCLK读脉冲宽度设为4个HCLK算下来RD低电平约23.8纳秒稍微紧了一点。稳妥起见我把读脉冲宽度调到6个HCLK约35.7纳秒留足余量。注意FSMC的时序参数是以HCLK周期为单位的配置时一定要根据实际主频换算。换芯片或者改主频之后这些参数必须重新算。2.3 过采样与抗混叠的取舍AD7606内置了数字滤波器支持2到64倍的过采样。过采样倍率越高信噪比越好但转换时间也线性增加。无过采样时SNR约86dB8倍过采样能到90dB左右64倍能到92dB以上。但过采样不是白给的。转换时间从4微秒涨到20微秒采样率直接从200kSPS掉到50kSPS。做谐波分析时如果信号最高次谐波是2.5kHz按照奈奎斯特采样定理采样率至少5kSPS50kSPS完全够用。但如果做暂态分析或者高频振动监测过采样就得慎重了。我的经验是先确定信号带宽再反推所需采样率最后在满足采样率的前提下选最高的过采样倍率。不要一上来就拉满64倍结果采样率不够波形全是混叠。3. 硬件连接从原理图到PCB的实战细节3.1 模拟输入端的保护与滤波AD7606的模拟输入端虽然内置了钳位保护但外部还是要做处理。每路输入串一个1kΩ电阻再并一个1nF电容到地构成简单的RC低通。截止频率算一下1/(2π×1000×1e-9)≈159kHz。这个频率远高于工频信号的谐波范围不会影响测量精度但能把高频噪声挡在外面。电阻选1kΩ是有讲究的。AD7606的输入阻抗在±5V范围下约1MΩ1kΩ的串联电阻引入的增益误差是1k/(1M1k)≈0.1%对于16位ADC来说这个误差对应约65个LSB有点大了。如果做精密测量串联电阻要降到100Ω以下或者用运放做缓冲。我这边做电力监测0.1%的误差可以接受所以用了1kΩ。RANGE引脚接高电平选±10V输入范围。这样1LSB对应20V/65536≈305微伏。如果接±5V1LSB约152微伏分辨率更高但输入信号不能超过5V。根据实际信号幅度选别浪费动态范围。3.2 数字接口的走线要点并行数据总线DB0-DB15加上控制信号一共二十多根线。走线时注意几点数据线尽量等长偏差控制在5毫米以内。FSMC的读周期只有几十纳秒线长差异太大会导致数据建立时间不够。CS、RD、RESET这些控制信号远离模拟输入区域避免数字噪声耦合到模拟端。BUSY信号是AD7606的输出接到STM32的GPIO或者FSMC的等待信号上。如果用中断方式接到外部中断引脚如果用轮询普通GPIO就行。我第一版PCB把数据线和模拟输入线平行走了很长一段结果采样数据跳变严重后来把数字线移到板子另一层中间加地平面隔离问题才解决。这个坑希望大家别踩。3.3 电源与基准的处理AD7606需要AVCC5V模拟电源和VDRIVE数字接口电源2.3V到5.25V。VDRIVE接STM32的3.3V这样数据总线电平匹配不需要电平转换芯片。基准电压方面AD7606内置了2.5V基准也可以外接。内置基准的温漂约10ppm/°C做一般测量够用。如果环境温度变化大或者要求更高精度可以外接ADR421之类的低漂移基准。外接时把REFIN/REFOUT引脚配置好具体看数据手册的基准选择章节。去耦电容别省。AVCC引脚旁边放一个10微法钽电容加一个100纳法陶瓷电容VDRIVE同样处理。基准引脚旁边放一个1微法加100纳法。这些电容尽量靠近引脚走线短而粗。4. FSMC配置让STM32把AD7606当成SRAM来读4.1 为什么选FSMC而不是GPIO模拟用GPIO模拟并行总线读AD7606理论上可行但实际很难做稳。STM32的GPIO翻转速度受限于代码执行效率C语言写个读周期光是函数调用和循环开销就几百纳秒了。而且中断一来时序直接乱掉。FSMC是硬件总线控制器配置好之后读操作就是一条内存访问指令。CPU执行*(volatile uint16_t*)addressFSMC硬件自动产生CS、RD、地址信号时序精确到HCLK周期不受中断影响。F407的FSMC支持SRAM、NOR Flash、NAND Flash等AD7606的并行接口跟SRAM的读时序几乎一样直接套用SRAM模式就行。4.2 地址映射与片选配置FSMC的NOR/SRAM区域从0x60000000开始分成4个bank每个bank 64MB。用NE1片选的话基地址是0x64000000。AD7606只需要一个地址来触发读操作所以地址线可以只接一根比如A0然后基地址就是0x64000000读0x64000000和0x64000002都行FSMC都会产生读时序。实际配置时用CubeMX可以图形化设置但有些参数CubeMX不一定给全建议生成代码后手动检查FSMC的初始化结构体。关键参数// FSMC NOR/SRAM 时序结构体 FSMC_NORSRAM_TimingTypeDef Timing; Timing.AddressSetupTime 2; // 地址建立时间2个HCLK Timing.AddressHoldTime 0; // 地址保持时间 Timing.DataSetupTime 6; // 数据建立时间6个HCLK Timing.BusTurnAroundDuration 0; Timing.CLKDivision 0; Timing.DataLatency 0; Timing.AccessMode FSMC_ACCESS_MODE_A;DataSetupTime对应RD低电平持续时间6个HCLK在168MHz下约35.7纳秒满足AD7606最小25纳秒的要求。AddressSetupTime是CS有效到RD有效的时间2个HCLK约11.9纳秒也够了。4.3 BUSY信号的两种处理方式BUSY下降沿表示转换完成STM32需要检测这个信号。两种方式轮询方式把BUSY接到普通GPIO启动转换后循环检测GPIO电平直到变低再读数据。代码简单但CPU会空转浪费算力。中断方式BUSY接到外部中断引脚配置下降沿触发。中断服务函数里置一个标志位主循环检测标志位后读数据。CPU利用率高但中断响应有延迟如果采样率很高中断频繁触发也会影响系统实时性。我这边采样率设的是10kSPS转换周期100微秒中断方式完全跟得上。中断服务函数里只置标志不做任何耗时操作读数据放在主循环里。void EXTI15_10_IRQHandler(void) { if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) ! RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_13); ad7606_busy_flag 1; } }提示中断里千万不要直接读FSMCFSMC读操作虽然快但中断上下文里做总线访问可能引发优先级反转问题。置标志、主循环处理这是最稳的做法。5. 代码实现从复位到读数的完整流程5.1 复位与初始化序列AD7606上电后需要复位把内部寄存器恢复到默认状态。复位脉冲宽度最小50纳秒我这边给10微秒绰绰有余。void ad7606_reset(void) { HAL_GPIO_WritePin(AD7606_RESET_PORT, AD7606_RESET_PIN, GPIO_PIN_SET); delay_us(10); HAL_GPIO_WritePin(AD7606_RESET_PORT, AD7606_RESET_PIN, GPIO_PIN_RESET); delay_us(10); }复位之后配置过采样引脚和量程引脚。这些引脚是静态配置不需要动态改变。void ad7606_init(void) { // 量程选择±10V HAL_GPIO_WritePin(AD7606_RANGE_PORT, AD7606_RANGE_PIN, GPIO_PIN_SET); // 过采样8倍 HAL_GPIO_WritePin(AD7606_OS0_PORT, AD7606_OS0_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(AD7606_OS1_PORT, AD7606_OS1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(AD7606_OS2_PORT, AD7606_OS2_PIN, GPIO_PIN_RESET); ad7606_reset(); }过采样引脚的组合对应关系OS2/OS1/OS0为000时无过采样001时2倍010时4倍011时8倍100时16倍101时32倍110时64倍。我设的011对应8倍过采样。5.2 启动转换与读取8通道数据启动转换就是给CONVST一个上升沿。CONVST A和CONVST B短接在一起一个信号同时触发8个通道。void ad7606_start_conversion(void) { HAL_GPIO_WritePin(AD7606_CONVST_PORT, AD7606_CONVST_PIN, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(AD7606_CONVST_PORT, AD7606_CONVST_PIN, GPIO_PIN_RESET); }BUSY变低之后连续读8次FSMC地址每次读到一个通道的数据。AD7606在RD下降沿输出数据第一个RD对应通道1第二个对应通道2依次类推。#define AD7606_BASE_ADDR ((uint32_t)0x64000000) void ad7606_read_channels(int16_t *buf) { for(uint8_t i 0; i 8; i) { buf[i] *(volatile int16_t*)(AD7606_BASE_ADDR); } }这里有个细节AD7606输出的是二进制补码16位有符号数。直接读回来存到int16_t数组里就行不需要额外转换。如果量程是±10V转换公式是float voltage (float)raw_data * 20.0f / 65536.0f;如果量程是±5V把20.0改成10.0。5.3 主循环的采样调度主循环里检测BUSY标志然后读数据、转换电压、存缓冲区。int16_t adc_raw[8]; float adc_voltage[8]; volatile uint8_t ad7606_busy_flag 0; void main_loop(void) { if(ad7606_busy_flag) { ad7606_busy_flag 0; ad7606_read_channels(adc_raw); for(uint8_t i 0; i 8; i) { adc_voltage[i] (float)adc_raw[i] * 20.0f / 65536.0f; } // 启动下一次转换 ad7606_start_conversion(); } }这个流程跑下来采样率取决于转换时间加上读取时间。8倍过采样下转换时间约20微秒读8个通道的FSMC操作大概几百纳秒加上中断和主循环开销实际采样率能到40kSPS左右。如果不需要这么高可以在主循环里加延时控制采样间隔。5.4 FRSTDATA信号的使用FRSTDATA引脚在第一个通道数据就绪时拉高读完第一个通道后变低。可以用它来判断数据是否有效但实际用下来只要BUSY变低之后读数据肯定是有效的。FRSTDATA更多是给那些需要精确控制读取时序的场景用的普通应用可以不管。我试过用FRSTDATA做触发发现它跟RD信号的配合有点微妙如果RD太快FRSTDATA还没来得及反应就读完了。后来干脆不用直接靠BUSY判断稳定得很。6. 实测中遇到的坑与解决方案6.1 数据跳变地弹与电源噪声第一版板子跑起来之后发现通道间串扰严重通道1有信号时通道2的读数也跟着跳。用示波器看电源发现AVCC上有明显的毛刺幅度大概50毫伏。排查过程先怀疑是AD7606本身的问题换了一片新的现象一样。然后查电源发现5V是从开关电源直接过来的纹波本来就大。在AVCC引脚旁边加了一个10微法钽电容和一个100纳法陶瓷电容毛刺降到10毫伏以内串扰明显改善。后来又发现地平面分割有问题。数字地和模拟地混在一起数字部分的回流路径经过模拟区域。把地平面重新规划模拟部分单独一块通过一个0欧姆电阻单点连接到数字地问题彻底解决。注意AD7606这种高精度ADC电源和地的处理比代码重要得多。代码写错了可以改PCB画错了只能飞线或者重新打板。6.2 BUSY信号抖动导致的误读有一批板子出现随机误读现象是偶尔读到的数据全是0xFFFF或者0x0000。查了半天发现是BUSY信号在下降沿附近有抖动STM32的外部中断触发了两次第二次读的时候AD7606已经进入下一次转换数据无效。解决方案有两个一是BUSY信号加RC滤波10kΩ电阻加100pF电容截止频率约160kHz能把抖动滤掉二是在中断服务函数里加一个简单的消抖检测到下降沿后延时1微秒再确认电平。我两个都用了再没出现过误读。6.3 FSMC时序余量不足前面提到DataSetupTime设的6个HCLK理论上35.7纳秒大于AD7606要求的25纳秒。但实测发现在高温环境下机箱内温度到70度偶尔会出现读数据错误。用示波器抓RD信号发现高温下FSMC的驱动能力下降RD下降沿变缓有效低电平时间缩短。把DataSetupTime加到8个HCLK约47.6纳秒问题消失。所以时序参数不要卡着最小值设留50%以上的余量温度变化、电压波动、器件个体差异都会影响实际时序。6.4 过采样配置错误导致转换时间翻倍有一次改代码想把过采样从8倍改成16倍结果忘了改OS引脚的配置只改了代码里的注释。实际跑起来发现采样率掉了一半查了半天才发现OS引脚还是8倍的配置但代码里按16倍算的转换时间等待时间设长了。这个坑很小但很典型。过采样倍率是硬件引脚决定的代码里只能读取不能设置。改过采样必须同时改硬件配置和代码里的时间参数两者要对应上。7. 性能优化把采样率再往上推一推7.1 用DMA搬运数据FSMC读操作虽然快但8个通道挨个读CPU还是要执行8次内存访问指令。如果采样率很高CPU时间会被大量占用。用DMA可以把数据搬运的工作交给DMA控制器CPU只需要启动转换和等待完成。不过FSMC的DMA支持有点特殊。F407的FSMC在NOR/SRAM模式下DMA请求是由FSMC内部产生的需要配置FSMC的DMA使能位。具体做法是把AD7606的数据地址配置成DMA的源地址内存缓冲区配置成目的地址传输长度设为8数据宽度16位。每次BUSY下降沿触发DMA传输传输完成后DMA产生中断CPU在中断里处理数据。这样CPU占用率能从30%降到5%以下采样率也能再往上提。7.2 双缓冲机制单缓冲下DMA传输和CPU处理是串行的。用双缓冲DMA写缓冲区A的时候CPU处理缓冲区B交替进行。STM32的DMA支持双缓冲模式配置好之后自动切换CPU只需要在传输完成中断里切换处理指针。这个机制对连续采样特别有用。我这边做电力谐波分析需要连续采集10个工频周期的数据双缓冲下数据无缝衔接不会丢点。7.3 定时器触发转换用定时器输出PWM或者比较信号来触发CONVST可以精确控制采样率。比如要10kSPS的采样率定时器设成100微秒周期每个周期产生一个脉冲触发CONVST。这样采样间隔由硬件定时器保证不受软件调度影响采样率的抖动可以控制在纳秒级。配置方法选一个通用定时器比如TIM3设成PWM输出模式周期100微秒占空比设成1%左右输出引脚接到CONVST。定时器启动后自动产生触发信号CPU只需要处理BUSY中断读数据。这个方案比软件循环启动转换要稳得多做频谱分析时采样率的稳定性直接影响频率分辨率。8. 几个容易被忽略的细节8.1 输入范围的动态切换RANGE引脚可以在运行时切换但切换之后需要重新复位AD7606否则内部电路可能处于不确定状态。我试过动态切换发现切换瞬间读到的数据会跳变后来改成上电时根据配置固定下来运行中不切换。如果确实需要动态切换建议在切换前停止转换切换后复位再重新启动。整个过程大概需要几十微秒对连续采样有影响。8.2 基准电压的负载效应AD7606的REFIN/REFOUT引脚如果外接基准要注意基准芯片的负载能力。AD7606内部基准输入阻抗不是无穷大会从基准芯片抽取电流。如果基准芯片驱动能力不足基准电压会被拉低导致所有通道的读数偏小。用ADR421时它的输出电流能力是10毫安AD7606的基准输入电流大概几百微安完全够用。但如果用一些低功耗基准比如REF3025输出电流只有几毫安就要算一下余量。8.3 通道间的相位一致性验证8通道同步采样的核心价值就是相位一致。验证方法很简单给所有通道接同一个正弦波信号采集一批数据然后比较各通道之间的相位差。理论上应该为零实际会有几个LSB的差异对应相位误差在0.01度以内。如果发现通道间相位差明显先查硬件输入RC滤波器的参数是否一致走线长度是否接近。再查软件读取顺序是否正确有没有通道间的时间偏移。我这边实测下来8个通道的相位一致性在0.02度以内做谐波分析完全够用。8.4 温度漂移的补偿AD7606的增益温漂典型值是2ppm/°C失调温漂是0.5ppm/°C。在0到50度的范围内增益变化约0.01%对应16位ADC的6个LSB。如果应用环境温度变化大可以在软件里做温度补偿。补偿方法在恒温箱里测几个温度点的读数拟合出增益和失调随温度变化的曲线运行时根据温度传感器读数修正。不过对于大多数工业应用AD7606的温漂已经足够小不做补偿也能满足精度要求。8.5 代码的可移植性设计AD7606的驱动代码尽量跟硬件平台解耦。把FSMC读写、GPIO控制、延时函数抽象成接口换平台时只需要改接口实现。我这边把驱动分成三层底层硬件抽象层、AD7606寄存器操作层、应用层。底层用函数指针或者宏定义换STM32型号或者换FSMC配置时只改底层。这样做的另一个好处是方便单元测试。在PC上模拟底层接口可以测试AD7606驱动的逻辑正确性不用每次都烧到板子上。这个项目从选型到调通前后花了大概两周时间。大部分时间不是在写代码而是在查硬件问题和调时序。AD7606本身不复杂但高速并行接口对硬件设计的要求比较高电源、地、走线、时序任何一个环节出问题都会导致数据异常。希望这篇总结能帮到正在做类似项目的朋友少走一些弯路。
返回列表