Verilog HDL數字設計教程(賀敬凱)第7章_第1頁
Verilog HDL數字設計教程(賀敬凱)第7章_第2頁
Verilog HDL數字設計教程(賀敬凱)第7章_第3頁
Verilog HDL數字設計教程(賀敬凱)第7章_第4頁
Verilog HDL數字設計教程(賀敬凱)第7章_第5頁
已閱讀5頁,還剩232頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

7.1跑馬燈控制器的設計7.28位數碼掃描顯示電路的設計

7.3數控分頻器的設計

7.4樂曲硬件演奏電路的設計

7.5數字跑表和數字鐘的設計

7.6用VerilogHDL狀態機實現A/D采樣控制電路

7.7交通控制器的設計

7.8空調控制器的設計

7.9飲料自動售賣機的設計

7.10小結

習題7

7.1跑馬燈控制器的設計

1.設計要求共8個LED燈,連成一排。要求實現幾種燈的組合顯示。具體要求如下:

(1)模式1:先奇數燈,即第1、3、5、7燈亮0.25s,然后偶數燈,即第2、4、6、8燈亮0.25s,依次循環。

(2)模式2:按照1、2、3、4、5、6、7、8的順序依次點亮所有燈,間隔時間為0.25s;然后再按1/2/3/4/5/6/7/8的順序依次熄滅所有燈,間隔時間為0.25s。

(3)模式3:按照1/8、2/7、3/6、4/5的順序依次點亮所有燈,間隔時間為0.25s,每次同時點亮兩個燈;然后再按照1/8、2/7、3/6、4/5的順序依次熄滅所有燈,間隔時間為0.25s,每次同時熄滅兩個燈。

(4)以上模式可以選擇。

2.設計說明

LED燈與FPGA的連接如圖7-1所示,設計要求很容易實現,在此不再說明。圖7-18個LED燈與FPGA的連接圖使用兩個鍵進行模式選擇,兩個鍵有00、01、10、11四種組合,使用其中的三種組合,分別對應設計要求的三種情況。

3.設計模塊(包含模塊劃分)該設計比較簡單,僅用一個模塊即可,輸入端口為rst、clk、sel[1..0],輸出端口為led[7..0],其中sel用于模式選擇,led用于控制8個LED燈,如圖7-2所示。圖7-2跑馬燈模塊端口框圖4.代碼分析【例7-1】設計源碼。modulepaomadeng(rst,clk,sel,led);inputrst,clk;input[1:0]sel;output[7:0]led;reg[7:0]led;reg[7:0]led_r,led_r1;regcnt1,dir;reg[2:0]cnt2;reg[1:0]cnt3;always@(posedge

clk) begin

if(rst)begincnt1<=0;cnt2<=0;cnt3<=0;dir<=0;end else

case(sel) 2'b00: begin

led_r=8'b01010101; if(cnt1==0)led<=led_r; elseled<=led_r<<1; cnt1<=cnt1+1; end

2'b01: begin

if(!dir) begin if(cnt2==0)beginled_r=8'b00000001;led<=led_r;end elsebeginled<=(led<<1)+led_r;end if(cnt2==7)begindir<=~dir;end cnt2<=cnt2+1; endelse begin if(cnt2==0)beginled_r=8'b11111110;led<=led_r;end elsebeginled<=led<<1;end if(cnt2==7)begindir<=~dir;end cnt2<=cnt2+1; end end2'b11: begin

if(!dir) begin if(cnt3==0)beginled_r=8'b00000001;led_r1=8'b10000000;end elsebeginled_r=(led_r<<1)|led_r;led_r1=(led_r1>>1)|led_r1;end led<=led_r|led_r1; if(cnt3==3)begindir<=~dir;end cnt3<=cnt3+1; end else

begin if(cnt3==0)beginled_r=8'b11111110;led_r1=8'b01111111;end elsebeginled_r=led_r<<1;led_r1=led_r1>>1;end led<=led_r&led_r1; if(cnt3==3)begindir<=~dir;end cnt3<=cnt3+1;

end end default:;

endcase endendmodule程序說明:

(1)?case語句用于選擇三種模式。當case表達式中的sel為2'b00時選擇模式1,為2'b01時選擇模式2,為2'b11時選擇模式3。

(2)?cnt1、cnt2、cnt3分別為三種模式下的計數器,用于控制流水燈的轉換節奏。

(3)?dir用于方向控制,與cnt1、cnt2、cnt3的具體數值相關。

5.仿真分析仿真波形如圖7-3所示。該仿真波形僅列出了sel為2'b11時跑馬燈的運行情況。從圖中可以看出,燈的運行與模式3一致,說明程序代碼實現了模式3。讀者也可以通過修改sel的值對模式1和模式2進行驗證。圖7-3跑馬燈仿真波形

6.引腳鎖定下載硬件驗證選擇GW48-PK2系統中的實驗電路5,引腳鎖定情況如圖7-4所示。將設計下載到實驗開發系統中,觀察實際運行情況。clk接FPGA的93引腳,頻率選擇4Hz,然后通過按鍵選擇跑馬燈的運行模式,觀察跑馬燈的實際運行情況。圖7-4引腳鎖定情況

7.擴展部分請讀者思考其他LED顯示方式,并實現之。例如:先循環左移,再循環右移(任一時刻只有一個LED燈亮),然后從兩頭至中間依次點亮(任一時刻只有兩個LED燈亮),之后不斷重復以上顯示方式。7.28位數碼掃描顯示電路的設計

1.設計要求共8個數碼管,連成一排,要求可以任意顯示其中一個或多個數碼管。具體要求如下:

(1)依次選通8個數碼管,并讓每個數碼管顯示相應的值,比如讓每個數碼管依次顯示13579BDF。

(2)要求能在實驗臺上演示出數碼管的動態顯示過程。

2.設計說明下面對實驗原理作簡單介紹。

(1)數碼管分共陰極和共陽極兩類。7段共陰極數碼管如圖7-5所示。當數碼管的輸入為“1101101”時,數碼管的7個段g、f、e、d、c、b、a分別接1、1、0、1、1、0、1;由于接有高電平的段發亮,因此數碼管顯示“5”。注意,這里沒有考慮表示小數點的發光管,如果要考慮,需要增加段h。圖7-5共陰極數碼管及其電路

(2)圖7-6所示的是8位數碼掃描顯示電路,其中每個數碼管的8個段h、g、f、e、d、c、b、a(h是小數點)都分別連在一起,8個數碼管分別由8個選通信號K1、K2、…、K8來選擇。被選通的數碼管顯示數據,其余關閉。如在某一時刻,k1為高電平,其余選通信號為低電平,這時僅K1對應的數碼管顯示來自段信號端的數據,而其他7個數碼管均不顯示。因此,如果希望在8個數碼管顯示不同的數據,就必須使得8個選通信號K1、K2、…、K8輪流被單獨選通,同時,在段信號輸入口加上希望在對應數碼管上顯示的數據,這樣隨著選通信號的變化,才能實現掃描顯示的目的。圖7-68位數碼掃描顯示電路

3.設計模塊(包含模塊劃分)該設計使用了2個模塊,如圖7-7所示。該設計的輸入為clk,輸出為SM_7S[6..0]、SM_B[7..0]。其中SM_7S為段選信號,對數碼管的每一段進行控制;SM_B為位選信號,用于在8個數碼管中選擇。圖7-7數碼管頂層模塊框圖

4.代碼分析例7-2的Dec7S模塊為7段譯碼器,輸入信號SM_in可取十六進制數0~F,輸出信號SM_7S的7位分別接數碼管的7個段,高位在左,低位在右。【例7-2】7段數碼顯示譯碼器設計。

moduleDec7S(SM_in,SM_7S);//七段譯碼電路

input[3:0]SM_in;

outputreg[6:0]SM_7S;

always@(SM_in)

begin

case(SM_in)

4'd0:SM_7S=7'b0111111; 4'd1:SM_7S=7'b0000110; 4'd2:SM_7S=7'b1011011; 4'd3:SM_7S=7'b1001111; 4'd4:SM_7S=7'b1100110; 4'd5:SM_7S=7'b1101101; 4'd6:SM_7S=7'b1111101; 4'd7:SM_7S=7'b0000111; 4'd8:SM_7S=7'b1111111;

4'd9:SM_7S=7'b1101111; 4'd10:SM_7S=7'b1110111; 4'd11:SM_7S=7'b1111100; 4'd12:SM_7S=7'b0111001; 4'd13:SM_7S=7'b1011110; 4'd14:SM_7S=7'b1111001; 4'd15:SM_7S=7'b1110001; default:;

endcaseendendmodule例7-3的GenBS模塊用于生成位選信號和待顯示數據。程序中的cnt8是一個3位計數器,產生掃描計數信號,SM_B=1<<cnt8;用于對8個數碼管掃描選通,SM_in<=2*cnt8+1;用于生成待顯示數據。例如當cnt8等于1時,K2對應的數碼管被選通,同時,A被賦值3,當cnt8不斷加1后,將能在8個數碼管上分別顯示數據1、3、5、7、9、B、D、F。【例7-3】位選和待顯示數據生成電路。moduleGenBS(clk,SM_in,SM_B);inputclk;outputreg[3:0]SM_in;outputreg[7:0]SM_B;reg[2:0]cnt8;always@(posedge

clk)begin

if(cnt8>=7)cnt8<=0; elsecnt8<=cnt8+1;

SM_in<=2*cnt8+1;//生成待顯示數據為1,3,5,... SM_B=1<<cnt8;//生成位選信號

endendmodule例7-4是掃描顯示的頂層模塊。其中:clk是掃描時鐘;SM_7S為7段控制信號,由高位至低位分別接g、f、e、d、c、b、a7個段;SM_B是位選控制信號,接圖7-6中的8個選通信號K1、K2、…、K8。

【例7-4】8位數碼掃描顯示電路頂層模塊。

moduleshumaguan(clk,SM_7S,SM_B);

inputclk;

output[6:0]SM_7S;//段控制信號輸出

output[7:0]SM_B;//位控制信號輸出

wire[3:0]SM_in;

GenBSinst1(clk,SM_in,SM_B);//調用位選和待顯示數據生成電路

Dec7Sinst2(SM_in,SM_7S);//調用譯碼電路

endmodule

5.仿真分析仿真波形如圖7-8所示。從圖中可以看出,在每一個時鐘上升沿選中下一個數碼管,同時送相應的數據給該數碼管顯示。圖7-8數碼管仿真波形

6.引腳鎖定下載硬件驗證將設計下載到實驗開發系統中,觀察實際運行情況。實驗方式:若考慮小數點,則SM_7S的8個段分別與PIO49、PIO48、…、PIO42(高位在左)連接、SM_B的8個位分別與PIO34、PIO35、…、PIO41(高位在左)連接。在GW48EDA系統數碼管左邊有一個跳線冒,將其跳下端“CLOSE”(平時跳上端“ENAB”),這時實驗系統的8個數碼管構成圖7-6所示的電路結構,時鐘clk可選擇clock0,通過跳線選擇16384Hz信號。引腳鎖定后進行編譯、下載和硬件測試實驗。引腳鎖定如圖7-9所示。圖7-9引腳鎖定情況時鐘clk選擇clock0,通過跳線選擇4Hz信號,可演示數碼管的動態掃描過程。

7.擴展部分請讀者嘗試完成以下幾種顯示方式:

(1)?8個數碼管同時顯示,每個數碼管的8個段,即a、b、c、d、e、f、g、dp依次顯示,每個段持續顯示時間為0.25s。

(2)?8個段和8個數碼管依次顯示,a段顯示在第1個數碼管上,b段顯示在第2個數碼管上,……,dp段顯示在第8個數碼管上,顯示持續時間為0.25s。

(3)將0~F這16個十六制數依次顯示在數碼管中,每個時刻只有一個數碼管顯示,持續時間為0.25s,即0顯示在第1個數碼管、1顯示在第2個數碼管、……、7顯示在第8個數碼管、8顯示在第1個數碼管、……、F顯示在第8個數碼管。7.3數控分頻器的設計

1.設計要求

(1)對于任意頻率,均可以對其進行數控分頻,以得較低的頻率。

(2)對于預定頻率,均可以通過對較高頻率分頻得到。

2.設計說明數控分頻器的功能是:當在輸入端給定不同輸入數據時,對輸入的時鐘信號有不同的分頻比。數控分頻器是用計數值可并行預置的加法計數器設計完成的,方法是將計數溢出位與預置數加載輸入信號相接,詳細設計程序如例7-5所示。

3.設計模塊(包含模塊劃分)本例僅有一個模塊,圖7-10給出了分頻器模塊的端口框圖。其中,CLK為時鐘信號;D為輸入數據,根據這個數據進行分頻;FOUT為分頻后的輸出。圖7-10分頻器模塊的端口框圖

4.代碼分析

【例7-5】數控分頻器的設計。

moduleDVF_v(CLK,D,FOUT);

inputCLK;

input[7:0]D;

outputregFOUT;

reg[7:0]FULL;

always@(posedgeCLK)

begin:P_REG

reg[7:0]CNT8;

if(CNT8==8'b11111111)beginCNT8=D;//當CNT8計數計滿時,輸入數據D被同步預置給計數器CNT8FULL<=1'b1;//同時使溢出標志信號FULL輸出為高電平

end else beginCNT8=CNT8+1;//否則繼續作加1計數

FULL<=1'b0;//且輸出溢出標志信號FULL為低電平

end

endalways@(posedgeFULL)

begin:P_DIV

regCNT2; CNT2=~CNT2;//如果溢出標志信號FULL為高電平,則D觸發器輸出取反

if(CNT2==1'b1)FOUT=1'b1;elseFOUT=1'b0;endendmodule

5.仿真分析圖7-11為數控分頻器的仿真結果。從圖中可以看出,FOUT的輸出頻率隨D的變化而變化,實現了數控分頻。圖7-11數控分頻器的仿真結果

6.引腳鎖定下載硬件驗證將設計下載到實驗開發系統中,觀察實際運行情況。選實驗電路模式1,鍵2/鍵1負責輸入8位預置數D(PIO7~PIO0),CLK由clock0輸入,頻率選65536Hz或更高(確保分頻后落在音頻范圍);輸出FOUT接揚聲器(SPKER)。編譯下載后進行硬件測試:改變鍵2/鍵1的輸入值,可聽到不同音調的聲音。引腳鎖定如圖7-12所示。圖7-12引腳鎖定

7.擴展部分

(1)利用本節的數控分頻器得到的頻率,其占空比為50%。若占空比可調,比如占空比為30%,如何實現?提示:可使用兩個8位輸入數據控制輸出脈沖的高低電平持續時間。

(2)嘗試使用其他分頻器的設計方法,例如第4章提出的方法,并比較這些方法的異同。7.4樂曲硬件演奏電路的設計

1.設計要求

(1)利用7.3節的數控分頻器設計硬件樂曲演奏電路。

(2)了解樂譜的一些基本知識,可以將樂譜轉換為相應的QuartusⅡ文件,掌握其演奏原理。

(3)掌握本設計中各模塊的功能,能夠填入并演奏一些新的曲子。

2.設計說明樂曲演奏的原理:組成樂曲的每個音符的頻率值(音調)及其持續時間(音長)是樂曲能連續演奏所需的兩個基本數據,因此只要控制輸出到揚聲器的激勵信號頻率的高低和持續的時間,就可以使揚聲器發出連續的樂曲聲。

(1)音調的控制。簡譜中音名與音頻的對應關系如圖7-13所示。圖7-13簡譜中音名與音頻的對應關系圖7-13中僅列出了低音、中音和高音的頻率,對于比低音低八度或者比高音高八度的音,可依據2倍規則很容易地求出。所謂2倍規則,是指中音1是低音1頻率的2倍,高音1是中音1頻率的2倍,依此類推。簡譜中音頻與分頻預置數的對應關系如圖7-14所示。圖7-14簡譜中音頻與分頻預置數的對應關系音名與音頻的對應關系以及計算音頻與分頻值、11位計數器的預置數的對應關系可由程序計算得出,相應的C語言程序代碼如例7-6所示。

【例7-6】計算分頻比與分頻預置數的程序。

//音名與音調之間的對應關系的計算程序

#include<stdio.h>

#include"math.h"

#defineN3

#defineM7

main(){

int

i,j; doublea[N][M]={0.0},freq_div_ratio[N][M]={0.0},ToneIndex[N][M]={0.0};//頻率,分頻比,預置數

doubleratio,counter_11,freq=12000000;//freq為系統頻率,12MHz ratio=pow(2.0,1.0/12);

printf("ratio=%lf\n",ratio); //計算低音1,2,3,4,5,6,7 a[0][5]=440.0;

a[0][6]=a[0][5]*ratio*ratio;a[0][4]=a[0][5]/ratio/ratio;a[0][3]=a[0][4]/ratio/ratio;a[0][2]=a[0][3]/ratio;a[0][1]=a[0][2]/ratio/ratio;a[0][0]=a[0][1]/ratio/ratio;//計算中音和高音:1,2,3,4,5,6,7for(i=1;i<=2;i++)

{ for(j=0;j<7;j++) { a[i][j]=a[i-1][j]*2; } } //打印低中高音1,2,3,4,5,6,7 printf("音調頻率如下:0--低音,1--中音,2--高音\n"); for(i=0;i<=2;i++)

{ for(j=0;j<7;j++)

{

printf("%d音%d:%.0lf",i,j+1,a[i][j]); }

printf("\n"); } //計算各音調的分頻值

counter_11=pow(2.0,11);//分頻值對應的位數應為11位,該位數由系統頻率分頻后的頻率決定

freq=freq/(12*2);//12MHz,12分頻,再2分頻

freq=freq/(12*2);//12MHz,12分頻,再2分頻

printf("音調分頻比freq_div_ratio如下:0--低音,1--中音,2--高音\n");

for(i=0;i<=2;i++) {

for(j=0;j<7;j++) {

freq_div_ratio[i][j]=freq/a[i][j];

printf("%d音%d:%.0lf",i,j+1,freq_div_ratio[i][j]); }

printf("\n"); } //計算各音調的分頻值相對應的預置數

printf("音調預置數ToneIndex如下:0--低音,1--中音,2--高音\n");

for(i=0;i<=2;i++)

{

for(j=0;j<7;j++) {

ToneIndex[i][j]=counter_11-freq_div_ratio[i][j];

printf("%d音%d:%.0lf",i,j+1,ToneIndex[i][j]); }

printf("\n"); }}

(2)音長的控制。音樂中的音除了有高低之分外,還有長短之分。如何記錄音的長短呢?簡譜中用一條橫線“—”在音符的右面或下面來標注音的長短。表7-1列出了常用音符和它們的長度標記。從表7-1中可以看出橫線有記在音符后面的,也有記在音符下面的,橫線標記的位置不同,被標記的音符的時值也不同。從表7-1中可以發現一個規律:要使音符時值延長,在四分音符右邊加橫線“—”,這時的橫線叫延時線。延時線越多,音持續的時間(時值)越長。記在音符右邊的小圓點稱為附點,表示增加前面音符時值的一半,帶附點的音符叫附點音符。例如:四分附點音符,八分附點音符:。音樂中除了有音的高低、長短之外,也有音的休止。表示聲音休止的符號叫休止符,用“0”標記。每增加一個0,就增加一個四分休止符的時值。

3.設計模塊(包含模塊劃分)主系統由3個模塊組成,例7-7是頂層設計文件,其內部有3個功能模塊(如圖7-15所示):ToneTaba、NoteTabs和Speakera。圖7-15硬件樂曲演奏電路結構與利用微處理器(CPU或MCU)來實現樂曲演奏相比,以純硬件完成樂曲演奏電路的邏輯要復雜得多,如果不借助于功能強大的EDA工具和硬件描述語言,僅憑傳統的數字邏輯技術,即使最簡單的演奏電路也難以實現。本例實現的樂曲演奏電路結構如圖7-15所示。在圖7-15中,模塊u1類似于彈琴的人的手指;u2類似于琴鍵;u3類似于琴弦或音調發聲器。下面首先來了解圖7-15的工作原理:

(1)音符的頻率可以由圖7-15中的模塊Speakera獲得。它是一個數控分頻器,由其clk端輸入一具有較高頻率(這里是12?MHz)的信號,通過Speakera分頻后由SPKOUT輸出。由于直接從數控分頻器中出來的輸出信號是脈寬極窄的脈沖式信號,為了有利于驅動揚聲器,需另加一個D觸發器以均衡其占空比,但這時的頻率將是原來的1/2。Speakera對clk輸入信號的分頻比由11位預置數Tone[10..0]決定。?SPKOUT的輸出頻率將決定每一音符的音調。這樣,分頻計數器的預置值Tone[10..0]與SPKOUT的輸出頻率就有了對應關系。例如在ToneTaba模塊中若取Tone[10..0]=1036,將發音符為“3”音的信號頻率。

(2)音符的持續時間須根據樂曲的速度及每個音符的節拍數來確定,圖7-15中模塊ToneTaba的功能首先是為Speakera提供決定所發音符的分頻預置數,而此數在Speakera輸入口停留的時間即為此音符的節拍值。模塊ToneTaba是樂曲簡譜碼對應的分頻預置數查表電路,其中設置了高音、中音、低音全部音符所對應的分頻預置數,共13個,每一音符的停留時間由音樂節拍和音調發生器模塊NoteTabs的clk的輸入頻率決定,這里為4?Hz。這13個值的輸出由對應于ToneTaba的4位輸入值Index[3..0]確定,而Index[3..0]最多有16種可選值。ToneIndex[3..0]輸向ToneTaba中的Index[3..0],其值與持續的時間由模塊NoteTabs決定。

(3)在NoteTabs中設置了一個9位二進制計數器(計數最大值為512),作為音符數據ROM的地址發生器。這個計數器的計數頻率選為4Hz,即每一計數值的停留時間為0.25?s,恰為當全音符設為1?s時,四四拍的4分音符的持續時間。當NoteTabs中的計數器按4?Hz的時鐘速率作加法計數(即地址值遞增)時,音符數據ROM中的音符數據將從ROM中通過ToneIndex[3..0]端口輸向ToneTaba模塊,樂曲就開始連續自然地演奏起來了。需定制例7-10的NoteTabs模塊中的音符數據ROM“music”。該ROM中的音符數據已列在例7-11中。注意該例數據表中的數據位寬、深度和數據的表達類型。此外,為了節省篇幅,例中的數據都橫排了,實用中應該以每一分號為一行來展開,否則會出錯。最后對該ROM進行仿真,確認例7-11中的音符數據已經進入ROM中。

4.代碼分析樂曲演奏電路的VerilogHDL描述見例7-7~例7-11。

【例7-7】硬件演奏電路的頂層設計。

module

Songer(Song_sel,CLK12MHZ,CLK8HZ,CODE1,HIGH_LOW,SPKOUT);

input[1:0]Song_sel; //對四首樂曲進行選擇

inputCLK12MHZ; //音調頻率信號

inputCLK8HZ; //節拍頻率信號

output[3:0]CODE1; //簡譜碼輸出顯示

output[1:0]HIGH_LOW; //高、中、低8度指示:00—低,01—中,10—高

outputSPKOUT; //聲音輸出

wire[10:0]Tone;

wire[4:0]ToneIndex;

NoteTabsu1(.sel(Song_sel),.clk(CLK8HZ),.ToneIndex(ToneIndex));

ToneTaba

u2(.Index(ToneIndex),.Tone(Tone),.CODE(CODE1),.HIGH(HIGH_LOW));

Speakerau3(.clk(CLK12MHZ),.Tone(Tone),.SpkS(SPKOUT));endmodule【例7-8】Speakera模塊。moduleSpeakera(clk,Tone,SpkS);inputclk;input[10:0]Tone;//分頻預置數-----跟音調相匹配outputreg

SpkS;//聲音輸出reg

PreCLK,FullSpkS;always@(posedge

clk)

begin:DivideCLK

reg[3:0]Count4;

PreCLK<=0;//將CLK進行12分頻,PreCLK為CLK的12分頻

if(Count4>11) beginPreCLK<=1;Count4=0;end else Count4=Count4+1;endalways@(posedge

PreCLK)

begin:GenSpkS //11位可預置計數器

reg[10:0]Count11; if(Count11==11'h7FF) //首先進行12分頻

beginCount11=Tone;FullSpkS<=1;end else beginCount11=Count11+1;FullSpkS<=0;endendalways@(posedge

FullSpkS)

begin:DelaySpkS //將輸出再2分頻,展寬脈沖,使揚聲器有足夠功率發音

regCount2; Count2=~Count2; if(Count2==1)SpkS<=1; elseSpkS<=0;endendmodule【例7-9】ToneTaba模塊。

moduleToneTaba(Index,CODE,HIGH,Tone);

input[4:0]Index; //音符

outputreg[3:0]CODE; //簡譜碼輸出

outputreg[1:0]HIGH; //高、中、低8度指示:

00—低,01—中,10—高

outputreg[10:0]Tone; //分頻預置數-----跟音調相匹配always@(Index)

begin:Search

case(Index) 5'b00000: begin Tone<=11'b11111111111; end //2047 5'b00001: begin Tone<=11'd137; end //137; 5'b00010:

begin Tone<=11'd345; end//345; 5'b00011: begin Tone<=11'd531; end//531; 5'b00100: beginTone<=11'd616;end//616;5'b00101:beginTone<=11'd773;end//773;5'b00110:beginTone<=11'd912;end//912;5'b0111:beginTone<=11'd1036;end//1036;5'b1000:beginTone<=11'd1092;end//1092;5'b1001:beginTone<=11'd1197;end//1197;5'b1010:beginTone<=11'd1290;end//1290;5'b1011:beginTone<=11'd1332;end//1332;5'b1100:beginTone<=11'd1410;end//1410;5'b1101:beginTone<=11'd1480;end//1480;5'b1110:beginTone<=11'd1542;end//15425'b1111:beginTone<=11'd1570;end//15705'b10000:beginTone<=11'd1622;end//16225'b10001:beginTone<=11'd1668;end//16685'b10010:beginTone<=11'd1690;end//16905'b10011:beginTone<=11'd1728;end//17285'b10100:beginTone<=11'd1764;end//17645'b10101:beginTone<=11'd1795;end//1795default:;endcaseendalways@(Index)begin:Encodereg[4:0]temp_Index;if(Index>=15)begin

temp_Index<=Index+2; CODE<={1'b0,temp_Index[2:0]}; HIGH<=temp_Index[4:3]; endelseif(Index>=8) begintemp_Index<=Index+1; CODE<={1'b0,temp_Index[2:0]}; HIGH<=temp_Index[4:3]; end else begin

temp_Index=Index; CODE<={1'b0,temp_Index[2:0]}; HIGH<=temp_Index[4:3]; end endendmodule【例7-10】NoteTabs模塊。moduleNoteTabs(sel,clk,ToneIndex);input[1:0]sel; //樂曲選擇信號inputclk;output[4:0]ToneIndex; //樂曲曲譜中的音符輸出reg[9:0]Counter; //計數器的位數應該根據存放音樂的ROM進行調整always@(posedge

clk)begin:CNT8if(sel==2'b00)//多個曲子可放在ROM中,通過按鍵進行選擇

if(Counter>=88)Counter<=8'd0;//演奏21個音調,從低到高

elseCounter<=Counter+1; //此處可通過sel選擇其他曲目播放

end

musicu1(.address(Counter),.q(ToneIndex),.inclock(clk));

endmodule【例7-11】演奏從低音到高音共21個音調的ROM文件。WIDTH=8;DEPTH=88;ADDRESS_RADIX=UNS;DATA_RADIX=UNS;CONTENTBEGIN[0..3]:0; [4..7]:1;[8..11]:2; [12..15]:3;[16..19]:4; [20..23]:5;[24..27]:6; [28..31]:7;[32..35]:8; [36..39]:9;[40..43]:10; [44..47]:11;[48..51]:12; [52..55]:13;[56..59]:14; [60..63]:15;[64..67]:16; [68..71]:17;[72..75]:18; [76..79]:19;[80..83]:20; [84..87]:21;END;

5.仿真分析請讀者自行仿真。

6.引腳鎖定下載硬件驗證實驗電路結構圖為No.1。先將引腳鎖定,使CLK12MHZ與clock9相接,接收12MHz時鐘頻率(用短路帽將clock9接“CLK12MHZ”);CLK8HZ與clock2相接,接收4Hz頻率;發音輸出SPKOUT接Speaker;與演奏發音相對應的簡譜碼輸出顯示可由CODE1在數碼管5顯示;HIGH_LOW為高、中、低八度音指示,可由發光管D6/D5指示。最后向目標芯片下載適配后的SOF邏輯設計文件。引腳鎖定如圖7-16所示。圖7-16引腳鎖定

7.擴展部分

(1)填入新的樂曲,如“梁祝”或其他熟悉的樂曲。操作步驟如下:①根據所填樂曲可能出現的音符,修改例7-11中的音符數據,同時注意每一音符的節拍長短。②如果樂曲比較長,可增加模塊NoteTaba中計數器的位數,如設為9位,則可有512個基本節拍。

(2)在一個ROM中裝入多首歌曲,可手動或自動選擇歌曲(推薦圖7-17~圖7-19所示的三首)。圖7-17梁祝的簡譜圖7-18兩只老虎的簡譜圖7-19難忘今宵的簡譜提示:仍采用No.1電路,加入多支曲目后的引腳鎖定如圖7-20所示。圖7-20引腳鎖定用鍵8/7控制四首曲目的選擇;與演奏發音相對應的簡譜碼輸出由數碼管5顯示;HIGH_LOW為高、中、低八度音指示,可由發光管D6/D5指示。

(3)結合本實驗,讀者可以查閱電子琴相關知識并設計一個簡易電子琴。

(4)考慮例7-8中的進程DelaySpkS對揚聲器發聲有什么影響。再考慮在電路上應該滿足哪些條件,才能用數字器件直接輸出的方波驅動揚聲器發聲。7.5數字跑表和數字鐘的設計

1.設計要求

(1)計時功能:設計一個具有“百分秒、秒、分、小時”計時功能的數字跑表,可以實現一個小時以內精確至百分之一秒的計時。要求具有復位和暫停功能。復位后,從00:00:00:00開始計數;暫停后,保持現有計數值不變。

(2)校準功能:根據當前時間校準鬧鐘。即增加一個校時鍵,增加時、分預置初值按鈕,這樣可以對小時、分鐘進行校準。

2.設計說明本例主要實現了計數及進位的設計。本數字跑表首先要從最低位的百分秒計數器開始,按照系統時鐘進行計數。計數至100后向秒計數器進位,秒計數器以百分秒計數器的進位位為時鐘進行計數,計數至60后向分計數器進位,分計數器以秒計數器的進位位為時鐘進行計數,計數至60后向小時計數器進位,小時計數器以分計數器的進位位為時鐘進行計數,如圖7-21所示。圖7-21設計說明注意:本設計要根據頻率輸入,將頻率分頻得到0.01Hz的頻率,用于百分秒的計數脈沖。

3.設計模塊(包含模塊劃分)模塊可劃分為以下幾個部分:一是分、秒、百分秒實現模塊;二是小時實現模塊;三是顯示譯碼模塊。采用自頂向下的設計,頂層模塊如圖7-22所示。圖7-22數字跑表與數字鐘的頂層模塊設計圖7-22中的端口信號定義如下:CLK_100Hz:時鐘信號,100Hz。RST:異步復位信號。PAUSE:暫停信號。MSH、MSL:百分秒的高位和低位。SH、SL:秒的高位和低位。MH、ML:分的高位和低位。各模塊說明如下:

(1)分、秒、百分秒實現模塊:可采用十進制計數器、六進制計數器完成整個分、秒和百分秒的設計。

(2)小時實現模塊:直接采用二十四進制計數器實現即可,具體實現見代碼分析部分。

(3)顯示譯碼模塊:見本章7.2節。

4.代碼分析本設計的代碼如例7-12~7-14所示。

【例7-12】十進制計數器。

modulepiaobiao_cnt10(reset,clk,count,cout);

inputreset,clk;

outputreg[3:0]count;

outputreg

cout;always@(posedge

reset,posedge

clk)

if(reset)count=0; elseif(count==9)begincount=0;cout=1;end elsebegincount=count+1;cout=0;endendmodule【例7-13】六進制計數器。

modulepiaobiao_cnt6(reset,clk,count,cout);

inputreset,clk;

outputreg[2:0]count;

outputreg

cout;

always@(posedge

reset,posedge

clk)

if(reset)count=0;

elseif(count==5)begincount=0;cout=1;end elsebegincount=count+1;cout=0;end

endmodule【例7-14】二十四進制計數器。

modulepiaobiao_cnt24(reset,clk,pause,HL,HH,cout);

inputreset,clk,pause;

output[3:0]HL,HH;

outputreg

cout;

reg[4:0]count;

assignHL=count%10, HH=count/10;

always@(posedge

reset,posedge

clk)if(reset)count=0; elseif(!pause)

if(count==23)begincount=0;cout=1;end elsebegincount=count+1;cout=0;endendmodule

5.仿真分析為了仿真方便,設定CLK_100Hz的時鐘周期為10ns(當然也可以采用實際的時鐘周期10?ms,對于功能仿真來說沒有本質的區別)。整個仿真波形如圖7-23所示。其中能夠說明設計正確性的部分仿真波形如圖7-24~圖7-26所示。圖7-23仿真波形圖7-24RST功能圖7-25PAUSE功能圖7-26分、秒、百分秒計時功能

6.引腳鎖定下載硬件驗證采用電路結構No.5,引腳設置如圖7-27所示:RST接按鍵1,PAUSE接按鍵2,百分秒顯示用數碼管1和2,秒顯示用數碼管3和4,分顯示用數碼管5和6,小時顯示用數碼管7和8。

CLK_100Hz接clock0,頻率選擇256?Hz,觀察并驗證整個跑表的設計。圖7-27引腳鎖定

7.擴展部分

(1)校時功能。增加一個校時鍵,增加4個時、分預置初值按鍵,分別用來調整時、分的各位。這一步由讀者自己完成。

(2)鬧鐘功能。增加一個鬧鐘功能鍵,同時使用校時功能中用到的4個銨鍵來設置鬧鐘時間。如果當前時間與設置的鬧鐘時間相同,則揚聲器發出蜂鳴聲。

(3)思考對于任意系統頻率,比如6MHz或者256Hz,如何獲得100Hz的百分秒頻率。例7-15給出了將256Hz轉換成100Hz的一種實現代碼,讀者也可以采用其他方法實現。【例7-15】輸入256Hz,輸出100Hz。

modulefreq_256_100(rst,clk,en,clk_100Hz);

inputrst,clk,en;//clk為256Hz輸入

outputregclk_100Hz;

reg[7:0]temp;

reg[7:0]temp_1,temp_2;

always@(posedge

clk)

begin

if(rst)temp<=0; elseif(en)

if(temp==255)temp<=0; elsetemp<=temp+1; temp_1<=(200*temp+128)/256; //加128的作用是四舍五入

temp_2<=temp_1; if((temp_1>temp_2)|(temp_2==200))clk_100Hz=~clk_100Hz;endendmodule本例的仿真波形如圖7-28所示。圖7-28256Hz轉換為100Hz的仿真波形

(4)試設計一萬進制計數器。提示:對于任意計數器,可以采用小位數計數器級聯進行設計,這是計數器的設計技巧之一。比如,對于一萬進制計數器,可以采用兩個一百進制計數器的級聯,也可以采用四個十進制計數器的級聯。從設計所占的面積和速度而言,采用4個十進制計數器級聯的效果更好。7.6用VerilogHDL狀態機實現A/D

采樣控制電路

1.設計要求(1)理解并掌握ADC0809芯片的工作時序要求。(2)采用狀態機來設計A/D轉換器ADC0809的采樣控制電路。

2.設計說明

ADC0809是CMOS的8位A/D轉換器,片內有8路模擬開關,可控制8個模擬量中的一個進入轉換器。轉換時間約為100μs,ADC0809含鎖存控制的8路開關,輸出由三態緩沖器控制,單5V電源供電。主要控制信號如圖7-29所示。START是轉換啟動信號,高電平有效。ALE是3位通道選擇地址信號(ADDC、ADDB、ADDA)的鎖存信號。當模擬量送至某一輸入端(如IN1或IN2等)時,由3位地址信號選擇,而地址信號由ALE鎖存。EOC是轉換狀態信號,轉換開始后EOC為低電平,當啟動轉換約100μs后,EOC由負變正,以示轉換結束。在EOC的上升沿后,若使輸出使能信號OE為高電平,則控制打開三態緩沖器,把轉換好的8位數據結果輸至數據總線,至此ADC0809的一次轉換結束。圖7-29ADC0809工作時序根據圖7-29,我們可以采用圖7-30描述的狀態圖控制ADC0809進行采樣。圖7-30控制ADC0809采樣的狀態圖

3.設計模塊(包含模塊劃分)本設計僅涉及一個狀態機,采用一個模塊即可。采樣控制模塊端口框圖如圖7-31所示,端口信號與圖7-29中的信號是一致的。圖7-31采樣控制模塊端口框圖其內部結構如圖7-32所示。圖7-32采樣控制模塊的內部結構

4.代碼分析根據圖7-30,可以得出由VerilogHDL描述的采樣控制狀態機,如例7-16所示。

【例7-16】VerilogHDL狀態機的A/D采樣控制電路實現。

module

ADCINT_v(D,CLK,EOC,ALE,START,OE,ADDA,LOCK0,Q);

input[7:0]D; //來自0809轉換好的8位數據

inputCLK; //狀態機工作時鐘

inputEOC; //轉換狀態指示,低電平表示正在轉換

outputregALE;//8個模擬信號通道地址鎖存信號outputregOE; //數據輸出三態控制信號outputADDA; //信號通道最低位控制信號outputLOCK0; //觀察數據鎖存時鐘output[7:0]Q; //8位數據輸出parameterst0=3‘b000, //定義各狀態子類型

st1=3'b001, st2=3'b010, st3=3'b011, st4=3'b100;reg[2:0]current_state,next_state;reg[7:0]REGL;regLOCK; //轉換后數據輸出鎖存時鐘信號assignADDA=1'b1;//當ADDA<=1'b0時,模擬信號進入通道IN0;當ADDA<=1'b1時,模擬信//號進入通道IN1assignQ=REGL,LOCK0=LOCK;always@(current_state,EOC)//規定各狀態轉換方式

begin:COMcase(current_state)st0: begin ALE<=1'b0;START<=1'b0;LOCK<=1'b0;OE<=1'b0;

next_state<=st1; //0809初始化

end st1: begin ALE<=1'b1;START<=1'b1;LOCK<=1'b0;OE<=1'b0;

next_state<=st2; //啟動采樣

endst2: begin ALE<=1'b0;START<=1'b0;LOCK<=1'b0;OE<=1'b0;

if(EOC==1)next_state<=st3; //EOC=1表明轉換結束

elsenext_state<=st2; //轉換未結束,繼續等待

endst3:

begin ALE<=1'b0;START<=1'b0;LOCK<=1'b0;OE<=1'b1;

next_state<=st4; //開啟OE,輸出轉換好的數據

endst4:

begin ALE<=1'b0;START<=1'b0;LOCK<=1'b1;OE<=1'b1;

next_state<=st0;

enddefault:

next_state<=st0;

endcaseendalways@(posedgeCLK)

begin:REG

current_state<=next_state;end//由信號current_state將當前狀態值帶出此進程:REGalways@(posedgeLOCK)begin:LATCH1 ?REGL<=D;end//此進程中,在LOCK的上升沿,將轉換好的數據鎖入endmodule

5.仿真分析請讀者自行仿真。需要說明的是,設置采樣控制的輸入信號時,其工作時序要按照圖7-29的要求給出,這樣才能得到正確結果。

6.引腳鎖定下載硬件驗證用硬件驗證例7-16電路對ADC0809的控制功能。測試步驟:建議選擇電路模式No.5,ADC0809的轉換時鐘CLK已經事先接有750?kHz的頻率,引腳鎖定為:START接PIO34,OE(ENABLE)接PIO35,EOC接PIO8,ALE接PIO33,狀態機時鐘CLK接clock0,ADDA接PIO32(ADDB和ADDC都接GND),ADC0809的8位輸出數據線接PIO23~PIO16,鎖存輸出Q顯示于數碼管8/數碼管7(PIO47~PIO40)。實驗操作:將GW48EDA系統左下角的撥碼開關的4、6、7向下撥,其余向上,即使0809工作使能,并且使FPGA能接受來自0809轉換結束的信號。下載ADC0809中的ADCINT.sof到實驗板的FPGA中;clock0的短路帽可選12MHz、6MHz、65536Hz等頻率;按動一次右側的復位鍵;用螺絲刀旋轉GW48系統左下角的精密電位器,以便為ADC0809提供變化的待測模擬信號(注意,這時必須在例7-16中賦值:ADDA=1'b1,這樣就能通過實驗系統左下的AIN1輸入端與電位器相接,并將信號輸入0809的IN1端)。這時數碼管8和7將顯示ADC0809采樣的數字值(十六進制),數據來自FPGA的輸出。數碼管2和1也將顯示同樣的數據,此數據直接來自0809的數據口。實驗結束后注意將撥碼開關撥向默認狀態,即僅“4”向下,其余向上。注意:可變電阻順時針旋轉可使采樣值變小,逆時針旋轉可使采樣值變大。引腳鎖定如圖7-33所示。圖7-33引腳鎖定

7.擴展部分

(1)在本實驗的基礎上增加存儲器,用于存儲A/D轉換后的數據,設計一個簡易存儲示波器。

(2)若不采用集成電路芯片ADC0809,可否采用比較器和D/A器件實現A/D轉換功能。請查閱相關資料,并給出電路設計。7.7交通控制器的設計

1.設計要求實現一個常見的十字路口交通燈控制功能。一個十字路口的交通燈一般分為兩個方向,每個方向具有紅燈、綠燈和黃燈3種。具體要求如下:

(1)十字路口包含A、B兩個方向的車道。A方向放行1分鐘(綠55s,黃5s),同時B方向禁行(紅60s);然后A方向禁行1分鐘(紅60s),同時B方向放行(綠燈55s,黃燈5s)。依此類推,循環往復。

(2)實現正常的倒計時功能,用2組數碼管作為A和B兩個方向的倒計時顯示。

(3)當遇特殊情況時,可通過按hold鍵來實現特殊的功能。使A、B方向的紅燈亮并且警告燈不停閃爍;計數器停止計數并保持在原來的狀態;特殊情況處理完畢后可通過按hold鍵使交通燈正常運行,并正常計數。

(4)系統已有時鐘為64?Hz。

2.設計說明本設計的重點在于:

(1)分頻器設計,根據已有時鐘頻率獲得需要的時鐘頻率。

(2)交通控制器設計,根據計時時間來控制交通燈與數碼管。

3.設計模塊(包含模塊劃分)本設計可劃分為三個模塊:一是分頻器模塊;二是交通燈控制器模塊;三是顯示譯碼模塊。圖7-34為整體設計框圖,圖中未包含顯示譯碼模塊,僅顯示了前兩個模塊。圖7-34交通控制器模塊框圖圖7-34中:

Clk64Hz:64Hz系統時鐘;

reset:系統復位信號;

HOLD:人工按鈕,用于特殊狀態,此時兩組路燈都顯示紅燈并且閃爍;

RedA、GreenA、YellowA、RedB、GreenB、YellowB:分別為A、B車道的紅、綠、黃燈信號;

DispA:用于A方向燈的時間顯示,8位,可驅動兩個數碼管;

DispB:用于B方向燈的時間顯示,8位,可驅動兩個數碼管。對圖7-34中兩個模塊的說明:

(1)分頻器模塊。分頻器模塊的作用是將現實可用的時鐘分頻至1Hz,以供交通控制器模塊使用。本例中可用的輸入時鐘頻率為64Hz。

(2)交通燈控制器模塊。交通燈控制器模塊是本設計的核心,它使交通燈按既定要求變化。

4.代碼分析

【例7-17】分頻器模塊。

moduletraffic_cnt64_v(clk64Hz,clk1Hz);

inputclk64Hz;

outputclk1Hz;

reg[5:0]count;

always@(posedgeclk64Hz)

count=count+1;

assignclk1Hz=count[5];

endmodule

【例7-18】交通燈控制器模塊。

module

traffic_control_v(clk,reset,hold,RedA,GreenA,YellowA,RedB,GreenB,YellowB,Flash,DispA,DispB);

inputclk,reset,hold; //本clk頻率為1Hz

outputreg

RedA,GreenA,YellowA,RedB,GreenB,YellowB,Flash;

output[7:0]DispA,DispB;

reg[5:0]NumA,NumB;

//中間變量用于計數

integercount;

assignDispA[7:4]=NumA/10, //用于向數碼管送顯示數據,A方向十位

DispA[3:0]=NumA%10; //用于向數碼管送顯示數據,A方向個位

assignDispB[7:4]=NumB/10, //用于向數碼管送顯示數據,B方向十位

DispB[3:0]=NumB%10; //用于向數碼管送顯示數據,B方向個位

always@(posedge

reset,posedge

clk)

beginif(reset)count=0; //復位信號,將計數器清零

elseif(hold)Flash=~Flash; else begin Flash=0;

if(count==119)count=0; elsecount=count+1; endendalways@(posedge

clk)begin

if(hold) //hold信號有效期間,交通燈閃爍

begin

RedA<=1'b1;GreenA<=1'b0;YellowA<=1'b0;

RedB<=1'b1;GreenB<=1'b0;YellowB<=1'b0; endelse//hold無效期間,系統行為,交通燈按既定方式循環運行

if(count<55) //前55s,A燈為綠,B燈為紅

begin

NumA<=55-count;

NumB<=60-count;

RedA<=1'b0;GreenA<=1'b1;YellowA<=1'b0;

RedB<=1'b1;GreenB<=1'b0;YellowB<=1'b0; endelseif(count<60) //55~60s,A燈為黃,B燈為紅

begin

NumA<=60-count;

NumB<=60-count;

RedA<=1'b0;GreenA<=1'b0;YellowA<=1'b1;

RedB<=1'b1;GreenB<=1'b0;YellowB<=1'b0; end

elseif(count<115) //60~115s,A燈為紅,B燈為綠

begin

NumA<=120-count;

NumB<=115-count;

RedA<=1'b1;GreenA<=1'b0;YellowA<=1'b0;

RedB<=1'b0;GreenB<=1'b1;YellowB<=1'b0; en

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論