設(shè)計背景
模數(shù)轉(zhuǎn)換器,又稱A/D轉(zhuǎn)換器,簡稱ADC,通常是指一個將模擬信號轉(zhuǎn)換為抗干擾性更強的數(shù)字信號的電子器件。一般的ADC是將一個輸入電壓信號轉(zhuǎn)換為一個輸出的數(shù)字信號。由于數(shù)字信號本身不具有實際意義,僅僅表示一個相對大小,故任何一個ADC都需要一個參考模擬量作為轉(zhuǎn)換標(biāo)準(zhǔn)。比較常見的參考標(biāo)準(zhǔn)為最大的可轉(zhuǎn)換信號大小,而輸出的數(shù)字量則表示輸入信號相對于參考信號的大小。本設(shè)計則通過對模數(shù)轉(zhuǎn)換芯片(TLC549)的采樣控制,實現(xiàn)一個簡易的數(shù)字電壓表。
設(shè)計原理
TLC549典型的配置電路如下圖所示:
TLC549的端口描述如下:
TLC549是一個8位的串行模數(shù)轉(zhuǎn)換器,A/D轉(zhuǎn)換時間最大為17us,最大轉(zhuǎn)換速率為4MHz。下圖為TLC549的訪問時序,從圖中可以看出,TLC549的使用只需對外接輸入輸出時鐘(I/O CLK)和芯片選擇(/CS)、輸入的模擬信號(ANALOG IN)的控制。
分析時序圖可知:當(dāng)片選信號(/CS)拉低時,ADC前一次的轉(zhuǎn)換數(shù)據(jù)(A)的最高位A7立即出現(xiàn)在數(shù)據(jù)線DATA OUT上,之后的數(shù)據(jù)在時鐘I/O CLOCK的下降沿改變,可在I/O CLOCK的上升沿讀取數(shù)據(jù)。轉(zhuǎn)換時,/CS要置為高電平。在設(shè)計操作時,要注意Tsu(CS)、Tconv、Twh(CS)和I/O CLOCK的頻率這幾個參數(shù)。Tsu(CS)為CS拉低到I/O CLOCK第一個時鐘到來的時間,至少要1.4us;Twh(CS)為ADC的轉(zhuǎn)換時鐘,不超過17us,Tconv的值也不超過17us;I/O CLOCK為 1.1MHz。其他參數(shù)可參考數(shù)據(jù)手冊。
由于ADC是8位的,所以采樣的電壓值為:
V =(D*Vref)/256
其中V為采樣的電壓值;D為ADC轉(zhuǎn)換后讀取的8位二進(jìn)制數(shù);Vref為參考電壓值,此處為2.5V。
設(shè)計架構(gòu)
本設(shè)計通過調(diào)節(jié)電位器RW1改變ADC的模擬輸入值,數(shù)據(jù)采樣讀取后由數(shù)碼管顯示,最后用萬用表測量輸入電壓,并與讀取在數(shù)碼管上的數(shù)據(jù)(單位為mV)作比較。設(shè)計的架構(gòu)圖如下:?
設(shè)計架構(gòu)圖對應(yīng)端口的功能描述表:
tlc549_Driver模塊采用序列機實現(xiàn)接口訪問時序,并且產(chǎn)生1MHz的ADC_Clk和采集到ADC_data;Control模塊,將采集到的ADC數(shù)據(jù)(ADC_data)換算成對應(yīng)的電壓值,并經(jīng)過二進(jìn)制到BCD轉(zhuǎn)換以后傳送到數(shù)碼管;DIG_LED_DRIVE模塊負(fù)責(zé)數(shù)碼管的驅(qū)動,將傳遞過來的數(shù)據(jù)顯示出來。
設(shè)計代碼
AD_TLC549頂層模塊代碼:
module AD_TLC549(Clk,Rst_n,ADC_Din,ADC_Clk,ADC_Cs_n,Dig_Led_sel,Dig_Led_seg); input Clk; input Rst_n; input ADC_Din; output ADC_Clk; output ADC_Cs_n; output [2:0]Dig_Led_sel; output [7:0]Dig_Led_seg; wire Get_Flag; wire [7:0]ADC_data; wire [23:0]seg_data; tlc549_Driver tlc549_Driver( .Clk(Clk), .Rst_n(Rst_n), .En(1'b1), .ADC_Din(ADC_Din), .ADC_Clk(ADC_Clk), .ADC_Cs_n(ADC_Cs_n), .Data(ADC_data), .Get_Flag(Get_Flag) ); Control Control( .Clk(Clk), .Rst_n(Rst_n), .Get_Flag(Get_Flag), .ADC_data(ADC_data), .seg_data(seg_data) ); DIG_LED_DRIVE DIG_LED_DRIVE( .Clk(Clk), .Rst_n(Rst_n), .Data(seg_data), .Dig_Led_seg(Dig_Led_seg), .Dig_Led_sel(Dig_Led_sel) ); endmodule
tlc549_Driver模塊代碼:
module tlc549_Driver (Clk,Rst_n,En,ADC_Din,ADC_Clk,ADC_Cs_n,Data,Get_Flag); input Clk; //系統(tǒng)50MHz時鐘輸入 input Rst_n;//全局復(fù)位 input En; //ADC轉(zhuǎn)換使能,高電平有效 input ADC_Din;//ADC串行數(shù)據(jù)輸入 output reg ADC_Clk; //ADC時鐘信號輸出 output reg ADC_Cs_n;//ADC片選信號輸出 output reg Get_Flag;//數(shù)據(jù)轉(zhuǎn)換完成標(biāo)志 output reg [7:0] Data;//ADC轉(zhuǎn)換以后的電壓值 reg [10:0] Cnt1; //系統(tǒng)時鐘計數(shù)器 reg [7:0] data_tmp;//數(shù)據(jù)寄存器 //系統(tǒng)時鐘上升沿計數(shù) always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Cnt1 <= 11'd0; else if(!En) Cnt1 <= 11'd0; else if(Cnt1 == 11'd1310) Cnt1 <= 11'd0; else Cnt1 <= Cnt1 + 1'b1; end always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) begin ADC_Clk <= 1'b0; ADC_Cs_n <= 1'b1; data_tmp <= 8'd0; Data <= 8'd0; end else if(En) begin case(Cnt1) 1 : ADC_Cs_n <= 1'b0; //1~71(Tsu) 71 : begin ADC_Clk <= 1; data_tmp[7] <= ADC_Din;end 96 : ADC_Clk <= 0; 121 : begin ADC_Clk <= 1; data_tmp[6] <= ADC_Din;end 146 : ADC_Clk <= 0; 171 : begin ADC_Clk <= 1; data_tmp[5] <= ADC_Din;end 196 : ADC_Clk <= 0; 221 : begin ADC_Clk <= 1; data_tmp[4] <= ADC_Din;end 246 : ADC_Clk <= 0; 271 : begin ADC_Clk <= 1; data_tmp[3] <= ADC_Din;end 296 : ADC_Clk <= 0; 321 : begin ADC_Clk <= 1; data_tmp[2] <= ADC_Din;end 346 : ADC_Clk <= 0; 371 : begin ADC_Clk <= 1; data_tmp[1] <= ADC_Din;end 396 : ADC_Clk <= 0; 421 : begin ADC_Clk <= 1; data_tmp[0] <= ADC_Din;end 446 : begin ADC_Clk <= 0; ADC_Cs_n <= 1'b1; Get_Flag<=1;end 447 : begin Data <= data_tmp; Get_Flag<=0; end //447~1310(Twh) 1310: ; default:; endcase end else begin ADC_Cs_n <= 1'b1; ADC_Clk <= 1'b0; end end endmodule? Control模塊代碼:
module Control(Clk,Rst_n,Get_Flag,ADC_data,seg_data); input Clk;//系統(tǒng)時鐘輸入 input Rst_n;//系統(tǒng)復(fù)位 input Get_Flag;//ADC采集數(shù)據(jù)完成標(biāo)志 input [7:0]ADC_data;//ADC采集數(shù)據(jù)輸入 output reg [23:0]seg_data;//數(shù)碼管待顯示數(shù)據(jù) reg [3:0]qianwei; //千位 reg [3:0]baiwei; //百位 reg [3:0]shiwei; //十位 reg [3:0]gewei; //個位 reg [15:0]tenvalue;//采樣的電壓值 //采集電壓值計算 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) tenvalue<=0; else if(Get_Flag)//新的數(shù)據(jù)采集完成,可以進(jìn)行計算 tenvalue<=(ADC_data*100*25)/256; end //二進(jìn)制轉(zhuǎn)BCD值 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) begin qianwei<=0; baiwei<=0; shiwei<=0; gewei<=0; end else begin qianwei<=tenvalue/1000; //2 baiwei<=(tenvalue/100)%10; //5 shiwei<=(tenvalue/10)%10; //0 gewei<=tenvalue%10; //0 end end //數(shù)碼管顯示數(shù)值 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) seg_data<=0; else seg_data<={ qianwei, //千位 baiwei, //百位 shiwei, //十位 gewei, //個位 8'hFF //空閑 }; end endmodule? DIG_LED_DRIVE模塊代碼:
/*數(shù)碼管掃描模塊,位選為外部74hc138譯碼器進(jìn)行控制*/ /*仿真時請將本文件設(shè)置為頂層,并在代碼中根據(jù)相應(yīng)注釋中的內(nèi)容選擇cnt1_MAX = 24*/ module DIG_LED_DRIVE(Clk,Rst_n,Data,Dig_Led_seg,Dig_Led_sel); input Clk; //系統(tǒng)時鐘輸入 input Rst_n; //系統(tǒng)復(fù)位 input [23:0]Data;//待顯示數(shù)據(jù) output [7:0]Dig_Led_seg;//數(shù)碼管段選 output [2:0]Dig_Led_sel;//數(shù)碼管位選 parameter system_clk = 50_000_000; localparam cnt1_MAX = 24;/*仿真的時候使用,板級驗證時請注釋掉*/ //localparam cnt1_MAX = system_clk/1000/2-1;/*板級驗證的時候使用,仿真時請注釋掉*/ reg [14 :0] cnt1; //分頻計數(shù)器 reg clk_1K; //掃描時鐘,1KHz reg [2:0]sel_r; //數(shù)碼管位選 reg [7:0]seg_r; //數(shù)碼管段選 reg [3:0]disp_data; //單位顯示數(shù)據(jù)緩存 //1KHz時鐘分頻計數(shù)器 always@(posedge Clk) begin if(!Rst_n)cnt1<=0; else if(cnt1==cnt1_MAX)cnt1<=0; else cnt1<=cnt1+1'b1; end //得到1KHz時鐘 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) clk_1K<=0; else if(cnt1==cnt1_MAX) clk_1K<=~clk_1K; end //位選信號控制 always@(posedge clk_1K or negedge Rst_n) begin if(!Rst_n) sel_r<=3'd0; else if(sel_r == 3'd3) sel_r<=3'd0; else sel_r<=sel_r+1'b1; end //根據(jù)不同的數(shù)碼管位選擇不同的待顯示數(shù)據(jù) always@(*) begin if(!Rst_n) disp_data=4'd0; else begin case(sel_r) 3'd0:disp_data=Data[23:20]; 3'd1:disp_data=Data[19:16]; 3'd2:disp_data=Data[15:12]; 3'd3:disp_data=Data[11:8]; 3'd4:disp_data=Data[7:4]; 3'd5:disp_data=Data[3:0]; default :disp_data=4'd0; endcase end end //數(shù)據(jù)譯碼,將待顯示數(shù)據(jù)翻譯為符合數(shù)碼管顯示的編碼 always@(*) begin if(!Rst_n) seg_r=8'hff; else begin case(disp_data) 4'd0: seg_r=8'hc0; 4'd1: seg_r=8'hf9; 4'd2: seg_r=8'ha4; 4'd3: seg_r=8'hb0; 4'd4: seg_r=8'h99; 4'd5: seg_r=8'h92; 4'd6: seg_r=8'h82; 4'd7: seg_r=8'hf8; 4'd8: seg_r=8'h80; 4'd9: seg_r=8'h90; 4'd10: seg_r=8'h88; 4'd11: seg_r=8'h83; 4'd12: seg_r=8'hc6; 4'd13: seg_r=8'ha1; 4'd14: seg_r=8'h86; 4'd15: seg_r=8'h8e; default : seg_r=8'hff; endcase end end assign Dig_Led_seg = seg_r; assign Dig_Led_sel = sel_r; endmodule? 仿真測試
AD_TLC549_tb頂層測試代碼如下:
?
`timescale 1ns/1ps module AD_TLC549_tb; reg Clk; reg Rst_n; reg ADC_Din; wire ADC_Clk; wire ADC_Cs_n; wire [2:0] Dig_Led_sel; wire [7:0] Dig_Led_seg; initial begin Clk = 1; Rst_n = 0; ADC_Din = 0; #200.1 Rst_n = 1; #1400 ADC_Din=1; //aa #1000 ADC_Din=0; #1000 ADC_Din=1; #1000 ADC_Din=0; #1000 ADC_Din=1; #1000 ADC_Din=0; #1000 ADC_Din=1; #1000 ADC_Din=0; #17000 #1400 ADC_Din=1; //98 #1000 ADC_Din=0; #1000 ADC_Din=0; #1000 ADC_Din=1; #1000 ADC_Din=1; #1000 ADC_Din=0; #1000 ADC_Din=0; #1000 ADC_Din=0; //#20000 $stop; end AD_TLC549 AD_TLC549_dut( .Clk(Clk), .Rst_n(Rst_n), .ADC_Din(ADC_Din), .ADC_Clk(ADC_Clk), .ADC_Cs_n(ADC_Cs_n), .Dig_Led_sel(Dig_Led_sel), .Dig_Led_seg(Dig_Led_seg) ); always #10 Clk = ~Clk; endmodule? 仿真圖如下所示:
? 觀察仿真圖,實現(xiàn)了數(shù)據(jù)的采集,并正確顯示,下板驗證結(jié)果也達(dá)到了設(shè)計的預(yù)期效果。
審核編輯:黃飛
?
評論
查看更多