• 小梅哥FPGA数字逻辑设计教程——基于线性序列机的TLC5620型DAC驱动设计


    基于线性序列机的TLC5620型DAC驱动设计

    目录

    TLC5620型DAC芯片概述:    2

    TLC5620型DAC芯片引脚说明:    2

    TLC5620型DAC芯片详细介绍:    3

    TLC 5620型DAC接口时序:    4

    TLC5620串行数字接口的关键时序参数:    5

    芯航线ADDA模块TLC5620电路介绍:    6

    线性序列机设计思想与TLC5620接口时序设计:    7

    视频教程中的工程源码:    10

    视频教程中的测试文件源码:    13

    板级验证方法:    15

    顶层例化模块源码:    15

    附录1:ADDA V1.1模块使用说明    17

    附录2:ADDA V1.1模块原理图    19

    TLC5620型DAC芯片概述:

    • TLC5620C是一个具有4个独立8位电压输出型DAC的数模转换器
    • 单电源5V供电
    • 采用串行接口时序
    • 具备4个高阻抗参考电压输入端口(对应四个DAC输出通道)    
    • 可编程的电压倍增模式

    TLC5620是一个内部具备4个独立 8位电压输出型数字——模拟转换器,每个DAC转换器都拥有一个带缓冲(高输入阻抗)的参考电压输入端口。每个DAC可以输出一倍或者两倍的参考电压与GND之间的电压值。

    TLC5620使用CMOS电平兼容的三线制串行总线与各种流行的处理器进行连接,TLC5620接收控制器发送过来的11位的命令字,这11位的控制字被分为3个部分,包括8位的数据位,2位的DAC选择位,1位的电压倍增控制位。每个DAC的寄存器都采用双缓冲结构,这样,可以实现首先通过数据总线给所有的DAC传输需要更新的数据,然后通过控制信号LDAC将所有DAC的电压同步更新到输出上。

    TLC5620芯片内部框图

    TLC5620型DAC芯片引脚说明:

    引脚名

    编号

    IO

    功能描述

    CLK

    7

    I

    串行接口时钟,每个时钟的下降沿,输入数字总线上的数据被移入内部的接口寄存器中

    DACA

    12

    O

    DAC A模拟输出端口

    DACB

    11

    O

    DAC B模拟输出端口

    DACC

    10

    O

    DAC C模拟输出端口

    DACD

    9

    O

    DAC D模拟输出端口

    DATA

    6

    I

    串行接口的数字数据输入线,发送给DAC的数据是通过串行的方式传入DAC的寄存器的,每个数据位都在时钟的下降沿被移入内部寄存器中

    GND

    1

    I

    GND

    LDAC

    13

    I

    加载DAC(更新DAC待输出数据),当该信号为高电平时,串行总线上传入的数据不会更新到DAC上去,只有当LDAC的电平由高电平变为低电平时,数据才会更新到DAC上去

    LOAD

    8

    I

    串行数据加载控制,当LDAC为低电平时,LOAD的下降沿将带输出数据锁存到输出锁存器并立即产生输出电压。

    REFA

    2

    I

    DAC A的参考电压,该电压决定了输出电压的范围,输出电压为0~VREFA或者0~2*VREFA(2VREFA <= VDD)

    REFB

    3

    I

    DAC B的参考电压,该电压决定了输出电压的范围,输出电压为0~VREFB或者0~2*VREFB(2VREFB <= VDD)

    REFC

    4

    I

    DAC C的参考电压,该电压决定了输出电压的范围,输出电压为0~VREFC或者0~2*VREFC(2VREFC <= VDD)

    REFD

    5

    I

    DAC D的参考电压,该电压决定了输出电压的范围,输出电压为0~VREFD或者0~2*VREFD(2VREFD <= VDD)

    VDD

    14

    I

    正电源输入

    TLC5620型DAC芯片详细介绍:

    TLC5620是由四个电阻串式DAC组成的,每个DAC的核心是一个拥有256个节点(抽头)的电阻,对应了256中不同的组合,如下表所示,每个电阻串的一段连接到GND,另一端来自参考输入缓存的输出。

    每个DAC的输出都接有一个可配置增益的输出放大器,该放大器的增益可以配置为1或者2。当芯片上电时,DAC的值全部被复位到0,。每个DAC通道的输出可由下列公式计算得出:

     

    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则打开输出电压倍增模式。

    当串行控制字中的数据部分为0~255,RNG bit为0或者1时,输出电压与数字量化值的关系如下表所示:

    D7

    D6

    D5

    D4

    D3

    D2

    D1

    D0

    输出电压

    0

    0

    0

    0

    0

    0

    0

    0

    GND

    0

    0

    0

    0

    0

    0

    0

    1

    1/256 * REF(1+RNG)

    127/256 * REF(1+RNG)

    128/256 * REF(1+RNG)

    255/256 * REF(1+RNG)

    TLC 5620型DAC接口时序:

    控制器对TLC5620的单个DAC设置包括两个主要操作

    1. 将数字量化值以及控制位发送到TLC5620中对应的寄存器中
    2. 控制DAC将寄存器中接收到的数据值更新到DAC输出上

    对于数据的传输,有连续传输(11个连续的时钟周期传输11位的控制字)和2个8时钟周期传输方式(使用两次8时钟周期的传输来实现11位数据的传输)。

    对于数据的更新,则使用LOAD和LDAC配合以实现。

    当LOAD为高电平时,在每个CLK的下降沿,数据被移入DAC的移位寄存器中。当所有的数据位被移入完成后,LOAD被拉低,以将数据从串行输入移位寄存器中转入选中的DAC中,如上图所示:

    当LDAC为低电平时,选中的DAC通道的输出电压在LOAD变为低电平时更新。

    当LDAC在串行数据传输过程中为高电平时,新的数据值被存在器件中,该值可以在稍后将LDAC拉低时传入DAC的输出,如下图2所示。串行总线上传输数据时,高位在前,低位在后。

    使用两个8时钟周期的传输数据(主要针对8位定长的SPI控制器)的时序图3和图4所示:

    在传输时序中,标为A0和A1的两位指定了需要设置输出的DAC,具体A0和A1值与对应被选择更新的DAC如下表所示:

    A1

    A0

    被更新的DAC通道

    0

    0

    DACA

    0

    1

    DACB

    1

    0

    DACC

    1

    1

    DACD

    TLC5620串行数字接口的关键时序参数:

    针对TLC5620的数字接口,其操作时序如下表所示。

    其中,从tSU(DATA-CLK)、开始一直到CLK frequency都是我们在设计接口时序时,需要重点关注的参数。我们设计的控制时序必须要严格满足表中各个时序参数,否则会导致数据传输或转换失败。

    芯航线ADDA模块TLC5620电路介绍:

    芯航线FPGA学习套件中,提供了一个多通道串行ADDA模块。其中,DA部分所使用的芯片就是上文介绍的TLC5620,TLC5620部分电路图如下图所示:

    为了给DAC的参考输入提供稳定的参考电压,这里使用专用精密参考源芯片TL431搭建了一个参考源电路,该电路如下图所示:

    根据5V的输入电压和输出电压/电流设计电路,按照上图设置电路即可,其中R2:R3=1:2.7得到的输出最接近3.3V(例如R1取值为1k,R2取值为2.7k)

    Vout = (R2+R3)*2.5/R3 = 3.7*2.5/2.7 = 3.42V

    为了保证TL431 1mA的工作电流,R1需要满足

    1mA< (Vcc-Vout)/R1< 500mA

    这里设置R1为150欧姆,则(Vcc-Vout)/R1 = 10.5mA,满足TL431工作要求。

    因此,当确定一个输出电压时,就可以得到对应的RNG和CODE了,如下式所示:

     

    然后,在我们控制DAC的输出时,只需根据所需输出的电压计算得到CODE和RNG,然后将该值通过串行接口传入TLC5620,再发出一个更新控制信号(LOAD + LDAC),就能实现控制TLC5620输出想要的电压了。

    线性序列机设计思想与TLC5620接口时序设计:

        这里以使用LOAD信号控制DAC更新的时序图来分析TLC5620的数字接口时序:

    从图中我们可以看到,该接口的时序是一个很有规律的序列,CLK信号什么时候该由低变高,什么时候由高变低。DATA信号什么时候该传输哪一位数据,LOAD信号什么时候拉低,什么时候拉高,都是可以根据时序参数唯一确定下来的。

    我们可以将该数据波形放到以时间为横轴的一个二维坐标系中,纵轴就是每个信号对应的状态:

    因此我们只需要在逻辑中使用一个计数器来计数,然后每个计数值时就相当于在t轴上对应了一个相应的时间点,那么在这个时间点上,各个信号需要进行什么操作,直接赋值即可。

    针对TLC5620的接口时序,在FPGA中,我们以时钟周期为20ns进行设计,由于TLC5620的数字接口工作时钟最高位1MHz,周期为1000ns,因此时钟的翻转时间最小为500ns,为了给设计留有余量,因此本设计使用1200ns作为时钟周期,即时钟信号每600ns翻转一次。从而可以通过每个信号变化时的时间得到对应计数器的值:

    每个时间点对应信号操作详表:

    时间

    计数器值

    对应信号状态

    00

    00

    CLK= 0;DATA = 0;LOAD = 1;LDAC = 0

    200

    10

    DATA = A1;CLK= 1;

    800

    40

    CLK= 0;

    1400

    70

    DATA = A0;CLK= 1;

    2000

    100

    CLK= 0;

    2600

    130

    DATA = RNG;CLK= 1;

    3200

    160

    CLK= 0;

    3800

    190

    DATA = D7;CLK= 1;

    4400

    220

    CLK= 0;

    5000

    250

    DATA = D6;CLK= 1;

    5600

    280

    CLK= 0;

    6200

    310

    DATA = D5;CLK= 1;

    6800

    340

    CLK= 0;

    7400

    370

    DATA = D4;CLK= 1;

    8000

    400

    CLK= 0;

    8600

    430

    DATA = D3;CLK= 1;

    9200

    460

    CLK= 0;

    9800

    490

    DATA = D2;CLK= 1;

    10400

    520

    CLK= 0;

    11000

    550

    DATA = D1;CLK= 1;

    11600

    580

    CLK= 0;

    12200

    610

    DATA = D0;CLK= 1;

    12800

    640

    CLK= 0;

    13400

    670

    LOAD = 0;

    16000

    800

    LOAD = 1;

    每个时刻计数器操作:

    Cnt

    Cnt Operation

    Cnt < 820

    Cnt <= Cnt + 1'b1;

    Cnt = 820

    Cnt <= 0;

    以上就是通过线性序列机设计接口时序的一个典型案例,可以看到,线性序列机可以大大简化我们的设计思路。线性序列机的设计思想就是使用一个计数器不断计数,由于每个计数值都会对应一个时间,那么当该时间符合我们需要操作信号的时刻时,就对该信号进行操作。这样,就能够轻松的设计出各种时序接口了。

    有了这两张表,我们就可以进行TLC5620的接口逻辑的编写了。设计TLC5620接口逻辑的模块如下图所示:

    其中,每个端口的作用如下所示:

    端口名称

    I/O

    端口功能描述

    Clk

    I

    为控制器的工作时钟,频率为50MHz,

    Rst_n

    I

    控制器复位,低电平复位

    CtrlWord[10:0]

    I

    控制器控制字,组成为{A1,A0,RNG,DATA[7:0]}

    UpdateReq

    I

    更新DAC输出请求信号,在该信号的高电平时,控制器寄存CtrlWord上的数据并按照TLC5620的接口时序将数据发送出去。每次高电平持续一个时钟周期

    UpdateDone

    O

    更新DAC完成标志,每次完成更新产生一个高电平脉冲,脉冲宽度为1个时钟周期

    TLC5620_CLK

    O

    TLC5620的CLK接口

    TLC5620_DATA

    O

    TLC5620的DATA接口

    TLC5620_LOAD

    O

    TLC5620的LOAD接口

    TLC5620_LDAC

    O

    TLC5620的LDAC接口

    有了这些之后,我们就可以开始进行控制器的具体逻辑设计了。具体逻辑设计过程请参看小梅哥FPGA设计思想与验证方法视频第17课。

    视频教程中的工程源码:

    /*=================================================================

    *

    * LOGIC CORE: TLC5620_CTRL

    * MODULE NAME: TLC5620_CTRL()

    * COMPANY: 芯航线电子工作室

    * http://xiaomeige.taobao.com

    * author: 小梅哥

    * author QQ Group472607506

    * REVISION HISTORY:

    *

    * Revision 1.0 01/01/2016 Description: Initial Release.

    *

    * FileType Testbench

    *

    * FUNCTIONAL DESCRIPTION:

    *

    =================================================================*/

    //VoDAC A|B|C|D = REF * CODE/256 *1 + RNG bit value

    001 module TLC5620_CTRL(

    002 Clk,

    003 Rst_n,

    004 UpdateReq,

    005 CtrlWord,

    006

    007 UpdateDone,

    008 TLC5620_CLK,

    009 TLC5620_DATA,

    010 TLC5620_LOAD,

    011 TLC5620_LDAC

    012 );

    013

    014

    015 input Clk;

    016 input Rst_n;

    017 input UpdateReq; //更新输出电压请求

    018 input [10:0]CtrlWord;//ADDR[1:0];RNG bit;CODE[7:0]

    019

    020 output reg UpdateDone; //更新输出电压完成标志信号

    021 output reg TLC5620_CLK; //TLC5620接口时钟信号

    022 output reg TLC5620_DATA; //TLC5620数据输入信号

    023 output reg TLC5620_LOAD; //

    024 output reg TLC5620_LDAC;

    025

    026 reg [9:0] Cnt; //线性序列机计数器

    027

    028 //线性序列机计数器计数进程

    029 always@(posedge Clk or negedge Rst_n)

    030 if(!Rst_n)

    031 Cnt <= 10'd0;

    032 else if(UpdateReq == 1 | (Cnt != 10'd0))begin

    033 if(Cnt == 10'd820)

    034 Cnt <= 10'd0;

    035 else

    036 Cnt <= Cnt + 10'd1;

    037 end

    038 else

    039 Cnt <= 10'd0;

    040

    041

    042 //线性序列机控制进程

    043 always@(posedge Clk or negedge Rst_n)

    044 if(!Rst_n)begin

    045 TLC5620_CLK <= 1'b0;

    046 TLC5620_DATA <= 1'b0;

    047 TLC5620_LOAD <= 1'b0;

    048 TLC5620_LDAC <= 1'b0;

    049 UpdateDone <= 1'b0;

    050 end

    051 else begin

    052 case(Cnt)

    053 0:

    054 begin

    055 TLC5620_CLK <= 1'b0;

    056 TLC5620_DATA <= 1'b0;

    057 TLC5620_LOAD <= 1'b1;

    058 TLC5620_LDAC <= 1'b0;

    059 UpdateDone <= 1'b0;

    060 end

    061 10:

    062 begin

    063 TLC5620_CLK <= 1'b1;

    064 TLC5620_DATA <= CtrlWord[10];

    065 end

    066 40: TLC5620_CLK <= 1'b0;

    067

    068 70:

    069 begin

    070 TLC5620_CLK <= 1'b1;

    071 TLC5620_DATA <= CtrlWord[9];

    072 end

    073

    074 100: TLC5620_CLK <= 1'b0;

    075 130:

    076 begin

    077 TLC5620_CLK <= 1'b1;

    078 TLC5620_DATA <= CtrlWord[8];

    079 end

    080 160: TLC5620_CLK <= 1'b0;

    081 190:

    082 begin

    083 TLC5620_CLK <= 1'b1;

    084 TLC5620_DATA <= CtrlWord[7];

    085 end

    086 220: TLC5620_CLK <= 1'b0;

    087 250:

    088 begin

    089 TLC5620_CLK <= 1'b1;

    090 TLC5620_DATA <= CtrlWord[6];

    091 end

    092 280: TLC5620_CLK <= 1'b0;

    093 310:

    094 begin

    095 TLC5620_CLK <= 1'b1;

    096 TLC5620_DATA <= CtrlWord[5];

    097 end

    098 340: TLC5620_CLK <= 1'b0;

    099 370:

    100 begin

    101 TLC5620_CLK <= 1'b1;

    102 TLC5620_DATA <= CtrlWord[4];

    103 end

    104 400: TLC5620_CLK <= 1'b0;

    105 430:

    106 begin

    107 TLC5620_CLK <= 1'b1;

    108 TLC5620_DATA <= CtrlWord[3];

    109 end

    110 460: TLC5620_CLK <= 1'b0;

    111 490:

    112 begin

    113 TLC5620_CLK <= 1'b1;

    114 TLC5620_DATA <= CtrlWord[2];

    115 end

    116 520: TLC5620_CLK <= 1'b0;

    117 550:

    118 begin

    119 TLC5620_CLK <= 1'b1;

    120 TLC5620_DATA <= CtrlWord[1];

    121 end

    122 580: TLC5620_CLK <= 1'b0;

    123 610:

    124 begin

    125 TLC5620_CLK <= 1'b1;

    126 TLC5620_DATA <= CtrlWord[0];

    127 end

    128 640: TLC5620_CLK <= 1'b0;

    129 670:TLC5620_LOAD <= 1'b0;

    130 800:TLC5620_LOAD <= 1'b1;

    131 820:UpdateDone <= 1'b1;

    132 default:;

    133 endcase

    134 end

    135

    136 endmodule

    137

    视频教程中的测试文件源码:

    01 `timescale 1ns/1ns

    02 `define clk_period 20

    03 module TLC5620_CTRL_tb;

    04

    05 reg Clk;

    06 reg Rst_n;

    07 reg UpdateReq;

    08 reg [10:0]CtrlWord;

    09

    10 wire UpdateDone;

    11 wire TLC5620_CLK;

    12 wire TLC5620_DATA;

    13 wire TLC5620_LOAD;

    14 wire TLC5620_LDAC;

    15

    16 TLC5620_CTRL TLC5620_CTRL0(

    17 .Clk(Clk),

    18 .Rst_n(Rst_n),

    19 .UpdateReq(UpdateReq),

    20 .CtrlWord(CtrlWord),

    21

    22 .UpdateDone(UpdateDone),

    23 .TLC5620_CLK(TLC5620_CLK),

    24 .TLC5620_DATA(TLC5620_DATA),

    25 .TLC5620_LOAD(TLC5620_LOAD),

    26 .TLC5620_LDAC(TLC5620_LDAC)

    27 );

    28

    29 initial Clk = 1;

    30 always #(`clk_period/2) Clk = ~Clk;

    31

    32 initial begin

    33 Rst_n = 1'b0;

    34 UpdateReq = 1'b0;

    35 CtrlWord = 0;

    36

    37 #(`clk_period*100 + 1);

    38 Rst_n = 1'b1;

    39 #(`clk_period*20);

    40 CtrlWord = {2'd0,1'b0,8'haa};

    41 UpdateReq = 1'b1;

    42 #(`clk_period);

    43 UpdateReq = 1'b0;

    44 @(posedge UpdateDone);

    45 #(`clk_period*40);

    46

    47 CtrlWord = {2'd0,1'b0,8'h55};

    48 UpdateReq = 1'b1;

    49 #(`clk_period);

    50 UpdateReq = 1'b0;

    51 @(posedge UpdateDone);

    52 #(`clk_period*20);

    53 $stop;

    54 end

    55

    56 endmodule

    板级验证方法:

    设计中使用了一个信号探针来通过电脑传递输出电压控制字给DAC控制逻辑,使用In system sources and probes editor工具,输入希望输出的电压值,则芯航线开发板上,FPGA控制TLC5620芯片输出对应的电压值

    顶层例化模块源码:

    01 module TCL5620_TOP(

    02 Clk,

    03 Rst_n,

    04

    05 TLC5620_CLK,

    06 TLC5620_DATA,

    07 TLC5620_LOAD,

    08 TLC5620_LDAC

    09 );

    10

    11 input Clk;

    12 input Rst_n;

    13

    14 output TLC5620_CLK;

    15 output TLC5620_DATA;

    16 output TLC5620_LOAD;

    17 output TLC5620_LDAC;

    18

    19 wire [10:0]CtrlWord;

    20

    21 CtrlWordSource CtrlWordSource(

    22 .probe(),

    23 .source(CtrlWord)

    24 );

    25

    26 TLC5620_CTRL TLC5620_CTRL0(

    27 .Clk(Clk),

    28 .Rst_n(Rst_n),

    29 .UpdateReq(1'b1),

    30 .CtrlWord(CtrlWord),

    31

    32 .UpdateDone(),

    33 .TLC5620_CLK(TLC5620_CLK),

    34 .TLC5620_DATA(TLC5620_DATA),

    35 .TLC5620_LOAD(TLC5620_LOAD),

    36 .TLC5620_LDAC(TLC5620_LDAC)

    37 );

    38

    39 endmodule

    如果希望更加细致详细的学习本实验,请观看《小梅哥FPGA设计思想与验证方法视频教程》第17课。

    如有更多问题,欢迎加入芯航线FPGA技术支持群:472607506

    小梅哥

    芯航线电子工作室

    附录1:ADDA V1.1模块使用说明

    本模块共有5个接插件:

    P1为与FPGA开发板连接的数字接口信号和电源信号,使用时使用杜邦线与开发板的GPIO 0中相应引脚相连,具体连接位置,可以参照我们提供的示例连接方法;其每个引脚的功能如下图所示:

    P2为VCC5V的连通短接点,如果使用P1上的供电引脚为模块供电,则需要将P2短接;

    P3为GND的连通短接点,如果使用P1上的GND引脚为GND连接到开发板,则需要将P3短接;

    P4为扩展供电引脚,由于芯航线核心板本身为高速数字电路,因此电源噪声相对较高,在一些需要精密测量的应用中,往往使用专用的独立供电电路为模块供电,因此可以直接将外部供电使用杜邦线接到P4上,其中P4靠近P2的针接VCC,P4靠近P3的针接GND,P3需要保持短接状态,P2则需要断开。

    注意,无论何时,请不要短接P4,否则会烧毁开发板。

    附录2:ADDA V1.1模块原理图

  • 相关阅读:
    MSSQL数据库 事务隔离级别
    CSS(Cascading Style Shee)
    Winform MD5
    Winform 基础
    ASP.NET 设置DropDownList的当前选项
    如何彻底关闭退出vmware虚拟机
    Winform GDI+
    SQL优化
    登录
    Spring AOP的应用
  • 原文地址:https://www.cnblogs.com/xiaomeige/p/5362370.html
Copyright © 2020-2023  润新知