DAC配置—SPI的使用

SPI总线详解见:SPI总线协议
DAC3282的datasheet可去免费下载:DAC3282datasheet免费下载

本文基于TI公司的DAC3283进行讲解,通过SPI对其进行讲解,并且配有详细的Verilog代码和testbench。

DAC寄存器:一共有32个寄存器,每个寄存器的详细配置手册上均有写。
在这里插入图片描述
每个寄存器可以写入的数据为5Byte,其中第1Byte的用法在Table2中详细说明,包括读写控制,剩余4Byte的用法,寄存机地址等。
在这里插入图片描述
读写时序如下图所示:Figure32中的写时序中,默认的是[N1:N0]=00,表示只传输1Byte的数据,SDENB为片选信号,SDIO为双向接口,此图是按照三线模式分析的,和四线模式的唯一差别就是四线模式用SDI、SDO来分别表示写数据和读数据。
在这里插入图片描述
在这里插入图片描述

详细设置过程:
a. 首先明确SDIO数据中心对应着SCLK时钟的上升沿,在时钟的下降沿读出数据,因此在产生SCLK的数据时,需要将时钟进行180度的相移。此时的建立保持时间也最大,效果最好。
b. 建立状态机,确定SPI的状态迁移,一上电为IDLE状态,其中WAIT状态用于分割两次命令的等待时间,READ_MEM是读取ROM中的状态信息,WRITE_REG将读取的信息写入DAC芯片,当32个寄存器均被配置完成,跳入STOP状态。

在这里插入图片描述

Verilog代码如下:

`timescale 1ns / 1ps
//
// Engineer:mxGe_UESTC 
// 
// Create Date: 2020/04/25 19:02:16
// Design Name: 
// Module Name: spi_ctrl
// Project Name: 
//
module spi_ctrl(
	input	wire		sclk,//系统时钟50Mhz
	input	wire		rst_n,
	input	wire		work_en,//触发配置操作的使能
	output	reg			conf_end,
	output	wire		spi_clk,//50-60mhz
	output	wire		spi_sdi,
	output	wire		spi_csn,
	input	wire		spi_sdo//读输入管脚不进行编程
);

parameter	IDLE = 5'b0_0001;
parameter	WAIT = 5'b0_0010;
parameter	R_MEM= 5'b0_0100;
parameter	W_REG= 5'b0_1000;
parameter	STOP = 5'b1_0000;

parameter	H_DIV_CYC	=	5'd25-1;



reg		[4:0]	state;//状态机的寄存器变量,编码方式采用独热码
reg		[4:0]	div_cnt;
reg				clk_p=1'b0;
wire			clk_n;
reg				pose_flag;
reg		[3:0]	wait_cnt;
reg		[3:0]	shift_cnt;
reg		[4:0]	r_addr;
wire	[15:0]	r_data;
wire			wren;
reg		[15:0]	shift_buf;
reg				data_end;
reg				sdi;
reg				csn;
reg				tck;

//分频计数器
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		div_cnt <= 5'd0;
	else if( div_cnt == H_DIV_CYC )
		div_cnt <= 'd0;
	else 
		div_cnt <= div_cnt + 1'b1;
//分频时钟不允许做寄存器的触发时钟,也就是不能写在always块的触发列表中
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		clk_p <= 1'b0;
	else if(div_cnt == H_DIV_CYC)
		clk_p <= ~clk_p;
		
assign	clk_n=~clk_p;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		pose_flag <= 1'b0;
	else if(clk_p == 1'b0 && div_cnt == H_DIV_CYC)
		pose_flag <= 1'b1;
	else	pose_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		wait_cnt <= 'd0;
	else if(state == WAIT && pose_flag == 1'b1)
		wait_cnt <= wait_cnt + 1'b1;
	else if(state != WAIT)
		wait_cnt <= 4'd0;

//fsm  命令8bit,数据8bit,一共16bit
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		state <= IDLE;
	else case (state)
		IDLE :if(work_en == 1'b1)
			state <= WAIT;
		WAIT :if(wait_cnt[3] == 1'b1)
			state <= R_MEM;
		R_MEM:	state <=W_REG;
		W_REG:	if(shift_cnt == 4'd15 && pose_flag == 1'b1 && data_end != 1'b1)
				state <= WAIT;
			else if(shift_cnt == 4'd15 && pose_flag == 1'b1 && data_end == 1'b1)
				state <= STOP;
		STOP:	state <= STOP;
		default : state <= IDLE;
		
	endcase

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		shift_cnt <= 'd0;
	else if(state == W_REG && pose_flag == 1'b1)
		shift_cnt <= shift_cnt + 1'b1;
	else if( state != W_REG)
		shift_cnt <= 4'd0;
//读mem的地址产生
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		r_addr <= 'd0;
	else if(state == R_MEM)
		r_addr <= r_addr + 1'b1;
//data_end 最后一个需要移位数据
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		data_end <= 1'b0;
	else if(state == R_MEM && (&r_addr) == 1'b1)//等效于r_addr == 5'd31
		data_end <= 1'b1;

assign	wren =1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		shift_buf<='d0;
	else if(state == R_MEM)
		shift_buf <= r_data;
	else if(state == W_REG && pose_flag == 1'b1)
		shift_buf <= {shift_buf[14:0],1'b1};

//数据的输出
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		sdi<=1'b0;
	else if(state == W_REG)
		sdi<=shift_buf[15];
	else
		sdi <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		csn <= 1'b1;
	else if(state == W_REG)
		csn <= 1'b0;
	else
		csn <= 1'b1;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		tck<=1'b0;
	else if(state == W_REG )
		tck<=clk_n;
	else
		tck<=1'b0;

assign spi_clk = tck;
assign spi_csn = csn;
assign spi_sdi = sdi;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		conf_end <= 1'b0;
	else if(state == STOP)
		conf_end <= 1'b1;
		
ram_16x32_sr ram_16x32_sr_inst (
  .clka(sclk),    // input wire clka
  .ena(1'b1),      // input wire ena
  .wea(wren),      // input wire [0 : 0] wea  写使能高有效,读使能低有效
  .addra(r_addr),  // input wire [4 : 0] addra
  .dina(16'd0),    // input wire [15 : 0] dina
  .douta(r_data)  // output wire [15 : 0] douta
);			
	
endmodule

testbench

`timescale 1ns / 1ps
//
// Engineer: mxGe_UESTC 
// Create Date: 2020/04/25 20:32:06
// Design Name: 
// Module Name: tb_spi_ctrl
// Project Name: 
//
module tb_spi_ctrl();

reg	sclk,rst_n;
reg	work_en;
wire	spi_clk,sclk_csn,spi_sdi;
reg	[15:0]	send_mem [31:0];
reg	[15:0]	shift_buf;	

initial	begin
	rst_n =0;
	sclk =0;
	#100;
	rst_n =1;
end  

initial	begin
	work_en =0;
	#150
	work_en =1;
end

initial begin
	$readmemb("dac_ini_16x32.mif",send_mem);
end

always #10 sclk = ~sclk;

initial begin
	rec_spi();
end

spi_ctrl	spi_ctrl_inst(
	.sclk		(sclk),//系统时钟50Mhz
	.rst_n		(rst_n),
	.work_en	(work_en),//触发配置操作的使能
	.conf_end	(),
	.spi_clk	(spi_clk),//50-60mhz
	.spi_sdi	(spi_sdi),
	.spi_csn	(spi_csn),
	.spi_sdo	()//读输入管脚不进行编程
);

task rec_spi();
	integer i,j;
	begin
		for (i=0;i<32;i=i+1)begin
			for(j=0;j<16;j=j+1)begin
				@(posedge spi_clk);
					shift_buf = {shift_buf[14:0],spi_sdi};
				if(j==15 && shift_buf == send_mem[i])
					$display("ok data index is %d rec_d=%d send_d=%d",i,shift_buf,send_mem[i]);
				else if(j== 15)
					$display("error");	
					
			end
		end
	end
endtask
endmodule

猜你喜欢

转载自blog.csdn.net/gemengxia/article/details/108223997