• STM32 USB复合设备编写


    目的

    • 完成一个CDC + MSC的复合USB设备
    • 可以方便在CDC,MSC,复合设备三者间切换
    • 可移植性强

    预备知识

    cube中USB只有两个入口。

    • main函数中的MX_USB_DEVICE_Init函数。
    /* init function */
    void MX_USB_DEVICE_Init(void)
    {
      /* Init Device Library,Add Supported Class and Start the library*/
      USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
      USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE_CLASS);
      USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_FS);
      USBD_Start(&hUsbDeviceFS);
    }
    
    • USB中断。USB的所有动作都是主机发起的,设备只是做响应。所以在cube中,所有的USB动作入口都是一个中断。
    void USB_LP_CAN1_RX0_IRQHandler(void)
    {
      /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */
    
      /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
      HAL_PCD_IRQHandler(&hpcd_USB_FS);
      /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */
    
      /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */
    }
    

    看一下中断响应函数的内容

    /**
      * @brief  This function handles PCD interrupt request.
      * @param  hpcd: PCD handle
      * @retval HAL status
      */
    void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
    {
      uint32_t wInterrupt_Mask = 0;
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_CTR))
      {
        /* servicing of the endpoint correct transfer interrupt */
        /* clear of the CTR flag into the sub */
        PCD_EP_ISR_Handler(hpcd);
      }
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_RESET))
      {
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_RESET);
        HAL_PCD_ResetCallback(hpcd);
        HAL_PCD_SetAddress(hpcd, 0);
      }
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_PMAOVR))
      {
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_PMAOVR);
      }
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_ERR))
      {
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_ERR);
      }
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_WKUP))
      {
        hpcd->Instance->CNTR &= ~(USB_CNTR_LP_MODE);
    
        /*set wInterrupt_Mask global variable*/
        wInterrupt_Mask = USB_CNTR_CTRM  | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ERRM 
          | USB_CNTR_ESOFM | USB_CNTR_RESETM;
    
        /*Set interrupt mask*/
        hpcd->Instance->CNTR = wInterrupt_Mask;
    
        HAL_PCD_ResumeCallback(hpcd);
    
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_WKUP);
      }
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_SUSP))
      {
        /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_SUSP);
    
        /* Force low-power mode in the macrocell */
        hpcd->Instance->CNTR |= USB_CNTR_FSUSP;
        hpcd->Instance->CNTR |= USB_CNTR_LP_MODE;
        if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_WKUP) == 0)
        {
          HAL_PCD_SuspendCallback(hpcd);
        }
      }
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_SOF))
      {
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_SOF);
        HAL_PCD_SOFCallback(hpcd);
      }
    
      if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_ESOF))
      {
        /* clear ESOF flag in ISTR */
        __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_ESOF);
      }
    }
    

    可以看出来,中断响应函数根据各个不同的中断源做出不同的操作。这点在参考手册也有提到

    在USB插入之后,主机开始进行枚举,复位USB,触发复位中断,会进入下面一个if语句。

    if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_RESET))
    {
      __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_RESET);
      HAL_PCD_ResetCallback(hpcd);
      HAL_PCD_SetAddress(hpcd, 0);
    }
    

    这两个函数主要是设置总线速度,复位USB,以及设置USB地址。分析了这两个函数,基本了解了程序的流程,主要是两个关键的结构体

    /**
      * @brief  PCD Handle Structure definition
      */
    typedef struct
    {
      PCD_TypeDef             *Instance;   /*!< Register base address               */
      PCD_InitTypeDef         Init;        /*!< PCD required parameters             */
      __IO uint8_t            USB_Address; /*!< USB Address: not used by USB OTG FS */
      PCD_EPTypeDef           IN_ep[15];   /*!< IN endpoint parameters              */
      PCD_EPTypeDef           OUT_ep[15];  /*!< OUT endpoint parameters             */
      HAL_LockTypeDef         Lock;        /*!< PCD peripheral status               */
      __IO PCD_StateTypeDef   State;       /*!< PCD communication state             */
      uint32_t                Setup[12];   /*!< Setup packet buffer                 */
      void                    *pData;      /*!< Pointer to upper stack Handler      */
    } PCD_HandleTypeDef;
    
    /* USB Device handle structure */
    typedef struct _USBD_HandleTypeDef
    {
      uint8_t                 id;
      uint32_t                dev_config;
      uint32_t                dev_default_config;
      uint32_t                dev_config_status;
      USBD_SpeedTypeDef       dev_speed;
      USBD_EndpointTypeDef    ep_in[15];
      USBD_EndpointTypeDef    ep_out[15];
      uint32_t                ep0_state;
      uint32_t                ep0_data_len;
      uint8_t                 dev_state;
      uint8_t                 dev_old_state;
      uint8_t                 dev_address;
      uint8_t                 dev_connection_status;
      uint8_t                 dev_test_mode;
      uint32_t                dev_remote_wakeup;
    
      USBD_SetupReqTypedef    request;
      USBD_DescriptorsTypeDef *pDesc;
      USBD_ClassTypeDef       *pClass;
      void                    *pClassData;
      void                    *pUserData;
      void                    *pData;
    } USBD_HandleTypeDef;
    
    

    这两个结构体很关键,看下复位操作的函数

    /**
      * @brief  Reset callback.
      * @param  hpcd: PCD handle
      * @retval None
      */
    void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
    {
      USBD_SpeedTypeDef speed = USBD_SPEED_FULL;
    
      /*Set USB Current Speed*/
      switch (hpcd->Init.speed)
      {
      case PCD_SPEED_FULL:
        speed = USBD_SPEED_FULL;
        break;
    
      default:
        speed = USBD_SPEED_FULL;
        break;
      }
      USBD_LL_SetSpeed((USBD_HandleTypeDef*)hpcd->pData, speed);
    
      /*Reset Device*/
      USBD_LL_Reset((USBD_HandleTypeDef*)hpcd->pData);
    }
    

    这里有一个强制类型转换,将一个void指针强制转换为USBD_HandleTypeDef,那么这个指针必然一早就指向了一个实体的USBD_HandleTypeDef,向上追朔调用关系

    main->MX_USB_DEVICE_Init->USBD_Init->USBD_LL_Init->pdev->hpcd_USB_FS.pData = pdev

    然后,hpcd_USB_FS作为中断处理函数的参数被传进来。

    /**
    * @brief  USBD_LL_Reset
    *         Handle Reset event
    * @param  pdev: device instance
    * @retval status
    */
    
    USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef  *pdev)
    {
      /* Open EP0 OUT */
      USBD_LL_OpenEP(pdev,
                  0x00,
                  USBD_EP_TYPE_CTRL,
                  USB_MAX_EP0_SIZE);
    
      pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE;
    
      /* Open EP0 IN */
      USBD_LL_OpenEP(pdev,
                  0x80,
                  USBD_EP_TYPE_CTRL,
                  USB_MAX_EP0_SIZE);
    
      pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE;
      /* Upon Reset call user call back */
      pdev->dev_state = USBD_STATE_DEFAULT;
    
      if (pdev->pClassData)
        pdev->pClass->DeInit(pdev, pdev->dev_config);
    
    
      return USBD_OK;
    }
    
    
    
    
    /**
    * @brief  USBD_LL_Reset
    *         Handle Reset event
    * @param  pdev: device instance
    * @retval status
    */
    USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef  *pdev, USBD_SpeedTypeDef speed)
    {
      pdev->dev_speed = speed;
      return USBD_OK;
    }
    

    SetSpeed比较简单,只是将hUsbDeviceFs中的dev_speed设置一下。
    Reset函数则是将端点0的IN OUT端点打开,然后有一句

    pdev->pClass->DeInit(pdev,pdev->dev_config)

    这里又是一个USBD_ClassTypeDef类型的指针

    typedef struct _Device_cb
    {
      uint8_t  (*Init)             (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx);
      uint8_t  (*DeInit)           (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx);
     /* Control Endpoints*/
      uint8_t  (*Setup)            (struct _USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef  *req);
      uint8_t  (*EP0_TxSent)       (struct _USBD_HandleTypeDef *pdev );
      uint8_t  (*EP0_RxReady)      (struct _USBD_HandleTypeDef *pdev );
      /* Class Specific Endpoints*/
      uint8_t  (*DataIn)           (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
      uint8_t  (*DataOut)          (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
      uint8_t  (*SOF)              (struct _USBD_HandleTypeDef *pdev);
      uint8_t  (*IsoINIncomplete)  (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
      uint8_t  (*IsoOUTIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
    
      uint8_t  *(*GetHSConfigDescriptor)(uint16_t *length);
      uint8_t  *(*GetFSConfigDescriptor)(uint16_t *length);
      uint8_t  *(*GetOtherSpeedConfigDescriptor)(uint16_t *length);
      uint8_t  *(*GetDeviceQualifierDescriptor)(uint16_t *length);
    #if (USBD_SUPPORT_USER_STRING == 1)
      uint8_t  *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev ,uint8_t index,  uint16_t *length);
    #endif
    
    } USBD_ClassTypeDef;
    

    可以看到这个类定义都是函数指针,在调用之前必定实例化(借用面向对象来词汇)过。

    main -> MX_USB_DEVICE_Init->USBD_RegisterClass->pdev->pClass=pclass

    如果是CDC类,那么最终指向的是在usbd_cdc.c中的USBD_CDC类,如果是复合类,那么就需要我们自己定义。
    在调用DeInit过程中,又会调用两个void 指针:pclassData,pUserData
    其中pClassData是初始化是在初始化各个类的时候,比如

    • MSC类

    USBD_MSC_Init -> pdev->pClassData = USBD_malloc(sizeof (USBD_MSC_BOT_HandleTypeDef));

    • CDC类

    USBD_CDC_Init -> pdev->pClassData = USBD_malloc(sizeof (USBD_CDC_HandleTypeDef));

    不同的类,指向不同的函数指针。

    pUserData是通过一个函数初始化,比如CDC类

    main -> USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);

    USBD_Interface_fops_FS定义如下:

    typedef struct _USBD_CDC_Itf
    {
      int8_t (* Init)          (void);
      int8_t (* DeInit)        (void);
      int8_t (* Control)       (uint8_t, uint8_t * , uint16_t);
      int8_t (* Receive)       (uint8_t *, uint32_t *);
    
    }USBD_CDC_ItfTypeDef;
    

    pClassData和pUserData在USBD_HandleTypeDef中是指针形式,所以在调用不同类的时候,改变指针的指向,即可完成不同类的功能。我们复合设备类的设计思想既是如此。

    可以总结一下 USBD_HandleTypeDef中几个复杂的指针

     USBD_DescriptorsTypeDef *pDesc;
      USBD_ClassTypeDef       *pClass;
      void                    *pClassData;
      void                    *pUserData;
      void                    *pData;
    
    • pDesc指向描述符的数组,在枚举阶段启用
    • pClass指向设备类,比如CDC类的USBD_ClassTypeDef,负责类的底层Init,Setup,DataIn/Out等
    • pClassData指向设备类句柄,负责记录数据,比如RxBuffer,TxBuffer,RxLength等
    • pUserData指向设备类的接口操作函数,比如CDC的Init,Receive,Transmit,比如MSC的Read,Write等
    • pData指向hpcd_USB_FS

    注意点:

    • pClassData要在堆上动态生成,不能调用静态分配函数,否则两次分配会被分配到同一段内存中
    #define USBD_malloc               malloc
    #define USBD_free                 free
    
    • 在pClass->Init之后,pClassData分配完成,此时需要一个全局指针将这个pClassData记录下来
    • 在pClass->DeInit时,pClassData根据不同的类读回之前的全局指针,然后free掉堆上的内存
    • 由于使用了两次malloc所以heap_size要分配足够大,同时也要注意修改stack_size。
    Stack_Size EQU 0x1000
    Heap_Size  EQU 0x800
    

    准备工作

    • 利用cube生成一个CDC工程和一个MSC工程,将CDC工程中的usbd_cdc以及usbd_cdc_if文件拷贝出来。
    • 修改都在MSC工程下面完成。在工程下建立一个USBConfig文件夹,将之前拷贝的文件以及其他USB配置相关的文件放到这个文件夹下面。
    • 新建一个usbd_composite.c,usbd_composite.h,添加到工程。整体文件结构如下:



    • 编译(注意添加头文件路径)

    修改

    • 分配好端点号
    #define CDC_IN_EP                                   0x81  /* EP1 for data IN */
    #define CDC_OUT_EP                                  0x01  /* EP1 for data OUT */
    #define CDC_CMD_EP                                  0x83  /* EP3 for CDC commands */
    
    #define MSC_EPIN_ADDR                0x82
    #define MSC_EPOUT_ADDR               0x02
    
    • 将如下内容添加到usbd_composite.c以及usbdcomposite.h
    /**
     * @file        usbd_composite.c
     * @author      Weyne
     * @version     V01
     * @date        2016.10.28
     * @brief       MSC + CDC 复合设备
     * @note
     * @attention   COYPRIGHT WEYNE
     */
    
    #include "usbd_composite.h"
    #include "usbd_cdc.h"
    #include "usbd_msc.h"
    
    static USBD_CDC_HandleTypeDef *pCDCData;
    static USBD_MSC_BOT_HandleTypeDef *pMSCData;
    
    
    static uint8_t  USBD_Composite_Init (USBD_HandleTypeDef *pdev,
                                uint8_t cfgidx);
    
    static uint8_t  USBD_Composite_DeInit (USBD_HandleTypeDef *pdev,
                                  uint8_t cfgidx);
    
    static uint8_t  USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev);
    
    static uint8_t  USBD_Composite_Setup (USBD_HandleTypeDef *pdev,
                                 USBD_SetupReqTypedef *req);
    
    static uint8_t  USBD_Composite_DataIn (USBD_HandleTypeDef *pdev,
                                  uint8_t epnum);
    
    static uint8_t  USBD_Composite_DataOut (USBD_HandleTypeDef *pdev,
                                   uint8_t epnum);
    
    static uint8_t  *USBD_Composite_GetFSCfgDesc (uint16_t *length);
    
    static uint8_t  *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
    
    USBD_ClassTypeDef  USBD_COMPOSITE =
    {
      USBD_Composite_Init,
      USBD_Composite_DeInit,
      USBD_Composite_Setup,
      NULL, /*EP0_TxSent*/
      USBD_Composite_EP0_RxReady,
      USBD_Composite_DataIn,
      USBD_Composite_DataOut,
      NULL,
      NULL,
      NULL,
      NULL,
      USBD_Composite_GetFSCfgDesc,
      NULL,
      USBD_Composite_GetDeviceQualifierDescriptor,
    };
    
    /* USB composite device Configuration Descriptor */
    /*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
    __ALIGN_BEGIN uint8_t USBD_Composite_CfgFSDesc[USBD_COMPOSITE_DESC_SIZE]  __ALIGN_END =
    {
      0x09,   /* bLength: Configuation Descriptor size */
      USB_DESC_TYPE_CONFIGURATION,   /* bDescriptorType: Configuration */
      WBVAL(USBD_COMPOSITE_DESC_SIZE),
      USBD_MAX_NUM_INTERFACES ,  /* bNumInterfaces: */
      0x01,   /* bConfigurationValue: */
      0x04,   /* iConfiguration: */
      0xC0,   /* bmAttributes: */
      0x96,   /* MaxPower 300 mA */
    
    
      /****************************CDC************************************/
      /* Interface Association Descriptor */
      USBD_IAD_DESC_SIZE,               // bLength
      USBD_IAD_DESCRIPTOR_TYPE,         // bDescriptorType
      USBD_CDC_FIRST_INTERFACE,         // bFirstInterface
      USBD_CDC_INTERFACE_NUM,           // bInterfaceCount
      0x02,                             // bFunctionClass
      0x02,                             // bFunctionSubClass
      0x01,                             // bInterfaceProtocol
      0x04,                             // iFunction
    
      /*Interface Descriptor */
      0x09,   /* bLength: Interface Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
      /* Interface descriptor type */
      USBD_CDC_CMD_INTERFACE,   /* bInterfaceNumber: Number of Interface */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x01,   /* bNumEndpoints: One endpoints used */
      0x02,   /* bInterfaceClass: Communication Interface Class */
      0x02,   /* bInterfaceSubClass: Abstract Control Model */
      0x01,   /* bInterfaceProtocol: Common AT commands */
      0x01,   /* iInterface: */
    
      /*Header Functional Descriptor*/
      0x05,   /* bLength: Endpoint Descriptor size */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x00,   /* bDescriptorSubtype: Header Func Desc */
      0x10,   /* bcdCDC: spec release number */
      0x01,
    
      /*Call Management Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x01,   /* bDescriptorSubtype: Call Management Func Desc */
      0x00,   /* bmCapabilities: D0+D1 */
      0x01,   /* bDataInterface: 1 */
    
      /*ACM Functional Descriptor*/
      0x04,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
      0x02,   /* bmCapabilities */
    
      /*Union Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x06,   /* bDescriptorSubtype: Union func desc */
      USBD_CDC_CMD_INTERFACE,   /* bMasterInterface: Communication class interface */
      USBD_CDC_DATA_INTERFACE,   /* bSlaveInterface0: Data Class Interface */
    
      /*Endpoint 2 Descriptor*/
      0x07,                           /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
      CDC_CMD_EP,                     /* bEndpointAddress */
      0x03,                           /* bmAttributes: Interrupt */
      LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
      HIBYTE(CDC_CMD_PACKET_SIZE),
      0x01,                           /* bInterval: */
    
    
      /*Data class interface descriptor*/
      0x09,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
      USBD_CDC_DATA_INTERFACE,   /* bInterfaceNumber: Number of Interface */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints: Two endpoints used */
      0x0A,   /* bInterfaceClass: CDC */
      0x02,   /* bInterfaceSubClass: */
      0x00,   /* bInterfaceProtocol: */
      0x01,   /* iInterface: */
    
      /*Endpoint OUT Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC_OUT_EP,                        /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x01,                              /* bInterval: ignore for Bulk transfer */
    
      /*Endpoint IN Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC_IN_EP,                         /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x01,                               /* bInterval: ignore for Bulk transfer */
    
    
     /****************************MSC************************************/
      /* Interface Association Descriptor */
      USBD_IAD_DESC_SIZE,                        // bLength
      USBD_IAD_DESCRIPTOR_TYPE,                  // bDescriptorType
      USBD_MSC_FIRST_INTERFACE,                  // bFirstInterface
      USBD_MSC_INTERFACE_NUM,                    // bInterfaceCount
      0x08,                                      // bFunctionClass
      0x06,                                      // bFunctionSubClass
      0x50,                                      // bInterfaceProtocol
      0x05,
    
      /********************  Mass Storage interface ********************/
      0x09,   /* bLength: Interface Descriptor size */
      USB_DESC_TYPE_INTERFACE,   /* bDescriptorType: */
      USBD_MSC_INTERFACE,   /* bInterfaceNumber: Number of Interface */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints*/
      0x08,   /* bInterfaceClass: MSC Class */
      0x06,   /* bInterfaceSubClass : SCSI transparent*/
      0x50,   /* nInterfaceProtocol */
      0x05,          /* iInterface: */
    
      /********************  Mass Storage Endpoints ********************/
      0x07,   /*Endpoint descriptor length = 7*/
      0x05,   /*Endpoint descriptor type */
      MSC_EPIN_ADDR,   /*Endpoint address (IN, address 1) */
      0x02,   /*Bulk endpoint type */
      LOBYTE(MSC_MAX_FS_PACKET),
      HIBYTE(MSC_MAX_FS_PACKET),
      0x01,   /*Polling interval in milliseconds */
    
      0x07,   /*Endpoint descriptor length = 7 */
      0x05,   /*Endpoint descriptor type */
      MSC_EPOUT_ADDR,   /*Endpoint address (OUT, address 1) */
      0x02,   /*Bulk endpoint type */
      LOBYTE(MSC_MAX_FS_PACKET),
      HIBYTE(MSC_MAX_FS_PACKET),
      0x01,     /*Polling interval in milliseconds*/
    
    };
    
    
    /* USB Standard Device Descriptor */
    __ALIGN_BEGIN  uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC]  __ALIGN_END =
    {
      USB_LEN_DEV_QUALIFIER_DESC,
      USB_DESC_TYPE_DEVICE_QUALIFIER,
      0x00,
      0x02,
      0x00,
      0x00,
      0x00,
      0x40,
      0x01,
      0x00,
    };
    
    
    /**
      * @brief  USBD_Composite_Init
      *         Initialize the Composite interface
      * @param  pdev: device instance
      * @param  cfgidx: Configuration index
      * @retval status
      */
    static uint8_t  USBD_Composite_Init (USBD_HandleTypeDef *pdev,
                                uint8_t cfgidx)
    {
      uint8_t res = 0;
    
      pdev->pUserData =  &USBD_CDC_Interface_fops_FS;
      res +=  USBD_CDC.Init(pdev,cfgidx);
      pCDCData = pdev->pClassData;
      pdev->pUserData = &USBD_Storage_Interface_fops_FS;
      res +=  USBD_MSC.Init(pdev,cfgidx);
      pMSCData = pdev->pClassData;
      return res;
    }
    
    /**
      * @brief  USBD_Composite_DeInit
      *         DeInitilaize  the Composite configuration
      * @param  pdev: device instance
      * @param  cfgidx: configuration index
      * @retval status
      */
    static uint8_t  USBD_Composite_DeInit (USBD_HandleTypeDef *pdev,
                                  uint8_t cfgidx)
    {
    	uint8_t res = 0;
    	pdev->pClassData = pCDCData;
    	pdev->pUserData = &USBD_CDC_Interface_fops_FS;
    	res +=  USBD_CDC.DeInit(pdev,cfgidx);
    
    	pdev->pClassData = pMSCData;
    	pdev->pUserData = &USBD_Storage_Interface_fops_FS;
    	res +=  USBD_MSC.DeInit(pdev,cfgidx);
    
    	return res;
    }
    
    
    static uint8_t  USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev)
    {
        return USBD_CDC.EP0_RxReady(pdev);
    }
    
    
    
    /**
    * @brief  USBD_Composite_Setup
    *         Handle the Composite requests
    * @param  pdev: device instance
    * @param  req: USB request
    * @retval status
    */
    static uint8_t  USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
    {
      switch (req->bmRequest & USB_REQ_RECIPIENT_MASK)
      {
       case USB_REQ_RECIPIENT_INTERFACE:
         switch(req->wIndex)
          {
             case USBD_CDC_DATA_INTERFACE:
             case USBD_CDC_CMD_INTERFACE:
    			 pdev->pClassData = pCDCData;
    			 pdev->pUserData =  &USBD_CDC_Interface_fops_FS;
               return(USBD_CDC.Setup(pdev, req));
    
             case USBD_MSC_INTERFACE:
    			 pdev->pClassData = pMSCData;
    			 pdev->pUserData =  &USBD_Storage_Interface_fops_FS;
               return(USBD_MSC.Setup (pdev, req));
    
             default:
                break;
         }
         break;
    
       case USB_REQ_RECIPIENT_ENDPOINT:
         switch(req->wIndex)
         {
    
             case CDC_IN_EP:
             case CDC_OUT_EP:
             case CDC_CMD_EP:
    			 pdev->pClassData = pCDCData;
    			 pdev->pUserData =  &USBD_CDC_Interface_fops_FS;
               return(USBD_CDC.Setup(pdev, req));
    
             case MSC_EPIN_ADDR:
             case MSC_EPOUT_ADDR:
    			 pdev->pClassData = pMSCData;
    			 pdev->pUserData =  &USBD_Storage_Interface_fops_FS;
               return(USBD_MSC.Setup (pdev, req));
    
             default:
                break;
         }
         break;
      }
      return USBD_OK;
    }
    
    
    
    
    /**
    * @brief  USBD_Composite_DataIn
    *         handle data IN Stage
    * @param  pdev: device instance
    * @param  epnum: endpoint index
    * @retval status
    */
    uint8_t  USBD_Composite_DataIn (USBD_HandleTypeDef *pdev,
                                  uint8_t epnum)
    {
      switch(epnum)
      {
          case CDC_INDATA_NUM:
    		pdev->pClassData = pCDCData;
    		pdev->pUserData =  &USBD_CDC_Interface_fops_FS;
             return(USBD_CDC.DataIn(pdev,epnum));
    
          case MSC_INDATA_NUM:
    			 pdev->pClassData = pMSCData;
    			 pdev->pUserData =  &USBD_Storage_Interface_fops_FS;
             return(USBD_MSC.DataIn(pdev,epnum));
    
          default:
             break;
    
      }
      return USBD_FAIL;
    }
    
    
    /**
    * @brief  USBD_Composite_DataOut
    *         handle data OUT Stage
    * @param  pdev: device instance
    * @param  epnum: endpoint index
    * @retval status
    */
    uint8_t  USBD_Composite_DataOut (USBD_HandleTypeDef *pdev,
                                   uint8_t epnum)
    {
      switch(epnum)
      {
          case CDC_OUTDATA_NUM:
          case CDC_OUTCMD_NUM:
    		pdev->pClassData = pCDCData;
    		pdev->pUserData =  &USBD_CDC_Interface_fops_FS;
             return(USBD_CDC.DataOut(pdev,epnum));
    
          case MSC_OUTDATA_NUM:
    			 pdev->pClassData = pMSCData;
    			 pdev->pUserData =  &USBD_Storage_Interface_fops_FS;
             return(USBD_MSC.DataOut(pdev,epnum));
    
          default:
             break;
    
      }
      return USBD_FAIL;
    }
    
    
    
    /**
    * @brief  USBD_Composite_GetHSCfgDesc
    *         return configuration descriptor
    * @param  length : pointer data length
    * @retval pointer to descriptor buffer
    */
    uint8_t  *USBD_Composite_GetFSCfgDesc (uint16_t *length)
    {
       *length = sizeof (USBD_Composite_CfgFSDesc);
       return USBD_Composite_CfgFSDesc;
    }
    
    /**
    * @brief  DeviceQualifierDescriptor
    *         return Device Qualifier descriptor
    * @param  length : pointer data length
    * @retval pointer to descriptor buffer
    */
    uint8_t  *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length)
    {
      *length = sizeof (USBD_Composite_DeviceQualifierDesc);
      return USBD_Composite_DeviceQualifierDesc;
    }
    
    
    /**
      * @}
      */
    
    
    /**
      * @}
      */
    
    
    /**
      * @}
      */
    
    /************************ (C) COPYRIGHT WEYNE *****END OF FILE****/
    
    
    
    /**
     * @file        usbd_composite.h
     * @author      Weyne
     * @version     V01
     * @date        2016.10.28
     * @brief       MSC + CDC 复合设备
     * @note
     * @attention   COYPRIGHT WEYNE
     */
    
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __USBD_COMPOSITE_H
    #define __USBD_COMPOSITE_H
    
    #ifdef __cplusplus
     extern "C" {
    #endif
    
    /* Includes ------------------------------------------------------------------*/
    #include  "usbd_msc.h"
    #include  "usbd_cdc.h"
    #include "usbd_storage_if.h"
    #include "usbd_cdc_if.h"
    
    #define WBVAL(x) (x & 0xFF),((x >> 8) & 0xFF)
    #define DBVAL(x) (x & 0xFF),((x >> 8) & 0xFF),((x >> 16) & 0xFF),((x >> 24) & 0xFF)
    
    #define USBD_IAD_DESC_SIZE           0x08
    #define USBD_IAD_DESCRIPTOR_TYPE     0x0B
    
    
    
    #define USBD_CDC_FIRST_INTERFACE     0          /* CDC FirstInterface */
    #define USBD_CDC_INTERFACE_NUM       2          /* CDC Interface NUM */
    
    #define USBD_CDC_CMD_INTERFACE       0
    #define USBD_CDC_DATA_INTERFACE      1
    
    #define USBD_MSC_FIRST_INTERFACE     2          /* MSC FirstInterface */
    #define USBD_MSC_INTERFACE_NUM       1          /* MSC Interface NUM */
    
    #define USBD_MSC_INTERFACE           2
    
    
    #define MSC_INDATA_NUM              (MSC_EPIN_ADDR & 0x0F)
    #define MSC_OUTDATA_NUM             (MSC_EPOUT_ADDR & 0x0F)
    
    #define CDC_INDATA_NUM              (CDC_IN_EP & 0x0F)
    #define CDC_OUTDATA_NUM             (CDC_OUT_EP & 0x0F)
    #define CDC_OUTCMD_NUM              (CDC_CMD_EP & 0x0F)
    
    #define USBD_COMPOSITE_DESC_SIZE    (9  + 58 + 8 +  32 + 8)
    
    
    extern USBD_ClassTypeDef    USBD_COMPOSITE;
    
    /**
      * @}
      */
    
    /**
      * @}
      */
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif  /* __USBD_MSC_H */
    /**
      * @}
      */
    
    /************************ (C) COPYRIGHT WEYNE *****END OF FILE****/
    
    

    注意我这里修改了cube原来在usbd_cdc_if中的fops变量名(原来的命名不合理),直接编译会报错,需要修改下。

    • 修改 usbd_device.h
    /* init function */
    void MX_USB_DEVICE_Init(void)
    {
      /* Init Device Library,Add Supported Class and Start the library*/
      USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
    
      USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE);
    
     // USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS);
    
      USBD_Start(&hUsbDeviceFS);
    
    }
    
    • 修改 usbd_conf.h
    #define USBD_MAX_NUM_INTERFACES     3
    
    #define USBD_malloc               malloc
    #define USBD_free                 free
    
    • 修改usbd_conf.c
    /**
      * @brief  Initializes the Low Level portion of the Device driver.
      * @param  pdev: Device handle
      * @retval USBD Status
      */
    USBD_StatusTypeDef  USBD_LL_Init (USBD_HandleTypeDef *pdev)
    {
      /* Init USB_IP */
      /* Link The driver to the stack */
      hpcd_USB_FS.pData = pdev;
      pdev->pData = &hpcd_USB_FS;
    
      hpcd_USB_FS.Instance = USB;
      hpcd_USB_FS.Init.dev_endpoints = 8;
      hpcd_USB_FS.Init.speed = PCD_SPEED_FULL;
      hpcd_USB_FS.Init.ep0_mps = DEP0CTL_MPS_8;
      hpcd_USB_FS.Init.low_power_enable = DISABLE;
      hpcd_USB_FS.Init.lpm_enable = DISABLE;
      hpcd_USB_FS.Init.battery_charging_enable = DISABLE;
      if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK)
      {
        Error_Handler();
      }
    
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0x98);
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xD8);
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPIN_ADDR , PCD_SNG_BUF, 0x118);
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPOUT_ADDR , PCD_SNG_BUF, 0x158);
      HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP   , PCD_SNG_BUF, 0x198);
    
      return USBD_OK;
    }
    

    修改完成

    安装驱动

    • 安装ST的虚拟串口驱动。插上复合设备后,如果提示找不到驱动,可以采用下面的办法
    • 在设备管理器中找到 STMicroelectronics -> 右键 -> 更新驱动程序软件 -> 浏览计算机以查找驱动
      程序软件 -> 从计算机的设备驱动程序列表中选择 -> 选择“端口(COM和LPT)”-> 从磁盘安装 ->
      找到stmcdc.inf的目录并选择stmcdc.inf(一般在C:Program Files (x86)STMicroelectronicsWin7)
      -> 确定执行安装。

    至此,设备可以枚举成功了,电脑中出现了串口号和U盘。

    测试

    这里只测试虚拟串口

    int main(void)
    {
    
      /* USER CODE BEGIN 1 */
    	char test[]="test
    ";
      /* USER CODE END 1 */
    
      /* MCU Configuration----------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_USART1_UART_Init();
      MX_USB_DEVICE_Init();
    
      /* USER CODE BEGIN 2 */
      HAL_Delay(10000);
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
      /* USER CODE END WHILE */
    	CDC_Transmit_FS((uint8_t*)test,sizeof(test));
    	HAL_Delay(500);
      /* USER CODE BEGIN 3 */
    
      }
      /* USER CODE END 3 */
    
    }
    

    测试串口可以正常收到数据

    转载请注明出处!

  • 相关阅读:
    C语言中条件表达式求最大值和最小值
    面向对象编程:Java的简单数据类型
    JAVA学习经验谈
    JAVA的入门基础一些精典
    面向对象编程:Java复杂数据类型用法
    面向对象编程:Java的简单数据类型
    面向对象编程:Java collection更有效管理elements
    从C++到Java 理解面向对象是关键所在
    JAVA学习经验谈
    JAVA的入门基础一些精典
  • 原文地址:https://www.cnblogs.com/WeyneChen/p/6007049.html
Copyright © 2020-2023  润新知