USB轉串口的實現過程

                                          USB轉串口的實現過程

 

       前面我已經說到了USB轉串口過程中USB是怎麼跟串口聯繫起來的,如何掛上去的。接下來對於程序的深入理解想寫一下關於USB轉串口的整個數據的收和發的過程,一達到理清思路解決遺留問題的目的。

 

關於初始化的部分不用詳細說明,主要是USB轉串口的獲得USB設備屬性,枚舉接口,掛上串口的過程。現在主要是根據串口的API來說下數據流的過程,COM_OPEN,COM_READ,COM_WRITE,COM_CLOSE.

 

COM_OPEN是一個相當重要的函數,這個函數自然石不需要做任何修改這是屬於微軟MDD的函數,其對應的兩個文件夾COM_MDD2,SERPDDCM,是基本上都不需要修改的。但是其流程需要好好的分析。

 

COM_INIT給的返回值是PHW_INDEP_INFO,這裏面的pHWOBJ是聯繫COM_MDD2到SERPDDCM的紐帶。

 

DCB是DATA Control Block的縮寫,DCB的結構成員全部都是串口數據流傳輸的控制參數,這些參數都是屬於硬件屬性範疇,是所有串口的特性,當然我們現在的這個設備並不會去採用流控。OPEN初始化兩個重要的結構體PHW_INDEP_INFO,PHW_OPEN_INFO,

HWSetCommTimeouts讀寫操作的超時的參數設置,HWOpen具體的做了SetDefaultConfiguration,InitLine,InitReceive,InitXmit。

 

SetDefaultConfiguration這個函數是設置系統默認的結構體的設置。流控,波特率,校驗位等等。主要是InitReceive,InitXmit。

 

InitReceive會對應到serialDataIn這個類InitReceive,ResetTransferQueue主要是去將正在傳輸隊列中的Transfer都清空,這些Transfer都是在init的時候new出來的,ResetPipe根據MSDN的說法是清除USB的halt狀態位,復位endpoint的數據到DATA0,他不會復位stall狀態,(所以後面就加了一個clearfeature來清除這個stall feature,但是這個feature會使得AT回覆阻塞),之後就會讓整個USB處於等待USB接收數據的狀態。它其實給了USB 4個設備緩衝,即USB接收到數據後就是通過這4個緩衝來把數據移到串口的緩衝中。IssueBulkTransfer是這個函數最重要的函數,在理解USB協議的時候再來分析。

 

一個重要的問題就是線程函數是在初始化的時候如何就進入等待狀態的,而CMiniThread這個類裏就有ThreadStart()這個成員函數,作用是清除掛起狀態(用到了這個函數ResumeThread)。構造函數創建了線程CMiniThread::ThreadProc,並且處於掛起狀態,ThreadProc調用了ThreadRun()這個成員函數(是個純虛函數,在CPddXXXUart裏有實現,而這個實現就是IST)。當這個函數執行後那麼整個初始化基本上就完成了其最後的結果就是讓這個線程等着USB那邊的數據過來。

 

InitXmit只是做了ResetPipe清除了相對應Pipe的一些標誌位。

 

 

其實所有的動力源都是COM_WRITE這個函數做的。只有向USB設備發送了請求或者寫入字節數就能收到USB設備相對應的返回。才能觸動整個流程。

DoTxData這個函數通過HWTxIntrHandler這個中斷處理函數來用bulk方式來把想要發的數發給USB設備。

 

對於IssueBulkTransfer這個函數中帶有一個回調函數地址,當flag中帶有USB_NO_WAIT的時候就會函數一執行就會直接返回,但是會用這個回調函數來等着USB設備給報告是否發送完成,這就是一個典型的異步模式,之後SerialDataOut這個線程會等到發送完成後收到USB的回調消息觸發串口MDD層的NotifyPDDInterrupt,然後由SerialEventHandle這個函數來處理這個事件最後再由DoTxData來發送剩下的字節,知道發送完所有的字節。

而當

 

還記得前面有個InitReceive這個函數嗎?當一有數據從USB設備返回的時候就會觸動它的回調函數(我的理解是,返回的概念肯定有一個超時的限制,比如說我一次來了一個數,但是很長時間不來了,只能算一次回調,應該是在兩個字符之間有多長時間的超時就會算上一次回調,當然得預先有個回調在那裏等着)。當接收的回調函數執行後會又回到NotifyPddInterupt這個函數當中來判別這個貌似中斷的類型是接收中斷還是發送中斷,之後會跟傳輸部分一樣在SerialEventHandle這個函數裏面來處理HWRxIntrHandler這個函數就是用來處理回調後來把收到的字符存儲到串口緩存裏面,

每一個Transfer對應着一個m_dwClientInfo,只有在CloseTransfer的時候纔會吧每一次傳輸的句柄置零,每個USBPIPE就會去有一個自己的USBTransfer,所以很多個pipe就會有一個transfer List,

 

突然間又不整個枚舉設備的過程重新的理了一下,從Attach()開始,在attach()中實際上回去做幾件事情

1, 創建了一個interface的鏈條。說是個鏈表也不像,就是通過類的構造函數的強大講所有interface的指針串成了一條線,

2, New出了所有的interface對象。

3, 對於interface進行了初始化,初始化得實際事情就是對於每個interface中的Endpoint進行初始化

4, 在EndPoint的構造中實際上就是將每個interface的Endpoint串成鏈。

5, 一個沒有實際意義的Init.

在做完attach後就會在註冊表的Active下去根據USB下的鍵值去創建相應的鍵值。這主要是通過一個函數GetSerialObject,這個函數是非常關鍵,至關重要的函數。它主要做的幾件事情。

1, 構造一個Driver類。與前面的對應關係是一個client類對應一個driver類。

2, 做這個driver的初始化。在初始化的過程中可以對你想要的接口進行初始化,在這裏面初始化的一些跟USB設備屬性無關的都是跟USB相關的具體操作類,比如說PIPE,UsbTransfer.

3, 在這個初始化的過程中會先根據一個接口的屬性來,多少個EndPoint,每個EndPoint的out或者In的屬性來創建CreateBulkIn,CreateBulkOut。

4, CreateBulkIn在這個函數裏面回去構造SerialDataIn這個類。在這個SerialDataIn類裏面會去啓動上面說的那個CminiThread這個線程,讓ThreadRun起來,同時也會調用UsbAsyncClassPipe這個的構造函數,實質上是UsbClassPipe的構造。這個構造實際上就是根據EndPoint來創建相應的PIPE.那麼就能得出這樣的一個結論,就是一個USB對應着多個Interface,每個Interface對應着幾個EndPoint,每個EndPoint對應着一個Pipe.

5, 接下來就是SerialDataIn的初始化了。這個初始化就會爲各個PIPE來創建各自的緩存,這個緩存就是後來處於接收狀態的時候USB的緩存。從程序來看每個PIPE都會有4個同樣大小的緩存對應在裏面。這四個緩存就是專門用於USB部分的接收。

6, 現在其實可以看出在COM_OPEN裏面在InitReceive裏面去初始化用IssueBulkTransfer來註冊回調的過程中,就是有每個m_hUsbTransfer就對應着一個緩存。當這個m_hUsbTransfer這個句柄沒有被清零就說明這次的傳輸就沒有結束。轉過頭來看看InitReceive這個函數裏面的4個註冊回調的函數IssueBulkTransfer發出去後等着回調的過程也就是接收數據的過程也是一個傳輸。直到將這次回調收到得數全部填入串口緩存。然後就會調用CloseTransfer去講m_hUsbTransfer置零就是一次Transfer的一次真正的結束。所以在串口真的處理接收字符的時候是傳輸一次並沒有結束的時候,所以此時IsTransferEmpty肯定是個FALSE,所以按照上面的總結我想對USB數據的處理過程做一個分析。

 

每個In Pipe有4個Transfer,這4個Transfer組成一個隊列,m_lpArmedTranfser在Pipe初始化的時候被賦予鏈表的首地址,IncTransfer這個函數就是遍歷這4個Transfer組成的鏈表。這就不難理解每次去GetClientInfo,只要這個Trnasfer結束,我就會去用下一個Transfer每一個Transfer有自己對應的ClientInfo,前面也講到了只有當把這個Transfer的USB部分的緩存得到的數據全部存入串口的緩存中,這次Transfer纔算結束,纔回去close,這個Transfer的句柄纔會被清0,那麼就會在GetClientInfo的時候m_lpArmedTranfser指向下一個,同時也會得到相應的clientinfo.這樣也不會讓數據亂序,搬完第一個buffer就會去搬運下一個buffer,知道把數據搬完。當然會是一次USB的回調纔會去搬運一次,GetTransferStatus這個USBd的函數能告訴我們一次Transfer會有多少的數據。

 

其實感覺只要弄清邏輯關係寫一個USB的驅動程序也不算是那麼的難。

 

 

 

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