簡(jiǎn)易電壓表設(shè)計(jì)
實(shí)驗(yàn)任務(wù)
實(shí)驗(yàn)?zāi)康?/h4>在基礎(chǔ)數(shù)字電路實(shí)驗(yàn)部分我們已經(jīng)掌握了FPGA驅(qū)動(dòng)獨(dú)立數(shù)碼管的原理及方法,本實(shí)驗(yàn)主要學(xué)習(xí)模數(shù)轉(zhuǎn)換器ADC的相關(guān)知識(shí),串行(SPI接口)ADC芯片ADC081S101的驅(qū)動(dòng)設(shè)計(jì),同時(shí)學(xué)習(xí)二進(jìn)制數(shù)轉(zhuǎn)換BCD碼的設(shè)計(jì)方法。
本文引用地址:http://2s4d.com/article/202312/453845.htm設(shè)計(jì)框圖
根據(jù)前面的實(shí)驗(yàn)解析我們可以得知,該設(shè)計(jì)可以拆分成三個(gè)功能模塊實(shí)現(xiàn),
實(shí)驗(yàn)原理
ADC介紹
數(shù)字系統(tǒng),是用數(shù)字信號(hào)完成對(duì)數(shù)字量進(jìn)行算術(shù)運(yùn)算和邏輯運(yùn)算的電路稱(chēng)為數(shù)字電路,或數(shù)字系統(tǒng)。而我們生活的世界是模擬的,想要讓數(shù)字系統(tǒng)幫我們處理我們模擬世界的問(wèn)題,就需要一個(gè)橋梁來(lái)溝通數(shù)字系統(tǒng)和模擬系統(tǒng)。
模數(shù)轉(zhuǎn)換器即A/D轉(zhuǎn)換器,或簡(jiǎn)稱(chēng)ADC,通常是指一個(gè)將模擬信號(hào)轉(zhuǎn)變?yōu)閿?shù)字信號(hào)的電子元件。通常的模數(shù)轉(zhuǎn)換器是將一個(gè)輸入電壓信號(hào)轉(zhuǎn)換為一個(gè)輸出的數(shù)字信號(hào)。由于數(shù)字信號(hào)本身不具有實(shí)際意義,僅僅表示一個(gè)相對(duì)大小。故任何一個(gè)模數(shù)轉(zhuǎn)換器都需要一個(gè)參考模擬量作為轉(zhuǎn)換的標(biāo)準(zhǔn),比較常見(jiàn)的參考標(biāo)準(zhǔn)為最大的可轉(zhuǎn)換信號(hào)大小。而輸出的數(shù)字量則表示輸入信號(hào)相對(duì)于參考信號(hào)的大小。
數(shù)模轉(zhuǎn)換器,又稱(chēng)D/A轉(zhuǎn)換器,簡(jiǎn)稱(chēng)DAC,它是把數(shù)字量轉(zhuǎn)變成模擬的器件。D/A轉(zhuǎn)換器基本上由4個(gè)部分組成,即權(quán)電阻網(wǎng)絡(luò)、運(yùn)算放大器、基準(zhǔn)電源和模擬開(kāi)關(guān)。模數(shù)轉(zhuǎn)換器中一般都要用到數(shù)模轉(zhuǎn)換器,模數(shù)轉(zhuǎn)換器即A/D轉(zhuǎn)換器,簡(jiǎn)稱(chēng)ADC,它是把連續(xù)的模擬信號(hào)轉(zhuǎn)變?yōu)殡x散的數(shù)字信號(hào)的器件。
作為模擬系統(tǒng)與數(shù)字系統(tǒng)轉(zhuǎn)換的橋梁,ADC和DAC有很多參數(shù)指標(biāo)來(lái)標(biāo)識(shí)其性能:
上圖兩個(gè)都是8位ADC模型,分辨率為 2的8次方等于256,即將Vref分成256份,能夠分辨的模擬步進(jìn)為Vref / 256,量化數(shù)據(jù)N = 256 * Vin / Vref 。
ADC模塊電路連接
這里我們以STEP BaseBoard V3.0底板上的ADC模塊電路,其電路圖如下:
如ADC模塊電路所示,FPGA直接連接ADC081S101芯片的控制端,ADC有6個(gè)管腳,3腳Vin為VCC和Vref功能復(fù)用,即Vin = VCC = Vref。ADC前端是運(yùn)放電路LMV721,運(yùn)放模塊為電壓跟隨電路,再往前端是一個(gè)跳冒排針,用來(lái)選擇ADC采樣信號(hào)的來(lái)源,當(dāng)短路帽將1、2腳短路時(shí),ADC采集電位計(jì)電壓,當(dāng)短路帽將2、3腳短路時(shí),ADC采射頻端子或P4排針信號(hào)。本設(shè)計(jì)我們是采樣旋轉(zhuǎn)編碼器的電壓,所以需要用短路帽將1、2腳短路。
ADC模塊驅(qū)動(dòng)設(shè)計(jì)
前面我們了解ADC081S101芯片和FPGA之間連接有三根線(cs、clk、din),兼容SPI總線,SPI是串行外設(shè)接口(Serial Peripheral Interface)的縮寫(xiě)。SPI是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線(cs、sck、mosi、miso),事實(shí)上3根也可以(單向傳輸時(shí)),占用管腳少節(jié)約了芯片的管腳,同時(shí)為PCB的布局上節(jié)省空間,正是出于這種簡(jiǎn)單易用的特性,如今越來(lái)越多的芯片集成這種通信協(xié)議。
SPI設(shè)備分為主設(shè)備和從設(shè)備,設(shè)備之間共用sck、mosi和miso,另外每個(gè)從設(shè)備有一根cs線(不共用),通信在主設(shè)備和從設(shè)備之間進(jìn)行,從設(shè)備與從設(shè)備之間無(wú)法直接通信,主設(shè)備可以同時(shí)連接多個(gè)從設(shè)備,當(dāng)主設(shè)備和某個(gè)從設(shè)備通信時(shí),先控制該從設(shè)備cs信號(hào)拉低,然后通過(guò)sck、mosi和miso進(jìn)行數(shù)據(jù)傳輸。
為了讓SPI總線更加靈活應(yīng)用,SPI總線分為4種模式,由兩個(gè)參數(shù)控制:
SPI總線協(xié)議4種模式
ADC081S101管腳說(shuō)明表:
注:SDATA信號(hào)在SCLK的節(jié)拍下傳輸數(shù)據(jù),當(dāng)SCLK下降沿時(shí)SDATA更新數(shù)據(jù)輸出,當(dāng)驅(qū)動(dòng)程序編程時(shí)我們要在上升沿采樣數(shù)據(jù)可以得到穩(wěn)定的輸出。
ADC081S101串行通信時(shí)序如下圖:
注:
針對(duì)ADC081S101時(shí)序,我們用Verilog設(shè)計(jì)一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器值不同時(shí)完成不同操作,實(shí)現(xiàn)一次ADC采樣,程序?qū)崿F(xiàn)如下:
reg [7:0] cnt; //計(jì)數(shù)器
always @(posedge clk or negedge rst_n)
if(!rst_n) cnt <= 1'b0;
else if(cnt >= 8'd34) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
reg [7:0] data;always @(posedge clk or negedge rst_n)
if(!rst_n) begin
adc_cs <= HIGH; adc_clk <= HIGH;
end else case(cnt)
8'd0 : begin adc_cs <= HIGH;
adc_clk <= HIGH; end
8'd1 : begin adc_cs <= LOW;
adc_clk <= HIGH; end
8'd2,8'd4,8'd6,8'd8,8'd10,8'd12,8'd14,8'd16,
8'd18,8'd20,8'd22,8'd24,8'd26,8'd28,8'd30,8'd32:
begin adc_cs <= LOW;
adc_clk <= LOW;
end
8'd3 : begin adc_cs <= LOW;
adc_clk <= HIGH;
end //0
8'd5 : begin adc_cs <= LOW;
adc_clk <= HIGH;
end //1
8'd7 : begin adc_cs <= LOW;
adc_clk <= HIGH;
end //2
8'd9 : begin adc_cs <= LOW;
adc_clk <= HIGH;
data[7] <= adc_dat;
end //3
8'd11 : begin adc_cs <= LOW;
adc_clk <= HIGH; data[6] <= adc_dat;
end //4
8'd13 : begin adc_cs <= LOW;
adc_clk <= HIGH; data[5] <= adc_dat;
end //5
8'd15 : begin adc_cs <= LOW;
adc_clk <= HIGH;
data[4] <= adc_dat;
end //6
8'd17 : begin adc_cs <= LOW;
adc_clk <= HIGH; data[3] <= adc_dat;
end //7
8'd19 : begin adc_cs <= LOW;
adc_clk <= HIGH; data[2] <= adc_dat;
end //8
8'd21 : begin adc_cs <= LOW;
adc_clk <= HIGH;
data[1] <= adc_dat;
end //9
8'd23 : begin adc_cs <= LOW;
adc_clk <= HIGH; data[0] <= adc_dat;
end //10
8'd25 : begin adc_cs <= LOW;
adc_clk <= HIGH; adc_data <= data;
end //11
8'd27 : begin adc_cs <= LOW;
adc_clk <= HIGH; adc_done <= HIGH;
end //12
8'd29 : begin adc_cs <= LOW;
adc_clk <= HIGH; adc_done <= LOW;
end //13
8'd31 : begin adc_cs <= LOW;
adc_clk <= HIGH;
end //14
8'd33 : begin adc_cs <= LOW;
adc_clk <= HIGH;
end //15
8'd34 : begin adc_cs <= HIGH;
adc_clk <= HIGH;
end
default : begin adc_cs <= HIGH;
adc_clk <= HIGH;
end
endcase
到這我們就完成了串行ADC芯片ADC081S101的驅(qū)動(dòng)設(shè)計(jì),整個(gè)采樣周期用了35個(gè)系統(tǒng)時(shí)鐘,如果我們采用12MHz時(shí)鐘作為該模塊系統(tǒng)時(shí)鐘,采樣率Fs = 12M/35 = 343Ksps,ADC主頻Fsclk = 12 MHz /2 = 6MHz。
ADC081S101主頻及采樣率要求如下,按照要求我們當(dāng)前的主頻和采樣率不足,所以在使用該模塊時(shí),可以使用更高的時(shí)鐘(比如24MHz)以達(dá)到芯片的要求
注:時(shí)鐘頻率Fsclk,最小值為10MHz,最大值為20MHz,采樣率在500Ksps~1Msps
模塊接口如下:clk和rstn為系統(tǒng)時(shí)鐘及復(fù)位,adccs,adcclk和adcdat為ADC控制管腳,adcdata為ADC采樣數(shù)據(jù),adcdone產(chǎn)生一個(gè)脈沖對(duì)應(yīng)adc_data得到一個(gè)有效數(shù)據(jù)
module ADC081S101_driver
(
input clk, //系統(tǒng)時(shí)鐘
input rst_n, //系統(tǒng)復(fù)位,低有效
output reg adc_cs, //SPI總線CS
output reg adc_clk, //SPI總線SCK
input adc_dat, //SPI總線SDA
output reg adc_done, //ADC采樣完成標(biāo)志
output reg [7:0] adc_data //ADC采樣數(shù)據(jù)
);
系統(tǒng)總體實(shí)現(xiàn)
因?yàn)樾枰叩臅r(shí)鐘供ADC模塊使用,我們例化pll核得到24MHz時(shí)鐘,例化PLL的方法我們?cè)诨A(chǔ)數(shù)字電路實(shí)驗(yàn)部分已經(jīng)練習(xí)過(guò),這里就簡(jiǎn)單描述一下過(guò)程
打開(kāi)Tools菜單下的IP Catalog工具,依次找到Libraty → Basic Functions → Clocks; PLLs and Resets → PLL → ALTPLL,打開(kāi)ALTPLL彈出配置界面,配置inclk0輸入為12MHz,配置c0的時(shí)鐘輸出為24MHz,其他所有選項(xiàng)全部默認(rèn),點(diǎn)擊Finish完成pll的IP核例化。
在頂層模塊VoltageMeas中,同時(shí)例化pll模塊和ADC081S101driver模塊,并將pll的c0輸出與ADC081S101_driver模塊的clk連線。
Pll模塊和ADC081S101_driver模塊的連接程序?qū)崿F(xiàn)如下:
wire clk_24mhz,locked;
pll u1
(
.areset (!rst_n ), //pll模塊的復(fù)位為高有效
.inclk0 (clk ), //12MHz系統(tǒng)時(shí)鐘輸入
.c0 (clk_24mhz ), //24MHz時(shí)鐘輸出
.locked (locked ) //pll lock信號(hào)輸出
);
wire adc_done;
wire [7:0] adc_data;//使用I2C總線驅(qū)動(dòng)PCF8591的ADC功能,例化
ADC081S101_driver u2(.clk (clk_24mhz ), //系統(tǒng)時(shí)鐘
.rst_n (rst_n ), //系統(tǒng)復(fù)位,低有效
.adc_cs (adc_cs ), //SPI總線CS
.adc_clk (adc_clk ), //SPI總線SCK
.adc_dat (adc_dat ), //SPI總線SDA
.adc_done (adc_done ), //ADC采樣完成標(biāo)志
.adc_data (adc_data ) //ADC采樣數(shù)據(jù)
);
現(xiàn)在可以得到ADC采樣數(shù)據(jù)了,假設(shè)ADC模擬輸入電壓為3.3V,理論上我們得到的采樣數(shù)據(jù)adc_data應(yīng)該為8’hff,而電壓表最終顯示在數(shù)碼管上的數(shù)據(jù)應(yīng)該為3.3,我們?nèi)绾螌?’hff轉(zhuǎn)換成可以顯示的3.3數(shù)據(jù)呢?這就設(shè)計(jì)到ADC量化數(shù)據(jù)的逆向運(yùn)算了,
我們知道量化運(yùn)算 N = 256 * Vin / Vref,
那么逆向運(yùn)算為Vin = N * Vref / 256,其中Vref = 3.3V,所以Vin = N * 0.0129
所以我們需要用FPGA計(jì)算adc_data * 0.0129的結(jié)果,然后為了使用十進(jìn)制的顯示,先將結(jié)果進(jìn)行BCD轉(zhuǎn)碼,然后顯示在數(shù)碼管上。
將ADC采樣數(shù)據(jù)按規(guī)則轉(zhuǎn)換為電壓數(shù)據(jù)(乘以0.0129),這里我們直接乘以129,得到的數(shù)據(jù)經(jīng)過(guò)BCD轉(zhuǎn)碼后小數(shù)點(diǎn)左移4位即可,程序?qū)崿F(xiàn)如下:
wire [15:0] bin_code = adc_data * 16'd129;
將二進(jìn)制數(shù)轉(zhuǎn)換成BCD碼的形式,采用左移加三的算法(以8’hff為例): 1、左移要轉(zhuǎn)換的二進(jìn)制碼1位 2、左移之后,BCD碼分別置于百位、十位、個(gè)位 3、如果移位后所在的BCD碼列大于或等于5,則對(duì)該值加3 4、繼續(xù)左移的過(guò)程直至全部移位完成
二進(jìn)制轉(zhuǎn)BCD碼程序?qū)崿F(xiàn)如下:
reg [35:0] shift_reg;
always@(bin_code or rst_n)begin
shift_reg = {20'h0,bin_code};
if(!rst_n) bcd_code = 0;
else begin
repeat(16) begin //循環(huán)16次
//BCD碼各位數(shù)據(jù)作滿(mǎn)5加3操作,
if (shift_reg[19:16] >= 5) shift_reg[19:16] = shift_reg[19:16] + 2'b11;
if (shift_reg[23:20] >= 5) shift_reg[23:20] = shift_reg[23:20] + 2'b11;
if (shift_reg[27:24] >= 5) shift_reg[27:24] = shift_reg[27:24] + 2'b11;
if (shift_reg[31:28] >= 5) shift_reg[31:28] = shift_reg[31:28] + 2'b11;
if (shift_reg[35:32] >= 5) shift_reg[35:32] = shift_reg[35:32] + 2'b11;
shift_reg = shift_reg << 1;
end
bcd_code = shift_reg[35:16];
end
end
最后得到20位的數(shù)據(jù)輸出,每4位表示一個(gè)BCD碼,所以有5位有效數(shù)據(jù),這里我們還需要將小數(shù)點(diǎn)左移4位,計(jì)算出來(lái)的數(shù)應(yīng)該是X.XXXX伏特,1個(gè)整數(shù)位和4個(gè)小數(shù)位,核心板上只有兩個(gè)數(shù)碼管,取最高的兩個(gè)BCD碼顯示到數(shù)碼管X.X伏特,個(gè)位小數(shù)點(diǎn)點(diǎn)亮,分位小數(shù)點(diǎn)熄滅,程序?qū)崿F(xiàn)如下:
//個(gè)位數(shù)碼管模塊例化 Segment_led u4(.seg_dot (1'b1 ), //seg_dot input
.seg_data (bcd_code[19:16]), //seg_data input
.segment_led (seg_1 ) //MSB~LSB = SEG,DP,G,F,E,D,C,B,A
); //分位數(shù)碼管模塊例化
Segment_led u5(.seg_dot (1'b0 ), //seg_dot input
.seg_data (bcd_code[15:12]), //seg_data input
.segment_led (seg_2 ) //MSB~LSB = SEG,DP,G,F,E,D,C,B,A
);
綜合后的設(shè)計(jì)框圖如下:
實(shí)驗(yàn)步驟
實(shí)驗(yàn)現(xiàn)象
將程序下載到FPGA中,P3接口用短路帽將1、2腳短路,旋轉(zhuǎn)底板右上角的電位計(jì),觀察核心板數(shù)碼管變化,如果有萬(wàn)用表可以測(cè)量P3短路處的電壓,與數(shù)碼管顯示對(duì)比。
在基礎(chǔ)數(shù)字電路實(shí)驗(yàn)部分我們已經(jīng)掌握了FPGA驅(qū)動(dòng)獨(dú)立數(shù)碼管的原理及方法,本實(shí)驗(yàn)主要學(xué)習(xí)模數(shù)轉(zhuǎn)換器ADC的相關(guān)知識(shí),串行(SPI接口)ADC芯片ADC081S101的驅(qū)動(dòng)設(shè)計(jì),同時(shí)學(xué)習(xí)二進(jìn)制數(shù)轉(zhuǎn)換BCD碼的設(shè)計(jì)方法。
本文引用地址:http://2s4d.com/article/202312/453845.htm根據(jù)前面的實(shí)驗(yàn)解析我們可以得知,該設(shè)計(jì)可以拆分成三個(gè)功能模塊實(shí)現(xiàn),
數(shù)字系統(tǒng),是用數(shù)字信號(hào)完成對(duì)數(shù)字量進(jìn)行算術(shù)運(yùn)算和邏輯運(yùn)算的電路稱(chēng)為數(shù)字電路,或數(shù)字系統(tǒng)。而我們生活的世界是模擬的,想要讓數(shù)字系統(tǒng)幫我們處理我們模擬世界的問(wèn)題,就需要一個(gè)橋梁來(lái)溝通數(shù)字系統(tǒng)和模擬系統(tǒng)。
模數(shù)轉(zhuǎn)換器即A/D轉(zhuǎn)換器,或簡(jiǎn)稱(chēng)ADC,通常是指一個(gè)將模擬信號(hào)轉(zhuǎn)變?yōu)閿?shù)字信號(hào)的電子元件。通常的模數(shù)轉(zhuǎn)換器是將一個(gè)輸入電壓信號(hào)轉(zhuǎn)換為一個(gè)輸出的數(shù)字信號(hào)。由于數(shù)字信號(hào)本身不具有實(shí)際意義,僅僅表示一個(gè)相對(duì)大小。故任何一個(gè)模數(shù)轉(zhuǎn)換器都需要一個(gè)參考模擬量作為轉(zhuǎn)換的標(biāo)準(zhǔn),比較常見(jiàn)的參考標(biāo)準(zhǔn)為最大的可轉(zhuǎn)換信號(hào)大小。而輸出的數(shù)字量則表示輸入信號(hào)相對(duì)于參考信號(hào)的大小。
數(shù)模轉(zhuǎn)換器,又稱(chēng)D/A轉(zhuǎn)換器,簡(jiǎn)稱(chēng)DAC,它是把數(shù)字量轉(zhuǎn)變成模擬的器件。D/A轉(zhuǎn)換器基本上由4個(gè)部分組成,即權(quán)電阻網(wǎng)絡(luò)、運(yùn)算放大器、基準(zhǔn)電源和模擬開(kāi)關(guān)。模數(shù)轉(zhuǎn)換器中一般都要用到數(shù)模轉(zhuǎn)換器,模數(shù)轉(zhuǎn)換器即A/D轉(zhuǎn)換器,簡(jiǎn)稱(chēng)ADC,它是把連續(xù)的模擬信號(hào)轉(zhuǎn)變?yōu)殡x散的數(shù)字信號(hào)的器件。
作為模擬系統(tǒng)與數(shù)字系統(tǒng)轉(zhuǎn)換的橋梁,ADC和DAC有很多參數(shù)指標(biāo)來(lái)標(biāo)識(shí)其性能:
上圖兩個(gè)都是8位ADC模型,分辨率為 2的8次方等于256,即將Vref分成256份,能夠分辨的模擬步進(jìn)為Vref / 256,量化數(shù)據(jù)N = 256 * Vin / Vref 。
這里我們以STEP BaseBoard V3.0底板上的ADC模塊電路,其電路圖如下:
如ADC模塊電路所示,FPGA直接連接ADC081S101芯片的控制端,ADC有6個(gè)管腳,3腳Vin為VCC和Vref功能復(fù)用,即Vin = VCC = Vref。ADC前端是運(yùn)放電路LMV721,運(yùn)放模塊為電壓跟隨電路,再往前端是一個(gè)跳冒排針,用來(lái)選擇ADC采樣信號(hào)的來(lái)源,當(dāng)短路帽將1、2腳短路時(shí),ADC采集電位計(jì)電壓,當(dāng)短路帽將2、3腳短路時(shí),ADC采射頻端子或P4排針信號(hào)。本設(shè)計(jì)我們是采樣旋轉(zhuǎn)編碼器的電壓,所以需要用短路帽將1、2腳短路。
前面我們了解ADC081S101芯片和FPGA之間連接有三根線(cs、clk、din),兼容SPI總線,SPI是串行外設(shè)接口(Serial Peripheral Interface)的縮寫(xiě)。SPI是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線(cs、sck、mosi、miso),事實(shí)上3根也可以(單向傳輸時(shí)),占用管腳少節(jié)約了芯片的管腳,同時(shí)為PCB的布局上節(jié)省空間,正是出于這種簡(jiǎn)單易用的特性,如今越來(lái)越多的芯片集成這種通信協(xié)議。
SPI設(shè)備分為主設(shè)備和從設(shè)備,設(shè)備之間共用sck、mosi和miso,另外每個(gè)從設(shè)備有一根cs線(不共用),通信在主設(shè)備和從設(shè)備之間進(jìn)行,從設(shè)備與從設(shè)備之間無(wú)法直接通信,主設(shè)備可以同時(shí)連接多個(gè)從設(shè)備,當(dāng)主設(shè)備和某個(gè)從設(shè)備通信時(shí),先控制該從設(shè)備cs信號(hào)拉低,然后通過(guò)sck、mosi和miso進(jìn)行數(shù)據(jù)傳輸。
為了讓SPI總線更加靈活應(yīng)用,SPI總線分為4種模式,由兩個(gè)參數(shù)控制:
SPI總線協(xié)議4種模式
ADC081S101管腳說(shuō)明表:
注:SDATA信號(hào)在SCLK的節(jié)拍下傳輸數(shù)據(jù),當(dāng)SCLK下降沿時(shí)SDATA更新數(shù)據(jù)輸出,當(dāng)驅(qū)動(dòng)程序編程時(shí)我們要在上升沿采樣數(shù)據(jù)可以得到穩(wěn)定的輸出。
ADC081S101串行通信時(shí)序如下圖:
注:
針對(duì)ADC081S101時(shí)序,我們用Verilog設(shè)計(jì)一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器值不同時(shí)完成不同操作,實(shí)現(xiàn)一次ADC采樣,程序?qū)崿F(xiàn)如下:
reg [7:0] cnt; //計(jì)數(shù)器 always @(posedge clk or negedge rst_n) if(!rst_n) cnt <= 1'b0; else if(cnt >= 8'd34) cnt <= 1'b0; else cnt <= cnt + 1'b1; reg [7:0] data;always @(posedge clk or negedge rst_n) if(!rst_n) begin adc_cs <= HIGH; adc_clk <= HIGH; end else case(cnt) 8'd0 : begin adc_cs <= HIGH; adc_clk <= HIGH; end 8'd1 : begin adc_cs <= LOW; adc_clk <= HIGH; end 8'd2,8'd4,8'd6,8'd8,8'd10,8'd12,8'd14,8'd16, 8'd18,8'd20,8'd22,8'd24,8'd26,8'd28,8'd30,8'd32: begin adc_cs <= LOW; adc_clk <= LOW; end 8'd3 : begin adc_cs <= LOW; adc_clk <= HIGH; end //0 8'd5 : begin adc_cs <= LOW; adc_clk <= HIGH; end //1 8'd7 : begin adc_cs <= LOW; adc_clk <= HIGH; end //2 8'd9 : begin adc_cs <= LOW; adc_clk <= HIGH; data[7] <= adc_dat; end //3 8'd11 : begin adc_cs <= LOW; adc_clk <= HIGH; data[6] <= adc_dat; end //4 8'd13 : begin adc_cs <= LOW; adc_clk <= HIGH; data[5] <= adc_dat; end //5 8'd15 : begin adc_cs <= LOW; adc_clk <= HIGH; data[4] <= adc_dat; end //6 8'd17 : begin adc_cs <= LOW; adc_clk <= HIGH; data[3] <= adc_dat; end //7 8'd19 : begin adc_cs <= LOW; adc_clk <= HIGH; data[2] <= adc_dat; end //8 8'd21 : begin adc_cs <= LOW; adc_clk <= HIGH; data[1] <= adc_dat; end //9 8'd23 : begin adc_cs <= LOW; adc_clk <= HIGH; data[0] <= adc_dat; end //10 8'd25 : begin adc_cs <= LOW; adc_clk <= HIGH; adc_data <= data; end //11 8'd27 : begin adc_cs <= LOW; adc_clk <= HIGH; adc_done <= HIGH; end //12 8'd29 : begin adc_cs <= LOW; adc_clk <= HIGH; adc_done <= LOW; end //13 8'd31 : begin adc_cs <= LOW; adc_clk <= HIGH; end //14 8'd33 : begin adc_cs <= LOW; adc_clk <= HIGH; end //15 8'd34 : begin adc_cs <= HIGH; adc_clk <= HIGH; end default : begin adc_cs <= HIGH; adc_clk <= HIGH; end endcase
到這我們就完成了串行ADC芯片ADC081S101的驅(qū)動(dòng)設(shè)計(jì),整個(gè)采樣周期用了35個(gè)系統(tǒng)時(shí)鐘,如果我們采用12MHz時(shí)鐘作為該模塊系統(tǒng)時(shí)鐘,采樣率Fs = 12M/35 = 343Ksps,ADC主頻Fsclk = 12 MHz /2 = 6MHz。
ADC081S101主頻及采樣率要求如下,按照要求我們當(dāng)前的主頻和采樣率不足,所以在使用該模塊時(shí),可以使用更高的時(shí)鐘(比如24MHz)以達(dá)到芯片的要求
注:時(shí)鐘頻率Fsclk,最小值為10MHz,最大值為20MHz,采樣率在500Ksps~1Msps
模塊接口如下:clk和rstn為系統(tǒng)時(shí)鐘及復(fù)位,adccs,adcclk和adcdat為ADC控制管腳,adcdata為ADC采樣數(shù)據(jù),adcdone產(chǎn)生一個(gè)脈沖對(duì)應(yīng)adc_data得到一個(gè)有效數(shù)據(jù)
module ADC081S101_driver ( input clk, //系統(tǒng)時(shí)鐘 input rst_n, //系統(tǒng)復(fù)位,低有效 output reg adc_cs, //SPI總線CS output reg adc_clk, //SPI總線SCK input adc_dat, //SPI總線SDA output reg adc_done, //ADC采樣完成標(biāo)志 output reg [7:0] adc_data //ADC采樣數(shù)據(jù) );
因?yàn)樾枰叩臅r(shí)鐘供ADC模塊使用,我們例化pll核得到24MHz時(shí)鐘,例化PLL的方法我們?cè)诨A(chǔ)數(shù)字電路實(shí)驗(yàn)部分已經(jīng)練習(xí)過(guò),這里就簡(jiǎn)單描述一下過(guò)程
打開(kāi)Tools菜單下的IP Catalog工具,依次找到Libraty → Basic Functions → Clocks; PLLs and Resets → PLL → ALTPLL,打開(kāi)ALTPLL彈出配置界面,配置inclk0輸入為12MHz,配置c0的時(shí)鐘輸出為24MHz,其他所有選項(xiàng)全部默認(rèn),點(diǎn)擊Finish完成pll的IP核例化。
在頂層模塊VoltageMeas中,同時(shí)例化pll模塊和ADC081S101driver模塊,并將pll的c0輸出與ADC081S101_driver模塊的clk連線。
Pll模塊和ADC081S101_driver模塊的連接程序?qū)崿F(xiàn)如下:
wire clk_24mhz,locked; pll u1 ( .areset (!rst_n ), //pll模塊的復(fù)位為高有效 .inclk0 (clk ), //12MHz系統(tǒng)時(shí)鐘輸入 .c0 (clk_24mhz ), //24MHz時(shí)鐘輸出 .locked (locked ) //pll lock信號(hào)輸出 ); wire adc_done; wire [7:0] adc_data;//使用I2C總線驅(qū)動(dòng)PCF8591的ADC功能,例化 ADC081S101_driver u2(.clk (clk_24mhz ), //系統(tǒng)時(shí)鐘 .rst_n (rst_n ), //系統(tǒng)復(fù)位,低有效 .adc_cs (adc_cs ), //SPI總線CS .adc_clk (adc_clk ), //SPI總線SCK .adc_dat (adc_dat ), //SPI總線SDA .adc_done (adc_done ), //ADC采樣完成標(biāo)志 .adc_data (adc_data ) //ADC采樣數(shù)據(jù) );
現(xiàn)在可以得到ADC采樣數(shù)據(jù)了,假設(shè)ADC模擬輸入電壓為3.3V,理論上我們得到的采樣數(shù)據(jù)adc_data應(yīng)該為8’hff,而電壓表最終顯示在數(shù)碼管上的數(shù)據(jù)應(yīng)該為3.3,我們?nèi)绾螌?’hff轉(zhuǎn)換成可以顯示的3.3數(shù)據(jù)呢?這就設(shè)計(jì)到ADC量化數(shù)據(jù)的逆向運(yùn)算了,
我們知道量化運(yùn)算 N = 256 * Vin / Vref,
那么逆向運(yùn)算為Vin = N * Vref / 256,其中Vref = 3.3V,所以Vin = N * 0.0129
所以我們需要用FPGA計(jì)算adc_data * 0.0129的結(jié)果,然后為了使用十進(jìn)制的顯示,先將結(jié)果進(jìn)行BCD轉(zhuǎn)碼,然后顯示在數(shù)碼管上。
將ADC采樣數(shù)據(jù)按規(guī)則轉(zhuǎn)換為電壓數(shù)據(jù)(乘以0.0129),這里我們直接乘以129,得到的數(shù)據(jù)經(jīng)過(guò)BCD轉(zhuǎn)碼后小數(shù)點(diǎn)左移4位即可,程序?qū)崿F(xiàn)如下:
wire [15:0] bin_code = adc_data * 16'd129;
將二進(jìn)制數(shù)轉(zhuǎn)換成BCD碼的形式,采用左移加三的算法(以8’hff為例): 1、左移要轉(zhuǎn)換的二進(jìn)制碼1位 2、左移之后,BCD碼分別置于百位、十位、個(gè)位 3、如果移位后所在的BCD碼列大于或等于5,則對(duì)該值加3 4、繼續(xù)左移的過(guò)程直至全部移位完成
二進(jìn)制轉(zhuǎn)BCD碼程序?qū)崿F(xiàn)如下:
reg [35:0] shift_reg; always@(bin_code or rst_n)begin shift_reg = {20'h0,bin_code}; if(!rst_n) bcd_code = 0; else begin repeat(16) begin //循環(huán)16次 //BCD碼各位數(shù)據(jù)作滿(mǎn)5加3操作, if (shift_reg[19:16] >= 5) shift_reg[19:16] = shift_reg[19:16] + 2'b11; if (shift_reg[23:20] >= 5) shift_reg[23:20] = shift_reg[23:20] + 2'b11; if (shift_reg[27:24] >= 5) shift_reg[27:24] = shift_reg[27:24] + 2'b11; if (shift_reg[31:28] >= 5) shift_reg[31:28] = shift_reg[31:28] + 2'b11; if (shift_reg[35:32] >= 5) shift_reg[35:32] = shift_reg[35:32] + 2'b11; shift_reg = shift_reg << 1; end bcd_code = shift_reg[35:16]; end end
最后得到20位的數(shù)據(jù)輸出,每4位表示一個(gè)BCD碼,所以有5位有效數(shù)據(jù),這里我們還需要將小數(shù)點(diǎn)左移4位,計(jì)算出來(lái)的數(shù)應(yīng)該是X.XXXX伏特,1個(gè)整數(shù)位和4個(gè)小數(shù)位,核心板上只有兩個(gè)數(shù)碼管,取最高的兩個(gè)BCD碼顯示到數(shù)碼管X.X伏特,個(gè)位小數(shù)點(diǎn)點(diǎn)亮,分位小數(shù)點(diǎn)熄滅,程序?qū)崿F(xiàn)如下:
//個(gè)位數(shù)碼管模塊例化 Segment_led u4(.seg_dot (1'b1 ), //seg_dot input .seg_data (bcd_code[19:16]), //seg_data input .segment_led (seg_1 ) //MSB~LSB = SEG,DP,G,F,E,D,C,B,A ); //分位數(shù)碼管模塊例化 Segment_led u5(.seg_dot (1'b0 ), //seg_dot input .seg_data (bcd_code[15:12]), //seg_data input .segment_led (seg_2 ) //MSB~LSB = SEG,DP,G,F,E,D,C,B,A );
綜合后的設(shè)計(jì)框圖如下:
將程序下載到FPGA中,P3接口用短路帽將1、2腳短路,旋轉(zhuǎn)底板右上角的電位計(jì),觀察核心板數(shù)碼管變化,如果有萬(wàn)用表可以測(cè)量P3短路處的電壓,與數(shù)碼管顯示對(duì)比。
評(píng)論