如何使用STM32的USB庫支持控制端點(diǎn)0

發(fā)布時(shí)間:2009-11-25 17:59    發(fā)布者:STM32
關(guān)鍵詞: USB , 端點(diǎn)
首先我們先回顧一下控制端點(diǎn)的傳輸方式:

控制端點(diǎn)的傳輸有三個(gè)階段,SETUP階段、數據階段和狀態(tài)階段;數據階段又分為數據入(DATA IN)和數據出(DATA OUT),控制端點(diǎn)傳輸可以沒(méi)有數據階段;狀態(tài)階段有狀態(tài)入(STATUS IN)和狀態(tài)出(STATUS OUT)。

總結起來(lái),控制端點(diǎn)有如下三種可能的傳輸過(guò)程(以下括號中的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)

這里做一個(gè)約定,把上述過(guò)程一定義為“數據入過(guò)程”,過(guò)程二定義為“數據出過(guò)程”,過(guò)程三定義為“無(wú)數據過(guò)程”。所有的USB控制端點(diǎn)的數據傳輸都可以而且只用這三種傳輸過(guò)程表示。HID的SET_REPORT是數據出過(guò)程,HID的GET_REPORT是數據入過(guò)程,USB的GET DEVICE DESCRIPTOR是數據入過(guò)程,USB的SET CONFIGURATION是無(wú)數據過(guò)程,等等。

接下來(lái),我們看看STM32的USB庫是如何處理控制端點(diǎn)0的傳輸。

根據USB協(xié)議,每個(gè)SETUP包都由8個(gè)字節構成,用戶(hù)程序可以通過(guò)結構體Device_Info(類(lèi)型DEVICE_INFO)訪(fǎng)問(wèn)SETUP包的數據,因為在整個(gè)的USB處理中都要用到結構體Device_Info的內容,庫中定義了一個(gè)全局的指針pInformation指向這個(gè)結構體,用戶(hù)可以通過(guò)這個(gè)指針訪(fǎng)問(wèn)結構體的內容。

對應SETUP包的8個(gè)字節,用戶(hù)可以用下述方式訪(fǎng)問(wèn):

  pInformation->USBbmRequestType (字節類(lèi)型)
  pInformation->USBbRequest  (字節類(lèi)型)
  pInformation->USBwValue  (雙字節類(lèi)型)
  pInformation->USBwIndex  (雙字節類(lèi)型)
  pInformation->USBwLength (雙字節類(lèi)型)

使用pInformation->USBwValue0訪(fǎng)問(wèn)wValue的低字節,pInformation->USBwValue1訪(fǎng)問(wèn)wValue的高字節。

使用pInformation->USBwIndex0訪(fǎng)問(wèn)USBwIndex的低字節,pInformation->USBwIndex1訪(fǎng)問(wèn)USBwIndex的高字節。

使用pInformation->USBwLength0訪(fǎng)問(wèn)USBwLength的低字節,pInformation->USBwLength1訪(fǎng)問(wèn)USBwLength的高字節。

通過(guò)分析SETUP包的8個(gè)字節,可以判斷出一個(gè)SETUP的傳輸過(guò)程是屬于數據入過(guò)程、數據出過(guò)程還是無(wú)數據過(guò)程。STM32的USB庫中處理了所有的USB協(xié)議文本中定義的標準SETUP命令,對于USB協(xié)議文本中未定義的命令,USB庫按照數據入過(guò)程、數據出過(guò)程或無(wú)數據過(guò)程通過(guò)回調函數交給用戶(hù)程序處理。

全局變量Device_Property(DEVICE_PROP類(lèi)型)封裝了所有的回調函數,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);   // 數據入/出過(guò)程處理回調函數
  RESULT (*Class_NoData_Setup)(u8 RequestNo); // 無(wú)數據過(guò)程處理回調函數

  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; // 最大包長(cháng)度
} DEVICE_PROP;

結合SETUP的三種傳輸過(guò)程,用戶(hù)通過(guò)實(shí)現不同的回調函數即可完成對各種USB類(lèi)命令的處理,下面以HID的SET REPORT為例說(shuō)明。

在介紹具體實(shí)現之前,先介紹一下另一個(gè)回調函數CopyRoutine的概念,這個(gè)函數的原型是:

   u8 *CopyRoutine(u16 length);    // 返回一個(gè)緩沖區指針

USB庫通過(guò)這個(gè)函數獲得用戶(hù)的數據緩沖區地址,從而可以在數據出過(guò)程中把收到的數據拷貝到用戶(hù)緩沖區,或在數據入過(guò)程中把用戶(hù)緩沖區的數據拷貝到USB發(fā)送緩沖區。每個(gè)數據出過(guò)程可能有若干次DATA_OUT傳輸,USB庫每完成一次這樣的傳輸都會(huì )調用一次回調函數CopyRoutine,參數length是本次傳輸所收到的數據字節數目,CopyRoutine必須返回一個(gè)緩沖區指針,這個(gè)緩沖區必須能夠容納length字節的數據,CopyRoutine返回到USB庫之后,USB庫將把收到的數據拷貝到用戶(hù)指定的緩沖區。同樣每個(gè)數據入過(guò)程也可能有若干次DATA_IN傳輸,每次需要向主機傳輸數據時(shí),USB庫都會(huì )調用一次回調函數CopyRoutine,參數length是本次傳輸所要發(fā)送的數據字節數目,CopyRoutine必須返回一個(gè)緩沖區指針,這個(gè)緩沖區中必須包含要求的數據字節,USB庫將把用戶(hù)緩沖區的數據拷貝到USB緩沖區并擇機發(fā)送出去。

當以length=0調用CopyRoutine時(shí),CopyRoutine需要返回用戶(hù)緩沖區的長(cháng)度,因為CopyRoutine的返回類(lèi)型是一個(gè)指針,所以需要通過(guò)類(lèi)型的強制轉換返回緩沖區長(cháng)度。這個(gè)功能是為了處理用戶(hù)緩沖區的長(cháng)度與主機SETUP數據請求長(cháng)度不符的情況,而不至于造成用戶(hù)緩沖區的溢出。

介紹完上述若干概念和回調函數,再看SET_REPORT的實(shí)現就很容易了。

SET_REPORT是一個(gè)數據出過(guò)程,因此需要實(shí)現一個(gè)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長(cháng)度和Buffer長(cháng)度為10

    return My_Buffer;
}

上面介紹的CopyRoutine用于把多次傳輸的數據包合并到一個(gè)完整的緩沖區中,因此只有到STATUS階段才能夠知道一次SETUP傳輸是否結束,所以用戶(hù)程序需要在回調函數 Process_Status_IN中處理從SET_REPORT接收到的數據。因為所有的回調函數都是USB中斷處理的一部分,所以更好的辦法是在 Process_Status_IN中設置一個(gè)標記,然后在用戶(hù)主程序中判斷這個(gè)標記并做處理。

上述示意代碼是以My_Buffer長(cháng)度為10字節為例,而USB庫的默認包長(cháng)度為16字節,因此My_Data_Request并沒(méi)有多包的處理。

關(guān)于多包的緩沖區處理的示意代碼可以是這樣的:

u8 *My_Data_Request(u16 length)
{
    if (length == 0)
        return (u8*)100;    // 假定你的REPORT長(cháng)度和Buffer長(cháng)度為100

    return &My_Buffer[pInformation->Ctrl_Info.Usb_wOffset];
}

這里有一個(gè)庫中使用的變量pInformation->Ctrl_Info.Usb_wOffset,這個(gè)變量會(huì )在傳輸每個(gè)數據包時(shí)候由庫中的程序按數據包長(cháng)度增加,如最大包長(cháng)為16字節時(shí),第一次調用My_Data_Request時(shí)Usb_wOffset=0,第二次調用 My_Data_Request時(shí)Usb_wOffset=16,第三次調用My_Data_Request時(shí)Usb_wOffset=32,依此類(lèi)推。這樣就可以使用Usb_wOffset作為My_Buffer的下標從My_Data_Request返回相應的緩沖區地址。

前面已經(jīng)說(shuō)明,參數length是用于檢測緩沖區長(cháng)度是否足夠,如果你有足夠長(cháng)的緩沖區,可以不必檢測,上述示例中使用了一個(gè)固定的緩沖區,所以不必使用參數length檢測緩沖區長(cháng)度。

注意,STM32的USB庫設計成以回調函數處理用戶(hù)命令請求,包含類(lèi)命令請求,是為了能夠清晰地區分庫程序和用戶(hù)程序,使這兩者不會(huì )混在一起,這樣的好處是非常明顯的,當USB庫需要更新升級時(shí),只需替換掉相應的程序模塊,而不必修改用戶(hù)已經(jīng)完成的程序。

以上的介紹都可以在STM32 USB庫的說(shuō)明手冊中找到。

最初發(fā)表日期:2008-8-25
本文地址:http://selenalain.com/thread-5772-1-1.html     【打印本頁(yè)】

本站部分文章為轉載或網(wǎng)友發(fā)布,目的在于傳遞和分享信息,并不代表本網(wǎng)贊同其觀(guān)點(diǎn)和對其真實(shí)性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問(wèn)題,我們將根據著(zhù)作權人的要求,第一時(shí)間更正或刪除。
您需要登錄后才可以發(fā)表評論 登錄 | 立即注冊

相關(guān)視頻

關(guān)于我們  -  服務(wù)條款  -  使用指南  -  站點(diǎn)地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權所有   京ICP備16069177號 | 京公網(wǎng)安備11010502021702
快速回復 返回頂部 返回列表
午夜高清国产拍精品福利|亚洲色精品88色婷婷七月丁香|91久久精品无码一区|99久久国语露脸精品|动漫卡通亚洲综合专区48页