
今天給大俠帶來基于FPGA的數字電壓表設計,附源碼,獲取源碼,請在“FPGA技術江湖”公眾號內回復“數字電壓表設計源碼”,可獲取源碼文件。話不多說,上貨。

模數轉換器,又稱A/D轉換器,簡稱ADC,通常是指一個將模擬信號轉換為抗干擾性更強的數字信號的電子器件。一般的ADC是將一個輸入電壓信號轉換為一個輸出的數字信號。由于數字信號本身不具有實際意義,僅僅表示一個相對大小,故任何一個ADC都需要一個參考模擬量作為轉換標準。比較常見的參考標準為最大的可轉換信號大小,而輸出的數字量則表示輸入信號相對于參考信號的大小。本設計則通過對模數轉換芯片(TLC549)的采樣控制,實現一個簡易的數字電壓表。

TLC549典型的配置電路如下圖所示:

TLC549的端口描述如下:

TLC549是一個8位的串行模數轉換器,A/D轉換時間最大為17us,最大轉換速率為4MHz。下圖為TLC549的訪問時序,從圖中可以看出,TLC549的使用只需對外接輸入輸出時鐘(I/O CLK)和芯片選擇(/CS)、輸入的模擬信號(ANALOG IN)的控制。

分析時序圖可知:當片選信號(/CS)拉低時,ADC前一次的轉換數據(A)的最高位A7立即出現在數據線DATA OUT上,之后的數據在時鐘I/O CLOCK的下降沿改變,可在I/O CLOCK的上升沿讀取數據。轉換時,/CS要置為高電平。在設計操作時,要注意Tsu(CS)、Tconv、Twh(CS)和I/O CLOCK的頻率這幾個參數。Tsu(CS)為CS拉低到I/O CLOCK第一個時鐘到來的時間,至少要1.4us;Twh(CS)為ADC的轉換時鐘,不超過17us,Tconv的值也不超過17us;I/O CLOCK為 1.1MHz。其他參數可參考數據手冊。
由于ADC是8位的,所以采樣的電壓值為:
V =(D*Vref)/256
其中V為采樣的電壓值;D為ADC轉換后讀取的8位二進制數;Vref為參考電壓值,此處為2.5V。

本設計通過調節電位器RW1改變ADC的模擬輸入值,數據采樣讀取后由數碼管顯示,最后用萬用表測量輸入電壓,并與讀取在數碼管上的數據(單位為mV)作比較。設計的架構圖如下:

設計架構圖對應端口的功能描述表:

tlc549_Driver模塊采用序列機實現接口訪問時序,并且產生1MHz的ADC_Clk和采集到ADC_data;Control模塊,將采集到的ADC數據(ADC_data)換算成對應的電壓值,并經過二進制到BCD轉換以后傳送到數碼管;DIG_LED_DRIVE模塊負責數碼管的驅動,將傳遞過來的數據顯示出來。

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; //系統50MHz時鐘輸入input Rst_n;//全局復位input En; //ADC轉換使能,高電平有效input ADC_Din;//ADC串行數據輸入output reg ADC_Clk; //ADC時鐘信號輸出output reg ADC_Cs_n;//ADC片選信號輸出output reg Get_Flag;//數據轉換完成標志output reg [7:0] Data;//ADC轉換以后的電壓值reg [10:0] Cnt1; //系統時鐘計數器reg [7:0] data_tmp;//數據寄存器//系統時鐘上升沿計數always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)Cnt1 <= 11'd0;else if(!En)Cnt1 <= 11'd0;else if(Cnt1 == 11'd1310)Cnt1 <= 11'd0;elseCnt1 <= Cnt1 + 1'b1;endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginADC_Clk <= 1'b0;ADC_Cs_n <= 1'b1;data_tmp <= 8'd0;Data <= 8'd0;endelse if(En)begincase(Cnt1)1 : ADC_Cs_n <= 1'b0; //1~71(Tsu)71 : begin ADC_Clk <= 1; data_tmp[7] <= ADC_Din;end96 : ADC_Clk <= 0;121 : begin ADC_Clk <= 1; data_tmp[6] <= ADC_Din;end146 : ADC_Clk <= 0;171 : begin ADC_Clk <= 1; data_tmp[5] <= ADC_Din;end196 : ADC_Clk <= 0;221 : begin ADC_Clk <= 1; data_tmp[4] <= ADC_Din;end246 : ADC_Clk <= 0;271 : begin ADC_Clk <= 1; data_tmp[3] <= ADC_Din;end296 : ADC_Clk <= 0;321 : begin ADC_Clk <= 1; data_tmp[2] <= ADC_Din;end346 : ADC_Clk <= 0;371 : begin ADC_Clk <= 1; data_tmp[1] <= ADC_Din;end396 : ADC_Clk <= 0;421 : begin ADC_Clk <= 1; data_tmp[0] <= ADC_Din;end446 : begin ADC_Clk <= 0; ADC_Cs_n <= 1'b1; Get_Flag<=1;end447 : begin Data <= data_tmp; Get_Flag<=0; end //447~1310(Twh)1310: ;default:;endcaseendelsebeginADC_Cs_n <= 1'b1;ADC_Clk <= 1'b0;endendendmodule
module Control(Clk,Rst_n,Get_Flag,ADC_data,seg_data);input Clk;//系統時鐘輸入input Rst_n;//系統復位input Get_Flag;//ADC采集數據完成標志input [7:0]ADC_data;//ADC采集數據輸入output reg [23:0]seg_data;//數碼管待顯示數據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)beginif(!Rst_n)tenvalue<=0;else if(Get_Flag)//新的數據采集完成,可以進行計算tenvalue<=(ADC_data*100*25)/256;end//二進制轉BCD值always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginqianwei<=0;baiwei<=0;shiwei<=0;gewei<=0;endelsebeginqianwei<=tenvalue/1000; //2baiwei<=(tenvalue/100)%10; //5shiwei<=(tenvalue/10)%10; //0gewei<=tenvalue%10; //0endend//數碼管顯示數值always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)seg_data<=0;elseseg_data<={qianwei, //千位baiwei, //百位shiwei, //十位gewei, //個位8'hFF //空閑};endendmodule
/*數碼管掃描模塊,位選為外部74hc138譯碼器進行控制*//*仿真時請將本文件設置為頂層,并在代碼中根據相應注釋中的內容選擇cnt1_MAX = 24*/module DIG_LED_DRIVE(Clk,Rst_n,Data,Dig_Led_seg,Dig_Led_sel);input Clk; //系統時鐘輸入input Rst_n; //系統復位input [23:0]Data;//待顯示數據output [7:0]Dig_Led_seg;//數碼管段選output [2:0]Dig_Led_sel;//數碼管位選parameter system_clk = 50_000_000;localparam cnt1_MAX = 24;/*仿真的時候使用,板級驗證時請注釋掉*///localparam cnt1_MAX = system_clk/1000/2-1;/*板級驗證的時候使用,仿真時請注釋掉*/reg [14 :0] cnt1; //分頻計數器reg clk_1K; //掃描時鐘,1KHzreg [2:0]sel_r; //數碼管位選reg [7:0]seg_r; //數碼管段選reg [3:0]disp_data; //單位顯示數據緩存//1KHz時鐘分頻計數器always@(posedge Clk)beginif(!Rst_n)cnt1<=0;elseif(cnt1==cnt1_MAX)cnt1<=0;else cnt1<=cnt1+1'b1;end//得到1KHz時鐘always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)clk_1K<=0;else if(cnt1==cnt1_MAX)clk_1K<=~clk_1K;end//位選信號控制always@(posedge clk_1K or negedge Rst_n)beginif(!Rst_n)sel_r<=3'd0;elseif(sel_r == 3'd3)sel_r<=3'd0;elsesel_r<=sel_r+1'b1;end//根據不同的數碼管位選擇不同的待顯示數據always@(*)beginif(!Rst_n)disp_data=4'd0;elsebegincase(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;endcaseendend//數據譯碼,將待顯示數據翻譯為符合數碼管顯示的編碼always@(*)beginif(!Rst_n)seg_r=8'hff;elsebegincase(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;endcaseendendassign Dig_Led_seg = seg_r;assign Dig_Led_sel = sel_r;endmodule

`timescale 1ns/1psmodule 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 beginClk = 1;Rst_n = 0;ADC_Din = 0;#200.1Rst_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;endAD_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

END
往期精選


FPGA技術江湖廣發江湖帖
無廣告純凈模式,給技術交流一片凈土,從初學小白到行業精英業界大佬等,從軍工領域到民用企業等,從通信、圖像處理到人工智能等各個方向應有盡有,QQ微信雙選,FPGA技術江湖打造最純凈最專業的技術交流學習平臺。
FPGA技術江湖微信交流群

加群主微信,備注姓名+公司/學校+崗位/專業進群
FPGA技術江湖QQ交流群

備注姓名+公司/學校+崗位/專業進群
