1??項(xiàng)目背景? ?(技術(shù)Q交流群:544453837)
? ?? ?RS-485
? ?? ?是從RS-422基礎(chǔ)上發(fā)展而來的,所以RS-485許多電氣規(guī)定與RS-422相仿。如都采用平衡傳輸方式、都需要在傳輸線上接終接電阻等。RS-485可以采用二線與四線方式,二線制可實(shí)現(xiàn)真正的多點(diǎn)雙向通信,而采用四線連接時(shí),與RS-422一樣只能實(shí)現(xiàn)點(diǎn)對(duì)多的通信,即只能有一個(gè)主(Master)設(shè)備,其余為從設(shè)備,但它比RS-422有改進(jìn),無論四線還是二線連接方式總線上可多接到32個(gè)設(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ū)動(dòng)器可以在RS-422網(wǎng)絡(luò)中應(yīng)用。
? ?? ?RS-485與RS-422一樣,其最大傳輸距離約為1219米,最大傳輸速率為10Mb/s。平衡雙絞線的長(zhǎng)度與傳輸速率成反比,在100kb/s速率以下,才可能使用規(guī)定最長(zhǎng)的電纜長(zhǎng)度。只有在很短的距離下才能獲得最高速率傳輸。一般100米長(zhǎng)雙絞線最大傳輸速率僅為1Mb/s。
? ?? ?CH340
? ?? ?由于串口(COM)不支持熱插拔及傳輸速率較低,目前大部分新主板和便攜電腦已開始取消該接口,只有工控和測(cè)量設(shè)備以及部分通信設(shè)備中還保留有串口。
? ?? ?現(xiàn)在的電腦大部分都有USB接口而沒有串口,為了使用串口,我們需要一個(gè)USB轉(zhuǎn)串口的芯片,它的功能是讓電腦把USB當(dāng)串口來使用。這種類型的芯片很多,明德?lián)P教學(xué)板使用的是CH340芯片。
? ?? ?CH340是一個(gè)USB總線的轉(zhuǎn)接芯片,實(shí)現(xiàn)USB轉(zhuǎn)串口、USB轉(zhuǎn)IrDA紅外或者USB轉(zhuǎn)打印口。
? ?? ?在串口方式下,CH340提供常用的MODE聯(lián)絡(luò)信號(hào),用于為計(jì)算機(jī)擴(kuò)展異步串口中,或者將普通的串口設(shè)備直接升級(jí)到USB總線。
? ?? ?明德?lián)P教學(xué)板的串口功能原理如下圖所示。電腦通過USB線,連接到教學(xué)板上的USB接口,USB接口連接到CH340芯片,CH340芯片與FPGA相連。在FPGA看來,串口其實(shí)就是兩根線:輸入線USB_RXD和輸出線USB_TXD,其他電氣特性、電平轉(zhuǎn)換的工作,都由CH340搞好了。FPGA通過USB_RXD接收來自電腦過來的串口數(shù)據(jù);通過USB_TXD發(fā)數(shù)據(jù)給電腦。

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

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


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

Tri_ff0是觸發(fā)器的輸出,因此tri_ff0的信號(hào)與trigger信號(hào)相似,只是相差一個(gè)時(shí)鐘周期。我們也可以這樣理解:每個(gè)時(shí)鐘上升沿看到的tri_ff0的值,其實(shí)就是triffer信號(hào)上一個(gè)時(shí)鐘看到的值,也就是tri_ff0是trigger之前的值。
? ?? ?然后我們?cè)诳吹?時(shí)鐘上升沿,此時(shí)trigger值為0,而tri_ff0的值為1,即當(dāng)前trigger的值為0,之前的值為1,這就是下降沿,此時(shí)neg_edge為1。當(dāng)看到neg_edge為1,就表示檢測(cè)到trigger的下降沿了。
? ?? ?同樣道理,在第7個(gè)時(shí)鐘上升沿,看到trigger值為1,而之前值為0,pos_edge為1,表示檢測(cè)到trigger的上升沿。
? ?? ?Verilog實(shí)現(xiàn)邊沿檢測(cè)電路的代碼。
?Tri_ff0是觸發(fā)器的輸出,因此tri_ff0的信號(hào)與trigger信號(hào)相似,只是相差一個(gè)時(shí)鐘周期。我們也可以這樣理解:每個(gè)時(shí)鐘上升沿看到的tri_ff0的值,其實(shí)就是triffer信號(hào)上一個(gè)時(shí)鐘看到的值,也就是tri_ff0是trigger之前的值。
? ?? ?然后我們?cè)诳吹?時(shí)鐘上升沿,此時(shí)trigger值為0,而tri_ff0的值為1,即當(dāng)前trigger的值為0,之前的值為1,這就是下降沿,此時(shí)neg_edge為1。當(dāng)看到neg_edge為1,就表示檢測(cè)到trigger的下降沿了。
? ?? ?同樣道理,在第7個(gè)時(shí)鐘上升沿,看到trigger值為1,而之前值為0,pos_edge為1,表示檢測(cè)到trigger的上升沿。
? ?? ?Verilog實(shí)現(xiàn)邊沿檢測(cè)電路的代碼。
3.2.2??異步信號(hào)同步化
? ?? ?在討論邊沿檢測(cè)的波形中,我們把trigger當(dāng)成理想的同步信號(hào),也就是trigger是滿足D觸發(fā)器的建立和保持時(shí)間的,這在同步系統(tǒng)中不是問題。但如果trigger不是理想的同步信號(hào),例如外部按鍵信號(hào),例如本工程的rx_uart信號(hào)。這些信號(hào)什么時(shí)候變化,完全是隨機(jī)的。很有可能,在時(shí)鐘上升沿變化,從而不滿足觸發(fā)器的建立時(shí)間和保持時(shí)間要求,從而出現(xiàn)亞穩(wěn)態(tài),導(dǎo)致系統(tǒng)崩潰。詳細(xì)的原因,可以看D觸發(fā)器中,亞穩(wěn)態(tài)一節(jié)的內(nèi)容。根據(jù)這一節(jié)內(nèi)容的結(jié)論,我們需要對(duì)進(jìn)來的信號(hào)打兩拍(用兩個(gè)觸發(fā)器寄存一下),再來使用。
假設(shè)輸入的信號(hào)trigger不是同步信號(hào),那么要將該信號(hào)用2個(gè)觸發(fā)器進(jìn)行寄存,得到tri_ff0和tri_ff1。需要特別注意的是,tri_ff0絕對(duì)不要拿來當(dāng)條件使用,只能使用tri_ff1。我們還需要檢測(cè)邊沿,根據(jù)前面所說,再用寄存器寄存,得到tri_ff2。根據(jù)tri_ff1和tri_ff2,我們就可以得到邊沿檢測(cè)。當(dāng)tri_ff1==1且tri_ff2==0時(shí),上升沿的pos_edge有效;當(dāng)tri_ff1==0且tri_ff2==1時(shí),下降沿的neg_edge有效。
? ?? ?我們總結(jié)一下。如果通過打兩拍的方式,實(shí)現(xiàn)了信號(hào)的同步化。我們通過打一拍的方式,實(shí)現(xiàn)邊沿檢測(cè)電路。這兩者不是一定同時(shí)出現(xiàn)的。如果進(jìn)來的信號(hào)是異步信號(hào),那就必須先同步化,然后再做檢測(cè)。如果進(jìn)來的信號(hào)本身就是同步信號(hào),那就沒有必要做同步化了,直接做邊沿檢測(cè)即可。
? ?? ?回到本工程的設(shè)計(jì),我們需要檢測(cè)rx_uart的下降沿,從而讓flag_add變高。同時(shí),我們注意到rx_uart是異步信號(hào)(PC 什么時(shí)候發(fā)送數(shù)據(jù)就是隨機(jī)的)。所以需要將rx_uart先同步化,再做下降沿檢測(cè)。所以先設(shè)計(jì)如下代碼:
? ?? ?這樣,flag_add變1的條件就變成:rx_uart_ff1==0&& rx_uart_ff2==1。
? ?? ?Flag_add變0的條件,可以完成收完9比特?cái)?shù)據(jù)就變0,不用再計(jì)數(shù)了。所以變0條件:end_cnt1。
? ?? ?綜上所述,可以寫出flag_add的代碼。

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

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

首先,不能在end_cnt0的時(shí)候賦值,如上圖的點(diǎn)。因?yàn)槲覀冞@里的5208個(gè)時(shí)鐘周期是理想、估算的數(shù)值,實(shí)際上是非常有可能有偏差的。如果我們?cè)趀nd_cnt0的時(shí)候取值,就有可能采錯(cuò)。
? ?? ?最保險(xiǎn)的做法是在中間點(diǎn)取值。這樣,即使有比較多的偏差,都不會(huì)影響到采樣的正確性。
綜上所述,我們?cè)赾nt0數(shù)到一半時(shí)采到當(dāng)前rx_uart的值賦給dout,其中第2比特賦給led[0],第3比特賦給led[1],以此類推,第9比特賦給led[7]。
? ?? ?進(jìn)一步用信號(hào)表示,可翻譯成:數(shù)到add_cnt0 && cnt0==5208/2 -1時(shí),如果cnt1==1,則將rx_uart_ff1賦給led[0]。如果cnt1==2,則將rx_uart_ff1賦給led[1],以此類推,如果cnt1==8,將rx_uart_ff1賦給led[7]。
? ?? ?那么直接翻譯成代碼。
上面代碼可優(yōu)化,簡(jiǎn)寫成如下:

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

?flag_add是用always方式設(shè)計(jì)的,因此類型為reg。并且其值是0或者1,1根線表示即可。因此代碼如下:
?rx_uart_ff0、rx_uart_ff1和rx_uart_ff2是用always方式設(shè)計(jì)的,因此類型為reg。并且其值是0或1,需要1根線表示即可。
4? ???綜合工程和上板4.1??新建工程
? ?? ?1.首先在d盤中創(chuàng)建名為“uart”的工程文件夾,將寫的代碼命名為“uart.v”,頂層模塊名為“uart”。
電子發(fā)燒友App
















































評(píng)論