stm32f4 USB項目開發詳解


一、USB總體概況

 

1.1、USB通信過程簡介

  1. 設備插到主機上
  2. 主機開始檢測設備類型(高速/全速/低速)
  3. 復位設備
  4. 主機開始對設備枚舉(根據枚舉得到的各種信息加載合適的驅動程序,比如根據信息知道是一個鼠標設備,則加載鼠標的驅動程序對接下來的數據進行處理)
  5. 枚舉完成后主機要發送令牌包(IN / OUT)查詢有效端點是否有數據,有數據時設備自然會返還給主機

 

1.2、USB枚舉過程簡介

  1. 主機獲取設備描述符(部分)
  2. 主機對從機設置設備地址(非零,相當於我們的學號id)
  3. 主機再次獲取從機設備描述符(全部)
  4. 主機獲取配置描述符(了解從機配置,接口,端點)情況
  5. 如果有字符串描述符還有獲取字符串描述符
  6. 設置配置請求,就是激活配置,如果沒有這一步對應的配置就不可用
  7. 針對不同的類,獲取它們獨特的類描述符(比如HID報告描述符)

注意:

  1. 上面的枚舉1-4,6步驟是必須的,
  2. 主機和從機通信時,從機時不能主動發數據給主機的,必須要等主機給從機發送令牌包后,根據主機的需求發送相應的數據

 

1.3、USB 配置  接口  端點  的關系

  1、一個設備可以有多個配置,不同的配置對應不同的功能

    比如,一個USB接口CDROM,作為一個設備,

    它具有兩種功能,1讀取光盤 和  2播CD,所以有2Configuration描述符

  2、一個功能的實現要涉及許多接口,

    比如CD播放機使用時,需要音頻接口,同時還需要控制CD機的接口。 

  3、一個接口又有許多端點組成,一般真正通信都是針對端點進行的,比如用端點0來進行控制枚舉傳輸

    stm32  支持8個雙向端點,16個單向端點,每個端點只能時一個方向(OUT / IN),除了端點0

 

 

 

 、

二、區分高速全速 低速設備的方法

 

d+上面接電阻:全速/高速設備             d-  上接電阻:低速設備

 

 、

三、USB設備插拔檢測機制

3.1、沒有插上usb設備主機情況

  1. D+D-數據線上的下拉電阻起作用,使得二者都在低電平;主機端看來就是個SE0狀態;
  2. 同樣地,當數據線上的SE0狀態持續一段時了,就被主機認為是斷開狀態 

 

 

3.2、 插上usb設備時

 

當主機檢測到某一個數據線電平拉高並保持了一段時間,就認為有設備連上來了

主機必需在驅動SE0狀態以復位設備之前, 立刻采樣總線狀態來判斷設備的速度 

 

 

 四、USB通信數據包解釋以及包的傳輸過程

 4.1、Packet的組成

4.2 、USB包的分類(令牌,數據包,握手包、幀首包)

包的分類組要是靠PID域來說明的,其中令牌包最重要

 

4.2.1、令牌包:

IN  OUT  SETUP  三個命令可選,並且都由主機發出,用來啟動一次傳輸

主機和從機通信時,從機時不能主動發數據給主機的,必須要等主機給從機發送令牌包后,根據主機的需求發送相應的數據

 

  1、IN   主機用來從設備讀取數據 

  2、OUT   主機用來向設備發送數據

  3、SETUP  主機用來向設備發送控制命令,接下來就是主機發送一次命令數據給(這個數據一般時獲取描述符請求)從機  一般枚舉的時候用

 

例如SETUP包:

 

 4.2.2、數據包:

數據包:一般是DATA0 DATA1交替發出,如本次發送DATA0 下次就發送DATA1,主要目的就是為了校驗

圖中DATA0 編碼是0xC3

 

 4.2.3、握手包

用來確認應答

  >> ACK:傳輸正確完成
  >> NAK:設備暫時沒有准備好接收數據,或沒有准備好發送數據
  >> STALL:設備不能進行傳輸
  >> NYET/ERR:僅用於高速傳輸,設備沒有准備好或出錯

4.3、包的傳輸順序

一般都是先由主機發送命令,然后才是數據過程,再時應答過程

 

 

五、枚舉過程

枚舉過程都是主機發送標准請求然后從機做出相應的應答

5.1、枚舉過程的通俗比喻 

1、主機(下稱H):你是什么設備(獲取設備描述符請求)?
  設備(下稱D):12 01 0100……Device Descriptor
2、H:你有幾種功能(獲取配置描述符請求)?
  D:09 02 09……Configuration Descriptor
3、H:每個功能有幾個接口(獲取接口描述符)?
  D:09 04 00……Interface Descriptor
4、H:每個接口使用哪幾個端點(獲取端點描述符)?
  D:06 05 82……Endpoint Descriptor
5、H:好了,我知道你是誰了,開始傳輸數據吧!
  D:OK, Read Go

5.2、主機標准請求

 

主機要獲得從機的描述符就要發送相關8字節標准請求

 

 

 

 5.3、描述符介紹

5.3.1、設備描述符

 

 以stm32為例:

/*usb_desc.c*/

/* USB Standard Device Descriptor */
const uint8_t Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DESC] =
  {
    0x12,                       /*bLength */
    USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
    0x00,                       /*bcdUSB */
    0x02,
    0x00,                       /*bDeviceClass*/
    0x00,                       /*bDeviceSubClass*/
    0x00,                       /*bDeviceProtocol*/
    0x40, /*bMaxPacketSize 64*/
    0x83,                       /*idVendor (0x0483)*/
    0x04,
    0x10,                       /*idProduct = 0x5710*/
    0x57,
    0x00,                       /*bcdDevice rel. 2.00*/
    0x02,
    1,                          /*Index of string descriptor describing
                                                  manufacturer */
    2,                          /*Index of string descriptor describing
                                                 product*/
    3,                          /*Index of string descriptor describing the
                                                 device serial number */
  0x01 /*bNumConfigurations*/
  }
  ; /* Joystick_DeviceDescriptor */

 

 

5.3.2、配置描述符

 

 

 

    0x09, /* bLength: Configuration Descriptor size */
    USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
    JOYSTICK_SIZ_CONFIG_DESC,  /*#define JOYSTICK_SIZ_CONFIG_DESC   34   這是一個大數組,整個數組大小34字節*/
    /* wTotalLength: Bytes returned   */
    0x00,
    0x01, /*bNumInterfaces: 1 interface*/
    0x01,         /*bConfigurationValue: Configuration value*/
    0x00,         /*iConfiguration: Index of string descriptor describing
                                     the configuration*/
    0xE0,         /*bmAttributes: Self powered */
    0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/

 

5.3.3、 接口描述符

 

    /************** Descriptor of Joystick Mouse interface ****************/
    /* 09 */
    0x09,         /*bLength: Interface Descriptor size*/
    USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
    0x00, /*bInterfaceNumber: Number of Interface 序號,編號從0開始,第二個接口就是1*/
    0x00,         /*bAlternateSetting: Alternate setting*/
    0x01, /*bNumEndpoints 該接口的端點數目*/
   0x03, /*bInterfaceClass: HID*/
    0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
    0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
    0,            /*iInterface: Index of string descriptor*/

 5.3.4、類描述符(比如HID類描述符)這個時可選的(如果不是標准類,就不需要)

 

 

   /******************** Descriptor of Joystick Mouse HID ********************/
    /* 18 */
    0x09,         /*bLength: HID Descriptor size*/
    HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
    0x00,         /*bcdHID: HID Class Spec release number*/
    0x01,
    0x00,         /*bCountryCode: Hardware target country*/
    0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
    0x22,         /*bDescriptorType*/
    JOYSTICK_SIZ_REPORT_DESC,/*wItemLength: Total length of Report descriptor*/
    0x00,
    /******************** Descriptor of Joystick Mouse endpoint ********************/

 

 

 5.3.5、端點描述符

    /******************** Descriptor of Joystick Mouse endpoint ********************/
    /* 27 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/

   0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    0x03, /*bmAttributes: Interrupt endpoint 0控制 1等時 2批量 3中斷*/
    0x04,          /*wMaxPacketSize: 4 Byte max */
    0x00,
    0x20,          /*bInterval: Polling Interval (32 ms)*/

 

5.4、獲取設備描述符

 

 

 

 

 

5.5、設置地址請求

 

 

 

 

 5.6、獲取配置描述符

 

 

 

 5.7、設置配置請求

就是讓某個配置有效

 

 

 

 

5.8、stm32 枚舉代碼分析

/*usbh_core.c*/

void
USBH_Process(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost) { volatile USBH_Status status = USBH_FAIL; switch (phost->gState) { 、、、、、、、、、、、、 case HOST_ENUMERATION: /* Check for enumeration status */ if ( USBH_HandleEnum(pdev , phost) == USBH_OK) { /* The function shall return USBH_OK when full enumeration is complete */ /* user callback for end of device basic enumeration */ phost->usr_cb->EnumerationDone(); phost->gState = HOST_USR_INPUT; } break; 、、、、、、、、、、 default : break; } }

 

/*usbh_core.c*/

/*
* * @brief USBH_HandleEnum * This function includes the complete enumeration process * @param pdev: Selected device * @retval USBH_Status */ static USBH_Status USBH_HandleEnum(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost) { USBH_Status Status = USBH_BUSY; uint8_t Local_Buffer[64]; switch (phost->EnumState) { case ENUM_IDLE: /* Get Device Desc for only 1st 8 bytes : To get EP0 MaxPacketSize 得到8字節設備描述符 */ if ( USBH_Get_DevDesc(pdev , phost, 8) == USBH_OK) { phost->Control.ep0size = phost->device_prop.Dev_Desc.bMaxPacketSize; /* Issue Reset 復位設備 */ HCD_ResetPort(pdev); phost->EnumState = ENUM_GET_FULL_DEV_DESC; /* modify control channels configuration for MaxPacket size */ USBH_Modify_Channel (pdev, phost->Control.hc_num_out, 0, 0, 0, phost->Control.ep0size); USBH_Modify_Channel (pdev, phost->Control.hc_num_in, 0, 0, 0, phost->Control.ep0size); } break; case ENUM_GET_FULL_DEV_DESC: /* Get FULL Device Desc 得到整個設備描述符 */ if ( USBH_Get_DevDesc(pdev, phost, USB_DEVICE_DESC_SIZE)\ == USBH_OK) { /* user callback for device descriptor available */ phost->usr_cb->DeviceDescAvailable(&phost->device_prop.Dev_Desc); phost->EnumState = ENUM_SET_ADDR; } break; case ENUM_SET_ADDR: /* 設置地址 set address */ if ( USBH_SetAddress(pdev, phost, USBH_DEVICE_ADDRESS) == USBH_OK) /*#define USBH_DEVICE_ADDRESS 1*/ { USB_OTG_BSP_mDelay(2); phost->device_prop.address = USBH_DEVICE_ADDRESS; /* user callback for device address assigned */ phost->usr_cb->DeviceAddressAssigned(); phost->EnumState = ENUM_GET_CFG_DESC; /* modify control channels to update device address */ USBH_Modify_Channel (pdev, phost->Control.hc_num_in, phost->device_prop.address, 0, 0, 0); USBH_Modify_Channel (pdev, phost->Control.hc_num_out, phost->device_prop.address, 0, 0, 0); } break; case ENUM_GET_CFG_DESC: /* 得到配置描述符get standard configuration descriptor */ if ( USBH_Get_CfgDesc(pdev, phost, USB_CONFIGURATION_DESC_SIZE) == USBH_OK) { phost->EnumState = ENUM_GET_FULL_CFG_DESC; } break; case ENUM_GET_FULL_CFG_DESC: /* get FULL config descriptor (config, interface, endpoints) */ if (USBH_Get_CfgDesc(pdev, phost, phost->device_prop.Cfg_Desc.wTotalLength) == USBH_OK) { /* User callback for configuration descriptors available */ phost->usr_cb->ConfigurationDescAvailable(&phost->device_prop.Cfg_Desc, phost->device_prop.Itf_Desc, phost->device_prop.Ep_Desc[0]); phost->EnumState = ENUM_GET_MFC_STRING_DESC; } break; case ENUM_GET_MFC_STRING_DESC: if (phost->device_prop.Dev_Desc.iManufacturer != 0) { /* 字符串描述符Check that Manufacturer String is available */ if ( USBH_Get_StringDesc(pdev, phost, phost->device_prop.Dev_Desc.iManufacturer, Local_Buffer , 0xff) == USBH_OK) { /* User callback for Manufacturing string */ phost->usr_cb->ManufacturerString(Local_Buffer); phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC; } } else { phost->usr_cb->ManufacturerString("N/A"); phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC; } break; case ENUM_GET_PRODUCT_STRING_DESC: if (phost->device_prop.Dev_Desc.iProduct != 0) { /* Check that Product string is available */ if ( USBH_Get_StringDesc(pdev, phost, phost->device_prop.Dev_Desc.iProduct, Local_Buffer, 0xff) == USBH_OK) { /* User callback for Product string */ phost->usr_cb->ProductString(Local_Buffer); phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC; } } else { phost->usr_cb->ProductString("N/A"); phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC; } break; case ENUM_GET_SERIALNUM_STRING_DESC: if (phost->device_prop.Dev_Desc.iSerialNumber != 0) { /* Check that Serial number string is available */ if ( USBH_Get_StringDesc(pdev, phost, phost->device_prop.Dev_Desc.iSerialNumber, Local_Buffer, 0xff) == USBH_OK) { /* User callback for Serial number string */ phost->usr_cb->SerialNumString(Local_Buffer); phost->EnumState = ENUM_SET_CONFIGURATION; } } else { phost->usr_cb->SerialNumString("N/A"); phost->EnumState = ENUM_SET_CONFIGURATION; } break; case ENUM_SET_CONFIGURATION: /* 設置配置請求set configuration (default config) */ if (USBH_SetCfg(pdev, phost, phost->device_prop.Cfg_Desc.bConfigurationValue) == USBH_OK) { phost->EnumState = ENUM_DEV_CONFIGURED; } break; case ENUM_DEV_CONFIGURED: /* user callback for enumeration done */ Status = USBH_OK; break; default: break; } return Status; }

 

 

 六、 USB枚舉后的數據傳輸過程  以及    四種傳輸類型

usb 四種傳輸類型是針對端點而言的,並不是usb整個傳輸過程為一種傳輸類型

1、控制傳輸(Control Transfers):  枚舉  

  (一般用於枚舉過程端點0,主機給從機發命令或回應狀態時,這個過程是隨機突發的的,也就是主機隨時發命令,從機只能是待命)

 

 

2、大容量數據傳輸 批量傳輸(Bulk Transfers):       U盤

        大容量傳輸一般用於非零端點,並且針對大數據傳輸(如U盤),數據可以占用任意帶寬,並容忍延遲 ,並且這個過程也是隨機突發的,因為控制權在主機,用戶隨時要讀取設備(u盤)數據,所以這個過程是非周期的

 

 

stm32 大容量傳輸分析

 

 

3、同步傳輸(Isochronous Transfers):    攝像頭           

        周期性的,持續性的傳輸,用於傳輸與時效相關的信息,並且在數據中保存時間戳的信息 比如(攝像頭圖像傳

 

 

 

4、中斷傳輸(Interrupt Transfers):   鼠標鍵盤

  周期性,低頻率,比如hid鼠標鍵盤主機是周期性查詢端點有沒有數據的,只要鼠標有動作,就可以往端點緩存送數據,等到主機查詢的時候,數據就會被主機讀走)所以在設備初始化的時候要初始化主機查詢時間間隔

超時輪詢時間間隔在從機端點描述符中獲取

 

 

 stm32中斷傳輸分析

/*主機 usbh_hid_core.c*/

static USBH_Status USBH_HID_Handle(USB_OTG_CORE_HANDLE *pdev , 
                                   void   *phost)
{
 、、、、、、switch (HID_Machine.state)
  {
    
 、、、、、case HID_GET_DATA:

    USBH_InterruptReceiveData(pdev, 
                              HID_Machine.buff,
                              HID_Machine.length,
                              HID_Machine.hc_num_in);
    start_toggle = 1;
    
    HID_Machine.state = HID_POLL;
    HID_Machine.timer = HCD_GetCurrentFrame(pdev);
    break;
    
  case HID_POLL: if(( HCD_GetCurrentFrame(pdev) - HID_Machine.timer) >= HID_Machine.poll)//超時輪詢
    {
      HID_Machine.state = HID_GET_DATA;
    }
    else if(HCD_GetURB_State(pdev , HID_Machine.hc_num_in) == URB_DONE)
    {
      if(start_toggle == 1) /* handle data once */
      {
        start_toggle = 0;
       HID_Machine.cb->Decode(HID_Machine.buff);
      }
    }
    else if(HCD_GetURB_State(pdev, HID_Machine.hc_num_in) == URB_STALL) /* IN Endpoint Stalled */
    {
      
      /* Issue Clear Feature on interrupt IN endpoint */ 
      if( (USBH_ClrFeature(pdev, 
                           pphost,
                           HID_Machine.ep_addr,
                           HID_Machine.hc_num_in)) == USBH_OK)
      {
        /* Change state to issue next IN token */
        HID_Machine.state = HID_GET_DATA;
        
      }
      
    }      
    break;
    
  default:
    break;
  }
  return status;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM