浅析高性能AD采集芯片AD4630—四通道SPI模式的配置与采集(FPGA)
目录
一、浅析芯片手册(Data Sheet)
1.芯片概述
2.AD4630的SPI信号协议介绍
3.配置寄存器与时序
4.AD数据转换与采集
二、FPGA代码设计
1.稳定与复位
2.初始化模式配置
3.AD数据转换与读取
三、CNV优化与测试验证
1.CNV采样时钟的硬件优化
2.回环模式验证配置
3.测试模式验证AD采集转换
4.一点心得体会
前言:做FPGA相关设计的时候用到了一块高精度的AD转换芯片,是ADI公司的AD4630芯片,网上对这块芯片的使用和配置并没有过多的详细介绍,ADI公司有配套的评估板,看了一下比较贵还是算了,因此打算自己写一套AD4630的配置和采集程序,之前也做过不少硬件,那就浅浅操刀一下吧QAQ。
一、浅析芯片手册(Data Sheet)
1.芯片概述
从Feature中,可以看出,所用的AD4630-24为双通道,转换速率最高2M,逐次逼近型的(SAR)AD转换芯片,其主要采用SPI协议通信,支持1,2,4路并行转换,降低了对SCK的频率要求,使得慢SCK也可以完成快速的采样。逐次逼近型ADC,位数越多,转换的相对速率就要越慢,因为需要一位一位比较,因此AD4630-24适合对精度要求特别高,而对转换速率要求一般的场景(比如高精度传感器,医疗仪器等),在General Description中,从“The wide differential input and common-mode ranges allow inputs to use the full voltage reference” 可知该器件的输入为差分输入,这将有效的克服共模干扰而带来的ADC的误差,也就是意味着,我们需要将采集的信号转换为差分形式给到AD4630,手册后面附了全差分的配置。
2.AD4630的SPI信号协议介绍
SPI是摩托罗拉公司提出的一种广泛用于主控于外设之间数据传输的协议,以SCLK(本文引为SCK)的时钟为同步时钟基准,收发使用同一源时钟(SCK)以保证同步,在通信中不同步很可能回造成乱码误码,SPI的时钟速率决定其通信的速率,为全双工通信。SPI通过拉低片选信号,可以实现多主从机之间的通信。典型的SPI的构成为CS,SCK,SDI,SDO四根线,有的SPI线多一点基本上多的是SDO,比如AD4630,SDO0-7,共八组。
从Data Sheet中可知
CS:片选,低有效信号
SDI:接受FPGA发来的数据
CNV:关键引脚,AD4630的转换基准时钟,CNV的时钟频率决定转换速率,这里援引一下Pin Function中对CNV的补充介绍。
这表明CNV 输入的上升沿会启动器件并开始新的转换,该信号必须具有低抖动才能实现 ADC 的指定性能,所以CNV时钟的质量会影响AD4630转换的性能(为后面的优化铺垫一下)。
SCK:时钟基准,AD4630支持三种SCK的模式,正常模式(从机),ECHO回环,HOST使用内部自己的时钟(作为主机)。最大频率支持为86MHz。
SDO组:0-3对应1通道的,4-7对应2通道的,至于用几个就看你配置是几LANE了。
这里先援引一下时序,后续会再分析
BUSY/SCKOUT:在正常SPI模式下,BUSY信号是跟随CNV的,当正在转换的时候BUSY会置高,转换完了再拉低。(本设计将利用这一特性进行采集)。当ECHO回环和HOST模式的时候,会时滞一个周期输出SCK信号(本设计将利用这一特性辅助验证)。
3.配置寄存器与时序
本文主要介绍基于Altera的Cyclon4系列FPGA(主频为50MHz)进行4LANE,SPI时钟模式,SDR单倍速率,输出为24位差分数据的模式配置,并利用回环和测试模式,用于配置过程中的验证。AD4630的寄存器配置和SPI协议稍微复杂一点,为此,本文采用逐步介绍+Data Sheet援引原文+FPGA需求拆解的方式,力争简洁清晰。
Step1:重置复位
AD4630-24复位为低有效,本设计采用的是FPGA接入RST引脚,也就是软件复位。
- 从“Perform a reset no sooner than 3 ms after the power supplies are valid and stable”,可知,AD4630在上电到稳定需要3ms的稳定时间,这段时间RST会置高,RST拉低无效,因此需要3ms后再拉低复位。
- 从“The minimum RST pulse width is 50 ns”可知,RST的复位低电平的信号至少持续50ns。
- 从“After a hardware or software reset, no SPI commands or conversions can be started for 750µs”可知,在复位后存在750μs的SPI指令和转换时钟的静默期。
- 因此对于本步FPGA的设计,则是需要一个时钟计数器,计数3ms,50ns,750μs。而且几步跳转,可以使用状态机。
Step2:寄存器配置模式——总体流程
- 重点在右下角,右下角的流程告诉我们,要配置寄存器一共要分三步(就像把大象装进冰箱一样),首先是读一个虚拟的寄存器地址0X3FFF(这里DISS一下,它没有考虑首位读写的情况,容易误判,所以实际是0XBFFF)。
- 然后是你读或者写对应寄存器地址的对应指令(也就是你发送的码是由地址和数据构成的)
- 如果你读完了,写完了所有的操作,那么你要往0X0014地址写一个0X01数据,以保存并配置模式。
- 本步没有特别指定的FPGA需要。
Step3:寄存器配置模式——配置模式寄存器(绕口令hhh)
AD4630的Data Sheet的末尾很贴心的给了所以寄存器的地址和对应的功能,我们选我们要看的看(该看的看,不该看的不要乱看)
重点关注模式配置寄存器(地址0X20),这将决定我们的AD4630如何工作。本设计的工作模式配置为4Lane,SPI时钟模式,SDR单倍速率,24位差分数据输出。因此我们的数据码为10_00_0_000=0X80。上面提到本设计会利用回环模式和测试模式辅助验证,回环模式的数据码为10_01_0_000=0X90,测试模式的数据码为:10_00_0_100=0X84;
Step4:寄存器配置模式——读与写
重点来了,时序,芯片时序的逻辑顺序最好一点都别错。要看清时间的要求和时序的逻辑。配置模式的FPGA时序要求还是比较宽松的,时序约束的时候这块可以适当放松一点。
读寄存器配置的时序流程为(本设计的VIO接口电压为1.8V):
- CS至少应在SCK上升沿来之前的11.6ns就拉低
- CS至少要在SCK最后一个下降沿之后的5.2ns才能拉高
- SDI的数据至少在SCK上升沿来之前的1.5ns就应该准备好。
- SCK的周期不得低于11.6ns,高电平、低电平有效时间不得低于5.2ns
- SCK一共最多需要24个周期才能完成。
- SDI的构成为1位读写标志+15位地址,最高位为1代表读,0代表写(所以那个虚拟地址0X3FFFF,其实是全码是0XBFFF)
- 数据的写入和读出均遵循最高位在前,也就是MSB模式(所以上面的数据码也都是高位在前)。
- 读虚拟地址和写指令,不需要数据传回,不用关心SDO0
- 本步的FPGA,显然众多时序先后顺序和三条线的情况,优选状态机!配置模式的情况下,对时序要求一般,因此我们这里可以用部分独热码的状态机。
(请务必赏析语言艺术“就,才,一共只”,其实我觉得FPGA写配置时序不如ARM的串行结构有优势,因为这种串行协议计时还是ARM更方便一点,直接delay_ns(XXX)的)
本设计的主频时钟为50MHz,时钟周期为20ns,显然不能为这些几点几的时钟单独各自拉PLL,所以流程应该为:CS拉低,一个时钟后;SDI开始输出最高位,一个时钟后;SCK拉高开始24个周期计数,SDI同步发送数据,24个周期计数完;一个时钟后,CS拉高。(注意分号)
☆Step3里面的地址是不含首位的也就是15位(但他却披着0X的外衣,hhhh容易误判),所以我们SDI要准备的码为:读虚拟地址:0XBFFFFF;写配置模式4lane,SPI时钟,SDR单倍速率,24位差分数据:0X002080(因为MSB 所以前面00不能少);(回环:0X002090,测试:0X002084);保存并退出:0X1401;
STEP5:检验你的配置是否生效
对于配置AD4630的SPI来说,最折磨的就是搞了一遍,结果不对,还不知道是卡在哪里了,所以我们做一步就验证一步,先保证配置模式生效,在SPI模式下是不好判断的。因此我们利用回环模式来验证,将寄存器模式码调到回环。
仿照上文,写一个程序,先将CS先拉低,一个周期后,SCK给24个时钟,然后过一个周期再拉高CS。用示波器/Quartus的Signal Tap/Vivado的ILA IP核去检测在BUSY/SCKOUT上是否回环了SCK的时钟,并检查是否回环了24个周期,如果是,那么恭喜你,你的配置的代码是正确滴,别忘了再还原回去喔(文末给出相关图)。
4.AD数据转换与采集
下一个重点,SPI模式下的AD的数据转换,这块相对复杂一点,我们一点点看。从第一段“Once the conversion completes, CS can be asserted, which causes the current conversion result to load into the output shift register”可知,转换完成后,CS才可以拉低,当前的转换结果会寄存到输出寄存器上,确保输出的结果暂时不会丢失。
从左图中我们可以得知有两个读取转换结果的窗口区,也就是Zone1,2。先看Zone1,在SPI模式下,CS的片选可以在BUSY信号结束后就立刻拉低,启动读取转换,如果是回环Echo或者主机Host模式下,等300ns后再拉低CS片选。以2M的CNV时钟为例Zone1给我们留的时间是198.4ns。(这里的300ns主要是考虑到CNV的2M周期,在SPI模式下可以看到,是CNV低电平一段时间后,BUSY才会拉低,文末给相关图)
从右图中,可以解释一下Quiet Zone(静默区),通俗点就是,这段时间不能有任何SPI的操作,CS,SCK,SDI都维持该有的状态,不然容易毁坏程序所以这段时间我们是不能执行任何操作的。Zone1留的窗口期时长有点短,因此AD4630还提供了时间更长一点的Zone2区
Zone2的CS拉低应该在下一个CNV的上升沿后的9.8ns之后(静默期),以2M转换速率为例,Zone2提供了长达470.6ns的转换窗口期,如果你的SDK很慢,连4LANE都不能满足Zone1的时长限制的话,可以使用Zone2窗口期,但是你要注意你必须得在下一个采样周期的BUSY信号下降沿拉低之前的25ns,就得把CS拉高,不然数据就会被下一轮采样覆盖。(简言之就是BUSY信号的下降沿更新数据)。
再来关注一下SPI模式下的数据转换的时序要求,可以看见CS仍然有相对SCK超前和滞后的时间。而且转换数据超前SCK上升沿就准备好了。
本设计的SCK时钟和主频时钟一样为50Mhz,时钟周期为20ns,198.4/20 大概是9个时钟周期,4Lane模式,SCK需要6个时钟周期,CS前置拉低和滞后拉高各要1个周期,总共8个周期,时间裕量为38.4ns,是可以满足Zone1时间要求的。
本步FPGA的需求:采集和转换涉及到多线的要求和逻辑先后顺序,因此也是很推荐状态机的。而且转换是AD的关键,我们这里的状态机推荐采用独热码状态机。
二、FPGA代码设计
按照咱们上文的分析,一步步设计FPGA。
1.稳定与复位
需求:等待3ms稳定+复位信号拉低至少50s+复位信号拉高后内750us内不得操作,为了确保稳定,我这里设计的是6ms+200ns+1ms的组合。
//=============================// 初始化时间参数定义//=============================localparam time_stab = 24\'d300000, // 稳定时间(6ms,用于ADC上电稳定) time_keep = 24\'d300010, // 复位保持时间(6ms+200ns,低电平复位持续时间) time_realse = 24\'d350010; // 复位释放时间(6ms+200ns+1ms,完成复位后稳定时间)//=============================// 初始化配置计数器//=============================regcnt_flag; // 初始化完成标志(0:未完成 1:已完成)reg [23:0] cnt; // 初始化时间计数器// 计数器逻辑:从0计数到复位释放时间,完成后保持终值always @(posedge CLK or negedge N_RST) begin if(N_RST == 0) cnt <= 0; else if (cnt_flag == 1) // 初始化完成后保持 cnt <= time_realse; else cnt <= cnt + 1\'b1; // 计数递增end
复位的时序要求没那么严,就用二进制的分离式状态机了,cnt_flag标志着复位状态的结束,意味着这一阶段已经过完,但我并没有选择让计数器置零,因为这样要翻转很多位,不如直接停在最后一个数,反正也不影响后面。
//=============================// 初始化状态机(跳转与执行分离)// 功能:实现ADC上电复位时序控制//=============================reg [2:0] state_initial; // 初始化状态寄存器// 初始化状态定义localparam empty = 3\'d0, // 空闲状态 wait_stab = 3\'d1, // 等待稳定状态 rst_keep = 3\'d2, // 复位保持状态 rst_relese = 3\'d3, // 复位释放状态 rst_end = 3\'d4; // 复位完成状态// 状态机转换逻辑(基于计数器值跳转)always @(posedge CLK or negedge N_RST) begin if(N_RST == 0) state_initial <= empty; else if (cnt <= time_stab) state_initial <= wait_stab; else if (cnt <= time_keep) state_initial <= rst_keep; else if (cnt < time_realse) state_initial <= rst_relese; else state_initial <= rst_end;end// 状态机执行逻辑(控制NRST和初始化完成标志)always @(posedge CLK or negedge N_RST) begin if(N_RST == 0)begin NRST <= 1; // 复位无效(高电平) cnt_flag <= 0; // 初始化未完成 end else begin case (state_initial) empty : NRST <= 1; wait_stab : NRST <= 1; rst_keep : NRST <= 0; // 触发复位(低电平) rst_relese: NRST <= 1; // 释放复位(高电平) rst_end : cnt_flag <= 1; // 初始化完成 default : ; endcase endend
2.初始化模式配置
需求:先读虚拟地址+配置模式+退出。读/写都涉及,先拉低CS+数据提前准备+开始读/写+滞后CS拉高。三步的读写为了不相互干扰,我每次读写完都延时了一段时间,复用了地址计数器做计数器,减少了资源开支,配置模式对时序的要求一般,但比part1要求高一点,因此我在这里设计的是6位的局部独热码状态机,前两位表明模式,后四位表明操作阶段,优化了一下时序,其实主要是考虑read_pre,read这种来回跳转去回填数时候的时序稳定。
//=============================// 配置寄存器命令定义//=============================localparam REG_CONF_MODE = 24\'hBFFFFF, // 进入配置模式命令(读虚拟地址0x3FFF) MODES_REGISTER = 24\'h002080, // 模式配置寄存器(4lane模式,SPI时钟,SDR速率) EXIT_CONFIGURATION_MODE = 24\'h001401; // 退出配置模式命令//=============================// 配置状态机(SPI通信控制)// 功能:通过SPI接口配置ADC寄存器//=============================reg [5:0] state_setting; // 配置状态寄存器// 配置状态定义(局部独热码风格)localparam stand = 6\'b11_0000, // 待机状态 // 读配置阶段 read_cs = 6\'b00_0001, // 读操作片选使能 read_pre = 6\'b00_0010, // 读操作数据准备 read = 6\'b00_0100, // 读操作移位输出 read_after = 6\'b00_1000, // 读操作完成 // 写配置阶段 write_cs = 6\'b01_0001, // 写操作片选使能 write_pre = 6\'b01_0010, // 写操作数据准备 write = 6\'b01_0100, // 写操作移位输出 write_after = 6\'b01_1000, // 写操作完成 // 退出配置阶段 back_cs = 6\'b10_0001, // 退出操作片选使能 back_pre = 6\'b10_0010, // 退出操作数据准备 back = 6\'b10_0100, // 退出操作移位输出 back_after = 6\'b10_1000, // 退出操作完成 finish = 6\'b11_1111; // 配置完成状态regCS_S; // 配置阶段片选信号regSCK_S; // 配置阶段SPI时钟reg [4:0] address; // 配置数据位索引regflag_setting; // 配置完成标志// 配置状态机逻辑(控制SPI时序和数据输出)always@(posedge CLK or negedge N_RST)begin if(N_RST == 0)begin state_setting <= stand; CS_S <= 1; // 片选无效(高电平) SCK_S <= 0; // SPI时钟初始低 SDI <= 0; // 串行数据输出初始低 address <= 23; // 从最高位开始传输 flag_setting <= 0; // 配置未完成 end else begin case(state_setting) stand: // 等待初始化完成后进入配置 state_setting <= (cnt_flag == 1) ? read_cs : stand; // 读配置命令传输 read_cs: begin CS_S <= 0; // 片选使能(低电平) address <= 23; state_setting <= read_pre; end read_pre: begin // 准备当前位数据 SDI <= REG_CONF_MODE[address]; SCK_S <= 0; state_setting <= read; end read: begin // 时钟跳变,移位传输 address <= address - 1; SCK_S <= 1; state_setting <= (address == 0) ? read_after : read_pre; end read_after: begin // 读命令传输完成 SCK_S <= 0; CS_S <= (address == 31) ? 0 : 1; address <= address + 1; state_setting <= (address == 23) ? write_cs : read_after; end // 写配置命令传输(逻辑同读操作) write_cs: begin CS_S <= 0; address <= 23; state_setting <= write_pre; end write_pre: begin SDI <= MODES_REGISTER[address]; SCK_S <= 0; state_setting <= write; end write: begin address <= address - 1; SCK_S <= 1; state_setting <= (address == 0) ? write_after : write_pre; end write_after: begin SCK_S <= 0; SDI <= 0; CS_S <= (address == 31) ? 0 : 1; address <= address + 1; state_setting <= (address == 23) ? back_cs : write_after; end // 退出配置命令传输(逻辑同读操作) back_cs: begin CS_S <= 0; address <= 23; state_setting <= back_pre; end back_pre: begin SDI <= EXIT_CONFIGURATION_MODE[address]; SCK_S <= 0; state_setting <= back; end back: begin address <= address - 1; SCK_S <= 1; state_setting <= (address == 0) ? back_after : back_pre; end back_after: begin SCK_S <= 0; CS_S <= (address == 31) ? 0 : 1; address <= address + 1; state_setting <= (address == 23) ? finish : back_after; end finish: begin // 配置完成 SCK_S <= 0; CS_S <= 1; SDI <= 0; address <= 0; flag_setting <= 1; // 配置完成标志置位 end default:; endcase endend
3.AD数据转换与读取
需求:检测busy的下降沿CS拉低+六个通道转换+滞后CS拉高。对AD转换数据部分,这块对时序要求相对高一点,因此我设计的是全独热码状态机,并且不在数据转存期间进行复杂的计算,(比如这里就没用上面配置时候用的来回状态跳转填数据),先用多寄存器把数据存下来,在存完之后才进行整体拼接。数据的输出打了一拍增强一下时序的稳定性。
//=============================// 数据采集状态机// 功能:从ADC读取转换后的数据//=============================localparam wait_setting = 11\'b0000_0000_001, // 等待配置完成 wait_start = 11\'b0000_0000_010, // 等待ADC就绪 cs_low = 11\'b0000_0000_100, // 片选使能 sck_en = 11\'b0000_0001_000, // 时钟使能 convert_1 = 11\'b0000_0010_000, // 采集第1组数据 convert_2 = 11\'b0000_0100_000, // 采集第2组数据 convert_3 = 11\'b0000_1000_000, // 采集第3组数据 convert_4 = 11\'b0001_0000_000, // 采集第4组数据 convert_5 = 11\'b0010_0000_000, // 采集第5组数据 convert_6 = 11\'b0100_0000_000, // 采集第6组数据 convert_end = 11\'b1000_0000_000; // 采集完成reg [10:0] state_convert; // 采集状态寄存器regSCK_C; // 采集阶段SPI时钟regCS_C; // 采集阶段片选信号// 数据寄存器(每组4位,共6组拼接成24位数据)reg [3:0] lane1_1,lane1_2,lane1_3,lane1_4,lane1_5,lane1_6;reg [3:0] lane2_1,lane2_2,lane2_3,lane2_4,lane2_5,lane2_6;reg [23:0] lane1_O,lane2_O; // 通道1、2的24位数据reg [2:0] cnt_convert; // 采集计数器// 采集状态机逻辑always@(posedge CLK or negedge N_RST)begin if(N_RST == 0)begin // 初始化采集相关寄存器 SCK_C <= 0; CS_C <= 1; state_convert <= wait_setting; {lane1_1,lane1_2,lane1_3,lane1_4,lane1_5,lane1_6} <= 0; {lane2_1,lane2_2,lane2_3,lane2_4,lane2_5,lane2_6} <= 0; cnt_convert <= 5; lane1_O <= 0; lane2_O <= 0; end else begin case (state_convert) wait_setting: // 等待配置结束完成 state_convert <= (flag_setting == 1) ? wait_start : wait_setting; wait_start:begin // 等待ADC忙信号结束 state_convert <= (BUSY_SCKOUT == 0) ? cs_low : wait_start; cnt_convert <= 5; end cs_low: begin // 使能片选 CS_C <= 0; state_convert <= sck_en; end sck_en: begin // 使能采集时钟 SCK_C <= 1; state_convert <= convert_1; end // 分6次采集24位数据(每次4位)先将数据存储下来,避免在采集过程执行复杂的功能 convert_1: begin lane1_1 <= {SDO0,SDO1,SDO2,SDO3}; // 通道1第1组数据 lane2_1 <= {SDO4,SDO5,SDO6,SDO7}; // 通道2第1组数据 state_convert <= convert_2; end convert_2: begin lane1_2 <= {SDO0,SDO1,SDO2,SDO3}; // 通道1第2组数据 lane2_2 <= {SDO4,SDO5,SDO6,SDO7}; // 通道2第2组数据 state_convert <= convert_3; end convert_3: begin lane1_3 <= {SDO0,SDO1,SDO2,SDO3}; lane2_3 <= {SDO4,SDO5,SDO6,SDO7}; state_convert <= convert_4; end convert_4: begin lane1_4 <= {SDO0,SDO1,SDO2,SDO3}; lane2_4 <= {SDO4,SDO5,SDO6,SDO7}; state_convert <= convert_5; end convert_5: begin lane1_5 <= {SDO0,SDO1,SDO2,SDO3}; lane2_5 <= {SDO4,SDO5,SDO6,SDO7}; state_convert <= convert_6; end convert_6: begin lane1_6 <= {SDO0,SDO1,SDO2,SDO3}; lane2_6 <= {SDO4,SDO5,SDO6,SDO7}; SCK_C <= 0; state_convert <= convert_end; end convert_end: begin CS_C <= 1; //关闭片选 cnt_convert <= cnt_convert - 1; //稳定时序切换 state_convert <= (cnt_convert == 0) ? wait_start : convert_end; lane1_O <= {lane1_1,lane1_2,lane1_3,lane1_4,lane1_5,lane1_6}; lane2_O <= {lane2_1,lane2_2,lane2_3,lane2_4,lane2_5,lane2_6}; end default:; endcase endend//=============================// 输出数据同步// 功能:打一拍输出,稳定时序//=============================always@(posedge CLK or negedge N_RST)begin if(N_RST == 0)begin lane_total1 <= 0; lane_total2 <= 0; end else begin lane_total1 <= lane1_O; // 通道1数据输出 lane_total2 <= lane2_O; // 通道2数据输出 endend
补充说明,在配置寄存器阶段,我的SCK是人为的1,0写的,这其实已经在分频了,如果在转化过程中还人为的写1,0 实际的SCK的时钟是25M,周期为40ns,就不够时序裕量了,因此我采用的是,在配置的时候使用人为定义的,在转化模式用系统时钟复用。CNV的时钟来自PLL 的IP核2MHz的输入,采用类似两路选择器控制。
//=============================// 信号控制//=============================assign CNV_CLK = (flag_setting == 1) ? CLK_2M : 0; // 配置完成后输出转换时钟assign SCK = (flag_setting && SCK_C) ? CLK : SCK_S;// 配置阶段用SCK_S,采集阶段用系统时钟assign CS = (flag_setting == 1) ? CS_C : CS_S; // 配置阶段用CS_S,采集阶段用CS_C
三、CNV优化与测试验证
1.CNV采样时钟的硬件优化
前文提到CNV的时钟质量很影响转换的情况,在实测中,不论是自己单独写一个高低电平持续时间不一样的采样时钟,还是用PLL的IP核产生2MHz的时钟,他们的抖动感觉都比较大。
左为两种CNV时钟 右为FFT图
巧的是,我之前做的比赛正是基于FPGA、DDS的脉冲波信号发生器,甚至还专门设计方案优化了边沿时间过窄导致的过冲和振铃。主要是通过滤波,滤除波形的高频谐波分量,但我不能为一个CNV的采样时钟还专门写一套数字滤波器,这样很占FPGA资源。巧的是我之前还搞过硬件,所以想到可以利用电容和自身的阻抗进行RC低通滤波,经过试验,在我的板子上,CNV与地并一个MLCC X7R 0603 容值在470pf-820pf之间的电容都可以较好的实现目标效果(强调材质和封装是因为不同材质和封装大小,他自身的ESR不一样,可能会有点影响),至于滤波效果就看自己取了,多试几组,注意电容过大(大概10nF以上)会滤成三角波,相位时滞就很大了,小了滤波不到位。
来看看滤波后的效果吧,比之前平滑多了。(CNV高电平启动转换,可根据设计的需要,在PLL里面把占空比调小一点,不过不影响,反正转换速率都是2M)
2.回环模式验证配置
回环模式下,BUSY作为SCKOUT,相对SCK滞后一个周期,这表明我们的模式配置的程序是对的。(我这里回环的是6个)
3.测试模式验证AD采集转换
AD4630贴心的为大家准备了验证采样的测试模式,该模式下,可以修改相应的输出寄存器以改变固定输出,我这里就不修改了,用它默认的0X5A5A0F0F,这个值会用于两通道,也就是强制让两组SDO组都输出这组数据,因为我是24位差分数据,所以我的测试结果应该是0X5A5A0F
测试结果正确,从这里也能看到,BUSY信号跟CNV是有时差的。(记得选取MSB模式,我这里的total[0:23]的因为位反了,用的是LSB)
4.一点心得体会
在最开始的时候,我以为SCK和SDI的数据差个1.5ns不是什么大问题,但是实际的情况来看,就是会不对,所以时序的先后逻辑顺序是一点都不能错的。时序是要一点一点抠的,急不得,快不得。
在写AD转换的时候,我还想着跟之前一样SCK手动写,但是这样时序裕量就不满足了,因此一直在想怎么还能复用50MHz的时钟,我就想到了时钟和数据是同步的,可以用一个控制信号控制,其实我觉得逻辑运算门也可以,这个日后尝试一下。
AD4630的驱动从读DataSheet、写程序和modelsim仿真、到写完这篇文章大概花了四五天时间,还是比较有意思的。欢迎广大朋友对其进行批评指正。