USB入門總結(jié)
一,概述
現(xiàn)在很多的主控上都帶有USB的功能,但是對于初學(xué)者來說,這方面應(yīng)用還是比較棘手,因為usb的不但固件程序需要編寫,PC端的驅(qū)動也要編寫,而且驅(qū)動寫好了還要寫個上位機才能看出效果。這樣調(diào)試起來十分困難,建議從USB的鍵盤,鼠標開始做,了解清楚了,再做自己的協(xié)議就比較簡單了。
USB的概念歷史啥的這里就不說了。我們先不管具體的數(shù)據(jù)包格式,這一節(jié)先從整個包的層面上簡單的說,過程是這樣的,
---------------------------------------設(shè)備插入-------------------------------------------------------------
1)??? 主機會輪回查詢各個USB端口,主機檢測到D+與D-之間有電壓差,就認為有新的設(shè)置接入。主機等待100ms后發(fā)出復(fù)位請求。設(shè)備接到復(fù)位請求后將產(chǎn)生一個外部中斷信號。
---------------------------------------枚舉過程------------------------------------------------------------
2)??? 主機這時候只是知道有新的設(shè)備插入了,但是不知道插進來個什么東西,所以就開始詢問它是什么設(shè)備,怎么用,負荷能力怎么樣。這個時侯就進入了枚舉過程。因為剛剛插入的設(shè)備沒有分配地址,就用默認地址0,首先發(fā)送一個Get_descriptor(獲取設(shè)備描述符)指令包,設(shè)備接到包后就開始解析包(其實就是你在固件程序里判斷處理) ,然后按固定格式返回自己設(shè)備的設(shè)備描述符,這一步主要是主機知道你的USB設(shè)備的基礎(chǔ)屬性,比如支持的傳輸數(shù)據(jù)長度,電流負荷多少,支持那個USB版本,以及以后方便電腦找驅(qū)動的PID,VID。
3)??? 這時候主機知道你(你做的設(shè)備,簡稱你吧)的數(shù)據(jù)長度還有電流大小后,下一步就是給你分配一個屬于你的地址。
4)??? 給你一個地址后就開始詢問你的具體配置。首先發(fā)送一個試探性的設(shè)備配置請求Get_configuration(要求固定返回9個設(shè)備配置字),你接到后就開始發(fā)送9字節(jié)的設(shè)備配置字,其中包括你的配置字的總長度,這樣主機就知道你的配置到底有多長,然后再發(fā)一次設(shè)備配置請求,這時你就開始上傳所有的配置字。這個時侯主機就已經(jīng)很明白你的工作方式就各種特性,然后就可以正常工作了
5)??? 如果你在前面的某些配置(以后章節(jié)詳細說明)要求要說明自己的名字什么的,這里還要上傳字符串描述符。
6)??? 如果是鼠標或者鍵盤還要上傳報告描述符
---------------------------------------正常數(shù)據(jù)階段------------------------------------------------------
7)??? 這個時侯你已經(jīng)被主機正式接受并且注冊了,你可以通過自己寫測驅(qū)動或通用驅(qū)動與電腦進行通訊了。
以上是簡單的描述,詳細的后面章節(jié)再做介紹,學(xué)習(xí)一個東西關(guān)鍵是首先要知道這個東西是什么,簡單的工作原理。對于USB的工作我這里做個比方,
???? 主機好比一個公司,你就是USB設(shè)備,要進入公司首先要面試(枚舉),你到了面試現(xiàn)場(第一次插入設(shè)備),面試官首先了解到你的外表,性別已經(jīng)你要應(yīng)聘的崗位(設(shè)備描述符),然后給你一個號,以后就開始按號叫人,當(dāng)你被叫到就開始問你的專業(yè)知識,性格等(配置描述符),如果你比較合適(通過了枚舉)你就會錄取了,并且注冊一個你的信息到公司(驅(qū)動安裝,并且寫入注冊表)。等你下次來公司,只要把工號(PID,VID)報上,就知道是你來了。
初涉USB,初學(xué)者USB入門總結(jié)(2) 設(shè)備固件程序
二,實際數(shù)據(jù)過程測試
這節(jié)主要是對固件里的USB請求處理有個概念,還有就是調(diào)試的方法。大篇幅的程序配合,如果不關(guān)心這一塊的話可以跳過,呵呵。
為了更好的說明整個USB啟動過程,我們可以用串口實時的跟蹤各個USB中斷。不過這里先不用串口進行測試,只是簡單的用一組變量記錄過程。測試程序如下(以下會有程序的說明):
uchar test[100];//100長度的變量,記錄過程
uchar conters=0;//記錄計數(shù)值,
/*------------------------------------------------------------
??????? 高校電子聯(lián)盟--肖繼達
???? QQ:258347765???
-------------------------------------------------------------*/
void EXT_int(void)//USB中斷響應(yīng)函數(shù)
{
??? /*------------------------------------------------------------
????????? Check interrupt status register to know interrupt
?????? source.
??? ------------------------------------------------------------*/
??? if (USB_BUSRESET_ASS_INT())
??? {?? /* USB bus reset */
??????? /*? for USB Rev.1.1
???????????????????? After USB bus reset released, 10msec recoverly time we have.
???????????????????? Follwing request must be processed normally.
??????? */
??????? CLR_BUS_RESET_STATE();? /* USB bus reset status clear */
??????? /*------------------------------------------------------------
?????????? Endpoint0 setting
??????? ------------------------------------------------------------*/
??????? /* Tx/Rx payload size setting */
??????? /* Rx payload is fixed as 8-byte or 32-byte, therefor the
?????????? setting has no meaninig */
??????? SET_PAYLOAD_EPn(EP0RX, device_descriptor.bMaxPacketSize0);
??????? SET_PAYLOAD_EPn(EP0TX, device_descriptor.bMaxPacketSize0);
??????? /* Stall bit, the value undefined after reset, cleared */
??????? CLR_STALL_EPn(EP0);
?????????????
??????? /*------------------------------------------------------------
?????????? Misceronous status variable initialization
??????? ------------------------------------------------------------*/
??????? usb_status.configuration = NULL;
??????? usb_status.remote_wakeup = 0;
??????? usb_status.address = 0;
??????? usb_status.dvcstate = DEFAULT_STATE;??? /* Device state :DEFAULT */
??????? usb_status.stall_req = 0;
?????????????? #ifdef Debug
???????????????????? test[conters]='!';
???????????????????? conters++;
???????????????????? #endif
??????? /*------------------------------------------------------------
?????????? Callback to application layer
??????? ------------------------------------------------------------*/
??????? (*usb_status.callback)();
??? }
??? else if (SUSPENDED_INT())
??? {?? /* suspended state */
??????? /* for USB Rev.1.1
???????????????????? Transit to suspended state after detect the USB line has kept idle over 3msec.
???????????????????? After resume detected, end suspend state in 3msec to be able to respond
???????????????????? the host request.
??????? */
????????????? CLR_SUSPENDED_STATE();
???????????????????? #ifdef Debug
???????????????????? test[conters]='@';
???????????????????? conters++;
???????????????????? #endif
??? }
??? else if (AWAKE_INT())
??? {?? /* Deveice awake state */
??????? /* AWAKE procedure */
??????? CLR_AWAKE_STATE();????????? /* Request clear */
???????????????????? #ifdef Debug
???????????????????? test[conters]='#';
???????????????????? conters++;
???????????????????? #endif
??? }
??? else if (USB_BUSRESET_DES_INT())
??? {?? /* USB bus reset deassert */
??????? /* Procedure for USB bus reset de-assert */
???????
??????? CLR_BUS_RESET_DES_STATE();? /* Request clear */
???????????????????? #ifdef Debug
???????????????????? test[conters]='$';
???????????????????? conters++;
???????????????????? #endif
??? }
??? else if (SOF_INT())
??? {?? /* SOF interrupt status */
??????? CLR_B_SOF_STATE();??
?????????????? #ifdef Debug
???????????????????? test[conters]='%';
???????????????????? conters++;
???????????????????? #endif
?????? /* SOF interrupt status clear */
??? }?? /* SOF interrupt status */
??? if (SETUP_RDY_INT())
??? {?? /* setup ready */
????????????????? #ifdef Debug
???????????????????? test[conters]='^';
???????????????????? conters++;
???????????????????? #endif
??????? read_Device_Requests();
??? }
??? else if(EP1_PKTRDY_INT())
??? {?? /* EP1 packet ready */????
??????? read_FIFO(EP1);
?????? }
??? else if (EP2_PKTRDY_INT())
??? {?? /* EP2 packet ready */
??????? write_FIFO(EP2);
??? }
??? else if (EP0_RXPKTRDY_INT())
??? {?? /* EP0 receive packet ready */
??????? read_FIFO(EP0RX);
??? }
??? else if (EP0_TXPKTRDY_INT())
??? {?? /* EP0 transmit packet ready */
??????? write_FIFO(EP0TX);
??? }
}
計錄的結(jié)果在變量查看中顯示如下:
首先我解釋一下,這段程序是我在做USB設(shè)備時的中斷函數(shù)。主控(就是你往里面寫固件程序的那個東西)會在要求設(shè)備進行操作時,產(chǎn)生一個相應(yīng)的中斷(我們可以用中斷的方式,也可以用查詢的方式,中斷的方式的好處就是主機有需要操作的都會叫你,而用查詢你必須不斷的問主機“有事么”,這里采用中斷方式),比如主機給設(shè)備設(shè)置地址,主機會通過固定的通道(point0)發(fā)送一個‘設(shè)定地址’包,設(shè)備主控接到包后會產(chǎn)生中斷,并且把響應(yīng)的狀態(tài)保存在相應(yīng)的寄存器中,我們只要在中斷程序判斷各個寄存器就能完成主機的任務(wù)。
程序中藍色字是中斷類型的判斷,其對應(yīng)的宏定義就不列出來了。如果是這個中斷就會執(zhí)行相應(yīng)的中斷操作。并且一次中斷只有一種中斷類型,我們在每個中斷響應(yīng)中加一段紅色字的程序,是為了保存每次中斷的狀態(tài),比如剛插上設(shè)備,來了一次BUSRESET總線復(fù)位中斷,就會進入相應(yīng)的中斷操作,完了后記錄狀態(tài)test[conters]='!'; conters++;意思是進入了這個中斷就在這一組數(shù)的當(dāng)前位置設(shè)成'!',并且位置記錄的變量加一,以便下一次記錄到下一個位置。這樣USB的過程我們就記錄了下來,
???? 下面看一下記錄結(jié)果(其中的數(shù)字和字母是響應(yīng)標準請求時的程序產(chǎn)生的這里不羅列程序了)。
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image002.jpg
可以看到,一開始是一次總線復(fù)位,然后USB bus reset de-assert,然后再掛起總線。重復(fù)了兩次,然后就是上一節(jié)的具體配置了。
這節(jié)主要是對固件里的USB請求處理有個概念,還有就是調(diào)試的方法。
?
初涉USB,初學(xué)者USB入門總結(jié)(3) 數(shù)據(jù)包闡述
對于USB傳輸大體有個概念,下一步就來看看到底USB上傳的什么東西,以什么格式傳數(shù)據(jù),先不涉及端點的概念。
各種總線的數(shù)據(jù)傳輸都是以固定的層次協(xié)議進行的,USB當(dāng)然也不例外。所謂的層次也只是個抽象的概念罷了,就是表達一種依附關(guān)系,上層要依賴與底層,上層以底層為基礎(chǔ),上層只需要關(guān)心自己的東西就行了,如果你還不明白,那就繼續(xù)看,學(xué)習(xí)一個東西不可能一兩句話說的明白一個點,需要全面了解后才能清楚各個點。
要實現(xiàn)兩個機器(機器的范圍比較廣,可以是電腦,交換機,單片機)的通信總是要有一個載體才可以,對于機器當(dāng)然是電平高低為載體,具體的說機器甲要告訴機器乙一件事情(比如說一條指令),那么機器甲可以通過一根線(串行數(shù)據(jù)總線)連到機器乙的一個IO口上,甲發(fā)送一個個的高低電平,乙固定時間檢測自己的這個IO口,然后逐個記錄下放到自己的緩沖里,這樣乙就收到甲送的數(shù)據(jù)了。上述就是一個簡單的數(shù)據(jù)鏈路層(計算機網(wǎng)絡(luò)里這么叫)的描述,這一層要保證的就是甲發(fā)的每一位數(shù)據(jù),乙都可以正確及時的接受,并且對在傳輸過程中出錯的數(shù)據(jù)做出反應(yīng)。其實比數(shù)據(jù)連路更底層的還有物理層,這就是真正的物理介質(zhì),對于機器就是電線了,數(shù)據(jù)就是電線上傳輸?shù)碾妷?USB是用的四線,兩個電源,兩個數(shù)據(jù)線。
這里也打個比方,比如人與人進行交流,我們當(dāng)然是通過說話了,物理層就是空氣和傳輸?shù)穆暡ǎ瑪?shù)據(jù)鏈路層就是我們說的每一個字,物理層就是空氣,負責(zé)把我們說的話轉(zhuǎn)換成聲波傳給對方,數(shù)據(jù)鏈路層負責(zé)讓對方能正確的聽到每個字,如果聽的不清可以告訴對方重新說一遍。
經(jīng)過上述的兩個底層,就可以保證每一位數(shù)據(jù)可以正確的傳到對方那里去。下一步的工作當(dāng)然是解析數(shù)據(jù)代表了什么,一般來說,數(shù)據(jù)都是以一串?dāng)?shù)為單位,一般稱為一個包,機器間傳輸都是以一個包為單位傳出,就像人們說話都是以一句話為單位輸出一樣。每一個包包含有許多位數(shù)據(jù),這些數(shù)據(jù)又分段表示不同的意義,如圖一,這是一個USB令牌階段的包,Sync是同步數(shù)據(jù)(相當(dāng)于說話時先打個招呼,告訴對方要跟他說話了),PID是包標示(告訴對方這個包是干什么用的),ADDR是對方的地址(叫對方的名字),ENDP是用端點幾通訊(先不介紹這個),CRC5是校驗位(判斷這個包是否在傳輸中出錯),EOP是包結(jié)束。
|--------------------------------------------------------|
|?? Sync? |? PID? |? ADDR? |? ENDP? |? CRC5? |? EOP????? |
|________________________________________________________|
????????????????????? 圖一
USB的數(shù)據(jù)包又分為三種,一個是令牌包,一個是數(shù)據(jù)包,另一個是握手包。每一次的USB通訊事務(wù)處理都是以令牌包開頭,告訴對方要跟誰說話,這句話是用來干嘛的。如果要求有數(shù)據(jù)傳輸,則下一步就是數(shù)據(jù)包,另外如果要求對方要有反饋,則會發(fā)出握手包。令牌包又簡單的包括OUT,IN,STEP三種類型,OUT是用于主機告訴設(shè)備主機要向USB設(shè)備發(fā)送數(shù)據(jù),IN是用于主機告訴設(shè)備要上傳數(shù)據(jù),而STEUP是用于主機向USB設(shè)備發(fā)送配置信息,在枚舉過程中會用到。另外數(shù)據(jù)包和握手包的具體格式什么的,可以參照詳細的協(xié)議。
可以看到在所以的通訊過程中,主機都是發(fā)起者,不管是主機發(fā)送數(shù)據(jù)到USB設(shè)備還是USB設(shè)備發(fā)送數(shù)據(jù)到主機,都必須收主機控制。圖二為一次事務(wù)的過程
??
??????? 令牌階段?? ——》??????? 數(shù)據(jù)階段???? ——》???? 握手階段
??????????????????????????????? 圖二
這個過程可以這樣描述,甲和乙對話,甲是老板,乙是職員。第一節(jié)已經(jīng)講過了,乙面試就是枚舉,在這個過程中,甲多段的發(fā)送STEP令牌包給乙,乙收到后如果要反饋數(shù)據(jù),就發(fā)數(shù)據(jù)包給甲,甲正確接收后,跟甲握握手,表示這次對話成功。
乙被正式錄取后,甲會分派任務(wù)(OUT),這時甲對乙說有任務(wù)給你(令牌階段),然后乙就開始聽,甲說你的任務(wù)就是記錄數(shù)據(jù)并且上報(這段話就是數(shù)據(jù)包),乙說好的(握手包)。
乙開始正式工作,并且記錄數(shù)據(jù)。過了一段時間,甲開始要求提交數(shù)據(jù)(IN),乙把數(shù)據(jù)報告給甲(數(shù)據(jù)階段),甲說好(握手成功)。這里乙不能主動的去向老板匯報,只能被動的干活。
上面已經(jīng)講USB主機和設(shè)備間數(shù)據(jù)傳輸?shù)倪^程,都是我個人理解,有不正確和不到位的大家提出,方便初學(xué)者理解,謝謝??
初涉USB,初學(xué)者USB入門總結(jié)(4)USB通訊設(shè)備快速開發(fā)
經(jīng)過上述三節(jié)的描述,對USB應(yīng)該已經(jīng)有了初步的認識,其中具體的協(xié)議(比如各個描述符的定義什么的)這里不做描述了,網(wǎng)上一搜一大堆。下面我以一個實例來詳細說明快速開發(fā)USB設(shè)備的步驟,
一,設(shè)定規(guī)劃
凡事預(yù)則立,不預(yù)則費,所以開發(fā)一個小小的USB也要稍微規(guī)劃一下,比如想象要實現(xiàn)什么功能,傳輸?shù)臄?shù)據(jù)協(xié)議什么的。
二,固件編程,
固件編程說白了就是寫單片機程序,要實現(xiàn)USB一般可以使用帶USB功能的單片機,再個就是加一個專用的USB芯片。這里以內(nèi)部集成USB功能單片機為例
固件的USB開發(fā)一般就是先使能USB,使能USB時鐘,使能各個USB控制中斷(掛起,復(fù)位,標準請求,寫入,寫出等)然后USB就能正常工作了,這時候不如不寫別的東西,電腦就可以檢測出有USB設(shè)備插入了,具體的反應(yīng)是在設(shè)備管理器里會發(fā)現(xiàn)閃了一下說明發(fā)現(xiàn)了新的USB設(shè)備,接下來電腦會發(fā)送各種標準請求,因為這個時候你的程序還沒寫完整,對這些請求不會有反應(yīng),所以電腦不可能識別出是什么東西。
接下來的工作就是在中斷中響應(yīng)電腦傳來的各種標準請求。當(dāng)必要的請求都被正確的響應(yīng)的話,這個時候如果電腦里有正確的驅(qū)動,電腦就會去加載這個驅(qū)動,如果是第一次插入這個設(shè)備,還要把驅(qū)動安裝一下,然后設(shè)備就進入正常工作了,電腦會顯示“這個USB已經(jīng)成功安裝并可以應(yīng)用了”。
這里捎帶著說一下端點(endpoint)的概念,一般一個USB設(shè)備都會有數(shù)個端點,端點就是一個數(shù)據(jù)緩沖控制區(qū)(FIFO),每個緩沖區(qū)相當(dāng)于有一個出口一個進口的池子,數(shù)據(jù)通過進口進入到池子,然后你再在固件里去用這些數(shù)據(jù)。固件往電腦寫數(shù)據(jù),也是把數(shù)據(jù)先放到池子里,然后打開出口,就可以干自己的事情,不用一個個的把數(shù)據(jù)發(fā)出了,池子的出口自動把數(shù)據(jù)流出。
一般的端口0是用來做標準請求響應(yīng)用的,也就是在枚舉階段用到。我一般把端口1定義為出(OUT),端口2定義為入(IN)(注意,這個OUT和IN是相對與電腦的,也就是說OUT是數(shù)據(jù)從電腦出去到設(shè)備,IN是設(shè)備的數(shù)據(jù)進入電腦)。這些定義也是在標準請求中去告訴電腦的。
接下來就可以實現(xiàn)與電腦的通訊了,你把數(shù)據(jù)放到相應(yīng)的池子里就行了。下面就可以自己定義通訊的數(shù)據(jù)格式了。比如控制開發(fā)板上的8個LED的第一個燈亮,那么上位機發(fā)送數(shù)據(jù)0x55,0x01,0x80,0xaa。我們就可以規(guī)定第一個數(shù)據(jù)是啟示位,遇到這個表明開始一次控制指令,0x01表示這個是控制燈亮暗的指令,0x80表示LED的控制數(shù)據(jù),最高位是1,表示第一個亮,其他位是0,表示都暗。最后一個數(shù)據(jù)是0xaa,表示這是結(jié)束。其實所謂的數(shù)據(jù)協(xié)議不過就是自己定義的一套讓通訊雙方都能正確理解對方的數(shù)據(jù)格式。電腦比較是電腦,什么都要規(guī)定好了,它才能正確的工作。
三,驅(qū)動程序
??? 對于快速開發(fā)用Driverstudio就可以了,我先裝了VC6.0,然后裝了DDK2600,最后裝了Driverstudio,網(wǎng)上有說這個順序不容易出問題,我也沒時間去試別的順序會出怎么樣的特效,姑且不管他是否在忽悠,先這樣按了沒壞處。
??? 我一開始比較新潮的裝了DriverStudio3.2版本,然后按網(wǎng)上的方法破解了,生成了驅(qū)動是能打開設(shè)備,但是就是傳輸不了數(shù)據(jù),搞了兩天還是不行,后來想到是不是3.2版本太新了?或者破解沒完整?然后卸載了3.2裝了3.1,果然可以了,真不知道是Compuware做了手腳故意玩我還是本人愚笨弄錯了哪里。
驅(qū)動生成的步驟可以在百度,Google里搜“10分鐘完成一個USB驅(qū)動程序”能出來一
大堆,要是你嫌搜索麻煩就直接點這個算了http://m.1cnz.cn/soft/161/2006/20060325363.html按那個步驟操作就可以了,根據(jù)向?qū)Р僮魍炅艘院螅琕C就會出來一個驅(qū)動程序框架了,如果你在這個時候編譯一下就可你會碰到很多問題,我的操作是這樣的。首先把DDK的庫編譯一下,操作網(wǎng)上有, 網(wǎng)上有云:
1.啟動Visual C++ 。
2.選擇菜單 File|Open Workspace。打開位于DriverStudio/DriverWorks/Source/vdwlibs.dsw的工作空間文件。
3.選擇菜單 Build|Batch Build,在彈出的對話框中選擇你想編譯的庫。
4.點擊Build編譯你選擇的庫。
然后在VC的Driverstudio的工具條點擊“change environment variables”,在第一個選型卡把DDK的路徑選上,我的是C:\WINDDK\2600。然后點OK,接下來點DriverStudio工具條的編譯,就可以了,如果你還是碰到問題,你可以把VC顯示的錯誤復(fù)制到百度。
?
評論
查看更多