简介:面向电子工程师与嵌入式开发者,这套基于赛灵思ZYNQ7010与亚德诺AD7768的高速数据采集工程包,提供了从FPGA逻辑到ARM处理器软件的全套参考实现,适用于工业测量、声学分析、无线通信等需要高精度模数转换的场景。压缩包共有一千七百二十六个文件,约七十兆字节,文件类型涵盖设备树源码、C与C++程序、硬件描述语言设计、约束文件、工程脚本以及静态链接库,组成软硬件协同开发的完整目录结构。目前已有二百六十二人学习。从内容预览可见综合完成标志、系统模块定义与链接库文件,说明可直接用于构建工程并下载验证;结合名称中的时间采样含义,资料围绕长时间连续采集任务展开,可帮助理解ZYNQ可编程逻辑如何配置ADC、接收高速数据流,并借助ARM处理器完成信号分析、数据记录或网络发送。对于具备硬件描述语言基础并希望快速上手的开发者,这份资源提供了配置方法、代码框架与排错思路,能显著节省从零搭建系统的时间。 做过多通道同步采集的人应该都懂,最烦的不是ADC选型,而是“说好的同步”到了实际电路上变成“各有各的时差”。我之前在项目里用过好几片逐次逼近型ADC做并行采集,每一路单独起飞,最后做相位分析时各种校准,折腾得够呛。后来换方案,把一个ZYNQ7010和ADI的AD7768搭配起来,一下子把“8通道24位同步采集”这个事变成了常规操作。这篇就纯粹分享我在这个项目里的整体设计思路、硬件接口要点、逻辑代码框架,以及调试中踩过的一些坑,给正准备碰这套组合的同行省点时间。
ZYNQ7010负责跑系统、配寄存器、搬数据,AD7768负责把8路模拟信号精准同步采样,这套组合特别适合振动监测、声学定位、电能质量分析这类对通道间相位一致性要求高的场景。先说结论:ZYNQ7010的资源跑这个任务刚好,不高不低,AD7768的性能也确实对得起它的定位,两者配合好的话,单板就能完成从模拟前端到数据处理的全链路。
1. 项目背景与整体方案选型
1.1 为什么是ZYNQ7010 + AD7768
选AD7768的理由很直接:它单芯片就是8通道同步采样,内部每个通道都有独立的Σ-Δ调制器,共享同一套时钟和参考电压,通道间的采样时刻天然对齐,不存在“分时轮流采样”那种错开。24位分辨率,快速模式下MCLK用32.768MHz、抽取率配到128时,每通道能跑256kSPS,这个速率覆盖大部分振动和音频分析绰绰有余。再加上内置数字抽取滤波器,后级抗混叠做起来轻松不少。
ZYNQ7010这边,选中它是因为它正好卡在“硬核ARM+可编程逻辑”的甜点位。采集这类应用有两件事:一是要灵活控制SPI时序,读取AD7768这种连续输出数据流的ADC,用PL做时序最稳,ARM纯软件模拟SPI一旦被中断打扰就容易丢帧;二是需要跑协议栈或者做上位机交互,用ARM做这部分比纯FPGA省力太多。ZYNQ7010的逻辑单元有85K,我做完整个采集链路包括SPI接收、FIFO、AXI DMA、中断控制器,资源占用大概在一半左右,还有余量放算法模块,性价比确实合适。
1.2 系统整体架构和工作链路
整套系统的链路不复杂,但每一环都得咬合好。AD7768输出的数据走上PL自研的SPI接收模块,模块按照帧同步信号把8通道的24位数据拼好,塞进AXI Stream FIFO,再由Xilinx的AXI DMA引擎搬到DDR里,最后ARM侧做符号扩展、物理量换算和后续算法处理。AD7768的寄存器配置走的是PS自带的SPI控制器,配置完就不动了,数据通路上不再占用ARM中断,效率很高。
这里有个设计心得:AD7768的SPI接口既能做寄存器配置,也能读转换数据,但数据读出时序是连续的,每帧数据必须在一个转换周期内全部读走,否则下一帧就覆盖了。所以我坚持把寄存器配置和高速数据流分开,配置用PS自带的SPI主机,数据读取用PL定制的时序模块,两个物理链路互不干扰,调试的时候也能分开定位问题。
2. 硬件接口设计要点
2.1 关键信号与时序关系
AD7768的接口引脚需要仔细捋一遍:MCLK是主时钟输入,由Zynq的PL时钟直接提供,我这边配的是32.768MHz;DRDY是数据就绪信号,每完成一次转换,会输出一个帧同步脉冲,相当于告诉接收端“这一包数据可以拿了”;SCLK是SPI串行时钟,用于把转换结果移出;DOUT0到DOUT3四根数据线,每根线依次输出两个通道的24位数据,8个通道刚好一次帧周期级联读完。
时序上最需要注意的是SCLK和MCLK的关系,以及SPI模式配置。AD7768是主机时钟和串行时钟并行工作的,读取数据时,主控制器在SCLK的边沿移入数据。我建议在PL里直接根据DRDY产生读时钟窗口,SCLK频率留足裕量,我试过MCLK在32.768MHz时SCLK用8MHz到10MHz比较稳妥,太快了走线稍微长一点就容易丢bit。SPI的极性和相位,一定对着数据手册的读取时序图核对,实际用ILA抓波形验证是最靠谱的,不要凭感觉配。
接口电平也要注意。AD7768的IOVDD我用的3.3V,和ZYNQ的IO Bank电压匹配。ZYNQ的PL端所有连接AD7768的引脚配置为LVCMOS33,保证电平兼容,避免因为电平不匹配导致数据采样错误这种隐蔽问题。
2.2 电源与参考电压处理
模数混合电路,电源设计往往决定了系统最终的动态性能。AD7768有模拟电源和数字电源,模拟电源AVDD我用低噪声LDO供电,输入从5V转2.5V,LDO后面并联了多个去耦电容,从10uF到100nF铺满。数字电源DVDD用1.8V,和模拟电源之间用磁珠隔离,模拟地和数字地单点连接,PCB上把模拟区域和数字区域明确分开,这些属于基本功,但真踩过坑的人都知道,前期图省事后期调FFT全是泪。
参考电压对24位ADC的影响比很多人想象中大得多。AD7768用差分参考输入,我选了低噪声、低温度漂移的基准源,输出经过RC滤波后送入REFP/REFN。参考电压如果噪声大,转换结果的频谱上会明显看到底噪抬高,甚至出现杂散,这个问题在排查专栏里我详细写。
3. 软件与逻辑核心实现
3.1 AD7768 寄存器配置流程
AD7768上电后第一件事是复位,然后通过SPI配置寄存器,寄存器配置顺序建议固定,避免后续排查时不知道哪一步影响了行为。我一般是这么做的:
// 伪代码示意,实际可按Vitis或Linux驱动框架改写 // 1. 拉低RESET引脚,延时,释放 ad7768_reset(); mdelay(10); // 2. 写复位寄存器,确保内部状态干净 ad7768_spi_write(0x00, 0x02); mdelay(5); // 3. 配置主控制寄存器:快速模式,对应MCLK 32.768MHz ad7768_spi_write(0x01, 0x01); // 4. 配置抽取率,125kHz一档时抽取率对应128 ad7768_spi_write(0x02, 0x0C); // 5. 使能全部8个通道 ad7768_spi_write(0x03, 0xFF); // 6. 配置输出接口,采用4根DOUT线,TDM模式数据按0~7顺序输出 ad7768_spi_write(0x04, 0x00); // 7. 回读校验关键寄存器 uint8_t val = ad7768_spi_read(0x01); if (val != 0x01) { // 配置异常处理 }寄存器配置的关键点是:模式配置和抽取率决定输出速率,MCLK与抽取率共同决定输出数据率,务必要算好,别让数据速率超过后端能处理的能力。配置完成后,DRDY信号会按新配置的速率开始产生帧同步脉冲,这时候就可以准备接收数据了。
这里多说一句“HAL”的话题。我习惯给这类ADC驱动做一层硬件抽象,底层是SPI读写接口,上层是寄存器配置和状态查询,中间用函数指针或者宏隔离平台差异。这样同一套逻辑,既能在ZYNQ上跑,也能方便地搬到其他平台做验证,项目前期调试会顺手很多。
3.2 PL 端数据接收与 AXI DMA 通路
PL端的数据接收模块是整个系统的心脏。SPI接收模块的状态机设计,我分成两个部分:帧同步逻辑和bit接收逻辑。帧同步逻辑盯着DRDY,一旦捕获到有效沿,就启动一帧数据的移位接收;bit接收逻辑按照SCLK时钟把DOUT0到DOUT3上的数据依次打进移位寄存器,每条线上接收24位数据,四个通道组分别对应8个逻辑通道。
状态机大致是这样:IDLE等待DRDY,检测到有效沿后进入RUN状态,开始移位采样,计数满24位且4根数据线都收完当前通道组后,把数据按通道索引写入FIFO,然后回到IDLE等待下一帧。因为AD7768是4线并行输出,所以一个SCLK周期能收96bit,效率很高,8个通道全部读完也就是几十个SCLK周期的事,完全跟得上最高256kSPS的速率。
数据接收后进入AXI Stream FIFO缓存,然后由AXI DMA搬运到DDR。我用Xilinx的AXI DMA IP,配置为只使用S2MM通道,采用Scatter Gather模式,这样支持多缓存自动切换,每采满一块就产生一次中断通知ARM处理,避免数据覆盖。DMA描述符的准备工作要提前做好,缓冲区和描述符不要放在Linux不可见的内存区域,这块在裸机和Linux下的处理方式不一样,需要稍加小心。
3.3 PS 端驱动与数据处理
PS端的工作,我推荐在刚开始调试时用裸机,把所有复杂度降到最低,直接跑在Vitis里,读取DMA中断,把DDR里的裸数据打印出来,验证链路通不通。等裸机通了再上Linux驱动,能大幅减少排错范围。
裸机下,数据处理就是处理DMA中断时拿地址,然后按通道拆分。AD7768的24位转换码是有符号二进制补码格式,范围从0x800000到0x7FFFFF,对应负满量程到正满量程。转换成实际电压的公式是:
// code 需要先做符号扩展,从24位扩展到32位 int32_t ext = (int32_t)(code << 8) >> 8; float voltage = ext * 2.5f / 8388608.0f; // VREF=2.5V 时注意符号扩展不能省,不然负半周的数据会完全错掉。我最初在这里栽过一次,FFT做出来只有正半周对,负半周全是乱的,找了半天才发现是符号位没处理。
如果上Linux,我采用的是UIO方案加内核DMA驱动结合,UIO负责把DMA控制器的寄存器空间映射到用户态,同时把数据缓冲区通过mmap暴露出来,应用中通过等待中断来同步数据就绪。这样既能在用户态灵活做算法,又不用写太复杂的内核模块,适合调试和中小批量产品。
4. 调试过程与常见问题实录
4.1 最典型的四个坑
第一个坑是SPI时序配置错误导致读回来的数据全是0xFF或0x00。这个现象特别容易误导人,一开始以为是芯片坏了,后来用ILA抓波形才发现SCLK的极性和相位跟手册对不上,主机在错误的边沿采样,当然采不到数据。核对数据手册时序图,调整SPI模式后一次就通了。
第二个坑是DRDY信号没有接好,导致数据帧错位。AD7768是连续输出模式,每一帧数据以DRDY为起点标记。如果忽略DRDY,只靠SCLK连续读,数据通道顺序会随机错乱,甚至出现通道0数据跑到通道5的诡异情况。解决办法是在PL里严格用DRDY作为帧起点,每收到一次帧同步就重新开始排列通道顺序。
第三个坑是24位数据的符号扩展。前面已经说过了,由于AD7768是24位输出但ARM是32位,不做符号扩展,处理负电压时数值完全不对。这个坑隐蔽在算法阶段,FFT分析时才暴露。
第四个坑是电源和参考电压的杂散干扰。我在FFT测试时发现某些频率上有固定杂散峰,排查了一圈,最后发现是参考电压的退耦不够,换成低噪声基准并加强滤波后,杂散立刻消失了。模拟电路的问题在数字调试阶段很难定位,建议硬件设计时一步到位。
4.2 排查方法与验证手段
调试这套系统,我先不急着接真实信号,而是把ADC输入端拉到一个固定的直流电平,比如接地或接2.5V,然后看输出的码值。如果接地,读取的码值应该在0附近小幅波动;接2.5V参考电压的一半时,码值接近满量程的一半。这一步能快速判断配置和数据通路是否正常。
数据通路通了之后,再用信号发生器注入正弦波,用ARM做FFT,看频谱的SNR、THD和杂散。我会用ILA抓几组原始数据导出到MATLAB里离线分析,比在板子上看直观得多。寄存器配置是否正确,还可以通过SPI回读验证,写完之后读出来比对,能排除很多低级问题。
这中间还有一个比较重要的经验:怀疑数据不对时,先把采集速率降到最低,比如把抽取率调到最大,让数据速率降到每秒几kSPS,然后用LED翻转或者串口打印的方式观察数据变化。速率降低后逻辑时序余量大了,很多问题会自己暴露出来,比直接高速采集时猜来猜去高效得多。
5. 后续扩展建议
AD7768还支持菊花链模式,如果以后需要更多通道,可以多片AD7768级联,用SYNC_IN信号做多片同步启动,这样系统能从8通道扩展到16通道、32通道,而PL端的改动主要在多路片选和帧同步管理上,架构不用大改。ZYNQ7010的资源在8通道场景下是够用的,但真要做到32通道同时采集,建议评估一下逻辑资源和DMA带宽,必要时可以升级到ZYNQ7020,兼容PIN设计,成本增加不多但余量大了很多。
另外,前面提到的HAL抽象层,对于AD7768这种寄存器比较多的ADC特别有用。我把所有寄存器地址、配置组合、读取接口都封装成一个独立模块,上层业务逻辑完全不感知底层是SPI还是模拟接口。前段时间把这个模块从ZYNQ上平移到一个带硬核ARM的FPGA平台上,只改了底层接口函数,上层代码几乎没动,这种收益在项目越往后越明显。
我个人的感觉是,ZYNQ7010和AD7768的组合是一个很经典的“处理器+高精度ADC”搭配,它把多通道同步采样这个原本让人头疼的事变得规整可控。整个系统最核心的技术点其实不在单个芯片,而在PL和AD7768之间的时序交互,以及数据链路的稳定搬运。把这些基础打牢,后面不管是做实时处理还是波形分析,都会稳很多。
本文还有配套的精品资源,点击获取