大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。
今天給大俠帶來基于FPGA的數(shù)模轉(zhuǎn)換(DA)設(shè)計(jì),附源碼,獲取源碼,請?jiān)凇癋PGA技術(shù)江湖”公眾號內(nèi)回復(fù)“?數(shù)模轉(zhuǎn)換設(shè)計(jì)源碼”,可獲取源碼文件。話不多說,上貨。
設(shè)計(jì)背景:
數(shù)模轉(zhuǎn)換器(Digital to Analog Converter)即DAC,是數(shù)字世界和模擬世界之間的橋梁。人類生活在模擬世界中,雖然數(shù)字器件及設(shè)備的比重日益增強(qiáng),但是DAC的發(fā)展仍是必不可少的。從航空航天、國防軍事到民用通信、多媒體、數(shù)字信號處理等都涉及到DAC應(yīng)用。DAC基本上由4個(gè)部分組成,即權(quán)電阻網(wǎng)絡(luò)、運(yùn)算放大器、基準(zhǔn)電源和模擬開關(guān)。它是一種將二進(jìn)制數(shù)字量形式的離散信號轉(zhuǎn)換成以參考電壓為基準(zhǔn)的模擬量的轉(zhuǎn)換器。
設(shè)計(jì)原理:
本設(shè)計(jì)采用串行數(shù)/模轉(zhuǎn)換芯片TLC5620,TLC5620是一個(gè)擁有四路輸出的數(shù)/模轉(zhuǎn)換器,時(shí)鐘頻率最大可達(dá)到1MHz。TLC5620芯片接口如下:
該芯片主要有以下特點(diǎn):四通道8位電壓輸出DA轉(zhuǎn)換器、5V單電源供電、串行接口、高阻抗基準(zhǔn)輸入、可編程1或2輸出范圍、同時(shí)更新設(shè)備、內(nèi)部上電復(fù)位、低功耗、半緩沖輸出。該芯片主要應(yīng)用于:可編程電源、數(shù)字控制放大器/誤差器、移動通信、自動測試設(shè)備、研發(fā)過程檢測和控制和信號合成等。
芯片接口功能表如下:
轉(zhuǎn)換公式:V = REF*(CODE/256)* (1+RNG)
V:實(shí)際電壓;REF:基準(zhǔn)電壓;CODE:輸入8位數(shù)據(jù);RNG:范圍。
TLC5620的接口時(shí)序如下列圖所示:
圖1 LOAD控制更新(LDAC為低電平)
圖2 LDAC控制更新(LDAC為低電平)
圖3 LOAD控制更新(使用8位串行數(shù)據(jù),LOAD為低電平)
圖4 LDAC控制更新(使用8位串行數(shù)據(jù))
如圖1所示:當(dāng)LOAD為高電平時(shí),數(shù)據(jù)在CLK的下降沿被鎖存至DATA,只要所有數(shù)據(jù)被鎖存,則將LOAD拉低,將數(shù)據(jù)從串行輸入寄存器傳送到所選擇的DAC。如圖2所示:串行編程期間LDAC為高電平,數(shù)據(jù)在LOAD為低電平時(shí)進(jìn)行鎖存,當(dāng)LDAC變?yōu)榈碗娖綍r(shí)傳送至DAC輸出。如圖3、4所示:輸入數(shù)據(jù)最高位(MSB)在前,數(shù)據(jù)傳輸使用兩個(gè)8個(gè)時(shí)鐘周期。
在本設(shè)計(jì)中運(yùn)用的是圖1的工作時(shí)序:
數(shù)據(jù)通道選擇:
RNG:控制DAC輸出范圍。當(dāng)RNG為低時(shí),輸出范圍在基準(zhǔn)電壓和GND之間;當(dāng)RNG為高時(shí),輸出范圍為兩倍的基準(zhǔn)電壓和GND。
設(shè)計(jì)架構(gòu)
本設(shè)計(jì)驅(qū)動TLC5620將輸入的數(shù)字量轉(zhuǎn)換為實(shí)際的模擬量(電壓),通過四個(gè)按鍵控制四路輸出的電壓變化,每按一次,電壓值也隨之上升,同時(shí)在數(shù)碼管上也依次顯示相應(yīng)的值(依次為A1,A0,RNG,輸入DATA)。本設(shè)計(jì)采用的開發(fā)板的基準(zhǔn)電壓為2.5V。設(shè)計(jì)架構(gòu)圖如下所示:
key_test模塊通過四個(gè)按鍵輸入的值,組合輸出兩個(gè)數(shù)據(jù),11位的wr_data是TLC_DA模塊解碼所需的數(shù)據(jù)。20位的out_data是seg_num模塊數(shù)碼管顯示所需的數(shù)據(jù)。
設(shè)計(jì)代碼
頂層top模塊代碼如下:
module top( //頂層模塊:將各個(gè)模塊組合
//外部接口
input clk, //系統(tǒng)時(shí)鐘50MHz
input rst_n, //低電平復(fù)位
input [3:0] key, //四個(gè)按鍵組成的按鍵信號,低電平有效
output da_data,//DA串行接口數(shù)據(jù)
output da_clk, //DA串行接口時(shí)鐘
output da_ldac,//DA更新信號
output da_load, //DA串行接口加載控制信號
output [7:0] seg, //數(shù)碼管段選
output [2:0] sel //數(shù)碼管位選
);
//內(nèi)部信號:模塊內(nèi)部的接口信號,比如模塊TLC_DA的輸出信號data_in,通過內(nèi)部信號r_data與模塊key_test的輸入信號wr_data相連
wire [10:0] wr_data;
wire [19:0] out_data; //輸入給數(shù)碼管的數(shù)據(jù)
//模塊例化
TLC_DA TLC_DA_inst( //輸入數(shù)字量轉(zhuǎn)換為模擬量模塊
.clk(clk),
.rst_n(rst_n),
.da_clk(da_clk),
.da_data(da_data),
.da_ldac(da_ldac),
.da_load(da_load),
.data_in(wr_data)
);
key_test key_test_inst( //按鍵控制模塊
.clk(clk),
.rst_n(rst_n),
.key(key),
.wr_data(wr_data),
.out_data(out_data)
);
seg_num seg_num_inst( //數(shù)碼管顯示模塊
.clk(clk),
.rst_n(rst_n),
.data_in(out_data),
.seg(seg),
.sel(sel)
);
endmodule
key_test模塊代碼如下:
module key_test( //按鍵控制模塊
//端口信號:模塊的輸入輸出接口
input clk, //50MHZ
input rst_n, //低電平復(fù)位
input [3:0] key, //四個(gè)按鍵組合信號
output [10:0] wr_data, //輸出一幀數(shù)據(jù),為DA模塊的輸入數(shù)字量
output [19:0] out_data //輸出數(shù)碼管顯示數(shù)據(jù)
);
//計(jì)數(shù)器時(shí)鐘分頻
reg [30:0] cnt;
reg clk_r; //分頻時(shí)鐘:在消除抖動的時(shí)鐘頻率下進(jìn)行按鍵的檢測
always@(posedge clk or negedge rst_n) //按鍵消抖,時(shí)間為0.2s進(jìn)行一次檢測
if(!rst_n)
begin
cnt <= 0;
clk_r <= 0;
end
else if(cnt < 30'd1000_0000)
cnt <= cnt + 1'b1;
else
begin
cnt <= 0;
clk_r <= ~clk_r;
end
//按鍵為低電平有效,當(dāng)檢測到對應(yīng)按鍵之后,相應(yīng)數(shù)值加1,并顯示相應(yīng)的通道
reg [7:0] data; //按鍵輸入數(shù)據(jù)
reg [1:0] channel; //通道選擇
reg [7:0] key1,key2,key3,key4; //相應(yīng)四個(gè)按鍵
always@(posedge clk_r or negedge rst_n )
if(!rst_n)
begin
key1 <= 8'h00;
key2 <= 8'h00;
key3 <= 8'h00;
key4 <= 8'h00;
data <= 8'h00;
channel <= 2'b00;
end
else
case(key)
4'b1110 : begin //按鍵1:選擇通道A,且輸入數(shù)字量加1
channel <= 2'b00;
key1 <= key1 + 1'b1;
data <= key1;
end
4'b1101 : begin //按鍵2:選擇通道B,且輸入數(shù)字量加1
channel <= 2'b01;
key2 <= key2 + 1'b1;
data <= key2;
end
4'b1011 : begin //按鍵3:選擇通道C,且輸入數(shù)字量加1
channel <= 2'b10;
key3 <= key3 + 1'b1;
data <= key3;
end
4'b0111 : begin //按鍵4:選擇通道D,且輸入數(shù)字量加1
channel <= 2'b11;
key4 <= key4 + 1'b1;
data <= key4;
end
default :;
endcase
//用賦值語句將需要的數(shù)據(jù)組合起來,在此例中將RNG默認(rèn)為1
assign wr_data = {channel,1'b1,data};
assign out_data = {{3'b000,channel[1]},3'b000,channel[0],4'h1,data};
endmodule
TLC_DA模塊代碼如下:
module TLC_DA( //輸入數(shù)字量轉(zhuǎn)換為模擬量模塊,本實(shí)驗(yàn)用TLC5620
//端口信號:模塊的輸入輸出接口
input clk, //系統(tǒng)時(shí)鐘50MHz
input rst_n, //低電平復(fù)位
input [10:0] data_in, //輸入一幀數(shù)據(jù)
output da_data, //串行數(shù)據(jù)接口
output da_clk, //串行時(shí)鐘接口
output reg da_ldac, //更新控制信號
output reg da_load //串行加載控制接口
);
//計(jì)數(shù)器時(shí)鐘分頻:根據(jù)芯片內(nèi)部的時(shí)序要求進(jìn)行分頻
reg [30:0] cnt;
wire da_clk_r; //TLC 5620內(nèi)部時(shí)鐘信號
always@(posedge clk or negedge rst_n) //滿足協(xié)議中的時(shí)鐘要求,在TLC 5620中時(shí)鐘要求不大于1MHZ
if(!rst_n)
cnt <= 6'd0;
else
cnt <= cnt + 1'b1;
assign da_clk_r = cnt[5];
//接收時(shí)序狀態(tài)機(jī)
reg [2:0] state;
reg [3:0] cnt_da;
reg da_data_r;
reg da_data_en; //限定da_data,da_clk的有效區(qū)域
always@(posedge da_clk_r or negedge rst_n)
if(!rst_n)
begin
state <= 0;
cnt_da <= 0;
da_load <= 1;
da_ldac <= 0;
da_data_r <= 1'b1;
da_data_en <= 0;
end
else
case(state)
0: state <= 1;
1: begin
da_load <= 1;
da_data_en <= 1;
if(cnt_da <= 10)
begin
cnt_da <= cnt_da + 1'b1;
case(cnt_da)
0: da_data_r <= data_in[10];
1: da_data_r <= data_in[9];
2: da_data_r <= data_in[8];
3: da_data_r <= data_in[7];
4: da_data_r <= data_in[6];
5: da_data_r <= data_in[5];
6: da_data_r <= data_in[4];
7: da_data_r <= data_in[3];
8: da_data_r <= data_in[2];
9: da_data_r <= data_in[1];
10: da_data_r <= data_in[0];
default:;
endcase
state <= 1;
end
else
begin
cnt_da <= 0;
state <= 2;
da_data_en <= 0;
end
end
2: begin
da_load <= 0;
state <= 3;
end
3: begin
da_load <= 1;
state <= 0;
end
default: state <= 0;
endcase
assign da_data = (da_data_en) ? da_data_r : 1'b1;
assign da_clk = (da_data_en)?da_clk_r : 1'b0;
endmodule
seg_num模塊代碼如下:
module seg_num( //數(shù)碼管顯示模塊:選擇數(shù)碼管0-4共5個(gè)數(shù)碼管顯示{A1,A0,RNG,DATA}
//端口信號:模塊的輸入輸出接口
input clk, //系統(tǒng)時(shí)鐘50MHz
input rst_n, //低電平復(fù)位
input [19:0] data_in, //20位輸入數(shù)據(jù)
output reg [7:0] seg, //數(shù)碼管段選
output reg [2:0] sel //數(shù)碼管位選
);
//通過查找表的方式,將相應(yīng)位的數(shù)碼管與數(shù)據(jù)的相應(yīng)位一一對應(yīng)
reg [3:0] num;
always@(*)
case(sel)
4: num = data_in[3:0]; //第五個(gè)數(shù)碼管顯示數(shù)據(jù)的低四位[3:0]
3: num = data_in[7:4]; //第四個(gè)數(shù)碼管顯示數(shù)據(jù)的低四位[7:4]
2: num = data_in[11:8]; //第三個(gè)數(shù)碼管顯示數(shù)據(jù)的低四位[11:8]
1: num = data_in[15:12]; //第二個(gè)數(shù)碼管顯示數(shù)據(jù)的低四位[15:12]
0: num = data_in[19:16]; //第一個(gè)數(shù)碼管顯示數(shù)據(jù)的低四位[19:16]
default:;
endcase
//通過查找表的方式,將數(shù)據(jù)與數(shù)碼管的顯示方式一一對應(yīng)
always@(*)
case(num)
0: seg <= 8'hC0; //8'b1100_0000
1: seg <= 8'hF9; //8'b1111_1001
2: seg <= 8'hA4; //8'b1010_0100
3: seg <= 8'hB0; //8'b1011_0000
4: seg <= 8'h99; //8'b1001_1001
5: seg <= 8'h92; //8'b1001_0010
6: seg <= 8'h82; //8'b1000_0010
7: seg <= 8'hF8; //8'b1111_1000
8: seg <= 8'h80; //8'b1000_0000
9: seg <= 8'h90; //8'b1001_0000
default:seg <= 8'hFF; //8'b1111_1111
endcase
//計(jì)數(shù)器時(shí)鐘分頻:用cnt第10位的變化作為分頻時(shí)鐘
reg [23:0] cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
//在分頻時(shí)鐘下,數(shù)碼管的0-5位依次循環(huán)
always@(posedge cnt[10] or negedge rst_n) //分頻時(shí)鐘為2^10/50M
if(!rst_n)
sel <= 0;
else if(sel < 4)
sel <= sel + 1'b1;
else
sel <= 0;
仿真測試
test頂層模塊測試代碼:
`timescale 1 ns/ 1 ns //設(shè)置仿真時(shí)間單位與精度分別為1ns/1ns
//若設(shè)為`timescale 1ns/1ps (#200 就是延時(shí)200 ns; 1ps就是仿真的精度)
module test; //測試模塊:主要是將激勵信號賦相應(yīng)的值,仿真之后觀察波形,驗(yàn)證與實(shí)際功能是否一樣
//端口信號定義,激勵信號為reg型
reg clk;
reg rst_n;
reg [3:0] key;
wire [7:0] seg;
wire [2:0] sel;
//模塊例化
top top(
.clk(clk),
.rst_n(rst_n),
.key(key),
.seg(seg),
.sel(sel)
);
//初始化激勵,以及給相應(yīng)激勵賦值
initial
begin
clk = 0;rst_n = 0; key = 4'b1111; //在復(fù)位階段,將激勵賦初值
#200 rst_n = 1; //在延時(shí)200ns后將復(fù)位信號置為1
//實(shí)現(xiàn)按鍵1開,關(guān)
#500000 key = 4'b1110;
#500000 key = 4'b1111;
end
always #10 clk = ~clk; //時(shí)鐘的表示,即每隔10ns翻轉(zhuǎn)一次,一個(gè)周期的時(shí)間即為20ns,時(shí)鐘為1/20ns = 50MHZ
endmodule
仿真圖如下:
由于仿真時(shí)間原因,這里只測試按鍵1按下時(shí)的數(shù)碼管顯示,顯示為00100,表示通道A,RNG為1,輸入數(shù)字量為00。之后實(shí)際下板驗(yàn)證,用萬用表也可測出輸入數(shù)字量對應(yīng)的電壓值。