FPGA进阶篇--SPI控制双通道16bit串行DAC8532

一、芯片手册关键点笔记

1、时序

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、寄存器

看完了,时序部分,我们来看看寄存器部分,通过配置寄存器,可以控制我们的输出,所以寄存器也非常重要。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里就说了,如果SYNC(取反)信号的突然上升,在数据还没有传完的时候就上升,这种中断的方式,数据缓冲器内容,DAC寄存器内容的更新或操作模式的更改都不会发生,已经写入的这一段数据都会被丢弃,移位寄存器会重新置位。
在这里插入图片描述
上图为这几种模式的介绍,我们一般来说用正常模式就行,其余模式,可以根据实际需求更改。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
上图是三种powerdown模式,但是我们一般就设置为00,正常模式即可,不需要用到powerdown模式
下面,还给出了操作的例子
在这里插入图片描述
在这里插入图片描述
例子1是在等待两个24sclk序列加载完毕后,此时,A B 的寄存器都加载了数据后,再同时进行数模转换。
例子2是24个SCLK加载完数据到A后,立马就开始输出,第二个SCLK加载完数据到B后,B开始输出。
在这里插入图片描述
在这里插入图片描述

二、FPGA控制DAC8532驱动

时隔3天,终于调好了程序,非常适合几乎所有的SPI控制的ADC和DAC芯片。
只需要改动参数parameter即可。
这也是我写程序的一个良好习惯,尽量参数化,尽量让写过的程序以后复用,而不要今天用SPI写一次,隔天换了个DA的时序,也是SPI控制的,又用SPI再写一次,这样效率相当低下,尽量一次就写好,写工整。

软件:quartus 13.1
modelsim 10.1 c

代码:

顶层模块

DAC8532_drive_project

//功能描述:驱动双通道16bit的DAC同时产生正弦波形,或者三角波形
//author: ciscomonkey
//思路分析:组合控制+数据,组合完毕,发出传输指令,SPI模块开始传输,传输完毕信号给控制模块,控制模块接收到传输完毕信号,
//进入等待DA芯片的转换时间,等待结束,再次进入组合状态。
`timescale 1 ns/ 1 ns
module DAC8532_drive_project
#
(
parameter data_width=24,//控制+DAC_data数据长度
parameter DAC_data_width=16,//DAC_data数据长度
parameter wait_conversion_time=10,//转换时间,100M(10ns),100ns
parameter SPI_RATE=10_000_000,
parameter CLOCK_RATE=100_000_000
)
(
input clk,
input rst,
input start,	//触发启动程序

input switch,  //波形选择,1为电平1,2为电平2

output sclk,    //输出时钟信号

output SYNC_n,	//输出同步信号

output Din     //输出数据
);
//
//
//
//


reg [DAC_data_width-1:0] voltage_1_reg=16'b1010_1010_1010_1010;
reg [DAC_data_width-1:0] voltage_2_reg=16'b0101_0101_0101_0101;
wire [DAC_data_width-1:0] voltage_1;
wire [DAC_data_width-1:0] voltage_2;

assign voltage_1=voltage_1_reg;
assign voltage_2=voltage_2_reg;


wire [DAC_data_width-1:0] DAC_a_data = switch ? voltage_1 : voltage_2;    
wire [DAC_data_width-1:0] DAC_b_data = switch ? voltage_1 : voltage_2;    


wire spi_data_done;
wire mosi;
wire start_spi;
wire [data_width-1:0] miso_data_reg;
wire [data_width-1:0] data_reg;
wire write_busy;
wire data_DAC_ab_finish;
//-------------------------------------------------------------
DAC8532_DATA_Ctrl
# (
.data_width(data_width),//控制+DAC_data数据长度

.DAC_data_width(DAC_data_width),//DAC_data数据长度

.wait_conversion_time(wait_conversion_time)
)DAC8532_DATA_Ctrl_inst
(
.clk(clk),
.rst(rst),
.DAC_a_data(DAC_a_data),
.DAC_b_data(DAC_b_data),
.start(start),						//启动DAC8532
.spi_data_done(spi_data_done),				//24位宽的数据传输完的标志

.data_reg(data_reg),	//串行输出一帧数据(控制+数据)
.start_spi(start_spi),					//组合好一帧数据后开始传输命令
.data_DAC_ab_finish(data_DAC_ab_finish)
);


//----------------------------------------------------------------------

spi_data_transfer
# (
    .data_width(data_width)   ,           // SPI一次写入数据长度
    .SPI_RATE(SPI_RATE)     ,    // SPI时钟速率
    .CLOCK_RATE(CLOCK_RATE)    // 系统时钟速率
    )spi_data_transfer_inst
(
.clk(clk),
.rst(rst),
.data_reg(data_reg),					//输入数据,用于mosi
.start_spi(start_spi),//开启传输指令
.miso_data_reg(miso_data_reg),				//miso,主机(FPGA)接收的数据,从机发送
.miso(0),
.sclk(sclk),			//时钟
.write_busy(write_busy),	//写入繁忙
.spi_data_done(spi_data_done),			//数据传输完的标志
.mosi(mosi),      		//主发从收,主机(FPGA)发送,从机接收
.SYNC_n(SYNC_n)

);
assign Din=mosi;

endmodule


DAC8532_DATA_Ctrl模块:

//功能描述:驱动双通道16bit的DAC同时产生正弦波形,或者三角波形
//author: ciscomonkey
//思路分析:组合控制+数据,组合完毕,发出传输指令,SPI模块开始传输,传输完毕信号给控制模块,控制模块接收到传输完毕信号,
//进入等待DA芯片的转换时间,等待结束,再次进入组合状态。
`timescale 1 ns/ 1 ns
module DAC8532_drive_project
#
(
parameter data_width=24,//控制+DAC_data数据长度
parameter DAC_data_width=16,//DAC_data数据长度
parameter wait_conversion_time=15,//转换时间,100M(10ns),100ns
parameter SPI_RATE=10_000_000,
parameter CLOCK_RATE=100_000_000
)
(
input clk,
input rst,
input start,	//触发启动程序

input switch,  //波形选择,1为电平1,2为电平2

output sclk,    //输出时钟信号

output SYNC_n,	//输出同步信号
//
output [5:0] next_state_test, 	//状态机测试信号
output [5:0] now_state_test,

output Din     //输出数据
);
//
//
//
//


reg [DAC_data_width-1:0] voltage_1_reg=16'b1010_1010_1010_1010;
reg [DAC_data_width-1:0] voltage_2_reg=16'b0101_0101_0101_0101;
wire [DAC_data_width-1:0] voltage_1;
wire [DAC_data_width-1:0] voltage_2;

assign voltage_1=voltage_1_reg;
assign voltage_2=voltage_2_reg;


wire [DAC_data_width-1:0] DAC_a_data = switch ? voltage_1 : voltage_2;    
wire [DAC_data_width-1:0] DAC_b_data = switch ? voltage_1 : voltage_2;    


wire spi_data_done;
wire mosi;
wire start_spi;
wire [data_width-1:0] miso_data_reg;
wire [data_width-1:0] data;
wire write_busy;
wire data_DAC_ab_finish;
//-------------------------------------------------------------
DAC8532_DATA_Ctrl
# (
.data_width(data_width),//控制+DAC_data数据长度

.DAC_data_width(DAC_data_width),//DAC_data数据长度

.wait_conversion_time(wait_conversion_time)
)DAC8532_DATA_Ctrl_inst
(
.clk(clk),
.rst(rst),
.DAC_a_data(DAC_a_data),
.DAC_b_data(DAC_b_data),
.start(start),						//启动DAC8532
.spi_data_done(spi_data_done),				//24位宽的数据传输完的标志
.now_state_test(now_state_test),
.next_state_test(next_state_test),

.data(data),	//串行输出一帧数据(控制+数据)
.start_spi(start_spi),					//组合好一帧数据后开始传输命令
.data_DAC_ab_finish(data_DAC_ab_finish)
);


//----------------------------------------------------------------------

spi_data_transfer
# (
    .data_width(data_width)   ,           // SPI一次写入数据长度
    .SPI_RATE(SPI_RATE)     ,    // SPI时钟速率
    .CLOCK_RATE(CLOCK_RATE)    // 系统时钟速率
    )spi_data_transfer_inst
(
.clk(clk),
.rst(rst),
.data_reg(data),					//输入数据,用于mosi
.start_spi(start_spi),//开启传输指令
.miso_data_reg(miso_data_reg),				//miso,主机(FPGA)接收的数据,从机发送
.miso(0),
.sclk(sclk),			//时钟
.write_busy(write_busy),	//写入繁忙
.spi_data_done(spi_data_done),			//数据传输完的标志
.mosi(mosi),      		//主发从收,主机(FPGA)发送,从机接收
.SYNC_n(SYNC_n)

);
assign Din=mosi;

endmodule




spi_data_transfer模块:

//module:DAC8532_DATA_Ctrl
//author:Ciscomonkey
//functional description:提供mosi的数据,用于传输,发出传输指令则可以开启传输,并控制好时序,在传输完毕后,等待转换时间
`timescale 1 ns/ 1 ns
module DAC8532_DATA_Ctrl
# (
parameter data_width=24,//控制+DAC_data数据长度

parameter DAC_data_width=16,//DAC_data数据长度

parameter wait_conversion_time=15//A通道传输后,转换时间至少100ns(100M(10ns)),实际测试:+1
)
(
input clk,
input rst,
input 	[DAC_data_width-1:0] DAC_a_data,
input 	[DAC_data_width-1:0] DAC_b_data,
input    start,//启动DAC8532
input 	spi_data_done,				//24位宽的数据传输完的标志

output	[data_width-1:0] data,	//串行输出一帧数据(控制+数据)
output   start_spi, 					//组合好一帧数据后开始传输命令
output  [5:0]  now_state_test,
output  [5:0]  next_state_test,
output     data_DAC_ab_finish 		//conversion转换结束标志
);



reg start_spi_reg;
reg [data_width-1:0] data_reg;
reg data_DAC_ab_finish_reg;
//状态机  三段式

localparam IDLE_state=6'b000_000;
localparam combine_DAC_a_data_state=6'b000_001;    //控制+DAC_a_data
localparam spi_DAC_A_trans_state=6'b000_010;			//A通道SPI的传输
localparam conversion_DAC_a_data_state=6'b00_100;  //DAC_a_data的转换等待时间
localparam combine_DAC_b_data_state=6'b001_000;		//控制+DAC_b_data
localparam spi_DAC_B_trans_state=6'b010_000;				//B通道SPI的传输
localparam conversion_DAC_b_data_state=6'b100_000;  //DAC_b_data的转换等待时间

reg  [5:0] now_state=IDLE_state;
reg  [5:0] next_state=IDLE_state;

//----------
assign now_state_test=now_state;
assign next_state_test=next_state;
reg  [DAC_data_width-1:0]  conversion_time_cnt='d0;

		

//1、实现状态转换
always @ (posedge clk or negedge rst)
begin
	if(!rst)							//低电平复位
	now_state<=IDLE_state;
	else
	now_state<=next_state;
end

//2、根据条件产生下一个状态
always@(*)
begin
	case(now_state)
	IDLE_state:
					begin
					if(start)
						begin
							next_state=combine_DAC_a_data_state;							
						end
					else
						begin
							next_state=IDLE_state;
						end
					end
					
	combine_DAC_a_data_state:
					begin
							next_state=spi_DAC_A_trans_state;	
					end
	spi_DAC_A_trans_state:
					begin
						  if(spi_data_done)//如果传输完毕,则进入转换状态
							next_state=conversion_DAC_a_data_state;	
						  else
						  	next_state=spi_DAC_A_trans_state;
					
					end
					
	conversion_DAC_a_data_state:
					begin
							if(conversion_time_cnt==wait_conversion_time-1)
								begin
										next_state=combine_DAC_b_data_state;							
								end
							else
								begin
										next_state=conversion_DAC_a_data_state;							
								end
					end
					
	combine_DAC_b_data_state:
					begin
							next_state=spi_DAC_B_trans_state;
					end
	spi_DAC_B_trans_state:
					begin
						  if(spi_data_done)//如果传输完毕
							next_state=conversion_DAC_b_data_state;
					
							else
							next_state=spi_DAC_B_trans_state;
					end
					
	conversion_DAC_b_data_state:
					begin
							if(conversion_time_cnt==wait_conversion_time-1)
								begin
										next_state=IDLE_state;		//如果需要传输多个数据,比如正弦,那么就next_state回到combine_DAC_a_data_state					
								end
							else
								begin	
										next_state=conversion_DAC_b_data_state;
								end								
							
					end
			endcase
	
end
//3、状态条件输出
always @ (posedge clk)
begin
		case(next_state)
				IDLE_state:
							begin
							conversion_time_cnt<='d0;
							data_reg<='d0;//用于组合数据(控制+数据)
							start_spi_reg<=0;

							end
							
				combine_DAC_a_data_state:
							begin
							data_reg<={2'b00, 2'b00, 1'b0, 1'b0, 2'b0, DAC_a_data};	
							start_spi_reg<=1;//发出传输数据指令
							end
				spi_DAC_A_trans_state:
							begin
							start_spi_reg<=0;
							end
							
				conversion_DAC_a_data_state:
							begin
							conversion_time_cnt<=conversion_time_cnt+1'd1;
							end
				combine_DAC_b_data_state:
							begin
							conversion_time_cnt<=0;
							data_reg<={2'b00, 2'b11, 1'b0, 1'b1, 2'b0, DAC_b_data};	
							start_spi_reg<=1;//发出传输数据指令							
							end
				spi_DAC_B_trans_state:
							begin
							start_spi_reg<=0;
							end
							
				conversion_DAC_b_data_state:
							begin
							conversion_time_cnt<=conversion_time_cnt+1'd1;
							end
			endcase
			
end
//4、每个状态输出的值
always @ (posedge clk)//data_DAC_ab_finish转换完毕的标志
begin
		if(conversion_time_cnt==wait_conversion_time-1)
			data_DAC_ab_finish_reg<=1;
		else
			data_DAC_ab_finish_reg<=0;
end

//
assign start_spi=start_spi_reg;
assign data=data_reg;
assign data_DAC_ab_finish=data_DAC_ab_finish_reg;
endmodule


	

仿真:

// Copyright (C) 1991-2013 Altera Corporation
// Your use of Altera Corporation's design tools, logic functions 
// and other software and tools, and its AMPP partner logic 
// functions, and any output files from any of the foregoing 
// (including device programming or simulation files), and any 
// associated documentation or information are expressly subject 
// to the terms and conditions of the Altera Program License 
// Subscription Agreement, Altera MegaCore Function License 
// Agreement, or other applicable license agreement, including, 
// without limitation, that your use is for the sole purpose of 
// programming logic devices manufactured by Altera and sold by 
// Altera or its authorized distributors.  Please refer to the 
// applicable agreement for further details.

// *****************************************************************************
// This file contains a Verilog test bench template that is freely editable to  
// suit user's needs .Comments are provided in each section to help the user    
// fill out necessary details.                                                  
// *****************************************************************************
// Generated on "02/28/2019 16:07:31"
                                                                                
// Verilog Test Bench template for design : DAC8532_drive_project
// 
// Simulation tool : ModelSim (Verilog)
// 

`timescale 1 ns/ 1 ns
module DAC8532_drive_project_vlg_tst();
// constants                                           
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg rst;
reg start;
reg switch;
// wires                                               
wire Din;
wire SYNC_n;
wire [5:0]  next_state_test;
wire [5:0]  now_state_test;
wire sclk;

// assign statements (if any)                          
DAC8532_drive_project i1 (
// port map - connection between master ports and signals/registers   
	.Din(Din),
	.SYNC_n(SYNC_n),
	.clk(clk),
	.next_state_test(next_state_test),
	.now_state_test(now_state_test),
	.rst(rst),
	.sclk(sclk),
	.start(start),
	.switch(switch)
);
initial                                                
begin                                                  
#0 clk=0;
#0 rst=0;
#0 start=0;

#10 switch=1;
#60 rst=1;
#100 start=1;
#110 start=0;

                      
end                                                    
always #5                                                 
begin                                                  
clk<=~clk;
end                                                    
endmodule





约束:

create_clock -name {clk} -period 10.000 -waveform {0.000 5.000} [get_ports {clk}]


derive_pll_clocks
derive_clock_uncertainty

仿真结果:

两个通道,同时输出一个电压,有两种电压选择(根据switch 0 or 1)
第一个给了一个start,
先给一个start,然后隔了一段时间,再给一个start。
每start一次,就传输一次。
下图,两个通道均输出16‘b1010101010101010的电压
在这里插入图片描述
在这里插入图片描述

门级仿真:

门级仿真有点问题,明天再调调。

signaltap在线调试

在这里插入图片描述
注意:
//wire start=1;如果要使用signal_tap在线调试没有外部触发,就把这个start一直置位为1即可

经过测试:即使主频时钟设置为跑到200M,也不会出现时序违规
在这里插入图片描述
在这里插入图片描述

三、福利:工程链接

https://download.csdn.net/download/ciscomonkey/10981766

  • 10
    点赞
  • 125
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值