作者:李西锐 校对:陆辉
大侠好,欢迎来到FPGA技术江湖。本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。
系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。本次带来Vivado系列,TLC549驱动设计。话不多说,上货。
TCL549驱动设计
在生活中,数模转换的例子到处可见。但是在我们做FPGA设计时,需要对数字信号进行处理,但是,不是所有的信号都是以数字信号的形式体现的,比如光信号、声信号、电信号等等。那么此时就需要我们先进行一下模数转换之后再进行处理。在此,我们将介绍一款模数转换芯片TLC549。
TLC549是一款串行总线控制的8bit模数转换芯片,封装如下图:
管脚说明:
芯片特征:
1、8bit A/D转换器。
2、最大转换时间为17us。
3、供电3V~6V之间。
4、低功耗最大功率为15mW。
在官方手册中,对此芯片有这样的一段描述:
在这段描述中,大致说出了这个芯片的一些控制。比如:TLC548和TLC549都是8bit的模数转换器。这个芯片使用I/O CLOCK和CS_N来控制数据。TLC548的I/O CLOCK最大频率为2.048MHz,TLC549的I/O CLOCK最大频率为1.1MHz。
在这段描述中可以看出,AD芯片的数据输出,需要在I/O CLOCK和CS_N的控制下才能进行。
芯片示意图:
从图中可以看出,参考电压是模数转换的一个标准,CS_N和I/O CLOCK给到逻辑控制和输出计数。数据通过一个输出寄存器给到一个并串转换的模块。DATA_OUT数据是给到我们的FPGA的,所以,对于我们来说需要做的就是输出CS_N和I/O CLOCK的波形,同时,将给进来的数据采到。
上图为驱动的时序图,可以看到,当数据输出的时候,CS_N为低电平,总共输出8bit,输出完之后,CS_N拉高。我们在做驱动时。可以将一次转换过程看成一个周期,后续也是重复过程。那么,我们就需要搞清楚每段时间的要求,有助于我们写驱动。
在上图中,我们可以知道:
1、CS_N从拉低到第一bit数据出现在数据线上的时间最大为1.4us。
2、CS_N从拉低到第一个时钟上升沿出现,时间最小为1.4us。
3、I/O CLOCK频率最大为1.1MHz。为了方便计算,我们采取1MHz。
4、Tconv转换时间最大为17us。
5、CS_N拉高时间最小为17us。
在知道了以上信息后,我们开始写驱动,首先新建一下工程。
选择好之后点击完成,新建文件写代码。
代码如下:
1 module TLC549_driver(
2
3 input wire clk,
4 input wire rst_n,
5
6 output reg ad_clk,
7 output reg ad_cs_n,
8 input wire ad_data,
9
10 output reg [7:0] data
11 );
12
13 parameter t = 1300;
14
15 reg [10:0] cnt;
16 reg [7:0] data_temp;
17
18 always @ (posedge clk, negedge rst_n)
19 begin
20 if(rst_n == 1'b0)
21 cnt <= 11'd0;
22 else if(cnt == t - 1)
23 cnt <= 11'd0;
24 else
25 cnt <= cnt + 1'b1;
26 end
27
28 always @ (posedge clk, negedge rst_n)
29 begin
30 if(rst_n == 1'b0)
31 begin
32 ad_clk <= 1'b0;
33 ad_cs_n <= 1'b1;
34 data_temp <= 8'd0;
35 data <= 8'd0;
36 end
37 else
38 case(cnt)
39 0 : begin ad_cs_n <= 1'b0; ad_clk <= 1'b0; end
40 74 : begin ad_clk <= 1'b1; data_temp[7] <= ad_data; end
41 99 : begin ad_clk <= 1'b0; end
42 124 : begin ad_clk <= 1'b1; data_temp[6] <= ad_data; end
43 149 : begin ad_clk <= 1'b0; end
44 174 : begin ad_clk <= 1'b1; data_temp[5] <= ad_data; end
45 199 : begin ad_clk <= 1'b0; end
46 224 : begin ad_clk <= 1'b1; data_temp[4] <= ad_data; end
47 249 : begin ad_clk <= 1'b0; end
48 274 : begin ad_clk <= 1'b1; data_temp[3] <= ad_data; end
49 299 : begin ad_clk <= 1'b0; end
50 324 : begin ad_clk <= 1'b1; data_temp[2] <= ad_data; end
51 349 : begin ad_clk <= 1'b0; end
52 374 : begin ad_clk <= 1'b1; data_temp[1] <= ad_data; end
53 399 : begin ad_clk <= 1'b0; end
54 424 : begin ad_clk <= 1'b1; data_temp[0] <= ad_data; end
55 449 : begin ad_clk <= 1'b0; ad_cs_n <= 1'b1; end
56 1299: begin data <= data_temp; end
57 default : ;
58 endcase
59 end
60
61 endmodule
代码写好之后,我们做一下仿真看一下数据能否被正确采集,代码如下:
1 `timescale 1ns / 1ps
2
3 module TLC549_driver_tb;
4
5 reg clk;
6 reg rst_n;
7
8 wire ad_clk;
9 wire ad_cs_n;
10 reg ad_data;
11
12 wire [7:0] data;
13
14 initial begin
15 clk = 0;
16 rst_n = 0;
17 ad_data = 0;
18 #100;
19 rst_n = 1;
20 #1000;
21 ad_data = 1; //10100110
22 #1000;
23 ad_data = 0;
24 #1000;
25 ad_data = 1;
26 #1000;
27 ad_data = 0;
28 #1000;
29 ad_data = 0;
30 #1000;
31 ad_data = 1;
32 #1000;
33 ad_data = 1;
34 #1000;
35 ad_data = 0;
36 #20000;
37 $stop;
38 end
39
40 always #10 clk = ~clk;
41
42 TLC549_driver TLC549_driver_inst(
43
44 .clk (clk),
45 .rst_n (rst_n),
46
47 .ad_clk (ad_clk),
48 .ad_cs_n (ad_cs_n),
49 .ad_data (ad_data),
50
51 .data (data)
52 );
53
54 endmodule
编译无误,打开波形观察。
仿真中给出的数据位8’b10100110,换算成16进制为8’ha6。与波形中显示一致,仿真正确。