一、ADC128S052时序图

1.时序图

ADC128S052为12 位、8通道的模数转换器 (ADC),转换速度高达 10 MSPS,采用SPI串行通信。
(1)工作时序图
工作时序图
应注意DIN中配置好的通道地址ADD在下一次采样时才生效
从时序图中可得出,ADC芯片在上升沿时采样,读取DIN数据;DOUT在下降沿时改变数据。

(2)串行时序图
串行时序图
片选信号在SCLK第16个上升沿时被拉高。

2.设计要点

根据时序图和数据手册,得出以下设计要点:
(1)DIN在SCLK第3、4、5个下降沿改变,发送通道值ADD,先发高位,后发低位
(2)FPGA在SCLK上升沿时读取DOUT数据,DOUT前4位为0,在SCLK第5个上升沿开始读取DB11
(3)SCLK时钟为3.2-8Mhz,系统时钟为50Mhz,取8分频6.25Mhz
(4)片选信号在SCLK第16个上升沿时被拉高

二、ADC128S052代码设计

1.模块端口说明

端口名称方向说明
Clkinput系统时钟
Rst_ninput系统复位
Channel[2:1]input通道选择
Startinput采集开始标志位
Conv_doneoutput采集完成标志位
DATA[11:0]output采集到的并行数字信号
ADC_CS_NoutputADC片选
ADC_DINoutput串行数据输出至ADC芯片
ADC_SCLKoutputADC工作时钟
ADC_OUTinput采集到的串行数字信号

端口图
ADC芯片将转换后的数字信号通过ADC_OUT串行传送给模块,模块将其变成并行信号data传输给FPGA,保存到FIFO存储器中。

2.代码设计

模块采用线性序列机编写SPI时序,代码逻辑与DAC类似,可分为以下5个部分:通道地址缓存、分频计数器、生成序列计数器、SPI线性序列机、标志位赋值及DATA输出。
(1)通道地址缓存
接收到Start开始转换信号时,开始AD转换,为避免在发送过程中由于干扰导致输入的数据变化,在Start为高电平时,将通道地址Channel缓存到r_Channel中。

	reg [2:0] r_Channel;	//通道地址寄存
	always@(posedge Clk or negedge Rst_n)//数据缓存
	if(!Rst_n)
		r_Channel <= 3'd0;
	else if(Start)
		r_Channel <= Channel;
	else
		r_Channel <= r_Channel;

(2)分频计数器
前面提到SCLK频率范围为3.2-8Mhz,取系统时钟的八分频6.25Mhz为SCLK的时钟。由于数据在SCLK边沿变化和读取,需计算SCLK二倍频以区分上升沿和下降沿。

	parameter DIV_PARAM = 8;//分频系数 50/8 = 6.25Mhz
	reg [7:0] DIV_cnt;		//分频计数器
	reg SCLK2X;					//SCLK二倍频

	always@(posedge Clk or negedge Rst_n)//分频计数器
	if(!Rst_n)
		DIV_cnt <= 8'd0;
	else if(ADC_State) begin
		if(DIV_cnt == DIV_PARAM/2 - 1)
			DIV_cnt <= 8'd0;
		else
			DIV_cnt <= DIV_cnt + 1'b1;
	end
	else
		DIV_cnt <= 8'd0;

	always@(posedge Clk or negedge Rst_n)//SCLK二倍频
	if(!Rst_n)
		SCLK2X <= 1'b0;
	else if(ADC_State && (DIV_cnt == DIV_PARAM/2 - 1))
		SCLK2X <= 1'b1;
	else
		SCLK2X <= 1'b0;

(3)生成序列计数器
线性序列机在写代码时,应根据时序图列出信号在不同时刻的状态,这里根据时序图将SPI划分为34个状态,故生成序列计数器SCLK_GEN_CNT计数到33,对SCLK的边沿进行计数。

	always@(posedge Clk or negedge Rst_n)//生成序列计数器,对SCLK脉冲进行计数
	if(!Rst_n)
		SCLK_GEN_CNT <= 6'd0;
	else if(ADC_State)
		if(SCLK2X) begin
			if(SCLK_GEN_CNT == 33)
				SCLK_GEN_CNT <= 6'd0;
			else
				SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'b1;
		end
		else
			SCLK_GEN_CNT <= SCLK_GEN_CNT;
	else
		SCLK_GEN_CNT <= 6'd0;

(4)SPI线性序列机
根据时序图和前面总结的设计要点,DIN在SCLK第3、4、5个下降沿改变,发送通道值,先发高位,后发低位;在SCLK第5个上升沿开始读取DB11;片选信号在SCLK第16个上升沿时被拉高。

	always@(posedge Clk or negedge Rst_n)//线性序列机发送数据
	if(!Rst_n) begin
		ADC_SCLK <= 1'b1;
		ADC_CS_N <= 1'b1;
		ADC_DIN <= 1'b1;
	end
	else if(SCLK2X)
		case(SCLK_GEN_CNT)
			0: begin ADC_CS_N <= 1'b0; ADC_SCLK <= 1'b1; end
			1: ADC_SCLK <= 1'b0;
			2: ADC_SCLK <= 1'b1;
			3: ADC_SCLK <= 1'b0;
			4: ADC_SCLK <= 1'b1;
			5: begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[2]; end
			6: ADC_SCLK <= 1'b1;
			7: begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[1]; end
			8: ADC_SCLK <= 1'b1;
			9: begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[0]; end
			11,13,15,17,19,21,23,25,27,29,31: ADC_SCLK <= 1'b0;
			10:begin ADC_SCLK <= 1'b1; r_data[11] <= ADC_OUT; end
			12:begin ADC_SCLK <= 1'b1; r_data[10] <= ADC_OUT; end
			14:begin ADC_SCLK <= 1'b1; r_data[9] <= ADC_OUT; end
			16:begin ADC_SCLK <= 1'b1; r_data[8] <= ADC_OUT; end
			18:begin ADC_SCLK <= 1'b1; r_data[7] <= ADC_OUT; end
			20:begin ADC_SCLK <= 1'b1; r_data[6] <= ADC_OUT; end
			22:begin ADC_SCLK <= 1'b1; r_data[5] <= ADC_OUT; end
			24:begin ADC_SCLK <= 1'b1; r_data[4] <= ADC_OUT; end
			26:begin ADC_SCLK <= 1'b1; r_data[3] <= ADC_OUT; end
			28:begin ADC_SCLK <= 1'b1; r_data[2] <= ADC_OUT; end
			30:begin ADC_SCLK <= 1'b1; r_data[1] <= ADC_OUT; end
			32:begin ADC_SCLK <= 1'b1; r_data[0] <= ADC_OUT; end
			33:ADC_CS_N <= 1'b1;
			default:ADC_CS_N <= 1'b1;
		endcase

(5)标志位赋值及DATA输出
从ADC中读取的串行数据按位寄存在r_data中,在AD转换结束时,Conv_done置1,将寄存的r_data传给DATA一并输出。

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		ADC_State <= 1'b0;
	else if(Start)
		ADC_State <= 1'b1;
	else if((SCLK_GEN_CNT == 33) && SCLK2X && ADC_State)
		ADC_State <= 1'b0;
		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
		Conv_done <= 1'b0;
		DATA <= 12'd0;
	end
	else if((SCLK_GEN_CNT == 33) && SCLK2X && ADC_State)
		begin
			Conv_done <= 1'b1;
			DATA <= r_data;
		end
	else begin
		Conv_done <= 1'b0;
		DATA <= DATA;
	end

AD采集源代码

module adc128s052(
	input Clk,					//系统时钟
	input Rst_n,				//系统复位
	
	input [2:0] Channel,		//通道选择
	input Start,   			//开始标志位
	output reg Conv_done,	//完成标志位
	output reg [11:0] DATA,	//并行数字信号
	
	output reg ADC_CS_N,		//片选
	output reg ADC_DIN, 		//串行数据送给ADC芯片
	output reg ADC_SCLK,		//工作时钟
	input ADC_OUT				//串行数字信号
);

	reg ADC_State;			//AD采集状态标志
	reg [2:0] r_Channel;	//通道地址寄存
	reg [11:0] r_data;	//采集数据缓存,一次性输出
	
	parameter DIV_PARAM = 8;//分频系数 50/8 = 6.25Mhz
	reg [7:0] DIV_cnt;		//分频计数器
	reg SCLK2X;					//SCLK二倍频
	reg [5:0] SCLK_GEN_CNT;	//SCLK序列生成计数器
	
	always@(posedge Clk or negedge Rst_n)//数据缓存
	if(!Rst_n)
		r_Channel <= 3'd0;
	else if(Start)
		r_Channel <= Channel;
	else
		r_Channel <= r_Channel;
	
	always@(posedge Clk or negedge Rst_n)//分频计数器
	if(!Rst_n)
		DIV_cnt <= 8'd0;
	else if(ADC_State) begin
		if(DIV_cnt == DIV_PARAM/2 - 1)
			DIV_cnt <= 8'd0;
		else
			DIV_cnt <= DIV_cnt + 1'b1;
	end
	else
		DIV_cnt <= 8'd0;

	always@(posedge Clk or negedge Rst_n)//SCLK二倍频
	if(!Rst_n)
		SCLK2X <= 1'b0;
	else if(ADC_State && (DIV_cnt == DIV_PARAM/2 - 1))
		SCLK2X <= 1'b1;
	else
		SCLK2X <= 1'b0;
		
	always@(posedge Clk or negedge Rst_n)//生成序列计数器,对SCLK脉冲进行计数
	if(!Rst_n)
		SCLK_GEN_CNT <= 6'd0;
	else if(ADC_State)
		if(SCLK2X) begin
			if(SCLK_GEN_CNT == 33)
				SCLK_GEN_CNT <= 6'd0;
			else
				SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'b1;
		end
		else
			SCLK_GEN_CNT <= SCLK_GEN_CNT;
	else
		SCLK_GEN_CNT <= 6'd0;

	always@(posedge Clk or negedge Rst_n)//线性序列机发送数据
	if(!Rst_n) begin
		ADC_SCLK <= 1'b1;
		ADC_CS_N <= 1'b1;
		ADC_DIN <= 1'b1;
	end
	else if(SCLK2X)
		case(SCLK_GEN_CNT)
			0: begin ADC_CS_N <= 1'b0; ADC_SCLK <= 1'b1; end
			1: ADC_SCLK <= 1'b0;
			2: ADC_SCLK <= 1'b1;
			3: ADC_SCLK <= 1'b0;
			4: ADC_SCLK <= 1'b1;
			5: begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[2]; end
			6: ADC_SCLK <= 1'b1;
			7: begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[1]; end
			8: ADC_SCLK <= 1'b1;
			9: begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[0]; end
			11,13,15,17,19,21,23,25,27,29,31: ADC_SCLK <= 1'b0;
			10:begin ADC_SCLK <= 1'b1; r_data[11] <= ADC_OUT; end
			12:begin ADC_SCLK <= 1'b1; r_data[10] <= ADC_OUT; end
			14:begin ADC_SCLK <= 1'b1; r_data[9] <= ADC_OUT; end
			16:begin ADC_SCLK <= 1'b1; r_data[8] <= ADC_OUT; end
			18:begin ADC_SCLK <= 1'b1; r_data[7] <= ADC_OUT; end
			20:begin ADC_SCLK <= 1'b1; r_data[6] <= ADC_OUT; end
			22:begin ADC_SCLK <= 1'b1; r_data[5] <= ADC_OUT; end
			24:begin ADC_SCLK <= 1'b1; r_data[4] <= ADC_OUT; end
			26:begin ADC_SCLK <= 1'b1; r_data[3] <= ADC_OUT; end
			28:begin ADC_SCLK <= 1'b1; r_data[2] <= ADC_OUT; end
			30:begin ADC_SCLK <= 1'b1; r_data[1] <= ADC_OUT; end
			32:begin ADC_SCLK <= 1'b1; r_data[0] <= ADC_OUT; end
			33:ADC_CS_N <= 1'b1;
			default:ADC_CS_N <= 1'b1;
		endcase

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		ADC_State <= 1'b0;
	else if(Start)
		ADC_State <= 1'b1;
	else if((SCLK_GEN_CNT == 33) && SCLK2X && ADC_State)
		ADC_State <= 1'b0;
		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
		Conv_done <= 1'b0;
		DATA <= 12'd0;
	end
	else if((SCLK_GEN_CNT == 33) && SCLK2X && ADC_State)
		begin
			Conv_done <= 1'b1;
			DATA <= r_data;
		end
	else begin
		Conv_done <= 1'b0;
		DATA <= DATA;
	end	
endmodule

三、仿真

1.ADC芯片采集信号模拟

模拟ADC芯片采集过程,在SCLK下降沿时改变ADC_OUT的值,将这个过程封装成task任务。

	task GENE_ADC_OUT;
		input [15:0] v_data;
		integer i;
		begin
			wait(!ADC_CS_N);//等待CS_N被拉低
			for(i=0;i<16;i=i+1) begin
				@(negedge ADC_SCLK)
					ADC_OUT = v_data[15-i];
			end
		end
	endtask

2.仿真文件

仿真时,调用ADC芯片采集信号模拟任务,模拟采集到的数字信号为12’h678和12’h9ab,等待AD采集完成,代码如下:

`timescale 1ns/1ns
module adc128s052_tb();

	reg Clk,Rst_n,Start,ADC_OUT;
	reg [2:0]Channel;
	
	wire ADC_CS_N,ADC_DIN,ADC_SCLK;
	wire Conv_done;
	wire [11:0] DATA;
	
	adc128s052 adc128s052(
		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.DATA(DATA),
		.Channel(Channel),
		.Start(Start),   
		.Conv_done(Conv_done),
		
		.ADC_CS_N(ADC_CS_N),
		.ADC_DIN(ADC_DIN),
		.ADC_SCLK(ADC_SCLK),
		.ADC_OUT(ADC_OUT)
	);
	
	initial Clk = 1'b1;
	always #10 Clk = ~Clk;
	
	initial begin
		Rst_n = 1'b0;
		Start = 1'b0;
		Channel = 3'd0;
		#201;
		
		Rst_n = 1'b1;
		#20;
		
		Channel = 3'd6;
		Start = 1'b1;
		#20;
		Start = 1'b0;
		GENE_ADC_OUT(16'h0678);
		wait(Conv_done);
		#2000;
		
		Channel = 3'd3;
		Start = 1'b1;
		#20;
		Start = 1'b0;
		GENE_ADC_OUT(16'h09ab);
		wait(Conv_done);
		#2000;
		$stop;
	end
	
	task GENE_ADC_OUT;
		input [15:0] v_data;
		integer i;
		begin
			wait(!ADC_CS_N);//等待CS_N被拉低
			for(i=0;i<16;i=i+1) begin
				@(negedge ADC_SCLK)
					ADC_OUT = v_data[15-i];
			end
		end
	endtask
endmodule

3.仿真结果

仿真结果

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐