小梅哥FPGA:基于线性序列机的TLC5620型DAC驱动设计
目标:学会使用线性序列机的思想设计常见的有串行执行特征的时序逻辑
实验现象:在QuartusⅡ软件中,使用ISSP工具,输入希望输出的电压值,则AC620开发板上,FPGA控制的TLC5620芯片输出对应的电压值
TLC5620型DAC芯片概述:
- TLC5620C是一个具有4个独立8位电压输出型DAC的数模转换器
- 单电源5V供电
- 采用串行接口时序
- 具备4个高阻抗参考电压输入端口(对应四个DAC输出通道)
- 可编程的电压倍增模式
每个DAC可以输出一倍或者两倍的参考电压与GND之间的电压值。
下图为TCL5620内部框图:(一定要理解!!!)
TLC5620型DAC芯片引脚说明:
TLC5620的输出电压公式:
Vo(DAC A|B|C|D) = REF * CODE/256 *(1 + RNG bit value)
其中,Vo为输出电压值,REF为DAC的输出参考电压,CODE为输出电压值的数字量化量,如255表示按照参考电压的满幅输出(关闭电压倍增模式),0则0V输出,RNG bit value表示电压倍增模式,为0则关闭输出电压倍增模式,为1则打开输出电压倍增模式。
其中我们输入的Data是11位的数据,其中前两位{D10,D9}为DAC选择位,第三位D8为电压放大位,若为1则放大,若为0则不放大
TLC 5620型DAC接口时序:
控制器对TLC5620的单个DAC设置包括两个主要操作
- 将数字量化值以及控制位发送到TLC5620中对应的寄存器中
- 控制DAC将寄存器中接收到的数据值更新到DAC输出上 数据的更新,使用LOAD和LDAC配合以实现。
当LOAD为高电平时,在每个CLK的下降沿,数据被移入DAC的移位寄存器中。当所有的数据位被移入完成后,LOAD被拉低,以将数据从串行输入移位寄存器中转入选中的DAC中,如下图所示:
TLC5620串行数字接口的关键时序参数:
- tsu(DATA-CLK):clk的高脉冲最小为50ns
- tv:clk的低脉冲最小是50ns
- tsu(clk - LOAD):从D0发送完成到LOAD拉低最少需要50ns
- tsu(LOAD - clk):从LOAD升高到下一次发送最少需要50ns
- tw(LOAD):LOAD从低到高最少需要250ns
- Clk最大频率为1MHZ,意味着时钟周期最小为1000ns
我将CLK的时钟周期定为1200ns,此时tsu(DATA-CLK),tv已经满足,将其余的50ns改为60ns,250ns改为260ns
我定义一个计数器cnt来对时间进行计数(线性序列机思想)
在CLK上升沿的时候将DATA写入
注意注意:我们本章做的是产生输送给TCL5620的信号!!!!!!!所以是在上升沿将数据写入
TCL5620接口逻辑的编写(代码)
我们接下来进行TCL5620接口逻辑的编写:
UpdateReq是开关:当按下的时候开始产生信号,当传输结束UpdateDone出现一个时钟的高电平
以下是我编写的代码:
module DAC_ctrl(
clk,
Rst_n,
CtrlWord,
UpdateReq,
UpdateDone,
TCL5620_DATA,
TCL5620_clk,
TCL5620_LOAD,
TCL5620_LDAC
);
input clk;
input Rst_n;
input[10:0]CtrlWord;
input UpdateReq;
output reg UpdateDone;
output reg TCL5620_DATA;
output reg TCL5620_clk;
output reg TCL5620_LOAD;
output reg TCL5620_LDAC;
reg[9:0]cnt;
//计时器逻辑
always@(posedge clk or negedge Rst_n)
if(!Rst_n)
cnt <= 10'd0;
else if(UpdateReq || cnt != 10'd0)begin
if(cnt == 10'd660)
cnt <= 10'd0;
else
cnt <= cnt +1'b1;
end
else
cnt <= 10'd0;
//
always@(posedge clk or negedge Rst_n)
if(!Rst_n)begin
UpdateDone <= 1'b0;
TCL5620_DATA <=1'b0;
TCL5620_clk <=1'b0;
TCL5620_LOAD <=1'b0;
TCL5620_LDAC <= 1'b0;
end
else begin
case(cnt)
0:begin
TCL5620_DATA <=1'b0;
TCL5620_clk <=1'b0;
TCL5620_LOAD <=1'b1;
TCL5620_LDAC <= 1'b0;//一直为低电平
end
10:begin
TCL5620_DATA <=CtrlWord[10];
TCL5620_clk <=1'b1;
end
40: TCL5620_clk <=1'b0;
70:begin
TCL5620_DATA <=CtrlWord[9];
TCL5620_clk <=1'b1;
end
100:TCL5620_clk <=1'b0;
130:begin
TCL5620_DATA <=CtrlWord[8];
TCL5620_clk <=1'b1;
end
160:TCL5620_clk <=1'b0;
190:begin
TCL5620_DATA <=CtrlWord[7];
TCL5620_clk <=1'b1;
end
220:TCL5620_clk <=1'b0;
250:begin
TCL5620_DATA <=CtrlWord[6];
TCL5620_clk <=1'b1;
end
280:TCL5620_clk <=1'b0;
310:begin
TCL5620_DATA <=CtrlWord[5];
TCL5620_clk <=1'b1;
end
340:TCL5620_clk <=1'b0;
370:begin
TCL5620_DATA <=CtrlWord[4];
TCL5620_clk <=1'b1;
end
400:TCL5620_clk <=1'b0;
430:begin
TCL5620_DATA <=CtrlWord[3];
TCL5620_clk <=1'b1;
end
460:TCL5620_clk <=1'b0;
490:begin
TCL5620_DATA <=CtrlWord[2];
TCL5620_clk <=1'b1;
end
520:TCL5620_clk <=1'b0;
550:begin
TCL5620_DATA <=CtrlWord[1];
TCL5620_clk <=1'b1;
end
580:TCL5620_clk <=1'b0;
610:begin
TCL5620_DATA <=CtrlWord[0];
TCL5620_clk <=1'b1;
end
640:TCL5620_clk <=1'b0;
643:TCL5620_LOAD <=1'b0;
656:TCL5620_LOAD <=1'b1;
660:UpdateDone <= 1'b1;
default:;
endcase
end
endmodule
testbench的编写
`timescale 1ns/1ns
`define clk_period 20
module DAC_ctrl_tb;
reg clk;
reg Rst_n;
reg [10:0]CtrlWord;
reg UpdateReq;
wire UpdateDone;
wire TCL5620_DATA;
wire TCL5620_clk;
wire TCL5620_LOAD;
wire TCL5620_LDAC;
DAC_ctrl DAC_ctrl(
.clk(clk),
.Rst_n( Rst_n),
.CtrlWord(CtrlWord),
.UpdateReq(UpdateReq),
.UpdateDone(UpdateDone),
.TCL5620_DATA(TCL5620_DATA),
.TCL5620_clk(TCL5620_clk),
.TCL5620_LOAD(TCL5620_LOAD),
.TCL5620_LDAC(TCL5620_LDAC)
);
initial clk =1;
always#(`clk_period/2)clk = ~clk;
initial begin
Rst_n = 1'b0;
CtrlWord = 0;
UpdateReq = 1'b0;
#(`clk_period*100+1);
Rst_n =1'b1;
#(`clk_period*20);
CtrlWord = {2'd0,1'b0,8'haa};
UpdateReq = 1'b1;
#(`clk_period);
UpdateReq = 1'b0;
@(posedge UpdateDone);
#(`clk_period*20);
CtrlWord = {2'd0,1'b0,8'haa};
UpdateReq = 1'b1;
#(`clk_period);
UpdateReq = 1'b0;
@(posedge UpdateDone);
#(`clk_period*20);
$stop;
end
endmodule
仿真结果
顶层文件编写
需要建立一个ISSP的ip核来给CtrlWord提供数据
module DAC_ctrl_top(
clk,
Rst_n,
TCL5620_DATA,
TCL5620_clk,
TCL5620_LOAD,
TCL5620_LDAC
);
input clk;
input Rst_n;
output TCL5620_DATA;
output TCL5620_clk;
output TCL5620_LOAD;
output TCL5620_LDAC;
wire[10:0]CtrlWord;
ISSP ISSP(
.probe(),
.source(CtrlWord)
);
DAC_ctrl DAC_ctrl(
.clk(clk),
.Rst_n( Rst_n),
.CtrlWord(CtrlWord),
.UpdateReq(1'b1),
.UpdateDone(),
.TCL5620_DATA(TCL5620_DATA),
.TCL5620_clk(TCL5620_clk),
.TCL5620_LOAD(TCL5620_LOAD),
.TCL5620_LDAC(TCL5620_LDAC)
);
endmodule
因为没有电压表所以板机验证就不上传了,以下给出分配引脚的图片: