4.5.1、CUBEMX USB之MSC+CDC

2020-08-09 14:18:17

前言

	本意是做一個CDC+MSC的複合裝置,就是類似於ST-LINK2的那種裝置,
在平時使用串列埠功能通訊,偶爾可以通過拖拉檔案進行IAP升級,但是在網上
找了好多資料和教學後,卻發現基本都是同一個教學,而且效果都不盡人意,
總會出現一堆奇奇怪怪的效果,但是USB這個東西本身又是一個很複雜的協定
,我又不想去學習,所以就賊心不死,去google了一些資料,但發現按照
他們的流程走一遍還是有問題,大概記錄一下移植過程中遇到的問題。

1、準備內容

  • USB_CDC(VCP)工程一份
  • USB_MSC工程一份(確保可以從PC端看到的記憶體並且可以進行讀寫操作)

2、我是個辛勤的搬運工

  1. 在你的CDC工程中找到如下四個檔案:

     usbd_cdc.c   
     usbd_cdc.h
     usbd_cdc_if.c
     usbd_cdc_if.h
    
  2. 拷貝到MSC工程下,在KEIL裡新增標頭檔案路徑。

  3. 新建usbd_composite.c和usbd_composite.h檔案。

  4. 在usbd_composite.c檔案中新增如下內容。

/**
  ******************************************************************************
  * @file    usbd_MC.c
  * @author  MCD Application Team
  * @version V2.4.2
  * @date    11-December-2015
  * @brief   This file provides all the MC core functions.
  *
  * @verbatim
  *      
  *          ===================================================================      
  *                                MC Class  Description
  *          =================================================================== 
  *           This module manages the MC class V1.0 following the "Universal 
  *           Serial Bus Mass Storage Class (MC) Bulk-Only Transport (BOT) Version 1.0
  *           Sep. 31, 1999".
  *           This driver implements the following aspects of the specification:
  *             - Bulk-Only Transport protocol
  *             - Subclass : SCSI transparent command set (ref. SCSI Primary Commands - 3 (SPC-3))
  *      
  *  @endverbatim
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */ 

/* Includes ------------------------------------------------------------------*/
#include "usbd_composite.h"
#include "usbd_def.h"
#include "usbd_msc.h"
#include "usbd_cdc.h"
#include "usbd_storage_if.h"
#include "usbd_cdc_if.h"

/** @defgroup MC_CORE_Private_FunctionPrototypes
  * @{
  */ 
uint8_t  USBD_MC_Init (USBD_HandleTypeDef *pdev, 
                            uint8_t cfgidx);

uint8_t  USBD_MC_DeInit (USBD_HandleTypeDef *pdev, 
                              uint8_t cfgidx);

uint8_t  USBD_MC_Setup (USBD_HandleTypeDef *pdev, 
                             USBD_SetupReqTypedef *req);

uint8_t  USBD_MC_DataIn (USBD_HandleTypeDef *pdev, 
                              uint8_t epnum);


uint8_t  USBD_MC_DataOut (USBD_HandleTypeDef *pdev, 
                               uint8_t epnum);

uint8_t  *USBD_MC_GetHSCfgDesc (uint16_t *length);

uint8_t  *USBD_MC_GetFSCfgDesc (uint16_t *length);

uint8_t  *USBD_MC_GetOtherSpeedCfgDesc (uint16_t *length);

uint8_t  *USBD_MC_GetDeviceQualifierDescriptor (uint16_t *length);
static uint8_t  USBD_MC_RxReady (USBD_HandleTypeDef *pdev);
static void MC_Switch_MSC(USBD_HandleTypeDef *pdev);
static void MC_Switch_CDC(USBD_HandleTypeDef *pdev);


/**
  * @}
  */ 
extern USBD_HandleTypeDef hUsbDeviceFS;


/** @defgroup MC_CORE_Private_Variables
  * @{
  */ 


USBD_ClassTypeDef  USBD_MC = 
{
  USBD_MC_Init,
  USBD_MC_DeInit,
  USBD_MC_Setup,
  NULL, /*EP0_TxSent*/  
  USBD_MC_RxReady, /*EP0_RxReady*/
  USBD_MC_DataIn,
  USBD_MC_DataOut,
  NULL, /*SOF */ 
  NULL,  
  NULL,     
  USBD_MC_GetHSCfgDesc,
  USBD_MC_GetFSCfgDesc,  
  USBD_MC_GetOtherSpeedCfgDesc,
  USBD_MC_GetDeviceQualifierDescriptor,
};

/* USB Mass storage device Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
uint8_t USBD_MC_CfgDesc[USB_MC_CONFIG_DESC_SIZ] =
{
  /*Configuration Descriptor*/
  0x09,   /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
  USB_MC_CONFIG_DESC_SIZ,                /* wTotalLength:no of returned bytes */
  0x00,
  0x03,   /* bNumInterfaces: 3 interface */
  0x01,   /* bConfigurationValue: Configuration value */
  0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
  0xC0,   /* bmAttributes: self powered */
  0x32,   /* MaxPower 0 mA */
  
  /*---------------------------------------------------------------------------*/
  // IAD
  0x08,        //ÃèÊö·û´óС
  0x0B,        //IADÃèÊö·ûÀàÐÍ
  0x00,        // bFirstInterface 
  0x02,        // bInterfaceCount
  0x02,        // bFunctionClass: CDC Class
  0x02,        // bFunctionSubClass
  0x01,        // bFunctionProtocol
  0x00,        // iFunction       
  
  /*---------------------------------------------------------------------------*/
  /*Interface Descriptor */
  0x09,   /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
  /* Interface descriptor type */
  0x00,   /* 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 */
  0x00,   /* 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 */
  0x00,   /* bMasterInterface: Communication class interface */
  0x01,   /* bSlaveInterface0: Data Class Interface */
  
  /*Endpoint 2 Descriptor*/
  0x07,                           /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
  MC_CDC_CMD_EP,                     /* bEndpointAddress */
  0x03,                           /* bmAttributes: Interrupt */
  LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
  HIBYTE(CDC_CMD_PACKET_SIZE),
  0x10,                           /* bInterval: */ 
  
  /*Data class interface descriptor*/
  0x09,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
  0x01,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x02,   /* bNumEndpoints: Two endpoints used */
  0x0A,   /* bInterfaceClass: CDC */
  0x00,   /* bInterfaceSubClass: */
  0x00,   /* bInterfaceProtocol: */
  0x00,   /* iInterface: */
  
  /*Endpoint OUT Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  MC_CDC_OUT_EP,                        /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(MC_MAX_FS_PACKET),  /* wMaxPacketSize: */
  HIBYTE(MC_MAX_FS_PACKET),
  0x00,                              /* bInterval: ignore for Bulk transfer */
  
  /*Endpoint IN Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  MC_CDC_IN_EP,                         /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(MC_MAX_FS_PACKET),  /* wMaxPacketSize: */
  HIBYTE(MC_MAX_FS_PACKET),
  0x00,                               /* bInterval: ignore for Bulk transfer */

  /*---------------------------------------------------------------------------*/
  // IAD
  0x08,        //ÃèÊö·û´óС
  0x0B,        //IADÃèÊö·ûÀàÐÍ
  0x02,        // bFirstInterface
  0x01,        // bInterfaceCount
  0x08,        // bFunctionClass: MASS STORAGE Class
  0x06,        // bFunctionSubClass
  0x50,        // bFunctionProtocol
  0x01,        // iFunction    

  /********************  Mass Storage interface ********************/
  0x09,   /* bLength: Interface Descriptor size */
  0x04,   /* bDescriptorType: */
  0x02,   /* 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 */
  MC_MSC_EPIN_ADDR,   /*Endpoint address (IN, address 1) */
  0x02,   /*Bulk endpoint type */
  LOBYTE(MC_MAX_FS_PACKET),
  HIBYTE(MC_MAX_FS_PACKET),
  0x00,   /*Polling interval in milliseconds */
  
  0x07,   /*Endpoint descriptor length = 7 */
  0x05,   /*Endpoint descriptor type */
  MC_MSC_EPOUT_ADDR,   /*Endpoint address (OUT, address 1) */
  0x02,   /*Bulk endpoint type */
  LOBYTE(MC_MAX_FS_PACKET),
  HIBYTE(MC_MAX_FS_PACKET),
  0x00     /*Polling interval in milliseconds*/
};
/* USB Standard Device Descriptor */  
uint8_t USBD_MC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
{
  USB_LEN_DEV_QUALIFIER_DESC,
  USB_DESC_TYPE_DEVICE_QUALIFIER,
  0x00,
  0x02,
  0x00,
  0x00,
  0x00,
  MC_MAX_FS_PACKET,
  0x01,
  0x00,
};

/**
  * @}
  */ 

/** @defgroup MC_CORE_Private_Functions
  * @{
  */ 

/**
  * @brief  USBD_MC_Init
  *         Initialize  the mass storage configuration
  * @param  pdev: device instance
  * @param  cfgidx: configuration index
  * @retval status
  */
uint8_t  USBD_MC_Init (USBD_HandleTypeDef *pdev, 
                            uint8_t cfgidx)
{   
  {
    USBD_CDC_HandleTypeDef * hcdc;
    
    MC_Switch_CDC(pdev);
    
    USBD_LL_OpenEP(pdev,
                   MC_CDC_IN_EP,
                   USBD_EP_TYPE_BULK,
                   MC_MAX_FS_PACKET);
    
    USBD_LL_OpenEP(pdev,
                   MC_CDC_OUT_EP,
                   USBD_EP_TYPE_BULK,
                   MC_MAX_FS_PACKET);
    
    USBD_LL_OpenEP(pdev,
                   MC_CDC_CMD_EP,
                   USBD_EP_TYPE_INTR,
                   CDC_CMD_PACKET_SIZE);

    hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;

    ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
    
    hcdc->TxState =0;
    hcdc->RxState =0;
    
    USBD_LL_PrepareReceive(pdev,
                           MC_CDC_OUT_EP,
                           hcdc->RxBuffer,
                           MC_MAX_FS_PACKET);
  }
  
  {
    MC_Switch_MSC(pdev);
    
    USBD_LL_OpenEP(pdev,
                   MC_MSC_EPOUT_ADDR,
                   USBD_EP_TYPE_BULK,
                   MC_MAX_FS_PACKET);
    
    USBD_LL_OpenEP(pdev,
                   MC_MSC_EPIN_ADDR,
                   USBD_EP_TYPE_BULK,
                   MC_MAX_FS_PACKET);  
    
    MSC_BOT_Init(pdev); 
  }

  return USBD_OK;
}

/**
  * @brief  USBD_MC_DeInit
  *         DeInitilaize  the mass storage configuration
  * @param  pdev: device instance
  * @param  cfgidx: configuration index
  * @retval status
  */
uint8_t  USBD_MC_DeInit (USBD_HandleTypeDef *pdev, 
                              uint8_t cfgidx)
{
  return USBD_OK;
}
/**
* @brief  USBD_MC_Setup
*         Handle the MC specific requests
* @param  pdev: device instance
* @param  req: USB request
* @retval status
*/
uint8_t  USBD_MC_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
  if(req->wIndex == 0x0002)
  {
    MC_Switch_MSC(pdev);
    USBD_MSC_BOT_HandleTypeDef     *hmsc = (USBD_MSC_BOT_HandleTypeDef*) pdev->pClassData;   
    
    switch (req->bmRequest & USB_REQ_TYPE_MASK)
    {

    /* Class request */
    case USB_REQ_TYPE_CLASS :
      switch (req->bRequest)
      {
      case BOT_GET_MAX_LUN :

        if((req->wValue  == 0) && 
           (req->wLength == 1) &&
           ((req->bmRequest & 0x80) == 0x80))
        {
          hmsc->max_lun = ((USBD_StorageTypeDef *)pdev->pUserData)->GetMaxLun();
          USBD_CtlSendData (pdev,
                            (uint8_t *)&hmsc->max_lun,
                            1);
        }
        else
        {
           USBD_CtlError(pdev , req);
           return USBD_FAIL; 
        }
        break;
        
      case BOT_RESET :
        if((req->wValue  == 0) && 
           (req->wLength == 0) &&
          ((req->bmRequest & 0x80) != 0x80))
        {      
           MSC_BOT_Reset(pdev);
        }
        else
        {
           USBD_CtlError(pdev , req);
           return USBD_FAIL; 
        }
        break;

      default:
         USBD_CtlError(pdev , req);
         return USBD_FAIL; 
      }
      break;
    /* Interface & Endpoint request */
    case USB_REQ_TYPE_STANDARD:
      switch (req->bRequest)
      {
      case USB_REQ_GET_INTERFACE :
        USBD_CtlSendData (pdev,
                          (uint8_t *)&hmsc->interface,
                          1);
        break;
        
      case USB_REQ_SET_INTERFACE :
        hmsc->interface = (uint8_t)(req->wValue);
        break;
      
      case USB_REQ_CLEAR_FEATURE:  
        
        /* Flush the FIFO and Clear the stall status */    
        USBD_LL_FlushEP(pdev, (uint8_t)req->wIndex);
        
        /* Reactivate the EP */      
        USBD_LL_CloseEP (pdev , (uint8_t)req->wIndex);
        if((((uint8_t)req->wIndex) & 0x80) == 0x80)
        {
          if(pdev->dev_speed == USBD_SPEED_HIGH  ) 
          {
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPIN_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_HS_PACKET);  
          }
          else
          {   
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPIN_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_FS_PACKET);  
          }
        }
        else
        {
          if(pdev->dev_speed == USBD_SPEED_HIGH  ) 
          {
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPOUT_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_HS_PACKET);  
          }
          else
          {   
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPOUT_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_FS_PACKET);  
          }
        }
        
        /* Handle BOT error */
        MSC_BOT_CplClrFeature(pdev, (uint8_t)req->wIndex);
        break;
        
      }  
      break;
     
    default:
      break;
    }
  }
  else
  {
    MC_Switch_CDC(pdev);
    static uint8_t ifalt = 0;
    USBD_CDC_HandleTypeDef * hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;   

    switch (req->bmRequest & USB_REQ_TYPE_MASK)
    {
    case USB_REQ_TYPE_CLASS :
      if (req->wLength)
      {
        if (req->bmRequest & 0x80)
        {
          ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest,
                                                            (uint8_t *)hcdc->data,
                                                            req->wLength);
            USBD_CtlSendData (pdev, 
                              (uint8_t *)hcdc->data,
                              req->wLength);
        }
        else
        {
          hcdc->CmdOpCode = req->bRequest;
          hcdc->CmdLength = req->wLength;
          
          USBD_CtlPrepareRx (pdev, 
                             (uint8_t *)hcdc->data,
                             req->wLength);
        }
        
      }
      else
      {
        ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest,
                                                          (uint8_t*)req,
                                                          0);
      }
      break;
    
    case USB_REQ_TYPE_STANDARD:
      switch (req->bRequest)
      {      
      case USB_REQ_GET_INTERFACE :
        USBD_CtlSendData (pdev,
                          &ifalt,
                          1);
        break;
        
      case USB_REQ_SET_INTERFACE :
        break;
      }
    
    default: 
      break;
    }  
  }
  
  return USBD_OK;
}

/**
* @brief  USBD_MC_DataIn
*         handle data IN Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t  USBD_MC_DataIn (USBD_HandleTypeDef *pdev, 
                              uint8_t epnum)
{
  if(epnum == (MC_MSC_EPIN_ADDR & 0x7f))
  {
    MC_Switch_MSC(pdev);
    MSC_BOT_DataIn(pdev , epnum);
  }
  else if(epnum == (MC_CDC_IN_EP & 0x7f))
  {
    USBD_CDC_HandleTypeDef   *hcdc;
    
    MC_Switch_CDC(pdev);
    hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
    hcdc->TxState = 0;    
  }
  
  return USBD_OK;
}

/**
* @brief  USBD_MC_DataOut
*         handle data OUT Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t  USBD_MC_DataOut (USBD_HandleTypeDef *pdev, 
                               uint8_t epnum)
{
  if(epnum == MC_MSC_EPOUT_ADDR)
  {
    MC_Switch_MSC(pdev);
    MSC_BOT_DataOut(pdev , epnum);
  }
  else if(epnum == MC_CDC_OUT_EP)
  {
    USBD_CDC_HandleTypeDef   *hcdc;
    
    MC_Switch_CDC(pdev);
    hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
    
    hcdc->RxLength = USBD_LL_GetRxDataSize (pdev, epnum);
    ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength);
  }
  
  return USBD_OK;
}

/**
* @brief  USBD_MC_GetHSCfgDesc 
*         return configuration descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t  *USBD_MC_GetHSCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_MC_CfgDesc);
  return USBD_MC_CfgDesc;
}

/**
* @brief  USBD_MC_GetFSCfgDesc 
*         return configuration descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t  *USBD_MC_GetFSCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_MC_CfgDesc);
  return USBD_MC_CfgDesc;
}

uint8_t  *USBD_MC_GetOtherSpeedCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_MC_CfgDesc);
  return USBD_MC_CfgDesc;
}
uint8_t  *USBD_MC_GetDeviceQualifierDescriptor (uint16_t *length)
{
  *length = sizeof (USBD_MC_DeviceQualifierDesc);
  return USBD_MC_DeviceQualifierDesc;
}
static uint8_t  USBD_MC_RxReady (USBD_HandleTypeDef *pdev)
{
  USBD_CDC_HandleTypeDef   *hcdc;
  
  MC_Switch_CDC(pdev);
  hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
  
  if((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFF))
  {
    ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(hcdc->CmdOpCode,
                                                      (uint8_t *)hcdc->data,
                                                      hcdc->CmdLength);
      hcdc->CmdOpCode = 0xFF; 
      
  }
  
  return USBD_OK;
}

static void MC_Switch_MSC(USBD_HandleTypeDef *pdev)
{
  static USBD_MSC_BOT_HandleTypeDef msc_handle;
  
  USBD_MSC_RegisterStorage(pdev, &USBD_Storage_Interface_fops_FS);
  pdev->pClassData = &msc_handle;
}

static void MC_Switch_CDC(USBD_HandleTypeDef *pdev)
{
  static USBD_CDC_HandleTypeDef cdc_handle;
  
  USBD_CDC_RegisterInterface(pdev, &USBD_CDC_Interface_fops_FS);
  pdev->pClassData = &cdc_handle;
}

/**
  * @}
  */ 


/**
  * @}
  */ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

  1. 在usbd_composite.h檔案中新增如下內容。
#ifndef __USBD_COMPOSITE_H
#define __USBD_COMPOSITE_H

#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include  "usbd_ioreq.h"
#include "usbd_cdc.h"
#include "usbd_msc.h"

#define MC_MAX_FS_PACKET            0x40

#define USB_MC_CONFIG_DESC_SIZ      106 

#define MC_MSC_EPIN_ADDR                MSC_EPIN_ADDR 
#define MC_MSC_EPOUT_ADDR               MSC_EPOUT_ADDR 

#define MC_CDC_IN_EP                   CDC_IN_EP 
#define MC_CDC_OUT_EP                  CDC_OUT_EP  
#define MC_CDC_CMD_EP                  CDC_CMD_EP 

extern USBD_ClassTypeDef  USBD_MC;

/**
  * @}
  */

/**
  * @}
  */

#ifdef __cplusplus
}
#endif

#endif  /* __USBD_MSC_H */
/**
  * @}
  */

/************************ (C) COPYRIGHT WEYNE *****END OF FILE****/

  1. 修改usbd_cdc.h和usbd_msc.h中的部分內容。
    修改usbd_cdc.h檔案內容。
#define CDC_IN_EP                                   0x81U  /* EP1 for data IN */
#define CDC_OUT_EP                                  0x01U  /* EP1 for data OUT */
#define CDC_CMD_EP                                  0x83U  /* EP2 for CDC commands */

修改usbd_msc.h檔案內容

#define MSC_EPIN_ADDR                0x82U
#define MSC_EPOUT_ADDR               0x02U
  1. 修改usbd_conf.h檔案內部分內容。
    從1修改爲3
#define USBD_MAX_NUM_INTERFACES     3
  1. 修改usbd_conf.c檔案部分內容。
    找到USBD_LL_Init函數,修改爲如下內容
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.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( );
  }

#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
  /* Register USB PCD CallBacks */
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback);
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback);
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback);
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback);
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback);
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback);
  HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback);

  HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_FS, PCD_DataOutStageCallback);
  HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_FS, PCD_DataInStageCallback);
  HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_FS, PCD_ISOOUTIncompleteCallback);
  HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_FS, PCD_ISOINIncompleteCallback);
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
  /* USER CODE BEGIN EndPoint_Configuration */
//    HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
//    HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
//  /* USER CODE END EndPoint_Configuration */
//  /* USER CODE BEGIN EndPoint_Configuration_MSC */
//    HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x98);
//    HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0xD8);

  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);
  /* USER CODE END EndPoint_Configuration_MSC */
  return USBD_OK;
}

  1. 修改usbd_device.c檔案中部分內容。
void MX_USB_DEVICE_Init(void)
{
  if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MC) != USBD_OK)
  {
    Error_Handler();
  }
//  if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK)
//  {
//    Error_Handler();
//  }
  if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
  {
    Error_Handler();
  }
}
  1. 修改usbd_des.c
    主要是修改bDeviceClass及後續兩個裝置子類和裝置控制參數。
#define USBD_SERIALNUMBER_STRING_FS     "00000000001A"
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
  0x12,                       /*bLength */
  USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/
  0x00,                       /*bcdUSB */
  0x02,
  0xef,                       /*bDeviceClass*/
  0x02,                       /*bDeviceSubClass*/
  0x01,                       /*bDeviceProtocol*/
  USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/
  LOBYTE(USBD_VID),           /*idVendor*/
  HIBYTE(USBD_VID),           /*idVendor*/
  LOBYTE(USBD_PID_FS),        /*idProduct*/
  HIBYTE(USBD_PID_FS),        /*idProduct*/
  0x00,                       /*bcdDevice rel. 2.00*/
  0x02,
  USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/
  USBD_IDX_PRODUCT_STR,       /*Index of product string*/
  USBD_IDX_SERIAL_STR,        /*Index of serial number string*/
  USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/
};
uint8_t * USBD_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  UNUSED(speed);
  *length = USB_SIZ_STRING_SERIAL;

  /* Update the serial number string descriptor with the data from the unique
   * ID */
  Get_SerialNum();
  /* USER CODE BEGIN USBD_FS_SerialStrDescriptor */
    USBD_GetString((uint8_t *)USBD_SERIALNUMBER_STRING_FS, USBD_StrDesc, length);
  /* USER CODE END USBD_FS_SerialStrDescriptor */
  return (uint8_t *) USBD_StrDesc;
}

這裏USBD_FS_SerialStrDescriptor我是修改後才成功的,當然後來成功後還沒來得及驗證不修改該函數是否有效。後續繼續測試。
理論上來說,這個函數只是回傳序列字串描述符,只是對USB列舉階段有影響,對後續的傳輸應該是無任何影響的。

3、總結

	總體來說,所有的32HAL庫的移植步驟大概都分爲下面 下麪幾步,以CDC+MSC舉例。
	1. 先把類檔案較少的一個類的檔案移植到類檔案較多的工程中,一般該路徑在
USB_MSC\Middlewares\ST\STM32_USB_Device_Library\Class下。
	2. 找到該類的usbd_xxxx_if.c和標頭檔案,一併新增到工程。(上述流程的1-2)
	3. 在你想複合的裝置中找到端點號,進行端點號分配。(上述流程中的第7步)
	4. 新增一個新的c檔案和標頭檔案,名字隨便。該檔案的主要目的如下
		①. 整合你想移植類的一些函數(一般就是找到要整合的類的USBD_XXX_if.c檔案進行合併).
		②. 定義一個USBD_ClassTypeDef型別變數,將所有函數名按順序存放到該變數。
		③.定義複合裝置的設定描述符和USB標準描述符。
	5. 在usbd_conf.c檔案的USBD_LL_Init函數中新增你新增端點的PMA
	6. 在usbd_conf.h檔案中修改最大介面數,因爲是複合裝置,具體數量根據你
的實際使用情況來填寫。
    7. 修改USBD_device.c檔案中的註冊介面,將原有的變數修改爲你自定義的
USBD_ClassTypeDef型別的變數,註釋掉,USBD_XXX_RegisterXXXX等字樣的函數。

只有第5步是隻涉及到STM32本身的,在32裏面是由一段記憶體專門用於USB數據緩衝的

4、問題總結

大概分爲兩個階段的問題
	1. 列舉階段
		 - 列舉失敗
		 - 只識別一個裝置,另外一個裝置列舉失敗
	2. 使用階段
		 - U盤正常工作,可讀寫,但是CDC只有在開啓串列埠的時候會輸出兩條語句
		 - 使用U盤讀寫時CDC沒有數據輸出,但在彈出U盤後CDC正常工作
		 - 使用U盤讀寫後,CDC直接掛掉!!!無論如何都沒有數據
		 - CDC也沒數據,MSC出現請插入U盤

反正是問題一大堆,然後在網上翻來覆去的找,也沒找到一個好的解決辦法(太菜了,也懶,實在不想去看USB協定,裏面的分包機制 機製和應答機制 機製,最關鍵的是CDC和MSC的通訊機制 機製),最後在國外論壇找到了一個工程,打開了他的工程,然後參照着修修改改,最終才勉強能用,但還是會存在以下問題。

  • U盤進行較多的數據讀寫時,會照成CDC堵塞,但在檔案讀寫完後,也可以正常工作。
  • 在彈出U盤後,CDC也會停止工作,需要手動點選一下開啓串列埠纔可以正常工作。
4.1、解決辦法
	對於問題一對於目前我的需求來說並沒有什麼影響,主要在於問題二。
	經過測試,使用不同的串列埠工具最終會進入**CDC_Control_FS**函數的幾
個分支,但具體是通過setup還是EP0_RxReady進入我還沒有測試,後續測試後在
補上。如果知道瞭如何模擬開啓串列埠這一操作,那麼這一問題就好解決了。

說白了還是對CDC和MSC的通訊機制 機製不是很熟,英文的文件看着確實慢。 慢慢看吧,一個人摸索一個陌生的東西實在太難了,還好網上有大量技術人員的部落格,感謝他們的筆記和一些開源的程式碼資料,萬分感謝。

推薦參考