FPGA学习笔记:数据采集传输系统设计(二):TLV5618型DAC驱动

一、TLV5618时序图

1.时序图

时序图
TLV5618是低功耗双路12位的电压输出DAC,兼容SPI串行接口。软件部分FPGA需要提供给TLV5618芯片的信号包括SPI时钟SCLK片选信号CS_N16位串行数据DIN
时序图中一些参数的说明:
参数说明
t s u ( C S − C K ) t_{su(CS-CK)} tsu(CSCK):CS低电平到SCLK第一个下降沿的最短时间,VDD=5V时为5ns。
t w ( H ) t_{w(H)} tw(H):SCLK高电平宽度,最短25ns。
t w ( L ) t_{w(L)} tw(L):SCLK低电平宽度,最短25ns。
t s u ( D ) t_{su(D)} tsu(D):数据在SCLK下降沿到来前稳定建立的最短时间。
t h ( D ) t_{h(D)} th(D): 数据在SCLK下降沿到来后保持稳定的最短时间。
t h ( C S H ) t_{h(CSH)} th(CSH):两次传输之间片选信号保持高电平的最短时间。

2.设计要点

根据上述时序,得出以下设计要点:
(1)SCLK下降沿时,读取数据,数据保持稳定,DIN应在SCLK上升沿时变化
(2)SCLK高低电平时间不低于25ns,即SCLK一个时钟周期应大于50ns,频率小于20Mhz。以系统时钟为50Mhz为例,取四分频12.5Mhz作为SCLK时钟
(3)DIN发送串行数据时,先发送高位,后发送低位

3.DIN数据格式

DIN数据格式
16位DIN串行数据由两部分组成:编程数据位(控制位)和数据位
D15-D12为编程数据位:
D14位为速度控制位,1为快速模式,转换时间3us,0为低速模式,转换时间10us。
D13为电源控制位,1为待机模式,0为正常模式。
D15和D12组成寄存器选择位,00表示写数据到通道B和BUFFER;01表示写数据到BUFFER;10表示写数据到通道A,并将BUFFER的数据更新到通道B,这个模式可以同时对A、B两通道进行操作

4.输出电压计算

TLV5618功能框图:
功能框图
结合功能框图和芯片手册,可得出输出电压计算公式
输出电压 = 2 ∗ R E F ∗ C O D E 2 n 输出电压=2*REF*\frac{CODE}{2^n} 输出电压=2REF2nCODE
REF为参考电压,由硬件电路决定;CODE为软件中设定的数值,即D11-D0;n为DAC的位数,取12位。

二、TLV5618代码设计

1.模块端口说明

端口名称方向说明
Clkinput系统时钟
Rst_ninput系统复位
DAC_DATA[15:0]input并行数据输入端
StartinputDAC开始标志位
Conv_doneoutputDAC完成标志位
DAC_CS_NoutputDAC片选
DAC_DINoutput串行数据输出端
DAC_SCLKoutputDAC工作时钟

端口图
data为并行数据输入端,用于配置DAC参数,设置输出电压值。DAC_CS_N、DAC_DIN、DAC_SCLK为FPGA与DAC芯片的接口。

2.代码设计

模块采用线性序列机编写SPI时序,可分为以下5个部分:数据缓存、分频计数器、生成序列计数器、SPI线性序列机、标志位赋值。
(1)数据缓存
接收到Start开始转换信号时,数据开始发送,为避免在发送过程中由于干扰导致输入的数据变化,在Start为高电平时,将输入数据data缓存到r_DAC_data中。

	reg [15:0] r_DAC_DATA;	//数据缓存
	always@(posedge Clk or negedge Rst_n)//数据缓存
	if(!Rst_n)
		r_DAC_DATA <= 16'd0;
	else if(Start)
		r_DAC_DATA <= DAC_DATA;
	else
		r_DAC_DATA <= r_DAC_DATA;

(2)分频计数器
前面提到SCLK频率应小于20Mhz,取系统时钟的四分频12.5Mhz为SCLK的时钟。由于数据在SCLK上升沿时变化,下降沿时被DAC芯片读取,需计算SCLK二倍频以区分上升沿和下降沿

	parameter DIV_PARAM = 4;	//分频系数 50/4 = 12.5Mhz
	reg [7:0] DIV_cnt;			//分频计数器
	reg SCLK2X;					//SCLK二倍频
	
	always@(posedge Clk or negedge Rst_n)//分频计数器
	if(!Rst_n)
		DIV_cnt <= 8'd0;
	else if(DAC_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(DAC_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(DAC_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线性序列机
SCLK_GEN_CNT为偶数时,SCLK为上升沿,更改DIN数据;SCLK_GEN_CNT为奇数时,SCLK为下降沿,芯片读取DIN数据。16个SCLK时钟结束后,根据时序图,需要将SCLK和CS拉高。

	always@(posedge Clk or negedge Rst_n)//线性序列机发送数据
	if(!Rst_n) begin
		DAC_SCLK <= 1'b1;
		DAC_CS_N <= 1'b1;
		DAC_DIN <= 1'b1;
	end
	else if(SCLK2X)
		case(SCLK_GEN_CNT)
			0:begin 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: DAC_SCLK <= 1'b0;
			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:begin DAC_SCLK <= 1'b1; DAC_CS_N <= 1'b1; end
			default:DAC_CS_N <= 1'b1;
		endcase

(5)标志位赋值
对DAC_State和Conv_done进行赋值。开始转换时,DAC_State为高电平;结束转换时,DAC_State为低电平,Conv_done置1。

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		DAC_State <= 1'b0;
	else if(Start)
		DAC_State <= 1'b1;
	else if((SCLK_GEN_CNT == 33) && SCLK2X && DAC_State)
		DAC_State <= 1'b0;
		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Conv_done <= 1'b0;
	else if((SCLK_GEN_CNT == 33) && SCLK2X && DAC_State)
		Conv_done <= 1'b1;
	else
		Conv_done <= 1'b0;

DAC驱动源代码

module tlv5618(
	input Clk,				//系统时钟
	input Rst_n,			//系统复位
	
	input [15:0] DAC_DATA,	//并行数据输入端
	input Start,   			//DAC开始标志位
	output reg Conv_done,	//DAC完成标志位
	
	output reg DAC_CS_N,	//DAC片选
	output reg DAC_DIN, 	//串行数据输出端
	output reg DAC_SCLK		//DAC工作时钟
);
	
	reg DAC_State;			//DAC转换状态标志
	reg [15:0] r_DAC_DATA;	//数据缓存
	
	parameter DIV_PARAM = 4;	//分频系数 50/4 = 12.5Mhz
	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_DAC_DATA <= 16'd0;
	else if(Start)
		r_DAC_DATA <= DAC_DATA;
	else
		r_DAC_DATA <= r_DAC_DATA;
	
	always@(posedge Clk or negedge Rst_n)//分频计数器
	if(!Rst_n)
		DIV_cnt <= 8'd0;
	else if(DAC_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(DAC_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(DAC_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
		DAC_SCLK <= 1'b1;
		DAC_CS_N <= 1'b1;
		DAC_DIN <= 1'b1;
	end
	else if(SCLK2X)
		case(SCLK_GEN_CNT)
			0:begin 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: DAC_SCLK <= 1'b0;
			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:begin DAC_SCLK <= 1'b1; DAC_CS_N <= 1'b1; end
			default:DAC_CS_N <= 1'b1;
		endcase
		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		DAC_State <= 1'b0;
	else if(Start)
		DAC_State <= 1'b1;
	else if((SCLK_GEN_CNT == 33) && SCLK2X && DAC_State)
		DAC_State <= 1'b0;
		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Conv_done <= 1'b0;
	else if((SCLK_GEN_CNT == 33) && SCLK2X && DAC_State)
		Conv_done <= 1'b1;
	else
		Conv_done <= 1'b0;

endmodule

三、仿真

1.仿真文件

在仿真文件中发送16’habcd和16’hbbcc。

`timescale 1ns/1ns

module tlv5618_tb();
	
	reg Clk,Rst_n,Start;
	reg [15:0] DAC_DATA;
	
	wire DAC_CS_N,DAC_DIN,DAC_SCLK;
	wire Conv_done;

	tlv5618 tlv5618(
		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.DAC_DATA(DAC_DATA),
		.Start(Start),
		.Conv_done(Conv_done),
		
		.DAC_CS_N(DAC_CS_N),
		.DAC_DIN(DAC_DIN),
		.DAC_SCLK(DAC_SCLK)
	);

	initial Clk = 1;
	always #10 Clk = ~Clk;
	
	initial begin
		Rst_n = 0;
		DAC_DATA = 16'h0;
		Start = 0;
		#201;
		
		Rst_n = 1;
		#20;
		
		DAC_DATA = 16'habcd;
		Start = 1;
		#20;
		Start = 0;
		wait(Conv_done);
		#20000;
		
		DAC_DATA = 16'hbbcc;
		Start = 1;
		#20;
		Start = 0;
		wait(Conv_done);
		#20000;
		$stop;

	end

endmodule

2.仿真结果

整体结果
局部结果

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
基于FPGA数据采集系统。 主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。 该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、DAC驱动模块、ADC驱动模块、同步FIFO模块、FIFO控制模块、串口发送模块。各个模块的作用如下: (1)串口接收模块(UART_Byte_Rx.v):完成串口数据接收,将串行数据转换成并行数据输出。 (2)按键消抖模块(key_filter.v):进行按键消抖,可输出一个脉冲按键按下标志和按键按下时间标志。 (3)按键控制模块(key_ctrl.v):当在DA一直输出模拟信号时,按下按键控制ADC转换100次。 (4)ROM模块(single_port_rom.v):存储DA转换的数据,可存放正弦波形数据。 (5)DAC驱动模块(dac_driver.v):数模转换驱动模块,与外部DAC芯片相连,提供DAC芯片时钟和数据信号等。 (6)ADC驱动模块(adc_driver.v):模数转换驱动模块,与外部ADC芯片相连,提供ADC芯片时钟和控制信号等。 (7)同步FIFO模块(sync_fifo.v):存放ADC转换后的数据。 (8)FIFO控制模块(fifo_ctrl.v):当FIFO中有数据时,将FIFO中的数据转换成可以UART串口发送的数据。 (9)串口发送模块(Uart_Byte_Tx.v):经过FIFO控制模块转换的数据通过串口发送模块发送到串口,显示在pc端。 (10)DAC控制模块(dac_ctrl.v):当接收串口指定的指令时,开始将ROM的正弦数据进行DAC转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值