本次按照IC 型号:ADC128S022,逐次逼近型,进行设计FPGA程序。

目录

1. IC的硬件连接及时序

2. 程序设计

3. 仿真结果

4. 代码


1. IC的硬件连接及时序

1)硬件框图:

实战篇:AD转换-编程知识网实战篇:AD转换-编程知识网

2)IC 接口定义:

实战篇:AD转换-编程知识网

3)时序图如下:

实战篇:AD转换-编程知识网

2. 程序设计

1)模块连接关系

FPGA 与AD IC的连接关系如下图:                                                             FPGA AD程序模块接口图如下:

实战篇:AD转换-编程知识网                实战篇:AD转换-编程知识网
 

2)SCLK时钟产生

由于SCLK的时钟时0.8~3.2MHZ,这里我们采用1.92MHZ,然后采用线性序列机输出SCLK的依次高低电平。因此需要先两倍于SCLK的时钟SCLK2X,并对其计数。

SCLK2X的时钟是3.84MHZ,因此需设计计数器,每计到13时翻转一次。

3) 仿真时除了sclk,cs,din,从AD输出的dout如何仿真?

为了测试模块功能,模拟ADC芯片的输出,用sin3e产生一个正弦波,位宽12,个数4096。产生的这个.txt文件需要保存在当前工程的simulation目录下的modelsim文件夹中。为何?????

将产生的数据,发送到ADC驱动模块的输入现dout上。

文件读取:  采用系统任务$readmemh

`define sin_data_file "./sin_12bit.txt" // define the file's locationreg [11:0]memory [4095:0];
initial $readmemh(`sin_data_file,memory);

3. 仿真结果

通过各种调试bug,终于在下午成功的仿出波形。

实战篇:AD转换-编程知识网

写程序需注意:

1. 对于协议数据传输期间,最好有一个过程状态标志,协议执行时为1,其余时刻为0,在其他的状态或者计数的时候,可以采用这个过程标志作为判断的依据;

2. 程序计数或者状态的改变需要考虑从0到1的过渡,尤其是一个状态从0开始做出改变;

3. 计数器计数判断,必须有计数截至点,整个计数过程运行的标志判断,计数+1的判断条件;

4. 代码

/******************************************this file name: ad_ctrlthis file write time : 20210603this file function: protocol design of ADC	
******************************************/
module ad_ctrl(input clk,rst_n,input [2:0]channel,input dout,input start,output reg sclk,cs,din,output wire ad_done,output reg ad_state,output reg [11:0]data);reg dout_r;
reg [1:0]start_r; //if you want to generate start_flag, start_r must be 2 bit, because start_r is same as start
wire start_flag;
reg [2:0]channel_r;
parameter [5:0]ad_fre_cnt_num=6'd26;   // sclk2x frequency is 3.2M, and analog signal sample frequency is 120Kreg [5:0]clk_cnt;
reg sclk2x;
reg [5:0]sclk2x_cnt;
reg [11:0]data_r;//_flag signal generation
assign start_flag=(!start_r[1] & start_r[0])?1'b1:1'b0;
assign ad_done=(sclk2x_cnt==6'd33)?1'b1:1'b0;always @(posedge clk or negedge rst_n)beginif(~rst_n) data<=0;else if(sclk2x_cnt==6'd33) data<=data_r;else data<=data;end//input data register
always @(posedge clk or negedge rst_n)beginif(~rst_n) beginstart_r<=0;dout_r<=0;channel_r<=0;endelse beginstart_r[0]<=start;start_r[1]<=start_r[0];dout_r<=dout;channel_r<=channel;endend// clk generation	
always @(posedge clk or negedge rst_n)beginif	(~rst_n) clk_cnt<=0;else if(ad_state)if(clk_cnt==ad_fre_cnt_num) clk_cnt<=0;else clk_cnt<=clk_cnt+1'b1;else clk_cnt<=0;		end
always @(posedge clk or negedge rst_n)beginif	(~rst_n) sclk2x<=0;else if (ad_state) if(clk_cnt<=ad_fre_cnt_num/2) sclk2x<=1;else  sclk2x<=0;else sclk2x<=0;endalways @(posedge clk or negedge rst_n)beginif	(~rst_n) sclk2x_cnt<=1'd0;else if (sclk2x_cnt>6'd32) sclk2x_cnt<=1'd0;else if (ad_state & clk_cnt==ad_fre_cnt_num) sclk2x_cnt<=sclk2x_cnt+1'd1;else sclk2x_cnt<=sclk2x_cnt;endalways @(posedge clk or negedge rst_n)beginif	(~rst_n) ad_state<=0;else if (start_flag) ad_state<=1;else if (ad_done) ad_state<=0;else  ad_state<= ad_state;endalways @(posedge clk or negedge rst_n)beginif	(~rst_n) beginsclk<=1;cs<=1;din<=1;data_r<=0;endelse if (ad_state)begincase (sclk2x_cnt)0:begin  sclk<=1; cs<=0; data_r<=0;end1:begin  sclk<=0; end	3:begin  sclk=0;  end5:begin  sclk=0;  din=channel_r[2]; end7:begin  sclk=0;  din=channel_r[1]; end9:begin  sclk=0;  din=channel_r[0]; data_r[11]=dout_r; end11:begin sclk=0;  din=1; data_r[10]=dout_r; end13:begin sclk=0;  din=1; data_r[9]=dout_r; end15:begin sclk=0;  din=1; data_r[8]=dout_r; end17:begin sclk=0;  din=1; data_r[7]=dout_r; end19:begin sclk=0;  din=1; data_r[6]=dout_r; end21:begin sclk=0;  din=1; data_r[5]=dout_r; end23:begin sclk=0;  din=1; data_r[4]=dout_r; end25:begin sclk=0;  din=1; data_r[3]=dout_r; end27:begin sclk=0;  din=1; data_r[2]=dout_r; end29:begin sclk=0;  din=1; data_r[1]=dout_r; end31:begin sclk=0;  din=1; data_r[0]=dout_r; end2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32: begin  sclk=1;  end33:begin sclk=1;  cs=1; din=1; data_r=0; enddefault:;endcaseend
endendmodule