數模轉換器(Digital to Analog Converter)即DAC,是數字世界和模擬世界之間的橋梁。人類生活在模擬世界中,雖然數字器件及設備的比重日益增強,但是DAC的發展仍是必不可少的。
從航空航天、國防軍事到民用通信、多媒體、數字信號處理等都涉及到DAC應用。DAC基本上由4個部分組成,即權電阻網絡、運算放大器、基準電源和模擬開關。它是一種將二進制數字量形式的離散信號轉換成以參考電壓為基準的模擬量的轉換器。
本設計采用串行數/模轉換芯片TLC5620,TLC5620是一個擁有四路輸出的數/模轉換器,時鐘頻率最大可達到1MHz。TLC5620芯片接口如下:
該芯片主要有以下特點:四通道8位電壓輸出DA轉換器、5V單電源供電、串行接口、高阻抗基準輸入、可編程1或2輸出范圍、同時更新設備、內部上電復位、低功耗、半緩沖輸出。該芯片主要應用于:可編程電源、數字控制放大器/誤差器、移動通信、自動測試設備、研發過程檢測和控制和信號合成等。
轉換公式:V = REF*(CODE/256)* (1+RNG)
V:實際電壓;REF:基準電壓;CODE:輸入8位數據;RNG:范圍。
TLC5620的接口時序如下列圖所示:
圖1 LOAD控制更新(LDAC為低電平)
圖2 LDAC控制更新(LDAC為低電平)
圖3 LOAD控制更新(使用8位串行數據,LOAD為低電平)
圖4 LDAC控制更新(使用8位串行數據)
如圖1所示:當LOAD為高電平時,數據在CLK的下降沿被鎖存至DATA,只要所有數據被鎖存,則將LOAD拉低,將數據從串行輸入寄存器傳送到所選擇的DAC。
如圖2所示:串行編程期間LDAC為高電平,數據在LOAD為低電平時進行鎖存,當LDAC變為低電平時傳送至DAC輸出。如圖3、4所示:輸入數據最高位(MSB)在前,數據傳輸使用兩個8個時鐘周期。
在本設計中運用的是圖1的工作時序:
數據通道選擇:
RNG:控制DAC輸出范圍。當RNG為低時,輸出范圍在基準電壓和GND之間;當RNG為高時,輸出范圍為兩倍的基準電壓和GND。
本設計驅動TLC5620將輸入的數字量轉換為實際的模擬量(電壓),通過四個按鍵控制四路輸出的電壓變化,每按一次,電壓值也隨之上升,同時在數碼管上也依次顯示相應的值(依次為A1,A0,RNG,輸入DATA)。本設計采用的開發板的基準電壓為2.5V。設計架構圖如下所示:
key_test模塊通過四個按鍵輸入的值,組合輸出兩個數據,11位的wr_data是TLC_DA模塊解碼所需的數據。20位的out_data是seg_num模塊數碼管顯示所需的數據。
設計代碼:
key_test模塊代碼如下:
0 module key_test( //按鍵控制模塊
1 //端口信號:模塊的輸入輸出接口
2 input clk, //50MHZ
3 input rst_n, //低電平復位
4 input [3:0] key, //四個按鍵組合信號
5
6 output [10:0] wr_data, //輸出一幀數據,為DA模塊的輸入數字量
7 output [19:0] out_data //輸出數碼管顯示數據
8 );
9
10 //計數器時鐘分頻
11 reg [30:0] cnt;
12 reg clk_r; //分頻時鐘:在消除抖動的時鐘頻率下進行按鍵的檢測
13 always@(posedge clk or negedge rst_n) //按鍵消抖,時間為0.2s進行一次檢測
14 if(!rst_n)
15 begin
16 cnt <= 0;
17 clk_r <= 0;
18 end
19 else if(cnt < 30'd1000_0000)
20 cnt <= cnt + 1'b1;
21 else
22 begin
23 cnt <= 0;
24 clk_r <= ~clk_r;
25 end
26
27 //按鍵為低電平有效,當檢測到對應按鍵之后,相應數值加1,并顯示相應的通道
28 reg [7:0] data; //按鍵輸入數據
29 reg [1:0] channel; //通道選擇
30 reg [7:0] key1,key2,key3,key4; //相應四個按鍵
31 always@(posedge clk_r or negedge rst_n )
32 if(!rst_n)
33 begin
34 key1 <= 8'h00;
35 key2 <= 8'h00;
36 key3 <= 8'h00;
37 key4 <= 8'h00;
38 data <= 8'h00;
39 channel <= 2'b00;
40 end
41 else
42 case(key)
43 4'b1110 : begin //按鍵1:選擇通道A,且輸入數字量加1
44 channel <= 2'b00;
45 key1 <= key1 + 1'b1;
46 data <= key1;
47 end
48 4'b1101 : begin //按鍵2:選擇通道B,且輸入數字量加1
49 channel <= 2'b01;
50 key2 <= key2 + 1'b1;
51 data <= key2;
52 end
53 4'b1011 : begin //按鍵3:選擇通道C,且輸入數字量加1
54 channel <= 2'b10;
55 key3 <= key3 + 1'b1;
56 data <= key3;
57 end
58 4'b0111 : begin //按鍵4:選擇通道D,且輸入數字量加1
59 channel <= 2'b11;
60 key4 <= key4 + 1'b1;
61 data <= key4;
62 end
63 default :;
64 endcase
65
66 //用賦值語句將需要的數據組合起來,在此例中將RNG默認為1
67 assign wr_data = {channel,1'b1,data}; assignout_data={{3'b000,channel[1]},3'b000,channel[0],4'h1,data};
68
69 endmodule
TLC_DA模塊代碼如下:
0 module TLC_DA( //輸入數字量轉換為模擬量模塊,本實驗用TLC5620
1 //端口信號:模塊的輸入輸出接口
2 input clk, //系統時鐘50MHz
3 input rst_n, //低電平復位
4 input [10:0] data_in, //輸入一幀數據
5 output da_data, //串行數據接口
6 output da_clk, //串行時鐘接口
7 output reg da_ldac, //更新控制信號
8 output reg da_load //串行加載控制接口
9 );
10
11 //計數器時鐘分頻:根據芯片內部的時序要求進行分頻
12 reg [30:0] cnt;
13 wire da_clk_r; //TLC 5620內部時鐘信號
14 always@(posedge clk or negedge rst_n) //滿足協議中的時鐘要求,在TLC 5620中時鐘要求不大于1MHZ
15 if(!rst_n)
16 cnt <= 6'd0;
17 else
18 cnt <= cnt + 1'b1;
19
20 assign da_clk_r = cnt[5];
21
22 //接收時序狀態機
23 reg [2:0] state;
24 reg [3:0] cnt_da;
25 reg da_data_r;
26 reg da_data_en; //限定da_data,da_clk的有效區域
27 always@(posedge da_clk_r or negedge rst_n)
28 if(!rst_n)
29 begin
30 state <= 0;
31 cnt_da <= 0;
32 da_load <= 1;
33 da_ldac <= 0;
34 da_data_r <= 1'b1;
35 da_data_en <= 0;
36 end
37 else
38 case(state)
39 0: state <= 1;
40 1: begin
41 da_load <= 1;
42 da_data_en <= 1;
43 if(cnt_da <= 10)
44 begin
45 cnt_da <= cnt_da + 1'b1;
46 case(cnt_da)
47 0: da_data_r <= data_in[10];
48 1: da_data_r <= data_in[9];
49 2: da_data_r <= data_in[8];
50 3: da_data_r <= data_in[7];
51 4: da_data_r <= data_in[6];
52 5: da_data_r <= data_in[5];
53 6: da_data_r <= data_in[4];
54 7: da_data_r <= data_in[3];
55 8: da_data_r <= data_in[2];
56 9: da_data_r <= data_in[1];
57 10: da_data_r <= data_in[0];
58 default:;
59 endcase
60 state <= 1;
61 end
62 else
63 begin
64 cnt_da <= 0;
65 state <= 2;
66 da_data_en <= 0;
67 end
68 end
69 2: begin
70 da_load <= 0;
71 state <= 3;
72 end
73 3: begin
74 da_load <= 1;
75 state <= 0;
76 end
77 default: state <= 0;
78 endcase
79
80 assign da_data = (da_data_en) ? da_data_r : 1'b1;
81 assign da_clk = (da_data_en)?da_clk_r : 1'b0;
82
83 endmodule
seg_num模塊代碼如下:
0 module seg_num( //數碼管顯示模塊:選擇數碼管0-4共5個數碼管顯示{A1,A0,RNG,DATA}
1 //端口信號:模塊的輸入輸出接口
2 input clk, //系統時鐘50MHz
3 input rst_n, //低電平復位
4 input [19:0] data_in, //20位輸入數據
5
6 output reg [7:0] seg, //數碼管段選
7 output reg [2:0] sel //數碼管位選
8 );
9
10 //通過查找表的方式,將相應位的數碼管與數據的相應位一一對應
11 reg [3:0] num;
12 always@(*)
13 case(sel)
14 4: num = data_in[3:0]; //第五個數碼管顯示數據的低四位[3:0]
15 3: num = data_in[7:4]; //第四個數碼管顯示數據的低四位[7:4]
16 2: num = data_in[11:8]; //第三個數碼管顯示數據的低四位[11:8]
17 1: num = data_in[15:12]; //第二個數碼管顯示數據的低四位[15:12]
18 0: num = data_in[19:16]; //第一個數碼管顯示數據的低四位[19:16]
19 default:;
20 endcase
21
22 //通過查找表的方式,將數據與數碼管的顯示方式一一對應
23 always@(*)
24 case(num)
25 0: seg <= 8'hC0; //8'b1100_0000
26 1: seg <= 8'hF9; //8'b1111_1001
27 2: seg <= 8'hA4; //8'b1010_0100
28 3: seg <= 8'hB0; //8'b1011_0000
29 4: seg <= 8'h99; //8'b1001_1001
30 5: seg <= 8'h92; //8'b1001_0010
31 6: seg <= 8'h82; //8'b1000_0010
32 7: seg <= 8'hF8; //8'b1111_1000
33 8: seg <= 8'h80; //8'b1000_0000
34 9: seg <= 8'h90; //8'b1001_0000
35 default:seg <= 8'hFF; //8'b1111_1111
36 endcase
37
38 //計數器時鐘分頻:用cnt第10位的變化作為分頻時鐘
39 reg [23:0] cnt;
40 always@(posedge clk or negedge rst_n)
41 if(!rst_n)
42 cnt <= 4'd0;
43 else
44 cnt <= cnt + 1'b1;
45 //在分頻時鐘下,數碼管的0-5位依次循環
46 always@(posedge cnt[10] or negedge rst_n) //分頻時鐘為2^10/50M
47 if(!rst_n)
48 sel <= 0;
49 else if(sel < 4)
50 sel <= sel + 1'b1;
51 else
52 sel <= 0;
53
54 endmodule
top頂層模塊代碼如下:
0 module top( //頂層模塊:將各個模塊組合
1 //外部接口
2 input clk, //系統時鐘50MHz
3 input rst_n, //低電平復位
4 input [3:0] key, //四個按鍵組成的按鍵信號,低電平有效
5
6 output da_data,//DA串行接口數據
7 output da_clk, //DA串行接口時鐘
8 output da_ldac,//DA更新信號
9 output da_load, //DA串行接口加載控制信號
10 output [7:0] seg, //數碼管段選
11 output [2:0] sel //數碼管位選
12 );
13 //內部信號:模塊內部的接口信號,比如模塊TLC_DA的輸出信號data_in,通過內部信號r_data與模塊key_test的輸入信號wr_data相連
14 wire [10:0] wr_data;
15 wire [19:0] out_data; //輸入給數碼管的數據
16
17 //模塊例化
18 TLC_DA TLC_DA_inst( //輸入數字量轉換為模擬量模塊
19 .clk(clk),
20 .rst_n(rst_n),
21 .da_clk(da_clk),
22 .da_data(da_data),
23 .da_ldac(da_ldac),
24 .da_load(da_load),
25 .data_in(wr_data)
26 );
27
28 key_test key_test_inst( //按鍵控制模塊
29 .clk(clk),
30 .rst_n(rst_n),
31 .key(key),
32 .wr_data(wr_data),
33 .out_data(out_data)
34 );
35
36 seg_num seg_num_inst( //數碼管顯示模塊
37 .clk(clk),
38 .rst_n(rst_n),
39 .data_in(out_data),
40 .seg(seg),
41 .sel(sel)
42 );
43
44 endmodule
test頂層模塊測試代碼:
0 `timescale 1 ns/ 1 ns //設置仿真時間單位與精度分別為1ns/1ns
1 //若設為`timescale 1ns/1ps (#200 就是延時200 ns; 1ps就是仿真的精度)
2 module test; //測試模塊:主要是將激勵信號賦相應的值,仿真之后觀察波形,驗證與實際功能是否一樣
3
4 //端口信號定義,激勵信號為reg型
5 reg clk;
6 reg rst_n;
7 reg [3:0] key;
8 wire [7:0] seg;
9 wire [2:0] sel;
10
11 //模塊例化
12 top top(
13 .clk(clk),
14 .rst_n(rst_n),
15 .key(key),
16 .seg(seg),
17 .sel(sel)
18 );
19
20 //初始化激勵,以及給相應激勵賦值
21 initial
22 begin
23 clk = 0;rst_n = 0; key = 4'b1111; //在復位階段,將激勵賦初值
24
25 #200 rst_n = 1; //在延時200ns后將復位信號置為1
26
27 //實現按鍵1開,關
28 #500000 key = 4'b1110;
29 #500000 key = 4'b1111;
30
31 end
32
33 always #10 clk = ~clk; //時鐘的表示,即每隔10ns翻轉一次,一個周期的時間即為20ns,時鐘為1/20ns = 50MHZ
34
35 endmodule
仿真圖:
由于仿真時間原因,這里只測試按鍵1按下時的數碼管顯示,顯示為00100,表示通道A,RNG為1,輸入數字量為00。之后實際下板驗證,用萬用表也可測出輸入數字量對應的電壓值。