1.USB協議簡介

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 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(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
/* USB_DT_DEVICE: Device descriptor */
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


/* USB_DT_INTERFACE: Interface descriptor */
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



/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
    __u8  bLength;
    __u8  bDescriptorType;

    __u8  bEndpointAddress;
    __u8  bmAttributes;
    __le16 wMaxPacketSize;
    __u8  bInterval;

    /* NOTE:  these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8  bRefresh;
    __u8  bSynchAddress;
} __attribute__ ((packed));

#define USB_DT_ENDPOINT_SIZE        7
#define USB_DT_ENDPOINT_AUDIO_SIZE    9    /* Audio extension */

/* USB_DT_STRING: String descriptor */
struct usb_string_descriptor {
    __u8  bLength;
    __u8  bDescriptorType;

    __le16 wData[1];        /* UTF-16LE encoded */
} __attribute__ ((packed));

/* note that "string" zero is special, it holds language codes that
 * the device supports, not Unicode characters.
 */

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章