大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。
今天給大俠帶來基于FPGA的數(shù)字電壓表設(shè)計(jì),附源碼,獲取源碼,請(qǐng)?jiān)凇癋PGA技術(shù)江湖”公眾號(hào)內(nèi)回復(fù)“數(shù)字電壓表設(shè)計(jì)源碼”,可獲取源碼文件。話不多說,上貨。
設(shè)計(jì)背景
模數(shù)轉(zhuǎn)換器,又稱A/D轉(zhuǎn)換器,簡(jiǎn)稱ADC,通常是指一個(gè)將模擬信號(hào)轉(zhuǎn)換為抗干擾性更強(qiáng)的數(shù)字信號(hào)的電子器件。一般的ADC是將一個(gè)輸入電壓信號(hào)轉(zhuǎn)換為一個(gè)輸出的數(shù)字信號(hào)。由于數(shù)字信號(hào)本身不具有實(shí)際意義,僅僅表示一個(gè)相對(duì)大小,故任何一個(gè)ADC都需要一個(gè)參考模擬量作為轉(zhuǎn)換標(biāo)準(zhǔn)。比較常見的參考標(biāo)準(zhǔn)為最大的可轉(zhuǎn)換信號(hào)大小,而輸出的數(shù)字量則表示輸入信號(hào)相對(duì)于參考信號(hào)的大小。本設(shè)計(jì)則通過對(duì)模數(shù)轉(zhuǎn)換芯片(TLC549)的采樣控制,實(shí)現(xiàn)一個(gè)簡(jiǎn)易的數(shù)字電壓表。
設(shè)計(jì)原理
TLC549典型的配置電路如下圖所示:
TLC549的端口描述如下:
TLC549是一個(gè)8位的串行模數(shù)轉(zhuǎn)換器,A/D轉(zhuǎn)換時(shí)間最大為17us,最大轉(zhuǎn)換速率為4MHz。下圖為TLC549的訪問時(shí)序,從圖中可以看出,TLC549的使用只需對(duì)外接輸入輸出時(shí)鐘(I/O CLK)和芯片選擇(/CS)、輸入的模擬信號(hào)(ANALOG IN)的控制。
分析時(shí)序圖可知:當(dāng)片選信號(hào)(/CS)拉低時(shí),ADC前一次的轉(zhuǎn)換數(shù)據(jù)(A)的最高位A7立即出現(xiàn)在數(shù)據(jù)線DATA OUT上,之后的數(shù)據(jù)在時(shí)鐘I/O CLOCK的下降沿改變,可在I/O CLOCK的上升沿讀取數(shù)據(jù)。轉(zhuǎn)換時(shí),/CS要置為高電平。
在設(shè)計(jì)操作時(shí),要注意Tsu(CS)、Tconv、Twh(CS)和I/O CLOCK的頻率這幾個(gè)參數(shù)。Tsu(CS)為CS拉低到I/O CLOCK第一個(gè)時(shí)鐘到來的時(shí)間,至少要1.4us;Twh(CS)為ADC的轉(zhuǎn)換時(shí)鐘,不超過17us,Tconv的值也不超過17us;I/O CLOCK為 1.1MHz。其他參數(shù)可參考數(shù)據(jù)手冊(cè)。
由于ADC是8位的,所以采樣的電壓值為:
V =(D*Vref)/256
其中V為采樣的電壓值;D為ADC轉(zhuǎn)換后讀取的8位二進(jìn)制數(shù);Vref為參考電壓值,此處為2.5V。
設(shè)計(jì)架構(gòu)
本設(shè)計(jì)通過調(diào)節(jié)電位器RW1改變ADC的模擬輸入值,數(shù)據(jù)采樣讀取后由數(shù)碼管顯示,最后用萬用表測(cè)量輸入電壓,并與讀取在數(shù)碼管上的數(shù)據(jù)(單位為mV)作比較。設(shè)計(jì)的架構(gòu)圖如下:
設(shè)計(jì)架構(gòu)圖對(duì)應(yīng)端口的功能描述表:
tlc549_Driver模塊采用序列機(jī)實(shí)現(xiàn)接口訪問時(shí)序,并且產(chǎn)生1MHz的ADC_Clk和采集到ADC_data;Control模塊,將采集到的ADC數(shù)據(jù)(ADC_data)換算成對(duì)應(yīng)的電壓值,并經(jīng)過二進(jìn)制到BCD轉(zhuǎn)換以后傳送到數(shù)碼管;DIG_LED_DRIVE模塊負(fù)責(zé)數(shù)碼管的驅(qū)動(dòng),將傳遞過來的數(shù)據(jù)顯示出來。
設(shè)計(jì)代碼
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時(shí)鐘輸入
input Rst_n;//全局復(fù)位
input En; //ADC轉(zhuǎn)換使能,高電平有效
input ADC_Din;//ADC串行數(shù)據(jù)輸入
output reg ADC_Clk; //ADC時(shí)鐘信號(hào)輸出
output reg ADC_Cs_n;//ADC片選信號(hào)輸出
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í)鐘計(jì)數(shù)器
reg [7:0] data_tmp;//數(shù)據(jù)寄存器
//系統(tǒng)時(shí)鐘上升沿計(jì)數(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)時(shí)鐘輸入
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; //個(gè)位
reg [15:0]tenvalue;//采樣的電壓值
//采集電壓值計(jì)算
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
tenvalue<=0;
else if(Get_Flag)//新的數(shù)據(jù)采集完成,可以進(jìn)行計(jì)算
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, //個(gè)位
8'hFF //空閑
};
end
endmodule
DIG_LED_DRIVE模塊代碼:
/*數(shù)碼管掃描模塊,位選為外部74hc138譯碼器進(jìn)行控制*/
/*仿真時(shí)請(qǐng)將本文件設(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)時(shí)鐘輸入
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;/*仿真的時(shí)候使用,板級(jí)驗(yàn)證時(shí)請(qǐng)注釋掉*/
//localparam cnt1_MAX = system_clk/1000/2-1;/*板級(jí)驗(yàn)證的時(shí)候使用,仿真時(shí)請(qǐng)注釋掉*/
reg [14 :0] cnt1; //分頻計(jì)數(shù)器
reg clk_1K; //掃描時(shí)鐘,1KHz
reg [2:0]sel_r; //數(shù)碼管位選
reg [7:0]seg_r; //數(shù)碼管段選
reg [3:0]disp_data; //單位顯示數(shù)據(jù)緩存
//1KHz時(shí)鐘分頻計(jì)數(shù)器
always@(posedge Clk)
begin
if(!Rst_n)cnt1<=0;
else if(cnt1==cnt1_MAX)cnt1<=0;
else cnt1<=cnt1+1'b1;
end
//得到1KHz時(shí)鐘
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
clk_1K<=0;
else if(cnt1==cnt1_MAX)
clk_1K<=~clk_1K;
end
//位選信號(hào)控制
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
仿真測(cè)試
AD_TLC549_tb頂層測(cè)試代碼如下:
`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
仿真圖如下所示:
觀察仿真圖,實(shí)現(xiàn)了數(shù)據(jù)的采集,并正確顯示,下板驗(yàn)證結(jié)果也達(dá)到了設(shè)計(jì)的預(yù)期效果。