1.USB協定簡介
https://blog.csdn.net/songze_lee/article/details/77658094
2017年08月28日 23:20:16 songze_lee 閱讀數:22978
版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/songze_lee/article/details/77658094
最近學習usb相關的知識,一直感覺入不了門,看《linux那些事兒之我是usb》,對usb協定也不是很熟悉,沒能堅持看下去,直到看了《圈圈教你玩usb》一書,把自己的興趣立馬提了起來,大牛圈圈用51單片機實作了usb滑鼠鍵盤等裝置,讓人非常佩服,51單片機自己還是很熟悉,大學玩了四年單片機,單片機來實作立馬感覺親切了許多,決定先從單片機入手學,後面再看linux那些事兒之我是usb,深入學習linux核心下usb,以下先介紹usb協定,内容整理來自usb spec、網絡和圈圈書中,文中也大量引用STM32官方的USB2.0協定文檔,歡迎批評指正。另外學習《圈圈教你玩usb》動手調試實作了單片機usb滑鼠功能,源碼在https://github.com/lisongze2016/mcu_project/tree/master/USB,源碼中結合使用了linux kernel ch9.h中對描述符的結構體定義,對usb協定有了深入的了解。
USB(Universal Serial Bus)全稱通用序列槽總線,USB為解決即插即用需求而誕生,支援熱插拔。USB協定版本有USB1.0、USB1.1、USB2.0、USB3.1等,USB2.0目前比較常用,以下以2.0為主介紹。由于USB是主從模式的結構,裝置與裝置之間、主機與主機之間不能互連,為解決這個問題,擴大USB的應用範圍,出現了USB OTG,全拼 ON The Go。USB OTG 同一個裝置,在不同的場合下可行在主機和從機之間切換。
1.1 USB特點:
USB1.0和USB1.1支援1.5Mb/s的低速模式和12Mb/bs的全速模式。在USB2.0以上支援480Mb/s的高速模式。應用如下:
1.2 USB裝置供電方式:
USB裝置有兩種供電方式
自供電裝置:裝置從外部電源擷取工作電壓
總線供電裝置:裝置從VBUS(5v) 取電
對總線供電裝置,區分低功耗和高功耗USB裝置
低功耗總線供電裝置:最大功耗不超過100mA
高功耗總線供電裝置: 枚舉時最大功耗不超過100mA,枚舉完成配置結束後功耗不超過500mA
裝置在枚舉過程中,通過裝置的配置描述符向主機報告它的供電配置(自供電/總線供電)以及它的功耗要求
如下 USB 配置描述符(以Joystick為例),後面具體介紹:
1.3 USB總線信号:
USB使用的是差分傳輸模式,兩個資料線D+和D-
差分信号1:D+ > VOH(min) (2.8V) 且D- < VOL(max)(0.3V)
差分信号0:D- > VOH and D+ < VOL
J狀态(高電平):D+ 高,D- 低
K狀态(低電平):D+低,D- 高
SEO狀态:D+ 低,D- 高
Reset信号:D+ and D- < VOL for >= 10ms
主機在要和裝置通信之前會發送Reset信号來把裝置設定到預設的未配置狀态。即主機拉低兩根信号線(SE0狀态)
并保持10ms
Idle狀态:J狀态資料發、送前後總線的狀态
Suspend狀态:3ms以上的J狀态
SYNC: 3個KJ狀态切換,後跟随2位時間的K狀态(看到的波形變化是總線上發送0000 0001經過NRZI編碼後的波形)
Resume信号:20ms的K狀态+低速EOP
主機在挂起裝置後可通過翻轉資料線上的極性并保持20ms來喚醒裝置,并以低速EOP信号結尾
帶遠端喚醒功能的裝置還可自己發起該喚醒信号;前提是裝置已進入idle狀态至少5ms,然後發出喚醒K信号,維持1ms到15ms并由主機在1ms内接管來繼續驅動喚醒信号
SOP:從IDLE狀态切換到K狀态
EOP:持續2位時間的SE0信号,後跟随1位時間的J狀态
Keep alive即低速EOP信号
1.4 USB插入檢測和速度檢測:
主機通過裝置在D+或D-上的1.5K上拉來檢測裝置的連接配接和斷開事件,并由此判别裝置的速度
主機先把高速裝置檢測為全速裝置,然後再通過“Chirp序列”的總線握手機制來識别高速和全速裝置
USB連接配接和斷開連接配接:
裝置連上主機時(連接配接)
當主機檢測到某一個資料線電平拉高并保持了一段時間,就認為有裝置連上來了
主機必需在驅動SE0狀态以複位裝置之前,立刻采樣總線狀态來判斷裝置的速度
沒有裝置連上主機時(斷開)
D+和D-資料線上的下拉電阻起作用,使得二者都在低電平;主機端看來就是個SE0狀态;同樣地,當資料線上的SE0狀态持續一段時間了,就被主機認為是斷開狀态
1.5 資料編解碼和位填充
USB采用NRZI(非歸零編碼)對發送的資料包進行編碼
輸入資料0, 編碼成“電平翻轉”
輸入資料1, 編碼成“電平不變”
編碼出來的序列,高電平:J狀态;低電平:K狀态
位填充是為了保證發送的資料序列中有足夠多的電平變化
填充的對象是(輸入資料),即先填充再編碼
資料流中每6個連續的“1”,就要插入1個“0”,進而保證編碼
資料出現電平變化
接收方指派解碼NRZI碼流,然後識别出填充位,并丢棄它們
2. USB傳輸
一個傳輸有多個事務組成,一個事務由2/3個包組成。
傳輸又分為四種類型:批量傳輸、等時(同步)傳輸、中斷傳輸、控制傳輸。
注意:USB傳輸資料先發資料低位再發高位資料
2.1 包
包的組成:
包的内容:
Packet分四大類: 指令 (Token) 、Packet 幀首 (Start of Frame) 、Packet 資料 (Data) 、Packet 握手 (Handshake) Packet
不同類型包,以上的組成部件有所不同
PID:
這裡隻用(PID0~4),PID4~7是PID0~4的取反,用來校驗PID
PID1~0:01 令牌包、11 資料包、10 握手包、00 特殊包
位址:
幀号:
資料:
CRC:
四種Packet類型之令牌包(Token Packet):
令牌包用來啟動一次USB傳輸。
輸出(OUT)令牌包:用來通知裝置将要輸出一個資料包
輸入(IN)令牌包:用來通知裝置傳回一個資料包
建立(SETUP)令牌包:隻用在控制傳輸中,和輸出令牌包作用一樣,也是通知裝置将要輸出一個資料包,兩者差別在于:
SETUP令牌包後隻使用DATA0資料包,且隻能發送到裝置的控制端點,并且裝置必須要接收,而OUT令牌包沒有這些限制
例子:
四種Packet類型之SOF Packet
幀起始包:在每幀(或微幀)開始時發送,以廣播的形式發送,所有USB全速裝置和高速裝置都可以接收到SOF包。
例子:
0xA5:1010 0101:對應上面PID表可知是幀起始包
四種Packet類型之Data Packet
例子:
四種Packet類型之Handshake Packet
例子:
2.2 事務
Transaction可以分成三類
Setup transaction:主機用來向裝置發送控制指令
Data IN transaction:主機用來從裝置讀取資料
Data OUT transaction:主機用來向裝置發送資料
Transaction的packet組成
Token packet:總是由主機發出
Data packet:包含此次transaction的資料負載
可選的Handshake packet
例子:
2.3 傳輸
USB協定定義了四種傳輸類型:
批量(大容量資料)傳輸(Bulk Transfers): 非周期性,突發
大容量資料的通信,資料可以占用任意帶寬,并容忍延遲 。如USB列印機、掃描器、大容量儲存裝置等
中斷傳輸(Interrupt Transfers): 周期性,低頻率
允許有限延遲的通信 如人機接口裝置(HID)中的滑鼠、鍵盤、軌迹球等
等時(同步)傳輸(Isochronous Transfers): 周期性
持續性的傳輸,用于傳輸與時效相關的資訊,并且在資料中儲存時間戳的資訊 ,如音頻視訊裝置
控制傳輸(Control Transfers): 非周期性,突發
用于指令和狀态的傳輸
2.3.1 批量傳輸
批量輸出事務,(1)主機先發出一個OUT令牌包(包含裝置位址,端點号),(2)然後再發送一個DATA包,這時位址和端點比對的裝置就會收下這個資料包,主機切換到接收模式,等待裝置傳回握手包,(3)裝置解碼令牌包,資料包都準确無誤,并且有足夠的緩沖區來儲存資料後就會使用ACK/NYET握手包來應答主機(隻有高速模式才有NYET握手包,他表示本次資料成功接收,但是沒有能力接收下一次傳輸),如果沒有足夠的緩沖區來儲存資料,就傳回NAC,告訴主機目前沒有緩沖區可用,主機會在稍後時間重新該批量傳輸事務。如果裝置檢查到資料正确,但端點處于挂起狀态,傳回STALL。如果檢測到有錯誤(如校驗錯誤,位填充錯誤),則不做任何響應,讓主機等待逾時。
批量輸入事務,(1)主機首先發送一個IN令牌包(包含裝置位址,端點号),(2)主機切換到接收資料狀态等待裝置傳回資料。如果裝置檢測到錯誤,不做任何響應,主機等待逾時。如果此時有位址和端點比對的裝置,并且沒有檢測到錯誤,則該裝置作出反應:裝置有資料需要傳回,就将一個資料包放在總線上;如果沒有資料需要傳回,裝置傳回NAK響應主機;如果該端點處于挂起狀态,裝置傳回STALL。如果主機收到裝置發送的資料包并解碼正确後,使用ACK握手包應答裝置。如果主機檢測到錯誤,則不做任何響應,裝置會檢測到逾時。注意:USB協定規定,不允許主機使用NAK來拒絕接收資料包。主機收到NAK,知道裝置暫時沒有資料傳回,主機會在稍後時間重新該批量輸入事務。
PING令牌包,它不發送資料,直到等待裝置的握手包。
2.3.2 中斷傳輸
中斷傳輸是一種保證查詢頻率的傳輸。中斷端點在端點描述符中要報告它的查詢間隔,主機會保證在小于
這個時間間隔的範圍内安排一次傳輸。
2.3.3 等時傳輸
等時(同步)傳輸用在資料量大、對實時性要求高的場合,如音頻裝置,視訊裝置等,這些裝置對資料的延遲很敏感。對于音頻或視訊裝置資料的100%正确性要求不高,少量的資料錯誤是可以容忍的,主要是保證資料不能停頓,是以等時傳輸是不保證資料100%正确的。當資料錯誤時,不再重傳操作。是以等時傳輸沒有應答包,資料是否正确,由資料的CRC校驗來确認。
2.3.4 控制傳輸
控制傳輸可分為三個過程:(1)建立過程 (2)資料過程(可選) (3)狀态過程
特性:
每個USB裝置都必須有控制端點,支援控制傳輸來進行指令和狀态的傳輸。USB主機驅動将通過控制傳輸與USB裝置的控制端點通信,完成USB裝置的枚舉和配置
方向:
控制傳輸是雙向的傳輸,必須有IN和OUT兩個方向上的特定端點号的控制端點來完成兩個方向上的控制傳輸
資料的拆分和資料傳輸完畢的判定
以高速裝置的最大資料包長度64位元組為例
要傳輸250位元組,拆分成4個packet
要傳輸正好256位元組,通過最後一個0位元組包告訴裝置傳輸完成
各種傳輸特性比較
3. USB标準請求
3.1 USB标準請求的資料結構
3.2 USB 裝置枚舉及描述符介紹
當一個USB裝置插入主機後,會有以下活動:
-
include/uapi/linux/usb/ch9.h
-
struct usb_device_descriptor {
-
__u8 bLength;
-
__u8 bDescriptorType;
-
__le16 bcdUSB;
-
__u8 bDeviceClass;
-
__u8 bDeviceSubClass;
-
__u8 bDeviceProtocol;
-
__u8 bMaxPacketSize0;
-
__le16 idVendor;
-
__le16 idProduct;
-
__le16 bcdDevice;
-
__u8 iManufacturer;
-
__u8 iProduct;
-
__u8 iSerialNumber;
-
__u8 bNumConfigurations;
-
} __attribute__ ((packed));
-
#define USB_DT_DEVICE_SIZE 18
-
struct usb_config_descriptor {
-
__u8 bLength;
-
__u8 bDescriptorType;
-
__le16 wTotalLength;
-
__u8 bNumInterfaces;
-
__u8 bConfigurationValue;
-
__u8 iConfiguration;
-
__u8 bmAttributes;
-
__u8 bMaxPower;
-
} __attribute__ ((packed));
-
#define USB_DT_CONFIG_SIZE 9
-
struct usb_interface_descriptor {
-
__u8 bLength;
-
__u8 bDescriptorType;
-
__u8 bInterfaceNumber;
-
__u8 bAlternateSetting;
-
__u8 bNumEndpoints;
-
__u8 bInterfaceClass;
-
__u8 bInterfaceSubClass;
-
__u8 bInterfaceProtocol;
-
__u8 iInterface;
-
} __attribute__ ((packed));
-
#define USB_DT_INTERFACE_SIZE 9
-
struct usb_endpoint_descriptor {
-
__u8 bLength;
-
__u8 bDescriptorType;
-
__u8 bEndpointAddress;
-
__u8 bmAttributes;
-
__le16 wMaxPacketSize;
-
__u8 bInterval;
-
__u8 bRefresh;
-
__u8 bSynchAddress;
-
} __attribute__ ((packed));
-
#define USB_DT_ENDPOINT_SIZE 7
-
#define USB_DT_ENDPOINT_AUDIO_SIZE 9
-
struct usb_string_descriptor {
-
__u8 bLength;
-
__u8 bDescriptorType;
-
__le16 wData[1];
-
} __attribute__ ((packed));