新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 如何使用STM32的USB庫支持控制端點0

        如何使用STM32的USB庫支持控制端點0

        作者: 時間:2016-12-02 來源:網絡 收藏
        首先我們先回顧一下控制端點的傳輸方式:
        控制端點的傳輸有三個階段,SETUP階段、數據階段和狀態階段;數據階段又分為數據入(DATA IN)和數據出(DATA OUT),控制端點傳輸可以沒有數據階段;狀態階段有狀態入(STATUS IN)和狀態出(STATUS OUT)。
        總結起來,控制端點有如下三種可能的傳輸過程(以下括號中的0或1表示DATA0或DATA1傳輸):
        一、 SETUP DATA_IN(0) DATA_IN(1) DATA_IN(0) ...... STATUS_OUT(1)
        二、 SETUP DATA_OUT(0) DATA_OUT(1) DATA_OUT(0) ...... STATUS_IN(1)
        三、 SETUP STATUS_IN(1)
        這里做一個約定,把上述過程一定義為“數據入過程”,過程二定義為“數據出過程”,過程三定義為“無數據過程”。所有的USB控制端點的數據傳輸都可以而且只用這三種傳輸過程表示。HID的SET_REPORT是數據出過程,HID的GET_REPORT是數據入過程,USB的GET DEVICE DESCRIPTOR是數據入過程,USB的SET CONFIGURATION是無數據過程,等等。
        接下來,我們看看STM32USB庫是如何處理控制端點0的傳輸。
        根據USB協議,每個SETUP包都由8個字節構成,用戶程序可以通過結構體Device_Info(類型DEVICE_INFO)訪問SETUP包的數據,因為在整個的USB處理中都要用到結構體Device_Info的內容,庫中定義了一個全局的指針pInformation指向這個結構體,用戶可以通過這個指針訪問結構體的內容。
        對應SETUP包的8個字節,用戶可以用下述方式訪問:
        pInformation->USBbmRequestType (字節類型)
        pInformation->USBbRequest (字節類型)
        pInformation->USBwValue (雙字節類型)
        pInformation->USBwIndex (雙字節類型)
        pInformation->USBwLength (雙字節類型)
        使用pInformation->USBwValue0訪問wValue的低字節,pInformation->USBwValue1訪問wValue的高字節。
        使用pInformation->USBwIndex0訪問USBwIndex的低字節,pInformation->USBwIndex1訪問USBwIndex的高字節。
        使用pInformation->USBwLength0訪問USBwLength的低字節,pInformation->USBwLength1訪問USBwLength的高字節。
        通過分析SETUP包的8個字節,可以判斷出一個SETUP的傳輸過程是屬于數據入過程、數據出過程還是無數據過程。STM32的USB庫中處理了所有的USB協議文本中定義的標準SETUP命令,對于USB協議文本中未定義的命令,USB庫按照數據入過程、數據出過程或無數據過程通過回調函數交給用戶程序處理。
        全局變量Device_Property(DEVICE_PROP類型)封裝了所有的回調函數,DEVICE_PROP定義如下:
        typedef struct _DEVICE_PROP
        {
        void (*Init)(void); // 設備初始化回調函數
        void (*Reset)(void); // USB復位回調函數
        void (*Process_Status_IN)(void); // STATUS_IN階段處理回調函數
        void (*Process_Status_OUT)(void); // STATUS_OUT階段處理回調函數
        RESULT (*Class_Data_Setup)(u8 RequestNo); // 數據入/出過程處理回調函數
        RESULT (*Class_NoData_Setup)(u8 RequestNo); // 無數據過程處理回調函數
        RESULT (*Class_Get_Interface_Setting)(u8 Interface, u8 AlternateSetting); // GET_INTERFACE 回調函數
        u8* (*GetDeviceDescriptor)(u16 Length); // GET_DEVICE_DESCRIPTION回調函數
        u8* (*GetConfigDescriptor)(u16 Length); // GET_CONFIGURATION_DESCRIPTION回調函數
        u8* (*GetStringDescriptor)(u16 Length); // GET_STRING_DESCRIPTION回調函數
        u8 MaxPacketSize; // 最大包長度
        } DEVICE_PROP;
        結合SETUP的三種傳輸過程,用戶通過實現不同的回調函數即可完成對各種USB類命令的處理,下面以HID的SET REPORT為例說明。
        在介紹具體實現之前,先介紹一下另一個回調函數CopyRoutine的概念,這個函數的原型是:
        u8 *CopyRoutine(u16 length); // 返回一個緩沖區指針
        USB庫通過這個函數獲得用戶的數據緩沖區地址,從而可以在數據出過程中把收到的數據拷貝到用戶緩沖區,或在數據入過程中把用戶緩沖區的數據拷貝到USB發送緩沖區。每個數據出過程可能有若干次DATA_OUT傳輸,USB庫每完成一次這樣的傳輸都會調用一次回調函數CopyRoutine,參數length是本次傳輸所收到的數據字節數目,CopyRoutine必須返回一個緩沖區指針,這個緩沖區必須能夠容納length字節的數據,CopyRoutine返回到USB庫之后,USB庫將把收到的數據拷貝到用戶指定的緩沖區。同樣每個數據入過程也可能有若干次DATA_IN傳輸,每次需要向主機傳輸數據時,USB庫都會調用一次回調函數CopyRoutine,參數length是本次傳輸所要發送的數據字節數目,CopyRoutine必須返回一個緩沖區指針,這個緩沖區中必須包含要求的數據字節,USB庫將把用戶緩沖區的數據拷貝到USB緩沖區并擇機發送出去。
        當以length=0調用CopyRoutine時,CopyRoutine需要返回用戶緩沖區的長度,因為CopyRoutine的返回類型是一個指針,所以需要通過類型的強制轉換返回緩沖區長度。這個功能是為了處理用戶緩沖區的長度與主機SETUP數據請求長度不符的情況,而不至于造成用戶緩沖區的溢出。
        介紹完上述若干概念和回調函數,再看SET_REPORT的實現就很容易了。
        SET_REPORT是一個數據出過程,因此需要實現一個Class_Data_Setup回調函數,示例如下:
        RESULT HID_Data_Setup(u8 RequestNo)
        {
        u8 *(*CopyRoutine)(u16 length);
        CopyRoutine = NULL;
        if (pInformation->USBbmRequestType == CLASS_REQUEST|INTERFACE_RECIPIENT
        && RequestNo == SET_REPORT)
        CopyRoutine = My_Data_Request;
        if (CopyRoutine == NULL)
        return USB_UNSUPPORT;
        pInformation->Ctrl_Info.CopyData = CopyRoutine;
        pInformation->Ctrl_Info.Usb_wOffset = 0;
        pInformation->Usb_wLength = (*CopyRoutine)(0);
        return USB_SUCCESS;
        } // End of HID_Data_Setup()
        u8 My_Buffer[10];
        u8 *My_Data_Request(u16 length)
        {
        if (length == 0)
        return (u8*)10; // 假定你的REPORT長度和Buffer長度為10
        return My_Buffer;
        }
        上面介紹的CopyRoutine用于把多次傳輸的數據包合并到一個完整的緩沖區中,因此只有到STATUS階段才能夠知道一次SETUP傳輸是否結束,所以用戶程序需要在回調函數 Process_Status_IN中處理從SET_REPORT接收到的數據。因為所有的回調函數都是USB中斷處理的一部分,所以更好的辦法是在 Process_Status_IN中設置一個標記,然后在用戶主程序中判斷這個標記并做處理。
        上述示意代碼是以My_Buffer長度為10字節為例,而USB庫的默認包長度為16字節,因此My_Data_Request并沒有多包的處理。
        關于多包的緩沖區處理的示意代碼可以是這樣的:
        u8 *My_Data_Request(u16 length)
        {
        if (length == 0)
        return (u8*)100; // 假定你的REPORT長度和Buffer長度為100
        return &My_Buffer[pInformation->Ctrl_Info.Usb_wOffset];
        }
        這里有一個庫中使用的變量pInformation->Ctrl_Info.Usb_wOffset,這個變量會在傳輸每個數據包時候由庫中的程序按數據包長度增加,如最大包長為16字節時,第一次調用My_Data_Request時Usb_wOffset=0,第二次調用 My_Data_Request時Usb_wOffset=16,第三次調用My_Data_Request時Usb_wOffset=32,依此類推。這樣就可以使用Usb_wOffset作為My_Buffer的下標從My_Data_Request返回相應的緩沖區地址。
        前面已經說明,參數length是用于檢測緩沖區長度是否足夠,如果你有足夠長的緩沖區,可以不必檢測,上述示例中使用了一個固定的緩沖區,所以不必使用參數length檢測緩沖區長度。
        注意,STM32的USB庫設計成以回調函數處理用戶命令請求,包含類命令請求,是為了能夠清晰地區分庫程序和用戶程序,使這兩者不會混在一起,這樣的好處是非常明顯的,當USB庫需要更新升級時,只需替換掉相應的程序模塊,而不必修改用戶已經完成的程序。
        以上的介紹都可以在STM32 USB庫的說明手冊中找到。


        關鍵詞: STM32USB庫控制端

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 宁强县| 阳城县| 康乐县| 师宗县| 含山县| 涡阳县| 长春市| 丹寨县| 鸡东县| 土默特左旗| 仙居县| 寿光市| 凤城市| 舒城县| 灵石县| 林芝县| 团风县| 宁远县| 卢湾区| 邹城市| 邵东县| 沛县| 洛隆县| 科尔| 应城市| 寿阳县| 郁南县| 舒兰市| 昌乐县| 贵德县| 普安县| 吉林省| 黎川县| 米林县| 平罗县| 且末县| 安丘市| 鱼台县| 潍坊市| 大渡口区| 罗江县|