资源描述
实 验 报 告
实验名称: [TLC5620串行D/A实验]
姓 名: [古毅]
学 号: [2011082408]
指导教师: [解武]
实验时间: [2014年3月17日]
信息与通信工程学院
TLC5620串行D/A实验
1 实验目的
熟练掌握基本的编程语句;能够运用所学知识理解给出的程序,并按照要求修改程序。
2 实验要求
TLC5620是4通道8位的串行D/A转换器。
任务1:
观察已给程序的实验现象,并结合实验结果理解原程序语句的含义,体会基本句话,模块的用法。在掌握整个程序工作的原理后,修改程序,使其实现单通道的DA转换;在按下通道的按键之后,用数码管显示输入的数字量;停止按键,数码管计数停止,继续按键则继续计数。按下复位键,则系统清零,数码管显示0值。将转换后的模拟电压加到LED两端,观察LED亮度变化是否符合理论规律。
任务2:
在任务1的基础上,进一步体会程序的编写思想,将顶层图形文件修改成一个用语言描述的模块,实现原程序所要求的各项功能:通过4个按键选择不同通道,每个通道的数字量输入都可以在数码管上进行显示。松开按键,则计数显示保持前一状态,按下按键继续计数。设置复位键,按下复位键,系统清零。每个通道对应的模拟电压通过LED的亮度显示。通过硬件实现,观察实验结果。
3 实验原理
3.1 TLC5620基本原理
TLC5620封装如实验图1所示,是一个4通道8位的串行数模(D/A)转换器,其最大转换速度可达1MBps。其管脚REFA~REFD为四个通道的参考电压,实验平台的参考电压均为2.5V;DACA~DACD为4路四个模拟信号输出通道;DATA为串行数据输入;CLK为DAC串行数据输入时钟,其下降沿锁存输入数据DATA;LOAD为串行数据锁存信号,低电平锁存。LDAC为DAC输出更新控制信号,当LDAC为低电平时,则把锁存在锁存器的数据送到DAC并转换为模拟信号,在相应的通道进行输出,故可以始终把LDAC信号置为低电平,也就是说加载信号一旦产生,数据立刻转换输出。
表1为TLC5620各引脚功能介绍。
图1 TLC5620封装示意图
表1 TLC5620引脚功能
引脚
输入/输出
功能描述
名称
序号
GND
1
I
地回路及参考终端
DACA
12
O
DAC A模拟信号输出
DACB
11
O
DAC B模拟信号输出
DACC
10
O
DAC C模拟信号输出
DACD
9
O
DAC D模拟信号输出
DATA
6
I
串行数字数据输入接口,在CLK下降沿时将输入的数字量转发到串行接口寄存器里
CLK
7
I
串行接口时钟,用于控制串行数据的输入
LDAC
13
I
加载DAC。当引脚出现高电平时,即使有数字量被读入串行口也不会对DAC的输出进行更新。只有当引脚从高电平变为低电平时,DAC输出才更新。
LOAD
8
I
串口加载控制。当LDAC是低电平,并且LOAD引脚出现下降沿时数字量被保存到锁存器,随后输出端产生模拟电压。
REFA
2
I
输入到DAC A的参考电压。这个电压定义了输出模拟量的范围。
REFB
3
I
输入到DAC B的参考电压。这个电压定义了输出模拟量的范围。
REFC
4
I
输入到DAC C的参考电压。这个电压定义了输出模拟量的范围。
REFD
5
I
输入到DAC D的参考电压。这个电压定义了输出模拟量的范围。
VDD
14
I
输入电源正极
因为TLC5620为四通道的数模转换器,只有一个DATA数据输入端,所以传送的数据中要包含通道的信息,以便DAC能识别出该数据属于哪个通道,转换完成后的模拟信号输出到相应的通道中。TLC5620传输的一帧数据位11位,先传送高位,最后传送低位,帧格式如表2所示。
表2 TLC5620的数据结构
D10
D9
D8
D7
D6
D5
D4
D3
D2
D1
D0
通道选择
输出模式RNG
8位数据D7~D0
D10、D9为通道选择位,00~11分别选择DACA~DACD通道。RNG的数值为0或者1,为输出倍数。TLC5620的输出电压为:
DAC内部有移位寄存器和锁存器,要在工程中实现在LOAD高电平时把11位数据在CLK的下降沿逐位(由高位到低位)发送到DATA端,发送完毕后,LOAD置为低电平,指示DAC进行模数转换。
TLC5620的访问时序图如图2所示。
图2 TLC5620访问时序图
3.2 TLC5620与FPGA连接电路图
图3 TLC5620与FPGA连接示意图
LOAD的低电平的最小保持时间(LOAD)为250ns,各个和的最小保持时间为50ns。为了尽可能最大利用DAC的转换速度,为此状态机选用5000Hz(200ns)左右的输入时钟,在LOAD低电平时要等待 12个状态机时钟CLK0.5M。为此采用计数器判断等待时间是否满足条件,该计数器使用LOAD的高电平为异步复位信号,低电平时,对CLK0.5M进行计数,当计数器计数值大于12 时,说明LOAD为低电平的时间(LOAD)已满足,状态机可跳转到下一态。
在LOAD高电平时,需要产生11个DAC的CLK,同样采用计数器计数值判断,该计数器中,LOAD的低电平为异步复位信号,LOAD为高电平时对DA_CLK计数,满足计数器的值>11时,说明已经送入了11bit的串行数据,可以进行置LOAD为低电平,对11bit数据锁存进行数模转换。
3.3 任务原理
3.3.1任务原理1
将给定程序的4通道转换改成单通道的DA转换:修改程序,将四个通道的按键修改成一个按键,用一个5HZ的时钟信号进行采样,并将采样的数字信号传输给TLC5620,按照其时序要求,对5620初始化,使其将采样的按键的数字信号按照上述提供的公式转化为模拟信号,并输出给LED灯,并且随着按键时间延长,数字输入的增大,转换为的模拟电压增大,LED灯的亮度增加。同时,将采样的数字信号通过数码管显示。从而完成整个功能。
3.3.1任务原理2
将给定的程序模块合在一个顶层模块中,不使用图形文件作为顶层:使用verilog VHDL建立顶层文件,在顶层文件中设置连线型变量,调用事先写好的模块,用线型变量将两个模块需要连接的管脚连接在一起,从而形成一个完整功能的大模块。
3.4 实验流程图
4 FPGA所用的管脚分配
选用FPGA为Altera公司的CycloneII系列EP2C8Q208C8。主时钟50MHz。管脚分配如下。
5 实验结果
1、按不同的通道,数码管显示数字输入量,数字变化的频率和程序模块中分频的频率一致;停止按键,则数字不发生变化,继续按相同通道的按键,则数码管继续计数;若按下复位键,则数码管显示清零。
2、当数字输入显示超过1500时,可以观察到转化成模拟量的电压足以使LED灯发光,并且输入数字量越大,转化成的模拟电压越高,观察到的LED发光越亮。
3、当将程序修改成为只有一个通道的,同样可以实现显示,数字量转化为模拟量的变化。同样,将程序改为一个总程序时,也可以实现相应功能。
6 实验心得
通过这次实验,我的收获如下:
第一、了解 TLC5620串行D/A的工作原理,能熟练掌握基本的编程语句,并且能够运用所学知识理解给出的程序,按照要求基本修改程序。
第二、相比前一次的实验对verilog语言有了一个更加清醒的认识,在阅读程序、修改程序的过程中更加熟悉了verilog语言,掌握了基本的修改程序的技巧和能力。
第三、在学习了verilog语言编程的时候,将其与在上学期学习数字电路课程时,从门电路的角度出发,通过真值表、表达式、卡诺图、状态表、状态转换图、特征方程等方式用图形法或者基本的逻辑器件实现想要的功能,进行对比学习,这使我意识到,不能单从原有的思维方式出发进行分析和编程,必须建立整体的概念,从输入输出的整体功能出发用程序建立模块,并通过模块和模块的嵌套或者连接实现相应功能。
7 参考文献
[1] 王金明. 《数字系统设计与Verilog HDL》第3版 电子工业出版社, 2009 TP271/W24.
[2] 施齐云 潘大鹏 黄湘松 数字电子技术实践教程 哈尔滨工程大学出版社
附录:实验程序清单
//TASK 1:单通道数码管计数显示
//D/A 5620 运行程序顶层文件:
module top(clk,reset,key,tlc5620_clk,tlc5620_data,tlc5620_load,tlc5620_ldac,sel,ky1,ledcom,leddata);
input clk;
input reset;
input key;
output tlc5620_clk;
output tlc5620_data;
output tlc5620_load;
output tlc5620_ldac;
output[2:0] sel;
output ky1;
output[7:0] ledcom;
output[7:0] leddata;
wire a;
wire[10:0] b;
tlc5620(.clk(clk),.rst(reset),.write_n(a),.wr_data(b),.dac_clk(tlc5620_clk),.dac_data(tlc5620_data),.dac_load(tlc5620_load),.dac_ldac(tlc5620_ldac));
dac_test(.clk(clk),.rst(reset),.key(key),.wr_n(a),.wr_data(b),.seg_com(ledcom),.seg_data(leddata));
assign ky1=0;
assign sel[2]=0;
assign sel[1]=0;
assign sel[0]=1;
endmodule
//D/A 5620驱动程序:
module tlc5620(clk,rst,write_n,wr_data,dac_clk,dac_data,dac_load,dac_ldac);
input clk;
input rst;
input write_n;
input[10:0] wr_data;
output dac_clk;
output dac_data;
output dac_load;
output dac_ldac;
wire dac_done;
reg dac_clk_r;
reg dac_data_r;
reg [5:0] counter;
reg [31:0] DCLK_DIV;
parameter CLK_FREQ = 'D50_000_000;
//系统时钟50MHZ
parameter DCLK_FREQ = 'D1_000_000;
//AD_CLK输出时钟1M/2HZ
always @(posedge clk)
if(DCLK_DIV < (CLK_FREQ / DCLK_FREQ))
DCLK_DIV <= DCLK_DIV+1'b1;
else
begin
DCLK_DIV <= 0;
dac_clk_r <= ~dac_clk_r;
end
always @(posedge dac_clk_r or negedge rst)
begin
if(!rst)
counter <= 0;
else
if(counter<='d13)
counter <= counter + 1'b1;
else
counter <= 0;
end
assign dac_load = (counter == 4'd12) ? 1'b0 : 1'b1;
assign dac_clk = (counter > 'd0 && counter < 'd12) ? dac_clk_r : 1'b0;
assign dac_ldac = (counter == 4'd13) ? 1'b0 : 1'b1;
assign dac_done = (counter <= 4'd11) ? 1'b0 : 1'b1;
assign dac_data = dac_data_r;
/*先高位,把11位数据传输给DAC芯片*/
always @(counter[3:0] or wr_data or dac_done or write_n)
begin
if(!dac_done && !write_n)
case(counter[3:0])
4'd1: dac_data_r <= wr_data[10];
4'd2: dac_data_r <= wr_data[9];
4'd3: dac_data_r <= wr_data[8];
4'd4: dac_data_r <= wr_data[7];
4'd5: dac_data_r <= wr_data[6];
4'd6: dac_data_r <= wr_data[5];
4'd7: dac_data_r <= wr_data[4];
4'd8: dac_data_r <= wr_data[3];
4'd9: dac_data_r <= wr_data[2];
4'd10: dac_data_r <= wr_data[1];
4'd11: dac_data_r <= wr_data[0];
default: dac_data_r <= 1'b1;
endcase
else
dac_data_r <= 1'b1;
end
endmodule
//D/A 5620 测试程序:
/* DAC_TLC5620测试模块
* 按KEY1键,通道A的电压值递增;
* 通道的电压值显示于数码管.
*/
module dac_test(clk,rst,key,wr_n,wr_data,seg_com,seg_data);
input clk;
input rst;
Input key;
output wr_n;
output [10:0] wr_data;
output [7:0]seg_data;
output [7:0]seg_com;
parameter CLK_FREQ = 'D50_000_000;
//系统时钟50MHZ
parameter DCLK_FREQ = 'D10;
//AD_CLK输出时钟10/2HZ
always @(posedge clk)
if(DCLK_DIV < (CLK_FREQ / DCLK_FREQ))
DCLK_DIV <= DCLK_DIV+1'b1;
else
begin
DCLK_DIV <= 0;
CLK_DIV <= ~CLK_DIV;
end
/*高2位为通道选择,低8位为DA数据,第9位 RNG 为1时输出0到2倍Vref,为0时输出0到Vref*/
assign wr_data = {channel,1'b1,data_code_r};
assign wr_n = 1'b0;
always @(posedge CLK_DIV or negedge rst )
if(!rst)
begin
key0_r <= 8'h00;
data_code_r <= 8'h00;
end
else
case(key)
begin
channel <= 2'b00;
key0_r <= key0_r + 1'b1;
data_code_r <= key0_r;
end
default : begin end
endcase
/*将各通道的电压值显示于数码管上,单位mv */
always @(negedge rst or negedge CLK_DIV )
begin
if(!rst)
begin
datain[0]<=8'b00000000;
datain[1]<=8'b00000000;
datain[2]<=8'b00000000;
datain[3]<=8'b00000000;
datain[4]<=8'b00000000;
datain[5]<=8'b00000000;
datain[6]<=8'b00000000;
datain[7]<=8'b00000000;
end
else begin
/*电压值Vo=Vref * (RNG+1) * CODE / 256 */
vo_r = data_code_r * 13'd5000/9'd256;
datain[0]<=vo_r%10;
datain[1]<=vo_r/10%10;
datain[2]<=vo_r/100%10;
datain[3]<=vo_r/1000%10;
datain[4]<=vo_r/10000%10;
datain[5]<=vo_r/100000%10;
datain[6]<=vo_r/1000000%10;
datain[7]<=vo_r/10000000%10;
end
end
always @(posedge clk)
begin
count=count+1;
end
always @(count[14:12])
begin
case(count[14:12])
3'b000:
begin
bcd_led = datain[0];
seg_com = 8'b00000001;
end
3'b001:
begin
bcd_led=datain[1];
seg_com=8'b00000010;
end
3'b010:
begin
bcd_led=datain[2];
seg_com=8'b00000100;
end
3'b011:
begin
bcd_led=datain[3];
seg_com=8'b00001000;
end
3'b100:
begin
bcd_led=datain[4];
seg_com=8'b00010000;
end
3'b101:
begin
bcd_led=datain[5];
seg_com=8'b00100000;
end
3'b110:
begin
bcd_led=datain[6];
seg_com=8'b01000000;
end
3'b111:
begin
bcd_led=datain[7];
seg_com=8'b10000000;
end
endcase
end
always @(seg_com or bcd_led)
begin
case(bcd_led[3:0])
4'h0:seg_data=8'hc0;
4'h1:seg_data=8'hf9;
4'h2:seg_data=8'ha4;
4'h3:seg_data=8'hb0;
4'h4:seg_data=8'h99;
4'h5:seg_data=8'h92;
4'h6:seg_data=8'h82;
4'h7:seg_data=8'hf8;
4'h8:seg_data=8'h80;
4'h9:seg_data=8'h90;
4'ha:seg_data=8'h88;
4'hb:seg_data=8'h83;
4'hc:seg_data=8'hc6;
4'hd:seg_data=8'ha1;
4'he:seg_data=8'h86;
4'hf:seg_data=8'h8e;
endcase
end
endmodule
//TASK 2: 将两个模块合成一个
//D/A 5620 运行程序顶层文件:
module top(clk,reset,key,tlc5620_clk,tlc5620_data,tlc5620_load,tlc5620_ldac,sel,ky1,ledcom,leddata);
input clk;
input reset;
input[3:0] key;
output tlc5620_clk;
output tlc5620_data;
output tlc5620_load;
output tlc5620_ldac;
output[2:0] sel;
output ky1;
output[7:0] ledcom;
output[7:0] leddata;
wire a;
wire[10:0] b;
tlc5620(.clk(clk),.rst(reset),.write_n(a),.wr_data(b),.dac_clk(tlc5620_clk),.dac_data(tlc5620_data),.dac_load(tlc5620_load),.dac_ldac(tlc5620_ldac));
dac_test(.clk(clk),.rst(reset),.key(key),.wr_n(a),.wr_data(b),.seg_com(ledcom),.seg_data(leddata));
assign ky1=0;
assign sel[2]=0;
assign sel[1]=0;
assign sel[0]=1;
endmodule
////D/A 5620驱动程序:
module tlc5620(clk,rst,write_n,wr_data,dac_clk,dac_data,dac_load,dac_ldac);
parameter CLK_FREQ = 'D50_000_000;
//系统时钟50MHZ
parameter DCLK_FREQ = 'D1_000_000;
//AD_CLK输出时钟1M/2HZ
always @(posedge clk)
if(DCLK_DIV < (CLK_FREQ / DCLK_FREQ))
DCLK_DIV <= DCLK_DIV+1'b1;
else
begin
DCLK_DIV <= 0;
dac_clk_r <= ~dac_clk_r;
end
always @(posedge dac_clk_r or negedge rst)
begin
if(!rst)
counter <= 0;
else
if(counter<='d13)
counter <= counter + 1'b1;
else
counter <= 0;
end
assign dac_load = (counter == 4'd12) ? 1'b0 : 1'b1;
assign dac_clk = (counter > 'd0 && counter < 'd12) ? dac_clk_r : 1'b0;
assign dac_ldac = (counter == 4'd13) ? 1'b0 : 1'b1;
assign dac_done = (counter <= 4'd11) ? 1'b0 : 1'b1;
assign dac_data = dac_data_r;
/*先高位,把11位数据传输给DAC芯片*/
always @(counter[3:0] or wr_data or dac_done or write_n)
begin
if(!dac_done && !write_n)
case(counter[3:0])
4'd1: dac_data_r <= wr_data[10];
4'd2: dac_data_r <= wr_data[9];
4'd3: dac_data_r <= wr_data[8];
4'd4: dac_data_r <= wr_data[7];
4'd5: dac_data_r <= wr_data[6];
4'd6: dac_data_r <= wr_data[5];
4'd7: dac_data_r <= wr_data[4];
4'd8: dac_data_r <= wr_data[3];
4'd9: dac_data_r <= wr_data[2];
4'd10: dac_data_r <= wr_data[1];
4'd11: dac_data_r <= wr_data[0];
default: dac_data_r <= 1'b1;
endcase
else
dac_data_r <= 1'b1;
end
endmodule
/////D/A 5620 测试程序:
/* DAC_TLC5620测试模块
* 按KEY1键,通道D的电压值递增;
* 按KEY2键,通道C的电压值递增;
* 按KEY3键,通道B的电压值递增;
* 按KEY4键,通道A的电压值递增;
* 各通道的电压值显示于数码管.
*/
module dac_test(clk,rst,key,wr_n,wr_data,seg_com,seg_data);
reg [7:0] key3_r;
reg [31:0] vo_r;
parameter CLK_FREQ = 'D50_000_000;//系统时钟50MHZ
parameter DCLK_FREQ = 'D10;//AD_CLK输出时钟10/2HZ
always @(posedge clk)
if(DCLK_DIV < (CLK_FREQ / DCLK_FREQ))
DCLK_DIV <= DCLK_DIV+1'b1;
else
begin
DCLK_DIV <= 0;
CLK_DIV <= ~CLK_DIV;
end
/*高2位为通道选择,低8位为DA数据,第9位 RNG 为1时输出0到2倍Vref,为0时输出0到Vref*/
assign wr_data = {channel,1'b1,data_code_r};
assign wr_n = 1'b0;
/*根据按键不同,选择不同的DA通道,其值递增*/
always @(posedge CLK_DIV or negedge rst )
if(!rst)
begin
key0_r <= 8'h00;
key1_r <= 8'h00;
key2_r <= 8'h00;
key3_r <= 8'h00;
data_code_r <= 8'h00;
end
else
case(key)
4'b1110 : begin //key4
channel <= 2'b00;
key0_r <= key0_r + 1'b1;
data_code_r <= key0_r;
end
4'b1101 : begin //key3
channel <= 2'b01;
key1_r <= key1_r + 1'b1;
data_code_r <= key1_r;
end
4'b1011 : begin //key2
channel <= 2'b10;
key2_r <= key2_r + 1'b1;
data_code_r <= key2_r;
end
4'b0111 : begin //key1
channel <= 2'b11;
key3_r <= key3_r + 1'b1;
data_code_r <= key3_r;
end
default : begin end
endcase
/*将各通道的电压值显示于数码管上,单位mv */
always @(negedge rst or negedge CLK_DIV )
begin
if(!rst)
begin
datain[0]<=8'b00000000;
datain[1]<=8'b00000000;
datain[2]<=8'b00000000;
datain[3]<=8'b00000000;
datain[4]<=8'b00000000;
datain[5]<=8'b00000000;
datain[6]<=8'b00000000;
datain[7]<=8'b00000000;
end
else begin
/*电压值Vo=Vref * (RNG+1) * CODE / 256 */
vo_r = data_code_r * 13'd5000/9'd256;
datain[0]<=vo_r%10; /*% 表示取模*/
datain[1]<=vo_r/10%10;
datain[2]<=vo_r/100%10;
datain[3]<=vo_r/1000%10;
end
end
always @(posedge clk)
begin
count=count+1;
end
always @(count[14:12])
begin
case(count[14:12])
3'b000:
begin
bcd_led = datain[0]; //数码管扫描
seg_com = 8'b00000001; //数码管共阳极
end
3'b001:
begin
bcd_led=datain[1];
seg_com=8'b00000010;
end
3'b010:
begin
bcd_led=datain[2];
seg_com=8'b00000100;
end
3'b011:
begin
bcd_led=datain[3];
seg_com=8'b00001000;
end
3'b100:
begin
bcd_led=datain[4];
seg_com=8'b00010000;
end
3'b101:
begin
bcd_led=datain[5];
seg_com=8'b00100000;
end
3'b110:
begin
bcd_led=datain[6];
seg_com=8'b01000000;
end
3'b111:
begin
bcd_led=datain[7];
seg_com=8'b10000000;
end
endcase
end
always @(seg_com or bcd_led)
begin
case(bcd_led[3:0])
4'h0:seg_data=8'hc0; //11000000 显示 0
4'h1:seg_data=8'hf9; //11111001 显示 1
4'h2:seg_data=8'ha4; //.....
4'h3:seg_data=8'hb0; //.....
4'h4:seg_data=8'h99; //.....
4'h5:seg_data=8'h92;
4'h6:seg_data=8'h82;
4'h7:seg_data=8'hf8;
4'h8:seg_data=8'h80;
4'h9:seg_data=8'h90;
4'ha:seg_data=8'h88;
4'hb:seg_data=8'h83;
展开阅读全文