2.5 串口通信--明德?lián)P至簡設(shè)計法原理與應(yīng)用
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????第五章? 串口通信
需要看對應(yīng)的視頻,請點擊視頻編號:000900000038
1.至簡原理與應(yīng)用配套的案例
2.實現(xiàn)通過串口調(diào)試助手發(fā)送一個8位的數(shù)據(jù)data,其可以控制開發(fā)板上的8個LED燈,data[0]~data[7]分別控制LED0~LED7燈。當數(shù)據(jù)位為0時,對應(yīng)的LED燈點亮,數(shù)據(jù)為1時,對應(yīng)的LED燈熄滅。
3. ALTERA入門學(xué)習案例文檔
1?項目背景
串行接口簡稱串口,也稱串行通信接口或串行通訊接口(通常指COM接口),是采用串行通信方式的擴展接口。串行接口?(Serial Interface)?是指數(shù)據(jù)一位一位地順序傳送,其特點是通信線路簡單,只要一對傳輸線就可以實現(xiàn)雙向通信(可以直接利用電話線作為傳輸線),從而大大降低了成本,特別適用于遠距離通信,但傳送速度較慢。一條信息的各位數(shù)據(jù)被逐位按順序傳送的通訊方式稱為串行通訊。
? 串口的出現(xiàn)是在1980年前后,數(shù)據(jù)傳輸率是115kbps~230kbps。串口出現(xiàn)的初期是為了實現(xiàn)連接計算機外設(shè)的目的,初期串口一般用來連接鼠標和外置Modem以及老式攝像頭和寫字板等設(shè)備。串口也可以應(yīng)用于兩臺計算機(或設(shè)備)之間的互聯(lián)及數(shù)據(jù)傳輸。
接口劃分標準
串口可分成同步串行接口和異步串行接口。
同步串行接口(英文:SynchronousSerialInterface,SSI)是一種常用的工業(yè)用通信接口。
異步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用異步接收/發(fā)送。
串行接口按電氣標準及協(xié)議來分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422與RS-485標準只對接口的電氣特性做出規(guī)定,不涉及接插件、電纜或協(xié)議。
RS-232
也稱標準串口,最常用的一種串行通訊接口。它是在1970年由美國電子工業(yè)協(xié)會(EIA)聯(lián)合貝爾系統(tǒng)、調(diào)制解調(diào)器廠家及計算機終端生產(chǎn)廠家共同制定的用于串行通訊的標準。它的全名是“數(shù)據(jù)終端設(shè)備(DTE)和數(shù)據(jù)通訊設(shè)備(DCE)之間串行二進制數(shù)據(jù)交換接口技術(shù)標準”。傳統(tǒng)的RS-232-C接口標準有22根線,采用標準25芯D型插頭座(DB25),后來使用簡化為9芯D型插座(DB9),現(xiàn)在應(yīng)用中25芯插頭座已很少采用。
RS-232采取不平衡傳輸方式,即所謂單端通訊。由于其發(fā)送電平與接收電平的差僅為2V至3V左右,所以其共模抑制能力差,再加上雙絞線上的分布電容,其傳送距離最大為約15米,最高速率為20kb/s。RS-232是為點對點(即只用一對收、發(fā)設(shè)備)通訊而設(shè)計的,其驅(qū)動器負載為3~7kΩ。所以RS-232適合本地設(shè)備之間的通信。
RS-422
標準全稱是“平衡電壓數(shù)字接口電路的電氣特性”,它定義了接口電路的特性。典型的RS-422是四線接口。實際上還有一根信號地線,共5根線。其DB9連接器引腳定義。由于接收器采用高輸入阻抗和發(fā)送驅(qū)動器比RS232更強的驅(qū)動能力,故允許在相同傳輸線上連接多個接收節(jié)點,最多可接10個節(jié)點。即一個主設(shè)備(Master),其余為從設(shè)備(Slave),從設(shè)備之間不能通信,所以RS-422支持點對多的雙向通信。接收器輸入阻抗為4k,故發(fā)端最大負載能力是10×4k+100Ω(終接電阻)。RS-422四線接口由于采用單獨的發(fā)送和接收通道,因此不必控制數(shù)據(jù)方向,各裝置之間任何必須的信號交換均可以按軟件方式(XON/XOFF握手)或硬件方式(一對單獨的雙絞線)實現(xiàn)。
RS-422的最大傳輸距離為1219米,最大傳輸速率為10Mb/s。其平衡雙絞線的長度與傳輸速率成反比,在100kb/s速率以下,才可能達到最大傳輸距離。只有在很短的距離下才能獲得最高速率傳輸。一般100米長的雙絞線上所能獲得的最大傳輸速率僅為1Mb/s。
RS-485
是從RS-422基礎(chǔ)上發(fā)展而來的,所以RS-485許多電氣規(guī)定與RS-422相仿。如都采用平衡傳輸方式、都需要在傳輸線上接終接電阻等。RS-485可以采用二線與四線方式,二線制可實現(xiàn)真正的多點雙向通信,而采用四線連接時,與RS-422一樣只能實現(xiàn)點對多的通信,即只能有一個主(Master)設(shè)備,其余為從設(shè)備,但它比RS-422有改進,無論四線還是二線連接方式總線上可多接到32個設(shè)備。
RS-485與RS-422的不同還在于其共模輸出電壓是不同的,RS-485是-7V至+12V之間,而RS-422在-7V至+7V之間,RS-485接收器最小輸入阻抗為12kΩ、RS-422是4kΩ;由于RS-485滿足所有RS-422的規(guī)范,所以RS-485的驅(qū)動器可以在RS-422網(wǎng)絡(luò)中應(yīng)用。
RS-485與RS-422一樣,其最大傳輸距離約為1219米,最大傳輸速率為10Mb/s。平衡雙絞線的長度與傳輸速率成反比,在100kb/s速率以下,才可能使用規(guī)定最長的電纜長度。只有在很短的距離下才能獲得最高速率傳輸。一般100米長雙絞線最大傳輸速率僅為1Mb/s。
CH340
由于串口(COM)不支持熱插拔及傳輸速率較低,目前大部分新主板和便攜電腦已開始取消該接口,只有工控和測量設(shè)備以及部分通信設(shè)備中還保留有串口。
現(xiàn)在的電腦大部分都有USB接口而沒有串口,為了使用串口,我們需要一個USB轉(zhuǎn)串口的芯片,它的功能是讓電腦把USB當串口來使用。這種類型的芯片很多,明德?lián)P教學(xué)板使用的是CH340芯片。
CH340是一個USB總線的轉(zhuǎn)接芯片,實現(xiàn)USB轉(zhuǎn)串口、USB轉(zhuǎn)IrDA紅外或者USB轉(zhuǎn)打印口。
在串口方式下,CH340提供常用的MODE聯(lián)絡(luò)信號,用于為計算機擴展異步串口中,或者將普通的串口設(shè)備直接升級到USB總線。
明德?lián)P教學(xué)板的串口功能原理如下圖所示。電腦通過USB線,連接到教學(xué)板上的USB接口,USB接口連接到CH340芯片,CH340芯片與FPGA相連。在FPGA看來,串口其實就是兩根線:輸入線USB_RXD和輸出線USB_TXD,其他電氣特性、電平轉(zhuǎn)換的工作,都由CH340搞好了。FPGA通過USB_RXD接收來自電腦過來的串口數(shù)據(jù);通過USB_TXD發(fā)數(shù)據(jù)給電腦。

圖?199
串口時序
USB_RXD和USB_TXD傳輸數(shù)據(jù)時,是將傳輸數(shù)據(jù)的每個字符一位接一位地傳輸。下面是USB_RXD和USB_TXD的時序。USB_RXD的時序由CH340芯片產(chǎn)生,F(xiàn)PGA根據(jù)時序來接收數(shù)據(jù);USB_TXD的時序由FPGA芯片產(chǎn)生,F(xiàn)PGA要按規(guī)范來產(chǎn)生時序,使得CH340可以正確地接收。我們可以把產(chǎn)生時序的叫MASTER(主),接收數(shù)據(jù)叫SLAVE(從)。

圖?200
串口時序主要包括:空閑、起始位、數(shù)據(jù)拉、校驗位和停止位。
空閑:空閑狀態(tài)下,數(shù)據(jù)線一直處于高電平狀態(tài)。
起始位:當MASTER要發(fā)送數(shù)據(jù)時,首先會將數(shù)據(jù)線拉低“一段時間”,從而告知SLAVE有數(shù)據(jù)要傳輸了,要做好準備。
數(shù)據(jù)位:起始位之后是數(shù)據(jù)位,數(shù)據(jù)位的位數(shù)由雙方約定,支持4、5、6、7、8位等。雙方約定后才能正確地傳輸。每個數(shù)據(jù)位傳輸時都會占用“一段時間”,并且是從低位開始傳輸。圖中LSB是低位的意思,MSB是高位的意思。例如要傳輸數(shù)據(jù)8’b00000001,傳輸時是先送最低位的“1”。
檢驗位:奇偶校驗是一種非常簡單常用的數(shù)據(jù)校驗方式,分為奇校驗和偶校驗。奇校驗需要保證傳輸?shù)臄?shù)據(jù)總共有奇數(shù)個邏輯高電平,若是偶校驗則要保證傳輸?shù)臄?shù)據(jù)有偶數(shù)個邏輯高電平。即“奇偶”的意思就是數(shù)據(jù)中(包括該校驗位)中1的個數(shù)。例如:傳輸?shù)臄?shù)據(jù)位是0100_0011。如果是奇校驗,校驗位是0,偶校驗校驗位是1。校驗位不是必須項,雙方可以約定不需要校驗位,或者用奇校驗,或者使用偶校驗。
停止位:最后一個是停止位,MASTER必須保證有停止位,即把數(shù)據(jù)線變高“一段時間”。由于數(shù)據(jù)是在傳輸線上定時的,并且每一個設(shè)備有其自己的時鐘,很可能在通信中兩臺設(shè)備間出現(xiàn)了小小的不同步。因此停止位不僅僅是表示傳輸?shù)慕Y(jié)束,并且提供計算機校正時鐘同步的機會。讓SLAVE可以正確地識別下一輪數(shù)據(jù)的起始位。假如沒有停止位,校驗碼剛好是0,數(shù)據(jù)連續(xù)發(fā)送,那么SLAVE就沒法判斷下一輪的起始位。對于SLAVE來說,接收完數(shù)據(jù)位或校驗位后就表示接收完成,在停止位不需要做什么,只是等待下一輪起始就夠了。
在時序圖中,每個數(shù)據(jù)都會傳輸“一段時間”,這個一段時間非常重要,傳輸雙方都要做好約定,否則就不能正確地通信。那么這個“一段時間”是多長時間呢?這跟波特率有關(guān)。在串口通信中,波特率是一個非常重要的概念。串口通信中常用的波特率是9600、19200、38400、57600、115200。波特率是每個碼元傳輸?shù)乃俾?,在二進制數(shù)據(jù)傳輸中,和比特率相同,都是每個比特數(shù)據(jù)傳輸?shù)乃俾?,其倒?shù)為1bit數(shù)據(jù)的位寬,也就是1bit數(shù)據(jù)持續(xù)的時間。有了這一時間段,就可用FPGA構(gòu)造計數(shù)器實現(xiàn)比特周期的延時,從而實現(xiàn)特定的數(shù)據(jù)傳輸波特率。
例如,假設(shè)波特率為9600,數(shù)據(jù)位為8位,沒有校驗位,電腦要發(fā)數(shù)據(jù)8’b00110001給FPGA。考慮到波特率為9600,即每位占用時間為1s/9600=104166ns。那么FPGA的USB_RXD(圖中的rx_uart)這根線將如下圖變化。

圖?201
在使用教學(xué)板的串口前,需要安裝CH340的驅(qū)動程序(下載鏈接:www.mdy-edu.coc/xxxxx)。下載后直接解壓安裝就可以了。
當用USB線接上電腦和教學(xué)板,并且教學(xué)板上電后,打開電腦的“設(shè)備管理器”,將會看到如下顯示。當出現(xiàn)該顯示時,表示驅(qū)程安裝成功并且已經(jīng)被電腦正確地識別。
我們可以從“設(shè)備管理器”中查看串口號,如下圖所示。圖中表示該串口號為XX。我們可以修改串口號,方法是:XXXXXX。
電腦要發(fā)送數(shù)據(jù)給FPGA,需要用到串口調(diào)試助手(下載鏈接:www.mdy-edu.com/xxxxx)。

圖?202
串口:可以選擇串口號,支持串口號1~4。如果連接的串口號不在此范圍,則需要從設(shè)備管理器中修改串口號,詳細見本章前面的描述。
波特率:選擇串口的波特率,支持9600、19200、38400、57600、115200。該選項影響了每一位碼元占用的時間。
校驗位:可選擇沒有檢驗位、奇校驗和偶校驗。
數(shù)據(jù)位:可以設(shè)置數(shù)據(jù)位的位數(shù),可選擇4~8位的數(shù)據(jù)拉。
停止位:可以設(shè)置停止位的時間長度。有1位、1.5位和2位可供選擇。
打開/關(guān)閉串口:用來打開和關(guān)閉串口。打開軟件時,串口默認是關(guān)閉狀態(tài)。注意,一定要設(shè)置好參數(shù)后,才能打開串口;一定要關(guān)閉串口后,才能關(guān)掉教學(xué)板電源和撥掉USB線。
十六進制顯示:本軟件支持ASCII顯示和十六進制顯示。勾選后,用十六進制顯示。例如FPGA發(fā)送8’b00110001,本軟件收到后會顯示31。如果不勾選,則是用ASCII碼顯示。下圖就是ASCII表。FPGA發(fā)送8’b00110001,即下表中十六進制的31,它所對應(yīng)的圖形為1,所以軟件只會顯示1。假設(shè)FPGA發(fā)送的是8’h00100011,即下表中十六進制的23,它所對應(yīng)的是圖形是#,所示軟件會顯示#。

圖?203
十六進制發(fā)送:本軟件支持ASCII發(fā)送和十六進制發(fā)送。勾選后,用十六進制發(fā)送。例如填寫“31”,按手動發(fā)送,那么FPGA收到的值是8’b00110001。如果不勾選,則是用ASCII碼發(fā)送。例如填寫“31”,按下手動發(fā)送,軟件將首先發(fā)送ASCII碼“3”所對應(yīng)的十六進制值8’h33,再發(fā)送ASCII碼“1”所對應(yīng)的十六進制值8’h31。也就是FPGA將收到兩個字節(jié)數(shù)據(jù):8’h33和8’h31。
2?設(shè)計目標
用戶設(shè)置串口調(diào)試助手的參數(shù):波特率為9600;數(shù)據(jù)位為8位;無奇偶校驗位;停止位為2比特;接收和發(fā)送都是16進制數(shù)。
用戶通過串口調(diào)試助手發(fā)送一個8位數(shù)據(jù)data,該8位數(shù)據(jù)data用于控制開發(fā)板上的8個LED燈。其中data[0]~data[7]分別控制LED0~LED7。當對應(yīng)的數(shù)據(jù)位為0時,該LED燈保持為亮,如果對應(yīng)的數(shù)據(jù)為1時,該LED保持為滅。例如,用記發(fā)送數(shù)據(jù)data=8’b10110010,發(fā)送后,開發(fā)板上的LED1、LED4、LED5和LED7保持為滅,其他保持為亮。
上板效果圖如下圖所示

圖?204
3?設(shè)計實現(xiàn)
3.1?頂層信號
新建目錄:D:mdy_bookuart。在該目錄中,新建一個名為uart.v的文件,并用GVIM打開,開始編寫代碼。
我們分析一下功能。串口調(diào)試助手發(fā)數(shù)據(jù)給FPGA,站在FPGA的角度來看,就是CH340通過控制RX信號,讓RX信號根據(jù)串口時序變化,從而告知FPGA數(shù)據(jù)信號。那么FPGA工程必須有一個接口信號,命名為rx_uart。
FPGA要控制8個LED燈的亮滅,那就要8個信號,或者一個8比特的信號,命令為led。
下面表格表示了硬件電路圖的連接關(guān)系。

綜上所述,我們這個工程需要4個信號,時鐘clk,復(fù)位rst_n,串口輸入信號rx_uart和輸出控制LED燈的8位信號led。
將module的名稱定義為uart。并且我們已經(jīng)知道該模塊有四個信號:clk、rst_n、rx_uart和dout。為此,代碼如下:

其中clk、rst_n和rx_uart是輸入信號,dout是輸出信號,其中clk、rst_n、rx_uart的值是0或者1,一根線即可,dout為8位位寬的,根據(jù)這些信息,我們補充輸入輸出端口定義。代碼如下:

3.2?信號設(shè)計
我們先分析要實現(xiàn)的功能,led信號控制了8個LED燈的亮滅,而具體哪些燈亮哪些燈滅,是取決于串口過來的數(shù)據(jù)。那串口過來的數(shù)據(jù),是如何告知FPGA,并與led對應(yīng)起來呢?我們可以看一下串口過來的時序。

圖?205
上面波形是CH340控制的信號rx_uart。如果CH340要發(fā)送一個8位數(shù)據(jù)data,它首先會將信號rx_uart變0并持續(xù)一段時間(起啟位),然后發(fā)送data[0]并持續(xù)一段時間,然后發(fā)送data[1]并持續(xù)一段時間,以此類推,發(fā)送data[7]并持續(xù)一段時間,最后CH340將rx_uart為1并持續(xù)一段時間(結(jié)束位)。這樣CH340就完成了數(shù)據(jù)的發(fā)送。
例如,CH340要發(fā)送的數(shù)據(jù)data=8’h00110001,則rx_uart的波形如下。

圖?206
再考慮一下時間信息。由于波特率是9600,那么每位持續(xù)的時間是1s/9600=104166ns。將時間信息補上波形。

圖?207
本開發(fā)板的晶振時鐘是50Mz,104166ns/20ns?約等于5208個時鐘周期。也就是說上面波形中,每個比特的持續(xù)時間約等于5208個時鐘周期。需要注意的是,5208只是一個估計的大概數(shù)字,實際情況會有偏差。同時,我們還有計數(shù)這是第幾個比特,用于讓我們判斷是開始位、數(shù)據(jù)位和停止位等。

圖?208
可以看出,我們需要2個計數(shù)器,1個計數(shù)器用于計算1比特的位寬長度:5208個時鐘周期,命名為cnt0;另一個用于計算有多少個比特,命名為cnt1。
很明顯cnt0每次都是數(shù)5208個,但cnt0的加1條件需要仔細分析。我們很清楚cnt0的加1區(qū)域是下面的灰度地方。

圖?209
目前沒有任何信號可以區(qū)分出此區(qū)域。參考至簡設(shè)計法案例2的方法,設(shè)計一個信號flag_add,當其為1表示上述灰度區(qū)域,即cnt0的加1區(qū)域。

圖?210
有了flag_add,我們就很明確,cnt0的加1條件是flag_add==1,數(shù)到5208下就結(jié)束。為此,可以寫出cnt0的代碼。

接下來思考cnt1。Cnt1用于表示數(shù)到第幾比特,每間隔一個比特時間,cnt1就會加1,也就是說,每當end_cnt0時,cnt1就會加1。所以cnt1的加1條件是:end_cnt0。而cnt1一共要數(shù)9個。故可以寫出cnt1的代碼。

我們增加了輔導(dǎo)信號flag_add,現(xiàn)在我們來思考如何設(shè)計這個flag_add。Flag_add有2個變化點:變0和變1。Flag_add什么時候會變1呢?我們從功能上來理解一下。上電后,PC如果沒有發(fā)送任何數(shù)據(jù),rx_uart是一直保持為1。此時cnt0和cnt1也無須計數(shù),也就是flag_add也一直保持為0。當PC要發(fā)送數(shù)據(jù)了,rx_uart就按串口時序變化,首先會發(fā)送一個開始位,即rx_uart由1變成0。FPGA看到rx_uart變1變成0后,就明白PC有數(shù)據(jù)要過來了,并且現(xiàn)在是開始位,此時cnt0和cnt1要計數(shù)了,也就是flag_add要變成1了。
從功能上理解,很容易就知道當原來flag_add為0,此時收到rx_uart由1變0(下降沿)時,flag_add就變成1。關(guān)鍵的是,我們?nèi)绾沃纑x_uart變1變成0呢?
3.2.1?邊沿檢測電路設(shè)計
有讀者會用下面方式來檢測rx_uart的下降沿。

將rx_uart放到ALWAYS語句的敏感列表,而敏感列表里剛好有一個negedge檢測下降沿的語句,這樣就實現(xiàn)了rx_uart的下降沿檢測。不得不說,這是一個聰明的做法。從代碼層面來說,這個功能貌似是可以實現(xiàn)的。并且如果是實驗工程,好像也能得到正確的結(jié)果。然而,在真正的工程實踐中,這是不可取的做法。
讀者有沒有想過,為什么我們數(shù)字電路里都是二進制0和1?也就是低電平為0,高電平為1。有讀者覺得這多浪費啊,為啥不搞多幾個電平,例如可以低電平為0,1V為1,2V為2,3V為3。這樣一根線就可以表示4種狀態(tài),也是四進制,效率不是提高了一倍嗎?對于一根線來說是這樣的,但對于一個系統(tǒng)來說則完成不同。系統(tǒng)要求器件越簡單越好,考慮得越少越好,這樣才能方便集成和擴展,才能無限地復(fù)制。1個四進制的器件,情愿選擇2個二進制的器件。另外,越簡單的器件,故障的可能性就越低。越簡單的器件,越容易優(yōu)化和發(fā)展。例如,二進制器件,我們不斷地優(yōu)化其體積和電壓范圍,則能不斷地發(fā)展。則四進制器件,則會收到各個方面的制約,是會受到瓶頸的。這也是為什么數(shù)字電路比模塊電路快速發(fā)展的原因。
至簡設(shè)計法也是同樣道理。別看我們的規(guī)則簡單,但就是制定了這么簡單的規(guī)則,我們的設(shè)計角度就從波形設(shè)計轉(zhuǎn)到了功能設(shè)計。我們的頭腦中,不再去想復(fù)雜的波形,不再去想著對齊波形時序。我們更關(guān)注的是功能,例如計數(shù)10下,我們就直接用add_cnt && cnt==10-1表示。Dout信號在數(shù)到10個時就變高,我們就會寫出下面代碼:
If(add_cnt && cnt==10-1)
dout <= 1;
這就是功能設(shè)計。
當然,有讀者會疑問,這樣不用考慮波形,真能保證波形是正確的嗎?其實,這方面已經(jīng)有明德?lián)P的規(guī)范來保證,只要遵守了明德?lián)P的規(guī)范,一定能保證正確性。讀者可以試著挑些代碼,從波形上驗證正確性。
有工程師工作了10年,但卻只有2年的工作經(jīng)驗。即使工作10年,也只是舊經(jīng)驗的簡單重復(fù),絲毫沒有層次的上升。正常的上升道路應(yīng)該是:一年波形設(shè)計(熟練掌握各種接口時序設(shè)計),2年功能設(shè)計(任何算法和功能,都能簡單高效地設(shè)計出來),5年FPGA架構(gòu)設(shè)計(能設(shè)計出高效的FPGA內(nèi)部架構(gòu),精通模塊劃分),7年項目設(shè)計(ARM、DSP和FPGA之間的功能劃分),10年產(chǎn)品設(shè)計(客戶需求的落地,轉(zhuǎn)化到項目設(shè)計)。
時鐘和復(fù)位,關(guān)系到整個FPGA工程的穩(wěn)定。一般要求時鐘精確穩(wěn)定,抖動要小。FPGA里所有的觸發(fā)器,都在時鐘的節(jié)拍下,統(tǒng)一進行翻轉(zhuǎn)。由于時鐘周期是固定的,工程師在設(shè)計時會考慮電路延時,以便在時鐘下次上升沿前計算完畢。只要所有的電路延時,都能夠在在下次時鐘沿前處理完畢,統(tǒng)一翻轉(zhuǎn),那么整個系統(tǒng)都是穩(wěn)定的。但如果一個系統(tǒng)中,時鐘過多,擁有不同的時鐘周期,那么每個電路延時要求就不盡相同,工程師要考慮的也就越多,系統(tǒng)也就越不穩(wěn)定。所以,一個工程,時鐘越少越好,越簡單就越穩(wěn)定。復(fù)位也是同樣的道理。所以,我們在設(shè)計時,切忌將信號接到敏感列表那里,接到那里的信號,就會被系統(tǒng)認為是時鐘或者復(fù)位。
檢查rx_uart的下降沿,就要用到FPGA里的邊沿檢測技術(shù)。所謂的邊沿檢測,就是檢測輸入信號,或者FPGA內(nèi)部邏輯信號的跳變,即上升沿或者下降沿的檢測。這在FPGA電路設(shè)計中相當?shù)膹V泛。其電路圖如下。

圖?211

圖?212
中間信號,trigger連到觸發(fā)器的信號輸入端D,觸發(fā)器的輸出器連的是tri_ff0。將trigger取反,與tri_ff0相與,就得到信號neg_edge,如果neg_edge=1就表示檢測到trigger的下降沿。將tri_ff0取反,與trigger相與,就得到信號pos_edge,如果pos_edge=1,就表示檢測到trigger的上升沿。
我們來講解這個原理,畫出信號的波形圖。

圖?213
Tri_ff0是觸發(fā)器的輸出,因此tri_ff0的信號與trigger信號相似,只是相差一個時鐘周期。我們也可以這樣理解:每個時鐘上升沿看到的tri_ff0的值,其實就是triffer信號上一個時鐘看到的值,也就是tri_ff0是trigger之前的值。
然后我們在看第3時鐘上升沿,此時trigger值為0,而tri_ff0的值為1,即當前trigger的值為0,之前的值為1,這就是下降沿,此時neg_edge為1。當看到neg_edge為1,就表示檢測到trigger的下降沿了。
同樣道理,在第7個時鐘上升沿,看到trigger值為1,而之前值為0,pos_edge為1,表示檢測到trigger的上升沿。
Verilog實現(xiàn)邊沿檢測電路的代碼。

3.2.2?異步信號同步化
在討論邊沿檢測的波形中,我們把trigger當成理想的同步信號,也就是trigger是滿足D觸發(fā)器的建立和保持時間的,這在同步系統(tǒng)中不是問題。但如果trigger不是理想的同步信號,例如外部按鍵信號,例如本工程的rx_uart信號。這些信號什么時候變化,完全是隨機的。很有可能,在時鐘上升沿變化,從而不滿足觸發(fā)器的建立時間和保持時間要求,從而出現(xiàn)亞穩(wěn)態(tài),導(dǎo)致系統(tǒng)崩潰。詳細的原因,可以看D觸發(fā)器中,亞穩(wěn)態(tài)一節(jié)的內(nèi)容。根據(jù)這一節(jié)內(nèi)容的結(jié)論,我們需要對進來的信號打兩拍(用兩個觸發(fā)器寄存一下),再來使用。

圖?214
假設(shè)輸入的信號trigger不是同步信號,那么要將該信號用2個觸發(fā)器進行寄存,得到tri_ff0和tri_ff1。需要特別注意的是,tri_ff0絕對不要拿來當條件使用,只能使用tri_ff1。我們還需要檢測邊沿,根據(jù)前面所說,再用寄存器寄存,得到tri_ff2。根據(jù)tri_ff1和tri_ff2,我們就可以得到邊沿檢測。當tri_ff1==1且tri_ff2==0時,上升沿的pos_edge有效;當tri_ff1==0且tri_ff2==1時,下降沿的neg_edge有效。

我們總結(jié)一下。如果通過打兩拍的方式,實現(xiàn)了信號的同步化。我們通過打一拍的方式,實現(xiàn)邊沿檢測電路。這兩者不是一定同時出現(xiàn)的。如果進來的信號是異步信號,那就必須先同步化,然后再做檢測。如果進來的信號本身就是同步信號,那就沒有必要做同步化了,直接做邊沿檢測即可。
回到本工程的設(shè)計,我們需要檢測rx_uart的下降沿,從而讓flag_add變高。同時,我們注意到rx_uart是異步信號(PC?什么時候發(fā)送數(shù)據(jù)就是隨機的)。所以需要將rx_uart先同步化,再做下降沿檢測。所以先設(shè)計如下代碼:

這樣,flag_add變1的條件就變成:rx_uart_ff1==0 && rx_uart_ff2==1。
Flag_add變0的條件,可以完成收完9比特數(shù)據(jù)就變0,不用再計數(shù)了。所以變0條件:end_cnt1。
綜上所述,可以寫出flag_add的代碼。

設(shè)計下data信號,該信號的值來自于圖中第2~第9比特的值。第2比特的值賦給data[0],第3比特的值賦給data[1],以此類推,第9比特的值賦給data[7]。

圖?215
由于每一個比特都持續(xù)5208個時鐘周期,我們必須選定一個時刻,將值賦給data。

圖?216
首先,不能在end_cnt0的時候賦值,如上圖的點。因為我們這里的5208個時鐘周期是理想、估算的數(shù)值,實際上是非常有可能有偏差的。如果我們在end_cnt0的時候取值,就有可能采錯。
最保險的做法是在中間點取值。這樣,即使有比較多的偏差,都不會影響到采樣的正確性。

圖?217
綜上所述,我們在cnt0數(shù)到一半時采到當前rx_uart的值賦給dout,其中第2比特賦給led[0],第3比特賦給led[1],以此類推,第9比特賦給led[7]。
進一步用信號表示,可翻譯成:數(shù)到add_cnt0 && cnt0==5208/2 -1時,如果cnt1==1,則將rx_uart_ff1賦給led[0]。如果cnt1==2,則將rx_uart_ff1賦給led[1],以此類推,如果cnt1==8,將rx_uart_ff1賦給led[7]。
那么直接翻譯成代碼。

上面代碼可優(yōu)化,簡寫成如下:

通常我們設(shè)計時,首先是想到實現(xiàn)功能,所以會先寫出前面代碼。在功能實現(xiàn)的前提下,再考慮有沒有優(yōu)化空間,從而寫出后面代碼。好代碼都是一步步優(yōu)化出來的。
注意,上面代碼,我們采集的是rx_uart_ff1而不是rx_uart信號。這是因為rx_uart是異步信號,我們只能用同步化后的信號,否則會引起亞穩(wěn)態(tài)。所以只能是rx_uart_ff1。
至此,主體程序已經(jīng)完成。接下來是將module補充完整。
3.3?信號定義
cnt0是用always產(chǎn)生的信號,因此類型為reg。cnt0計數(shù)的最大值為5208,需要用13根線表示,即位寬是13位。因此代碼如下:

add_cnt0和end_cnt0都是用assign方式設(shè)計的,因此類型為wire。并且其值是0或者1,1個線表示即可。因此代碼如下:

cnt1是用always產(chǎn)生的信號,因此類型為reg。cnt1計數(shù)的最大值為9,需要用4根線表示,即位寬是4位。因此代碼如下:

add_cnt1和end_cnt1都是用assign方式設(shè)計的,因此類型為wire。并且其值是0或者1,1根線表示即可。因此代碼如下:

flag_add是用always方式設(shè)計的,因此類型為reg。并且其值是0或者1,1根線表示即可。因此代碼如下:

rx_uart_ff0、rx_uart_ff1和rx_uart_ff2是用always方式設(shè)計的,因此類型為reg。并且其值是0或1,需要1根線表示即可。因此代碼如下:

至此,整個代碼的設(shè)計工作已經(jīng)完成。下一步是新建工程和上板查看現(xiàn)象。
4?綜合工程和上板
4.1?新建工程
1.?首先在d盤中創(chuàng)建名為“uart”的工程文件夾,將寫的代碼命名為“uart.v”,頂層模塊名為“uart”。

圖?218

圖?219
2.?然后打開Quartus?Ⅱ,點擊File下拉列表中的New Project Wzard...新建工程選項。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??圖?220
3.在出現(xiàn)的界面中直接點擊最下方的“Next”。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??圖?221
4.之后出現(xiàn)的是工程文件夾、工程名、頂層模塊名設(shè)置界面。按照之前的命名進行填寫,第一欄選擇工程文件夾“uart”,第二欄選擇工程文件“uart.v”,最后一欄選擇頂層模塊名“uart”,然后點擊”Next”,再出現(xiàn)的界面選擇empty?project。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖?222

圖?223
5.之后是文件添加界面。在上方一欄中添加之前寫的uart.v文件,點擊右側(cè)的“Add”按鈕,之后文件還會出現(xiàn)在大方框中,之后點擊“Next”。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??圖?224
6.?器件型號選擇界面。在“Device family”處選擇Cyclone?ⅣE,在“Available devices”處選擇EP4CE15F23C8,然后點擊“Next”。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖?225
7.?EDA工具界面。該頁面用默認的就行,直接點擊最下方“Next”。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖?226
8.?之后出現(xiàn)的界面是我們前面的設(shè)置的總結(jié),確認沒有錯誤后點擊“Finish”。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖?227
4.2?綜合
1.新建工程步驟完成后,就會出現(xiàn)以下界面。在“Project Navigator”下選中要編譯的文件,點擊上方工具欄中“Start Compilation”編譯按鈕(藍色三角形)。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖?228
2.編譯成功后會出現(xiàn)以下界面,點擊“OK”。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??圖?229
4.3?配置管腳

圖?230
在菜單欄中,選中Assignments,然后選擇Pin Planner,就會彈出配置管腳的窗口。

圖?231
在配置窗口最下方中的location一列,參考下表中最右兩列配置好FPGA管腳。

配置完成后,關(guān)閉Pin Planner,軟件自動會保存管腳配置信息。
4.4?再次綜合

圖?232
在菜單欄中,選中Processing,然后選擇Start Compilation,再次對整個工程進行編譯和綜合。

圖?233
出現(xiàn)上面的界面,就說明編譯綜合成功。
4.5?連接開發(fā)板
圖中,下載器接入電腦USB接口,電源接入電源,uart線連接電腦USB,然后摁下電源開關(guān),看到開發(fā)板燈亮。

圖?234
4.6?上板
1.雙擊Tasks一欄中”Program Device”。

圖?235
2.會出現(xiàn)如下界面,點擊add file添加.sof文件,在右側(cè)點擊“Start”,會在上方的“Progress”處顯示進度。

圖?236
3.進度條中提示成功后,即可在顯示器上觀察到相應(yīng)的現(xiàn)象。
4.7?串口調(diào)試
1.安裝串口調(diào)試工具。

圖?237
2.開發(fā)板連接完成,打開電源后,在設(shè)備管理器中查看串口名。

圖?238
3.打開串口調(diào)試助手,在串口處選擇之前查看的串口名,波特率、校驗位、數(shù)據(jù)位、停止位根據(jù)之前給出的數(shù)據(jù)進行填寫,發(fā)送和接收都選擇十六進制。

圖?239
4.上板成功之后,在發(fā)送數(shù)據(jù)欄輸入相應(yīng)的數(shù)據(jù)(將8個LED燈對應(yīng)的8位二進制數(shù)轉(zhuǎn)化為十六進制),然后發(fā)送,即可在開發(fā)板上看到相應(yīng)的現(xiàn)象。

明德?lián)P科技教育