名称:AD7612 ADC 采集驱动 FPGA 设计 Verilog Vivado
软件:Vivado
语言:Verilog
功能介绍
本设计实现 AD7612 模数转换器的 FPGA 采集驱动逻辑,主模块为 AD7612_driver,使用 Verilog 编写,可在 Vivado 工程中直接查看、综合和仿真。设计面向 ADC 转换控制与串行数据接收场景,提供转换启动信号、串行时钟、16 位并行采样数据输出以及 data_valid 数据有效标志。 模块以 25MHz 时钟作为输入时钟,外部 adc_trig_i 信号作为一次 ADC 转换的触发来源。触发信号经过同步寄存后进行下降沿检测,触发后由状态机依次完成启动转换、等待 BUSY、读取串行数据、锁存并行数据和结束等待等流程。 数据读取部分按照 MSB first 的方式接收 ad_adout 串行数据,在读取状态下通过 data_cnt 对 16 位数据逐位计数,并将结果整理到 adc_data[15:0]。当一次采样数据整理完成后,data_valid 输出一个有效标志,方便后级模块进行数据缓存、滤波、显示或通信发送。
运行环境
开发语言:Verilog 开发软件:Vivado 工程形式:Vivado FPGA 工程,包含 AD7612_driver 顶层驱动模块与 AD7612_driver_tb 测试文件。
设计思路
设计核心采用同步状态机控制 ADC 的一次转换周期。系统复位后处于空闲状态,等待 adc_trig_i 的下降沿触发;触发到来后输出 ADC 转换启动控制信号,并结合 ad_busy 状态进入后续读取流程。这样的结构将触发、转换、等待和读数过程拆分为清晰的状态,便于理解 AD7612 的工作时序,也方便后续根据实际硬件时序要求调整状态停留时间。 触发输入采用两级寄存器同步处理,降低异步触发信号直接进入时序逻辑带来的亚稳态风险。下降沿检测逻辑由同步后的两拍信号组合得到,保证状态机只在有效触发边沿启动一次转换流程,避免长电平触发造成重复启动。 串行读取阶段由 data_cnt 计数控制,前 16 个读取周期使能串行时钟并采样 ad_adout。采样顺序从最高位到最低位写入 rd_data,完成后进入数据输出状态,将 rd_data 锁存到 adc_data,同时拉高 data_valid。后级逻辑只需要关注 adc_data 与 data_valid 即可获取一次完整的 16 位 ADC 数据。 整体结构适合用于学习 ADC 芯片时序控制、串行数据采集、FPGA 状态机设计以及 Vivado testbench 仿真流程,也可以作为 AD7612 采集接口的工程参考。
模块结构
主要模块包括: AD7612_driver:ADC 采集驱动顶层模块,负责触发同步、转换控制、BUSY状态处理、串行时钟输出、16位串行数据接收、并行数据输出和数据有效标志生成。 AD7612_driver_tb:仿真测试模块,用于对驱动逻辑进行行为仿真验证。 glbl:Vivado 仿真环境相关全局模块。
开发板验证
工程配有开发板实物验证参考图片,可用于对照硬件连接和板级运行效果。开发板验证内容与 AD7612 驱动工程配套,便于在实际 FPGA 硬件环境中核对 ADC 控制信号、采集流程和演示效果。
演示视频
配有演示视频,可直观看到工程运行和验证过程,适合下载前了解实际操作效果与板级演示状态。
演示视频请关注公众号后获取对应资料查看。
仿真图/仿真说明/设计文档图片
配套设计文档包含工程文件、程序文件、程序编译、testbench 和仿真图等内容说明,并提供时序图、配置图等参考图片。仿真工程中包含 AD7612_driver_tb 相关文件和 VCD 波形结果,可用于查看驱动逻辑的触发、状态跳转、串行读取和数据有效输出过程。
部分代码
以下展示顶层模块AD7612_driver的部分代码,完整代码可关注下方公众号卡片获取。
module AD7612_driver ( input clk_i, // 25MHz时钟输入 input rst_n_i, // 异步复位(低电平有效) input adc_trig_i, // ADC转换触发信号 output ad_cnv_n, // ADC转换启动信号(低电平有效) input ad_busy, // ADC忙状态指示 output ad_sdclk, // ADC串行数据时钟 input ad_sync, // (未使用) input ad_adout, // ADC串行数据输出 output reg [15:0] adc_data, // 并行ADC数据输出 output reg data_valid // 数据有效标志 ); // 触发器同步电路(消除亚稳态) reg adc_trig_r0; reg adc_trig_r1; wire adc_trig_n; // 下降沿检测信号 always @(posedge clk_i or negedge rst_n_i) if (!rst_n_i) adc_trig_r0 <= 0; else adc_trig_r0 <= adc_trig_i; always @(posedge clk_i or negedge rst_n_i) if (!rst_n_i) adc_trig_r1 <= 0; else adc_trig_r1 <= adc_trig_r0; assign adc_trig_n = ~adc_trig_r0 & adc_trig_r1; // 下降沿检测 // 状态机状态定义 parameter S_IDLE = 3'd0; // 空闲状态 parameter S_CNV = 3'd1; // 启动转换状态 parameter S_BUSY = 3'd2; // 等待忙状态 parameter S_READ = 3'd3; // 读取数据状态 parameter S_DATA = 3'd4; // 数据有效状态 parameter S_END = 3'd5; // 转换结束状态 reg [2:0] cur_state; // 当前状态寄存器 reg [4:0] data_cnt; // 数据位计数器 wire sclk_en; // 串行时钟使能 reg [15:0] rd_data; // 数据移位寄存器 // 数据位计数器控制 always @(posedge clk_i or negedge rst_n_i) if (!rst_n_i) data_cnt <= 5'd0; else if (cur_state == S_READ) data_cnt <= data_cnt + 5'd1; else data_cnt <= 5'd0; assign sclk_en = (cur_state == S_READ) && (data_cnt < 5'd16); // 仅在前16个时钟周期使能 // 串行数据移位寄存(MSB first) always @(posedge clk_i or negedge rst_n_i) if (!rst_n_i) rd_data <= 16'd0; else if (cur_state == S_READ) begin case (data_cnt) 5'd0: rd_data[15] <= ad_adout; 5'd1: rd_data[14] <= ad_adout; 5'd2: rd_data[13] <= ad_adout; 5'd3: rd_data[12] <= ad_adout; 5'd4: rd_data[11] <= ad_adout; 5'd5: rd_data[10] <= ad_adout; 5'd6: rd_data[9] <= ad_adout; 5'd7: rd_data[8] <= ad_adout; 5'd8: rd_data[7] <= ad_adout; 5'd9: rd_data[6] <= ad_adout; 5'd10: rd_data[5] <= ad_adout; 5'd11: rd_data[4] <= ad_adout; 5'd12: rd_data[3] <= ad_adout; 5'd13: rd_data[2] <= ad_adout; 5'd14: rd_data[1] <= ad_adout; 5'd15: rd_data[0] <= ad_adout; default: ; endcase end // 并行数据输出和数据有效信号 always @(posedge clk_i or negedge rst_n_i) if (!rst_n_i) begin adc_data <= 16'd0; data_valid <= 1'b0; end else if (cur_state == S_DATA) begin adc_data <= rd_data; data_valid <= 1'b1; end else data_valid <= 1'b0; // 主状态机控制 always @(posedge clk_i or negedge rst_n_i) if (!rst_n_i) cur_state <= S_IDLE; else case (cur_state) S_IDLE: if (adc_trig_n) // 检测到触发下降沿 cur_state <= S_CNV; S_CNV: if (ad_busy) // 等待ADC进入忙状态 cur_state <= S_BUSY; S_BUSY: cur_state <= S_READ; // 立即进入读取状态 S_READ: if (data_cnt == 5'd16) // 读完16位数据 cur_state <= S_DATA; S_DATA: cur_state <= S_END; // 数据输出完成 S_END: if (!ad_busy) // 等待ADC结束忙状态 cur_state <= S_IDLE; default: cur_state <= S_IDLE; endcase // ... 以下代码略,完整源码请下载压缩包查看
代码获取:点击下方公众号卡片