前记:师夷长技以自强
p272
1.基本概念
DAC: Digital to Analog Conver,将数字信号转变为模拟信号的电子元件。由两个分组成,第一部分是电阻阵列,第二部分是n个电流开关(或者电压开关)。实验中使用的型号为TLV5618。
线性序列机:使用一个计数器不断计数,每个计数值根据芯片的时序对信号操作。
2.TLV5618简介
2.1.电气特性:
TLV5618是12位数字端输入2通道输出的DAC,实际控制只有3个管脚,兼容SPI接口。编程控制字有16位,其中4位控制为,12位数据位。
Terminal Functions
TERMINAL NAME | I/O/P | DESCTRIPTION |
AGND | P | Group |
\CS | I | Chip select.Digital input active low,used to enable/disable inputs. |
DIN | I | Digital serial data input |
OUTA | O | DAC A analog voltage output |
OUTB | O | DAC B analog voltage output |
REF | I | Analog reference voltage input |
SCLK | I | Digital serial clock input |
VDD | P | Positive power supply |
2.2电路设计
TLV5618与FPGA采用三线制SPI通信,REF端有LM4040-2.0提供,是一个专用于12位精度场合的精密参考电源。
2.3输出电压计算原理
电阻网络型DAC的输出电压范围应该是0V~Vref,另外在每个DAC通道的电阻网络电压输出后级,连接了一个2倍增益放大器,则最终输出电压的范围是0V~4.096V。
2.4接口时序
接口时序有三个问题:什么时候开始传数据?每个数据什么时候移入芯片内部寄存器?什么时候所有数据位全部移完?什么时候真正开始转换而输出模拟电压?
应该什么时候给DAC传数据呢?当然是工作的时候,而什么时候工作呢?那就是\CS未低的时候。每个数据是在SCLK的下降沿被存入芯片内部寄存器的,因此应该是最后一个SCLK下降沿的上升沿后所有数据位全部移完。当\CS变为高电平时,SCLK不再影响数据芯片内部寄存器的数据,此时真正开始转换而输出模拟电压。
那个输入数据的格式应该是怎么样的呢?
D15 | D14 | D13 | D12 | D11 | D10 | D9 | D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
R1 | SPD | PWR | R0 | 12位数据 |
SPD:1-快速模式,0-低速模式
PWR:1-掉电模式,0-正常模式
芯片内部除了有DAC A和DAC B两个锁存器外,还有一个缓冲区用来辅助两个通道的同时控制。其中R1和R2就是用来控制数据输出到哪里的。
R1 | R0 | 描述 |
0 | 0 | 写数据到DAC B和Buf |
0 | 1 | 写数据到Buf |
1 | 0 | 写数据到DAC A同时把缓冲区内容更新到DAC B |
1 | 1 | resever |
3.基于线性序列机的DAC驱动设计
3.1 线性序列机思想
在逻辑中使用一个计数器来计数,然后每个计数值就相当于在相应的时间点,在这个时间点上,各个信号需要进行什么操作直接赋值即可。这里有一个问题,DAC的工作频率如何选?通过查看芯片手册可以知道,SCLK的最大频率为20MHz,而FPGA的工作频率是50MHz,可选DAC的工作频率为12.5MHz,设置一个两倍于SCLK的采样时钟SCLK2X,使用50M系统时钟二分频即可得到SCLK2X。下面根据DAC的时序图列出芯片的线性序列机的操作表:
时间点对应的信号操作表为:
计数值 | 对应信号状态 |
0 | CS_N = 0;SCLK = 1;DIN = DATA[15]; |
1 | SCLK = 0; |
2 | SCLK =1;DIN = DATA[14]; |
3 | SCLK = 0; |
4 | SCLK =1;DIN = DATA[13]; |
5 | SCLK = 0; |
6 | SCLK =1;DIN = DATA[12]; |
7 | SCLK = 0; |
8 | SCLK =1;DIN = DATA[11]; |
9 | SCLK = 0; |
10 | SCLK =1;DIN = DATA[10]; |
11 | SCLK = 0; |
12 | SCLK =1;DIN = DATA[9]; |
13 | SCLK = 0; |
14 | SCLK =1;DIN = DATA[8]; |
15 | SCLK = 0; |
16 | SCLK =1;DIN = DATA[7]; |
17 | SCLK = 0; |
18 | SCLK =1;DIN = DATA[6]; |
19 | SCLK = 0; |
20 | SCLK =1;DIN = DATA[5]; |
21 | SCLK = 0; |
22 | SCLK =1;DIN = DATA[4]; |
23 | SCLK = 0; |
24 | SCLK =1;DIN = DATA[3]; |
25 | SCLK = 0; |
26 | SCLK =1;DIN = DATA[2]; |
27 | SCLK = 0; |
28 | SCLK =1;DIN = DATA[1]; |
29 | SCLK = 0; |
30 | SCLK =1;DIN = DATA[0]; |
31 | SCLK = 0; |
32 | SCLK = 1; |
33 | CS_N = 1; |
3.2 DAC模块的接口设计
模块端口功能描述如下
端口名称 | I/O | 描述 |
Clk | I | 系统时钟,50MHz |
Rst_n | I | 系统复位信号,低电平有效 |
Start | I | 模块使能控制 |
Dac_data[15:0] | I | 控制字 |
Set_Done | O | 每次更新完成产生一个宽度为1个时钟周期的高电平脉冲 |
CS_N | O | TLV5618的片选信号 |
DIN | O | TLV5618的DIN信号 |
SCLK | O | TLV5618的SCLK信号 |
DAC_STATE | O | 0-忙碌,1-空闲 |
3.3 代码编写
为了辅助生成模块的输出信号,需要的内部信号有:
reg [15:0]Dac_data_r;//暂存输入的数据
reg en;//转换使能信号,标识ADC是否在工作
reg [7:0]DIV_CNT;//分频计数器
reg SCLK2X;//根据DIV_CNT生成的时钟信号
reg [5:0]SCL2X_CNT;//SCLK2X的计数器
wire Trans_done;//转换完成信号
需要注意的一点是,一次转换结束的标志是(SCL2X_CNT == 33 && SCLK2X)
module TLV5618(
input Clk,
input Rst_n,
input Start,
input [15:0]Dac_data,
output reg Set_Done,
output reg CS_N,
output reg DIN,
output reg SCLK,
output DAC_STATE
);
localparam DIV_PARAM = 2;//div system clock
reg en;
reg [15:0]Dac_data_r;
reg [7:0]DIV_CNT;
reg SCLK2X;
reg [5:0]SCLK2X_CNT;
wire Trans_done;
always@(posedge Clk,negedge Rst_n)//en
if(!Rst_n)
en <= 0;
else if(Start)
en <= 1;
else if(Set_Done)
en <= 0;
else
en <= en;
always@(posedge Clk,negedge Rst_n)//DIV_CNT
if(!Rst_n)
DIV_CNT <= 0;
else if(en)
if(DIV_CNT == DIV_PARAM - 1)
DIV_CNT <= 0;
else
DIV_CNT <= DIV_CNT + 8'b1;
else
DIV_CNT <= 0;
always@(posedge Clk,negedge Rst_n)//SCLK2X
if(!Rst_n)
SCLK2X <= 0;
else if(en&&(DIV_CNT == (DIV_PARAM - 1)))
SCLK2X <= 1;
else
SCLK2X <= 0;
always@(posedge Clk,negedge Rst_n)//SCLK2X_CNT
if(!Rst_n)
SCLK2X_CNT <= 0;
else if(en&&SCLK2X)
if(SCLK2X_CNT == 6'd33)
SCLK2X_CNT <= 0;
else
SCLK2X_CNT <= SCLK2X_CNT + 6'b1;
else
SCLK2X_CNT <= SCLK2X_CNT;
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)begin
SCLK <= 1;
DIN <= 1;
CS_N <= 1;
Dac_data_r <= 0;
end
else if(!Set_Done&&SCLK2X)begin
case(SCLK2X_CNT)
0:
begin
SCLK <= 1;
DIN <= Dac_data_r[15];
Dac_data_r <= Dac_data_r << 1;
CS_N <= 0;
end
1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31:
begin
SCLK <= 0;
end
2,4,6,8,10,12,14,16,18,20,22,24,26,28,30:
begin
SCLK <= 1;
DIN <= Dac_data_r[15];
Dac_data_r <= Dac_data_r << 1;
end
32:SCLK <= 1;
33:CS_N <= 1;
default:;
endcase
end
else if(Start)
Dac_data_r <= Dac_data;
assign Trans_done = (SCLK2X_CNT == 33)&&SCLK2X;
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)
Set_Done <= 0;
else if(Trans_done)
Set_Done <= 1;
else
Set_Done <= 0;
assign DAC_STATE = !en;
endmodule
3.4仿真测试
`timescale 1ns/1ns
`define period 20
module TLV5618_tb;
reg Clk;
reg Rst_n;
reg Start;
reg [15:0]Dac_data;
wire set_Done;
wire CS_N;
wire DIN;
wire SCLK;
wire DAC_STATE;
TLV5618 TLV5618_1(
.Clk(Clk),
.Rst_n(Rst_n),
.Start(Start),
.Dac_data(Dac_data),
.Set_Done(set_Done),
.CS_N(CS_N),
.DIN(DIN),
.SCLK(SCLK),
.DAC_STATE(DAC_STATE)
);
initial Clk <= 0;
always #10 Clk <= ~ Clk;
initial begin
Rst_n = 0;
Start = 0;
Dac_data = 0;
#`period;
Rst_n = 1;
#`period;
Dac_data = 16'hC_AAA;
Start = 1;
#`period;
Start = 0;
#200
wait(set_Done);
#20000;
Dac_data = 16'h4_555;
Start = 1;
#`period;
Start = 0;
#200;
wait(set_Done);
$stop;
end
endmodule
全局图
局部图