tag 标签: tlc5620

相关博文
  • 热度 23
    2014-12-27 14:32
    3109 次阅读|
    5 个评论
    本实验中,我们使用FPGA来驱动了一片DAC芯片TLC5620,该芯片的特性如下所示:   TLC5620特性:      4路8位电压输出;      单电源5V供电;      串行接口;      参考电压输入高阻;      可编程的1次或2次输出范围;      同时更新的能力;      内部自带上电复位功能;      低功耗;      半缓冲输出。       小梅哥设计的该芯片的驱动模块的接口如下所示:    各个端口定义如下: 以下是代码片段: input Clk; input Rst_n; input Do_DA; /*使能单次转换*/ input Data;/*{Addr1,Addr0,Range,Data_bit }*/ output reg DAC_Dout; /*DAC数据线*/ output reg DAC_Clk; /*DAC时钟线,最高速度1M*/ output reg DAC_LDAC; /**/ output reg DAC_LOAD; /**/ output reg DA_Done; /*单次转换完成标志信号*/     该芯片提供了类似于SPI的数字接口,因此,我们只需要使用该接口与芯片进行通信,再配合LOAD和LDAC两个控制线,即可实现对该DAC芯片的控制。TLC5620一次转换的操作时序如下:   图1 TLC5620单次转换时序图       TLC5620每次写入的数据为11位,其中前两位为DAC选择位A1、A0,通过不同的组合可以选择不同通道的DAC,具体分配为:    表1 DAC通道选择位与对应通道关系       第三位是电压输出增益位,0代表不变,1代表两倍,当设定参考电压为2.5V时,取这一位为1就可以得到最高5V的输出电压。后面8位是数据位,其中第四位是数据的最高位。对于TLC5620的输出电压公式是: VO=VREF ×CODE/256×(1+RNG)     VREF是参考电压,CODE是待转换的8位二进制代码,RNG是增益倍数。       写入数据时,首先LOAD和LDAC写高电平,这样在CLK的每个下降沿写入的每位数据被锁存到DATA端,当11位数据传送完毕后,拉低LOAD,芯片根据前两位数据,判断是哪一路DAC通道,然后将8位数据移入相应的通道,进行DA转换,这时拉低LDAC,再拉高LDAC,就可以再下次转化之前,保持此次的模拟输出。     TLC5620正常工作时的具体电压和时间参数如下表所示,通过该表,可知该芯片串行数字接口的时钟信号(CLK)最高为1MHz。该参数将作为我们采用FPGA产生TLC5620数字接口时钟的依据。同时,还有输入数据建立时间t su (data-clk)为50ns,即,FPGA数据送出,到能够被TLC5620正常读取,至少需要50ns,因此FPGA单位数据输出保持时间不得少于50ns。t v (data-clk)为时钟下降沿到来后多久时间数据线上的数据才能被芯片内部采集,该时间确定了,时钟下降沿出现多久后,数据线上的数据可以被更新。t su (LOAD-LDAC)为LOAD的上升沿到LDAC下降沿的建立时间,这里最小为0ns,因此忽略,即两者同时发生即可。t w (LDAC)为LDAC低电平所需的最短时间,为250ns。 表2 TLC5620关键参数       通过对TLC5620一次完整转换的时序进行分析,列出以下序列机对应的序列点:该序列机总共包含26个点,其中,当Cnt1=0(ST0)时,为空闲态,ST1—ST22为数据发送状态,ST23时拉低LOAD,即将数据加载入对应通道的DAC中,ST24时释放LOAD,同时拉低LDAC,以产生LDAC的下降沿,将对应通道的模拟输出保持住。ST25拉高LDAC,完成一次转换。   ST0 Cnt1 == 0 DAC_Dout = 1; DAC_Clk = 0; DAC_LOAD = 1; DAC_LDAC = 1; DA_Done = 1; ST1 Cnt1 == 1 DAC_Dout = Data_r ; DAC_Clk = 1; DA_Done = 0; ST2 Cnt1 == 2 DAC_Clk = 0; ST3 Cnt1 == 3 DAC_Dout = Data_r ; DAC_Clk = 1; ST4 Cnt1 == 4 DAC_Clk = 0; ST5 Cnt1 == 5 DAC_Dout = Data_r ; DAC_Clk = 1; ST6 Cnt1 == 6 DAC_Clk = 0; ST7 Cnt1 == 7 DAC_Dout = Data_r ; DAC_Clk = 1; ST8 Cnt1 == 8 DAC_Clk = 0; ST9 Cnt1 == 9 DAC_Dout = Data_r ; DAC_Clk = 1; ST10 Cnt1 == 10 DAC_Clk = 0; ST11 Cnt1 == 11 DAC_Dout = Data_r ; DAC_Clk = 1; ST12 Cnt1 == 12 DAC_Clk = 0; ST13 Cnt1 == 13 DAC_Dout = Data_r ; DAC_Clk = 1; ST14 Cnt1 == 14 DAC_Clk = 0; ST15 Cnt1 == 15 DAC_Dout = Data_r ; DAC_Clk = 1; ST16 Cnt1 == 16 DAC_Clk = 0; ST17 Cnt1 == 17 DAC_Dout = Data_r ; DAC_Clk = 1; ST18 Cnt1 == 18 DAC_Clk = 0; ST19 Cnt1 == 19 DAC_Dout = Data_r ; DAC_Clk = 1; ST20 Cnt1 == 20 DAC_Clk = 0; ST21 Cnt1 == 21 DAC_Dout = Data_r ; DAC_Clk = 1; ST22 Cnt1 == 22 DAC_Clk = 0; ST23 Cnt1 == 23 DAC_LOAD = 0; ST24 Cnt1 == 24 DAC_LOAD = 1; DAC_LDAC = 0; ST25 Cnt1 == 25 DAC_LDAC = 1; DA_Done = 1; 表3 TLC5620单次转换控制序列机       序列机的计数器计数条件如下,   Cnt_state == 0 | Cnt1 == 25 Cnt1 = 0; Cnt_state == 1 Cnt1 25 Cnt1 = Cnt1 + 1’b1 表4 TLC5620序列机计数器计数条件     线性序列机计数器Cnt1的控制代码如下: 以下是代码片段:  always @(posedge Clk or negedge Rst_n)  if(!Rst_n)   Cnt1 = 5'd0;  else if(Cnt_State == DO_CNT)  begin   if(Cnt1 == 5'd25)    Cnt1 = 5'd0;   else if(Cnt2 == Cnt2_Top)    Cnt1 = Cnt1 + 1'b1;   else    Cnt1 = Cnt1;   end  else   Cnt1 = 5'd0;     其中,涉及到了两个状态,当Cnt_State = 0时,表示没有转换请求,即系统处于空闲状态,DAC不工作,当外部有转换请求时,则系统进入转换状态,每当计数使能信号到来时,Cnt1自加一,当Cnt1=25后,表明一次转换完成,将计数器清零,同时状态跳回空闲态,等待下一次使能信号的到来。具体的状态转移图如下所示: 图2 系统状态转移图    该状态机的代码对应如下: 以下是代码片段:  always @(posedge Clk or negedge Rst_n)  if(!Rst_n)   Cnt_State = IDEL;  else  begin   case(Cnt_State)    IDEL:     if(Do_DA)      Cnt_State = DO_CNT;     else      Cnt_State = IDEL;        DO_CNT:     if(Cnt1 == 5'd25)      Cnt_State = IDEL;     else      Cnt_State = DO_CNT;    default:;   endcase   end     因此,我们,只需要将Do_DA给出1个时钟周期的高脉冲,即可启动一次转换。同时,在检测到该脉冲时,模块内部会将数据端口Data上的数据读入到内部数据寄存器中,代码如下: 以下是代码片段:  always@(posedge Clk or negedge Rst_n)  if(!Rst_n)   Data_r = 10'd0;  else if(Do_DA)   Data_r = Data;  else   Data_r = Data_r;     同时,为了产生1MHz的时钟,系统中使用了一个计数器Cnt2来专门产生该信号,该计数器对系统时钟进行计数,如当系统时钟为50M(周期为20ns)时,Cnt2计数到24,即计数了500ns,产生一个时钟周期的标志信号,则Cnt1在检测到这个标志信号后,便会自加1,因此,该标志信号出现两次则表明计时1000ns,对应时钟频率为1Mhz,即DAC芯片数字接口的时钟频率。该部分代码如下: 以下是代码片段:  always @ (posedge Clk or negedge Rst_n)  if(!Rst_n)   Cnt2 = 5'd0;  else if(Cnt_State == DO_CNT)  begin   if(Cnt2 == Cnt2_Top)    Cnt2 = 5'd0;   else    Cnt2 = Cnt2 + 1'b1;   end  else   Cnt2 = 5'd0;     为了兼容不同的系统时钟,这里采用参数化定制,得出对应的计数最大值,具体代码如下: 以下是代码片段: Localparam system_clk = 50_000_000; /*系统时钟*/ Localparam Cnt2_Top = system_clk / 1_000_000 / 2 - 1; /*500ns技术器计数最大值*/     系统时钟设置为50M,则计数最大值为50000000/1000000/2– 1 = 24,当系统时钟改变后,只需要修改system_clk的值,即可保证Cnt2计数一次的时间为500ns。     最后,附上主序列中的操作代码: 以下是代码片段:  always@(posedge Clk or negedge Rst_n)  if(!Rst_n)  begin   DAC_Dout = 1;   DAC_Clk = 0;   DAC_LOAD = 1;   DAC_LDAC = 1;   DA_Done = 1;   end  else  begin   case(Cnt1)    0:     begin      DAC_Dout = 1;      DAC_Clk = 0;      DAC_LOAD = 1;      DAC_LDAC = 1;      DA_Done = 1;      end    1:begin DAC_Dout = Data_r ; DAC_Clk = 1;DA_Done = 0;end    2:DAC_Clk = 0;    3:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    4:DAC_Clk = 0;    5:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    6:DAC_Clk = 0;    7:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    8:DAC_Clk = 0;    9:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    10:DAC_Clk = 0;    11:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    12:DAC_Clk = 0;    13:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    14:DAC_Clk = 0;    15:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    16:DAC_Clk = 0;    17:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    18:DAC_Clk = 0;    19:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    20:DAC_Clk = 0;    21:begin DAC_Dout = Data_r ; DAC_Clk = 1;end    22:DAC_Clk = 0;    23:DAC_LOAD = 0;    24:begin DAC_LOAD = 1; DAC_LDAC = 0; end    25:begin DAC_LDAC = 1; DA_Done = 1; end    default:;   endcase     end    该设计的仿真结果如下如所示:         由该仿真结果可知,时钟频率为1MHz,满足芯片工作要求,其它时序均与手册给出的时序保持一致。为了设计简洁,这里将LOAD和LDAC的低电平脉冲时间都设置为了500ns,而非最小时间250ns,这里主要是为了方便序列机的设计。当然,如此设计在一定程度上会影响DAC 的转换速率,不过在大多数应用场合已经足够,如需更加高效的设计,只需要对代码稍加修改即可。       本驱动的testbench编写较为简单,这里只附上对应代码,不做详细解释: 以下是代码片段: `timescale 1ns/1ns module TLC5620_Driver_tb;  reg Clk;  reg Rst_n;  reg Do_DA; /*使能单次转换*/  reg Data;/*{Addr1,Addr0,Range,Data_bit }*/    wire DAC_Dout; /*DAC数据线*/  wire DAC_Clk; /*DAC时钟线,最高速度1M*/  wire DAC_LDAC; /**/  wire DAC_LOAD; /**/    wire DA_Done; /*单次转换完成标志信号*/    TLC5620_Driver u1(   .Clk(Clk),   .Rst_n(Rst_n),   .Do_DA(Do_DA),   .Data(Data),   .DAC_Dout(DAC_Dout),   .DAC_Clk(DAC_Clk),   .DAC_LDAC(DAC_LDAC),   .DAC_LOAD(DAC_LOAD),   .DA_Done(DA_Done)  );    initial begin   Clk = 1;   Rst_n = 0;   Do_DA = 0;   Data = 11'd0;   #200;   Rst_n = 1;   #400;   Data = 11'b110_1011_1001;   Do_DA = 1;   @(posedge DA_Done)   Data = 11'b110_0000_1111;   #20   Do_DA = 1;   #20;   Do_DA = 0;   @(posedge DA_Done)   Data = 11'b110_1111_0000;   #20   Do_DA = 1;   #20;   Do_DA = 0;   @(posedge DA_Done)   #400;   $stop;   end     always #10 Clk = ~Clk;   endmodule     因为时间关系,这里只开发了该芯片的驱动,并用modelsim对该驱动进行了仿真,详细的调试和应用,小梅哥将在下一个实验中介绍。
  • 热度 24
    2013-9-10 13:57
    12243 次阅读|
    1 个评论
    这篇日志是本阶段最后一篇关于接口通信的实例 后续将把学习重心放在硬件设计和存储 显示上面    本文分三部分 1,AD转换分析 2,我的github搜集的verilog源码大放送(目前两百多兆大概300个实例均来自网络,辅助资料陆续汇总中) 3,AD  DA源码   第一部分:ADC dac AD DA转换在嵌入式系统中也是经常需要使用的测量和波形产生手段 现在大部分的单片机和ARM都已经内置了10-16位的高速AD转换器且使用简单通道数也比较多。 这里举一个fpga驱动TL549的adc转换的例子学习下   和前面介绍的SPI IICUART来讲 AD DA相对来说比较简单 da转换 不再做详细解释看下数据手册基本上就能理解可参考 http://hi.baidu.com/rabbitmysx/item/f04272ea9e1ba2e2fb42ba2f   先看下TL549的参数 TL549 8位单输入逐次逼近式AD转换器 内部4M时钟 IO最大速度I,IMHZ 正基准电压输入2.5V≤REF+≤Vcc+0.1。  负基准电压输入端-0.1V≤REF-≤2.5V。 VCC   系统电源3v≤Vcc≤6v。  GND  接地端。  /CS芯片选择输入端 DATA  OUT 转换结果数据串行输出端与TTL电平兼容输出时高位在前,低位在后。 ANALOGIN模拟信号输入端  REF-≤ANALOGIN≤Vcc 当ANALOGIN≥REF+电压时转换结果为全“1”(0FFH)ANALOGIN≤REF-电压时转换结果为全“0”(00H)。    I/O  CLOCK外接输入/输出时钟输入端,无需与芯片内部系统时钟同步. TLC549器件工作时序  当/CS变为低电平后TLC549芯片被选中同时前次转换结果的最高有效位MSBA7自DATA OUT端输出 接着要求自I/O CLOCK端输入8个外部时钟信号前7个I/O CLOCK 信号的作用是配合TLC549输出前次转换结果的A6-A0位(下降沿发送数据)并为本次转换做准备 在第4个I/O CLOCK信号由高至低的跳变之后,片内采样/保持电路对输入模拟量采样开始 第8个I/O CLOCK信号的下降沿使片内采样/保持电路进入保持状态并启动A/D开始转换。   转换时间为36个系统时钟周期最大为17us。直到A/D转换完成前的这段时间内TLC549的控制逻辑要求: 或者/CS保持高电平,或者I/O CLOCK时钟端保持36个系统时钟周期的低电平。   所以在自TLC549的I/O CLOCK端输入8个外部时钟信号期间需要完成以下工作 读入前次转换结果 对本次转换的输入模拟信号采样并保持 启动本次A/D转换开始   这个例子IO时钟为200Khz 在某次的时钟下降沿将cs拉低并开始从data线上获取数据   每个下降沿到下一个下降沿之间获取一次数据  转换收到的数据并显示到LED数码管     第二部分 verilog实例汇总( 如侵犯到代码作者权利请及时联系我 ) 学习期间我也买了些书 从图书馆 网络等地方获取了一些源代码 现汇总起来上传到github供fpga学习爱好者学习交流 我不保证代码都全部正确 所以学习之前自己做判断 使用方法很简单 1  你可以在网页上直接学习源代码 2   也可以打包成ZIP下载到你的电脑上学习 3   还可以使用git  clone到你的电脑 这样我的代码只要更新 你就会获得最新的代码   我的verilog-example 仓库 javascript:void(0);/*1378526084816*/ verilogHDL 设计与实战 https://github.com/sheepsleep/Verilog-HDL-design-and-training   第三部分  AD  DA源代码
相关资源