天天看點

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

一、任務

使用FPGA晶片控制DAC采集晶片,輸出指定的電壓值。

二、硬體部分

為了将FPGA輸出的數字電壓轉換成模拟電壓,我們使用到了數模轉換晶片(簡稱DAC)TLV5618。進行設計前,我們先到網上檢索并檢視了該晶片的資料手冊。

1.晶片功能圖

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

2.端口功能表

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

從功能圖和功能表中我們可以看出,TLV5618有四個輸入端口:

片選信号CS、資料串行輸入端口DIN、模拟參考電壓REF、數字時鐘SCLK。

兩個輸出端分别為OUTA和OUTB,均為對應的模拟電壓輸出端。

3.時序圖

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

從時序圖中我們可以看到使用該晶片時要注意這幾個參數:

tw(L):低電平最小寬度,25ns。

tw(H):高電平最小寬度,25ns。

tsu(D):資料最短建立時間。

th(D):資料最短保持時間。

tsu(CS-CK):片選信号下降沿到第一個時鐘下降沿最短時間。

th(CSH):片選信号最短拉高時間。

在我們寫FPGA代碼時,需要根據嚴格按照時序圖來。

4.輸出電壓計算

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

由手冊給出的公式知,輸出電壓與輸入的編碼值成正比,同時還要乘以一個系數REF,這個系數從晶片的REF引腳輸入。我們打開并檢視開發闆的原理圖:

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

從圖中知,我們用到了晶片LM4040-2.0給DAC供電,這個晶片工作時輸出電壓為4.028V(即精度為12位),故參數REF為4.028。

5.時鐘頻率與重新整理率計算

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

我們查閱手冊後知道,使用該晶片時,時鐘最大頻率為20MHz,重新整理率為時鐘頻率的1/16。而開發闆提供的原始時鐘為50MHz,是以可以采用四分頻後得到12.5MHz的時鐘頻率。

三、設計方案

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真

我們考慮用FPGA設計一個DAC驅動,通過CS、sclk、din三根信号線與DAC晶片連接配接,設計輸入端口Data[15:0]。同時為了便于與其他子產品共同協作,我們加上了使能端口en和轉換完成标志位Conv_done,這是FPGA設計時必須考慮的一點,對于複雜的驅動子產品,這兩個信号是不可或缺的。

四、軟體部分

這裡直接上代碼部分,注釋裡面有解讀。

// 驅動部分
module tlv5618(
	Clk,
	Rst_n,
	
	DAC_DATA,	//并行資料輸入端
	Start,		//開始标志位
	Set_Done,	//完成标志位
	
	DAC_CS_N,	//片選
	DAC_DIN,	//串行資料送給ADC晶片
	DAC_SCLK,	//工作時鐘SCLK
	DAC_State	//工作狀态
);
	
	parameter fCLK = 50;		//時鐘參數
	parameter DIV_PARAM = 2;	//分頻參數

	input Clk;
	input Rst_n;
	input [15:0] DAC_DATA;
	input Start;
	output reg Set_Done;
	
	output reg DAC_CS_N;
	output reg DAC_DIN;
	output reg DAC_SCLK;
	output DAC_State;
	
	assign DAC_State = DAC_CS_N;	//工作狀态标志與片選信号相同
	
	reg [15:0] r_DAC_DATA;
	
	reg [3:0] DIV_CNT;		//分頻計數器
	reg SCLK2X;				//2倍SCLK的采樣時鐘
	
	reg [5:0] SCLK_GEN_CNT;	//SCLK生成暨序列機計數器
	
	reg en;					//轉換使能信号
	
	wire trans_done; 		//轉換序列完成标志信号
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		en <= 1'b0;
	else if(Start)
		en <= 1'b1;
	else if(trans_done)
		en <= 1'b0;	//轉換完成後将使能關閉
	else
		en <= en;		

	//分頻計數器
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		DIV_CNT <= 4'd0;
	else if(en)begin
		if(DIV_CNT == (DIV_PARAM - 1'b1))	//前面設定了分頻系數為2,這裡計數器能夠容納2拍時鐘脈沖
			DIV_CNT <= 4'd0;
		else 
			DIV_CNT <= DIV_CNT + 1'b1;
		end
	else
		DIV_CNT <= 4'd0;

	//二分頻
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		SCLK2X <= 1'b0;
	else if(en && (DIV_CNT == (DIV_PARAM - 1'b1)))
		SCLK2X <= 1'b1;
	else
		SCLK2X <= 1'b0;
		
	//生成序列計數器,對SCLK脈沖進行計數
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		SCLK_GEN_CNT <= 6'd0;
	else if(SCLK2X && en)begin	//在高脈沖期間,累計拍數
		if(SCLK_GEN_CNT == 6'd33)
			SCLK_GEN_CNT <= 6'd0;
		else
			SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'd1;
		end
	else
		SCLK_GEN_CNT <= SCLK_GEN_CNT;

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		r_DAC_DATA <= 16'd0;
	else if(Start)	//收到開始發送指令時,寄存DAC_DATA值
		r_DAC_DATA <= DAC_DATA;
	else
		r_DAC_DATA <= r_DAC_DATA;
				
	//依次将資料移出到DAC晶片
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		DAC_DIN <= 1'b1;
		DAC_SCLK <= 1'b0;
		DAC_CS_N <= 1'b1;
		end
	else if(!Set_Done && SCLK2X) begin
		case(SCLK_GEN_CNT)
			0:
				begin	//高脈沖期間内,計數為0時了,打開片選使能,給予時鐘上升沿,将最高位資料送給ADC晶片
					DAC_CS_N <= 1'b0;
					DAC_DIN <= r_DAC_DATA[15];
					DAC_SCLK <= 1'b1;
				end
		
			1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31:
				begin
					DAC_SCLK <= 1'b0;	//時鐘低電平
				end
			
			2:  begin DAC_DIN <= r_DAC_DATA[14]; DAC_SCLK <= 1'b1; end
			4:  begin DAC_DIN <= r_DAC_DATA[13]; DAC_SCLK <= 1'b1; end
			6:  begin DAC_DIN <= r_DAC_DATA[12]; DAC_SCLK <= 1'b1; end
			8:  begin DAC_DIN <= r_DAC_DATA[11]; DAC_SCLK <= 1'b1; end			
			10: begin DAC_DIN <= r_DAC_DATA[10]; DAC_SCLK <= 1'b1; end
			12: begin DAC_DIN <= r_DAC_DATA[9];  DAC_SCLK <= 1'b1; end
			14: begin DAC_DIN <= r_DAC_DATA[8];  DAC_SCLK <= 1'b1; end
			16: begin DAC_DIN <= r_DAC_DATA[7];  DAC_SCLK <= 1'b1; end	
			18: begin DAC_DIN <= r_DAC_DATA[6];  DAC_SCLK <= 1'b1; end
			20: begin DAC_DIN <= r_DAC_DATA[5];  DAC_SCLK <= 1'b1; end				
			22: begin DAC_DIN <= r_DAC_DATA[4];  DAC_SCLK <= 1'b1; end
			24: begin DAC_DIN <= r_DAC_DATA[3];  DAC_SCLK <= 1'b1; end
			26: begin DAC_DIN <= r_DAC_DATA[2];  DAC_SCLK <= 1'b1; end
			28: begin DAC_DIN <= r_DAC_DATA[1];  DAC_SCLK <= 1'b1; end			
			30: begin DAC_DIN <= r_DAC_DATA[0];  DAC_SCLK <= 1'b1; end
			
			32: DAC_SCLK <= 1'b1; 	//時鐘拉高
			33: DAC_CS_N <= 1'b1;	//關閉片選
			default:;
		endcase
	end
	
	assign trans_done = (SCLK_GEN_CNT == 33) && SCLK2X;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Set_Done <= 1'b0;
	else if(trans_done)
		Set_Done <= 1'b1;
	else
		Set_Done <= 1'b0;
	
endmodule

           
//頂層子產品
module DAC_test(
			Clk,   //子產品時鐘50M
			Rst_n, //子產品複位
			
			DAC_CS_N,  //TLV5618的CS_N接口
			DAC_DIN,   //TLV5618的DIN接口
			DAC_SCLK   //TLV5618的SCLK接口
		);
	input Clk;
	input Rst_n;
	
	output DAC_CS_N;
	output DAC_DIN;
	output DAC_SCLK;
	
	reg Start;
	reg [15:0]r_DAC_DATA;
	wire DAC_State;
	wire [15:0]DAC_DATA;
	wire Set_Done;
	
	tlv5618 tlv5618(
		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.DAC_DATA(DAC_DATA),
		.Start(Start),
		.Set_Done(Set_Done),
		
		.DAC_CS_N(DAC_CS_N),
		.DAC_DIN(DAC_DIN),
		.DAC_SCLK(DAC_SCLK),
		.DAC_State(DAC_State)
	);

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		r_DAC_DATA <= 16'd0;
	else if(DAC_State)
		r_DAC_DATA <= DAC_DATA;
		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Start <= 1'd0;
	else if(r_DAC_DATA != DAC_DATA) 
		Start <= 1'b1;
	else
		Start <= 1'd0;

endmodule

           

五、仿真

FPGA驅動DAC晶片輸出(以TLV5618為例)一、任務二、硬體部分三、設計方案四、軟體部分五、仿真