• 正文
    • 設(shè)計(jì)背景
    • 設(shè)計(jì)原理
    • 設(shè)計(jì)架構(gòu)
    • 設(shè)計(jì)代碼
    • 仿真測(cè)試
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

源碼系列:基于FPGA的數(shù)字電壓表(AD)設(shè)計(jì)

01/04 08:55
1529
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

大俠好,歡迎來到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ù)期效果。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

任何技術(shù)的學(xué)習(xí)就好比一個(gè)江湖,對(duì)于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。