简介本资源为基于FPGA的数据采集系统完整工程包面向电子信息、自动化及集成电路相关专业学生与FPGA初学者可用于课程设计、毕业设计或自学练手。系统以串口指令控制DAC芯片TLV5618完成数模转换读取ROM中正弦波形数据输出模拟信号同时通过按键触发ADC128S052连续采样100次数据经同步FIFO缓存后由串口回传至PC显示。工程按功能划分为串口收发、按键消抖与控制、ROM、DAC/ADC驱动、同步FIFO及其控制等模块代码均附详细备注便于理解各模块接口与数据流。压缩包共390个文件约11.57MB包含Verilog源码、工程配置与综合报告、仿真波形、引脚约束及说明文档等覆盖从设计到验证的完整流程。目前已有4985人学习下载适合希望掌握FPGA与ADC/DAC协同采集、串口通信及FIFO缓存设计的读者参考复用。1. 基于FPGA的数据采集系统为什么它成了高速采集的默认答案做高速数据采集的人迟早会撞上一个选择ADC 出来的数据率一旦超过几百 MSPSMCU 就顶不住了。中断响应、总线仲裁、内存带宽每一环都在拖后腿。这时候基于 FPGA 的数据采集系统就成了默认答案——ADC 直接挂到 FPGA 的 IO 上用硬件逻辑在时钟沿上抓数据不经过任何操作系统调度确定性是 ns 级的。这套方案解决的核心问题就一个在采样率上去之后怎么保证数据不丢、时序不乱、上位机能稳定拿到完整波形。适合谁做雷达、超声、高速接口测试、电力监测这类需要连续或触发式采集的工程师以及想从 MCU 采集方案迁移到 FPGA 的开发者。资料齐全、代码备注清楚意味着这套东西不是只给你看个框图而是能直接跑起来的工程。2. 先想清楚架构ADC 接口、时钟树与数据通路怎么定2.1 从 ADC 输出格式反推 FPGA 接口类型选 FPGA 之前先看 ADC 吐什么。常见三类并行 LVDS、JESD204B、CMOS 并行。并行 LVDS 在 100~500 MSPS 区间最常见走 DDR 采样FPGA 侧用 IDDR 原语接收JESD204B 用于更高采样率但需要 GT 收发器和 SYSREF 同步链路调试周期长CMOS 并行简单但速率上不去一般不超过 100 MSPS。我一般会先确认 ADC 的 data sheet 里输出时序图看建立保持窗口再决定 FPGA 侧用哪种 IO 标准。这一步定错后面全是返工。2.2 时钟树采样时钟、同步时钟、系统时钟必须分开管时钟是采集系统的命门。采样时钟给 ADC必须低抖动同步时钟用于多通道对齐系统时钟给 FPGA 内部逻辑和存储。常见做法是用一片时钟芯片产生多路同源输出比如某款低抖动时钟发生器采样时钟和 FPGA 参考时钟同源但不同分频。注意采样时钟的抖动直接折算成 SNR 损失公式是 SNR -20log(2πf·t_jitter)1 ps 抖动在 100 MHz 输入下就吃掉约 4 dB。所以别拿普通晶振凑合。2.3 数据通路从 IO 到 DDR 再到上位机的三级缓冲数据从 ADC 进来后不能直接往 DDR 写中间要过一级 FIFO。典型路径是IDDR 接收 → 位对齐 → 异步 FIFO 跨时钟域 → 打包成 AXI Stream → DDR 控制器写入 → 上位机通过 PCIe 或千兆网读取。FIFO 深度要算如果 DDR 写入有仲裁延迟FIFO 至少要能扛住最大延迟乘以数据率。比如 500 MSPS × 14 bitDDR 延迟 200 nsFIFO 至少 1400 bit取整用 2 Kb 的 BRAM 做异步 FIFO 比较稳。// 异步FIFO跨时钟域ADC采样时钟域到系统时钟域 // 深度2048位宽16用双口BRAM实现 async_fifo_2048x16 u_adc_fifo ( .wr_clk (adc_clk), // ADC采样时钟500MHz .wr_rst_n (adc_rst_n), .wr_en (adc_valid), // ADC数据有效标志 .din (adc_data), // 14bit数据左对齐到16bit .rd_clk (sys_clk), // 系统时钟200MHz .rd_rst_n (sys_rst_n), .rd_en (fifo_rd_en), // 由DDR写入状态机控制 .dout (fifo_dout), .full (fifo_full), // 满标志反压ADC或丢弃 .empty (fifo_empty) );这段代码的关键在wr_clk和rd_clk完全异步FIFO 内部用格雷码指针做跨时钟域同步。参数上深度 2048 是经验值对应 500 MSPS 下约 4 μs 的缓冲足够覆盖 DDR 仲裁的最坏延迟。adc_valid必须由 ADC 的随路时钟或数据有效信号驱动不能自己造。如果fifo_full拉高说明 DDR 侧吞吐不够要么降采样率要么加 FIFO 深度要么检查 DDR 带宽分配。3. 动手搭最小系统从 ADC 配置到 DDR 写入的完整链路3.1 ADC 配置SPI 寄存器写入与回读校验大多数高速 ADC 上电后需要配置内部寄存器比如增益、偏置、测试模式、输出格式。用 FPGA 写一个 SPI 主控按 data sheet 的时序要求逐条写。注意写完后必须回读校验否则 ADC 没配好采回来的全是噪声。常见做法是写一个状态机先软复位再写配置表最后逐条回读比对。// ADC SPI配置状态机写-回读-比对 localparam S_IDLE0, S_WR1, S_RD2, S_CMP3, S_ERR4; reg [2:0] state; reg [7:0] cfg_addr, cfg_data, rd_data; reg [7:0] cfg_idx; always (posedge sys_clk or negedge rst_n) begin if(!rst_n) begin state S_IDLE; cfg_idx 0; end else begin case(state) S_IDLE: if(cfg_start) state S_WR; S_WR: begin spi_write(cfg_addr, cfg_data); // 写寄存器 state S_RD; end S_RD: begin spi_read(cfg_addr, rd_data); // 回读 state S_CMP; end S_CMP: begin if(rd_data ! cfg_data) state S_ERR; // 比对失败 else if(cfg_idx CFG_NUM-1) state S_IDLE; else begin cfg_idx cfg_idx 1; state S_WR; end end S_ERR: begin error_flag 1b1; // 配置错误点亮LED或上报 state S_IDLE; end endcase end end逻辑说明状态机按「写→读→比对」循环CFG_NUM是配置表条目数。参数上SPI 时钟一般不超过 10 MHz具体看 ADC 手册。error_flag拉高后不要自动重试先停下来查硬件因为回读失败通常是 SPI 时序或电源问题重试没用。3.2 DDR 写入AXI Stream 打包与突发长度设置ADC 数据进 DDR 不能零散写要打包成 AXI Stream 突发。常见做法是攒够 256 个采样点再发起一次 AXI Burst突发长度设 64 或 128位宽 128 bit。这样 DDR 效率最高。注意AXI 的tvalid和tready握手不能死等要加超时计数器否则 DDR 控制器一忙整个采集就卡死。// AXI Stream打包256点一组触发DDR突发写 reg [15:0] pkt_cnt; reg [127:0] axis_tdata; reg axis_tvalid; always (posedge sys_clk or negedge rst_n) begin if(!rst_n) begin pkt_cnt 0; axis_tvalid 0; end else begin if(!fifo_empty pkt_cnt 256) begin axis_tdata {axis_tdata[111:0], fifo_dout}; // 移位拼接 pkt_cnt pkt_cnt 1; axis_tvalid 1b1; end else if(pkt_cnt 256) begin axis_tvalid 1b0; // 一包结束等DDR写完 pkt_cnt 0; end end end参数说明pkt_cnt到 256 清零对应 256×16 bit 4 Kb 一包。AXI 突发长度在 DDR 控制器 IP 里设成 64位宽 128 bit这样一包正好 4 个突发。axis_tvalid拉低后要等axis_tready再拉高否则数据丢失。如果 DDR 带宽不够优先加大突发长度而不是加 FIFO 深度。3.3 上位机读取PCIe DMA 与千兆网 UDP 的取舍上位机拿数据两条路PCIe DMA 和千兆网 UDP。PCIe 带宽高适合连续采集但需要驱动和 DMA 引擎调试周期长千兆网 UDP 简单适合触发式采集但带宽上限约 110 MB/s500 MSPS×14 bit 肯定不够。我一般会先算数据率500 MSPS×14 bit 7 GbpsPCIe Gen2×4 够用千兆网只能降采样或触发模式。选型时别只看峰值要看持续写入能力。4. 避坑与排查采集系统最容易翻车的五个地方4.1 现象采回来的波形有周期性毛刺幅度不大但一直存在原因采样时钟和系统时钟同源但相位没对齐导致 IDDR 采样窗口偏移。解决用 MMCM 把采样时钟和系统时钟做相位扫描找到眼图最开的位置或者直接用 ADC 的随路时钟做接收时钟。4.2 现象DDR 写入一段时间后 FIFO 满数据丢失原因AXI 突发长度太短DDR 仲裁频繁切换有效带宽下降。解决把突发长度从 16 加到 64 或 128同时检查 DDR 控制器的读写优先级采集期间给写通道更高优先级。4.3 现象ADC 配置回读失败但 SPI 波形看起来正常原因ADC 的 SPI 时序要求建立保持时间比 FPGA 默认输出更严格或者 ADC 电源还没稳就发了配置。解决SPI 时钟降到 1 MHz 试配置前加 10 ms 延时等电源稳回读失败时用示波器抓 SPI 三线时序。4.4 现象上位机收到的数据包序号错乱偶尔丢包原因UDP 发送没有流控FPGA 发太快上位机网卡缓冲区溢出。解决加发送间隔或改用带确认的协议FPGA 侧加发送 FIFO 水位控制水位低于阈值才发下一包。4.5 现象多通道采集时通道间相位不一致差几个采样点原因各通道 ADC 的同步信号走线长度不一致或者 JESD204B 的 SYSREF 没对齐。解决用同源同步信号走线等长JESD204B 模式下检查 SYSREF 的建立保持窗口必要时用 FPGA 内部延迟单元做微调。5. 进阶技巧用触发状态机和在线校准把采集系统做稳5.1 触发状态机从自由采集到条件触发的平滑切换自由采集和触发采集不能靠上位机切换要在 FPGA 里做状态机。常见做法是自由模式下数据持续写 DDR 环形缓冲区触发条件满足后状态机把触发点前后的数据打上标记上位机只读标记段。这样既不丢触发前数据也不浪费 DDR 带宽。// 触发状态机自由采集→触发→标记→回读 localparam F_FREE0, F_ARM1, F_TRIG2, F_HOLD3; reg [1:0] trig_state; reg [31:0] pre_cnt, post_cnt; always (posedge sys_clk or negedge rst_n) begin if(!rst_n) trig_state F_FREE; else case(trig_state) F_FREE: if(arm_en) trig_state F_ARM; F_ARM: if(trig_in) begin trig_state F_TRIG; pre_cnt 0; // 开始记录触发前点数 end F_TRIG: begin pre_cnt pre_cnt 1; if(pre_cnt PRE_NUM) trig_state F_HOLD; end F_HOLD: if(read_done) trig_state F_FREE; endcase end参数说明PRE_NUM是触发前保留点数一般设 1024 或 2048对应环形缓冲区深度。trig_in要过两级同步器再进状态机否则亚稳态会导致误触发。read_done由上位机回读完成信号驱动。5.2 在线校准用已知测试信号修正增益和偏置ADC 的增益和偏置会随温度漂移高精度采集必须在线校准。做法是在采集间隙切到内部测试信号采一组数据算均值和方差和理想值比对把修正系数写回 FPGA 的乘法器。注意校准期间不要采真实信号否则修正系数会被污染。校准项测试信号计算方式更新时机偏置零输入均值每次上电或温度变化5℃增益满量程正弦峰值比对每 10 分钟或触发校准相位同源方波过零点计数多通道同步时5.3 验证方法用已知频率和幅度的信号源做端到端校验系统搭完后别急着上真实信号。先用信号源给一个已知频率和幅度的正弦波采回来做 FFT看 SNR、SFDR、ENOB 是否和 ADC 手册标称值接近。如果差太多先查时钟抖动再查电源噪声最后查 DDR 是否丢点。我一般会存一组「黄金数据」在 Flash 里每次上电自检时比对偏差超过阈值就报警。这套东西做下来最深的教训是别在时钟和电源上省钱。我见过太多采集系统逻辑代码写得漂亮最后败在一颗普通晶振上。还有FIFO 深度和 DDR 突发长度要一起调单独调一个永远调不通。希望帮到你。本文还有配套的精品资源点击获取