USB的NRZI編碼
首先,USB 的數據是串行發送的,就像 UART、I2C、SPI 等等,連續的01 信號只通過一根數據線發給接受者。
但是因為發送者和接收者運行的頻率不一樣,信號的同步就是個問題,比如,接受者接收到了一個持續
一段時間的低電平,無法得知這究竟是代表了5個0 還是1000個0。
一個解決辦法,就是在傳輸數據信號的同時,附加一個時鐘信號,用來同步兩端的傳輸,接受者在時鐘
信號的輔助下對數據信號采樣,就可以正確解析出發送的數據了,比如 I2C 就是這樣做的,SDA 來傳
輸數據,SCL 來傳輸同步時鐘:

雖然這樣解決了問題,但是卻需要附加一根時鐘信號線來傳輸時鐘。有沒有不需要附加的時鐘信號,也
能保持兩端的同步呢?
有的,這就是 RZ 編碼(Return-to-zero Code),也叫做歸零編碼。
在 RZ 編碼中,正電平代表邏輯 1,負電平代表邏輯 0,并且,每傳輸完一位數據,信號返回到零電
平,也就是說,信號線上會出現 3 種電平:正電平、負電平、零電平:

從圖上就可以看出來,因為每位傳輸之后都要歸零,所以接收者只要在信號歸零后采樣即可,這樣就不
在需要單獨的時鐘信號。實際上, RZ 編碼就是相當于把時鐘信號用歸零編碼在了數據之內。這樣的信
號也叫做自同步(self-clocking)信號。
這樣雖然省了時鐘數據線,但是還是有缺點的,因為在 RZ 編碼中,大部分的數據帶寬,都用來傳
輸“歸零”而浪費掉了。
那么,我們去掉這個歸零步驟,NRZ 編碼(Non-return-to-zero Code)就出現了,和 RZ 的區別就是
NRZ 是不需要歸零的:

這樣,浪費的帶寬又回來了,不過又喪失寶貴的自同步特性了,貌似我們又回到了原點,其實這個問題
也是可以解決的,不過待會兒再講,先看看什么是 NRZI:
NRZI 編碼(Non-Return-to-Zero Inverted Code)和 NRZ 的區別就是 NRZI 用信號的翻轉代表一個
邏輯,信號保持不變代表另外一個邏輯。
USB 傳輸的編碼就是 NRZI 格式,在 USB 中,電平翻轉代表邏輯 0,電平不變代表邏輯1:

翻轉的信號本身可以作為一種通知機制,而且可以看到,即使把 NRZI 的波形完全翻轉,所代表的數據
序列還是一樣的,對于像 USB 這種通過差分線來傳輸的信號尤其方便~
現在再回到那個同步問題:
的確,NRZ 和 NRZI 都沒有自同步特性,但是可以用一些特殊的技巧解決。
比如,先發送一個同步頭,內容是 0101010 的方波,讓接受者通過這個同步頭計算出發送者的頻率,
然后再用這個頻率來采樣之后的數據信號,就可以了。
在 USB 中,每個 USB 數據包,最開始都有個同步域(SYNC),這個域固定為 0000 0001,這個域通
過 NRZI 編碼之后,就是一串方波(復習下前面:NRZI 遇 0 翻轉遇 1
此外,因為在 USB 的 NRZI 編碼下,邏輯 0 會造成電平翻轉,所以接收者在接收數據的同時,根據接
收到的翻轉信號不斷調整同步頻率,保證數據傳輸正確。
但是,這樣還是會有一個問題,就是雖然接收者可以主動和發送者的頻率匹配,但是兩者之間總會有誤差。
假如數據信號是 1000個邏輯1,經過 USB 的 NRZI 編碼之后,就是很長一段沒有變化的電平,在這種
情況下,即使接受者的頻率和發送者相差千分之一,就會造成把數據采樣成 1001個或者 999個了。
USB對這個問題的解決辦法,就是強制插0,也就是傳說中的bit-stuffing,如果要傳輸的數據中有7個
連續的1,發送前就會在第6個1后面強制插入一個0,讓發送的信號強制出現翻轉,從而強制接受者進
行頻率調整。接受者只要刪除6個連續 1 之后的0,就可以恢復原始的數據了。
2,來至ST的另外一個解釋

評論