打印
[应用相关]

HAL库实现USB组合设备CDC+MSC

[复制链接]
1053|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
labasi|  楼主 | 2021-7-4 15:25 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
CD, USB, MSC, dc, ST, STM
工程试验环境

STM32F103RC
STM32CUBEIDE1.5.1
1、本教程默认你已经会使用STM32CUBEMX生成CDC代码和MSC代码,这两个工程的生成很简单,网络上的教程一搜遍地是。

2、USB组合设备的移植修改需要具备一定的USB知识储备,如果没有强烈建议看一下我的另一篇博客:STM32 USB相关知识扫盲

首先说一下STM32的USB库的初始化操作,MX_USB_DEVICE_Init函数中使用USBD_RegisterClass函数注册绑定了实际的端口初始化控制等操作,如果是CDC那么注册的就是USBD_CDC这个结构,如果是MSC那么就是注册的USB_MSC这个结构,所以我们的组合设备思路就是用哪个的时候,就将这个结构切换成对应的操作结构。

第一步:基础工程生成
首先先用STM32CUBEMX生成CDC的工程和MSC的工程,并测试通过没有问题后,我这里使用CDC工程为基础工程进行修改成USB组合设备。

基本步骤如下:

  • USB设备描述符修改成组合设备类型
  • 修改PMA端点分布
  • 修改USB配置描述符并添加MSC的配置描述
  • 修改初始化函数接口,改写成我们自己的组合设备初始化操作函数
  • 修改MX_USB_DEVICE_Init函数,注册成我们自己的组合设备
  • 下面进行分布修改。



使用特权

评论回复
沙发
labasi|  楼主 | 2021-7-4 16:00 | 只看该作者
第二步:USB设备描述符的修改

这一步很简单的,就是修改usbd_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型:


使用特权

评论回复
板凳
labasi|  楼主 | 2021-7-4 16:01 | 只看该作者
第三步:修改PMA端点分布

首先修改一下CDC所用到的端点地址,CDC的输入输出端点不动,将命令端点成0X83:


使用特权

评论回复
地板
labasi|  楼主 | 2021-7-4 16:01 | 只看该作者
在改一下MSC的输入输出端点,改成0X82和0X02:

使用特权

评论回复
5
labasi|  楼主 | 2021-7-4 16:01 | 只看该作者
然后进入usbd_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化:

使用特权

评论回复
6
labasi|  楼主 | 2021-7-4 16:01 | 只看该作者
这里的修改非常关键,为什么addr要从0X38开始呢?

因为我们目前用到了:

  • 默认的两个端点(0X80、0X00)
  • CDC的三个端点(0X81、0X01、0X83)
  • MSC的两个端点(0X82、0X02)

共7个端点,每个端点占用8字节,总共56字节,十六进制就是0X38!

好多人修改自己的USB应用时,就是卡在了这里!如果起始地址修改不对,那么USB端点缓冲就会覆盖PMA头部的端点描述!


使用特权

评论回复
7
labasi|  楼主 | 2021-7-4 16:01 | 只看该作者
第四步:编写我们自己的组合设备配置
这里我为了方便修改,新建了一个usbd_composite.c文件,用于编写组合设备驱动,文件内容如下:

#include "usbd_def.h"
#include "usbd_msc.h"
#include "usbd_cdc.h"
#include "usbd_storage_if.h"
#include "usbd_cdc_if.h"


#define USB_MC_CONFIG_DESC_SIZ 106


extern USBD_HandleTypeDef hUsbDeviceFS;


extern uint8_t USBD_MSC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
extern uint8_t USBD_MSC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
extern uint8_t  USBD_MSC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
extern uint8_t USBD_MSC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
extern uint8_t USBD_MSC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);


extern uint8_t  USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
extern uint8_t  USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
extern uint8_t  USBD_CDC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
extern uint8_t  USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
extern uint8_t  USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);
extern uint8_t  USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev);


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_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_GetHSCfgDesc (uint16_t *length);
static uint8_t  *USBD_Composite_GetFSCfgDesc (uint16_t *length);
static uint8_t  *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
static uint8_t  *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
static uint8_t USBD_Composite_RxReady (USBD_HandleTypeDef *pdev);
static void USBD_Composite_Switch_MSC(USBD_HandleTypeDef *pdev);
static void USBD_Composite_Switch_CDC(USBD_HandleTypeDef *pdev);


USBD_ClassTypeDef  USBD_Composite_CDC_MSC =
{
  USBD_Composite_Init,
  USBD_Composite_DeInit,
  USBD_Composite_Setup,
  NULL, /*EP0_TxSent*/
  USBD_Composite_RxReady, /*EP0_RxReady*/
  USBD_Composite_DataIn,
  USBD_Composite_DataOut,
  NULL, /*SOF */
  NULL,
  NULL,
  USBD_Composite_GetHSCfgDesc,
  USBD_Composite_GetFSCfgDesc,
  USBD_Composite_GetOtherSpeedCfgDesc,
  USBD_Composite_GetDeviceQualifierDescriptor,
};



使用特权

评论回复
8
labasi|  楼主 | 2021-7-4 16:02 | 只看该作者
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
__ALIGN_BEGIN uint8_t USBD_Composite_CfgDesc[USB_MC_CONFIG_DESC_SIZ] __ALIGN_END =
{
  /*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 */ /* +++lakun:CDC用了两个接口,MSC用了一个接口,所以是3 */
  0x01,   /* bConfigurationValue: Configuration value */
  0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
  0xC0,   /* bmAttributes: self powered */
  0x32,   /* MaxPower 0 mA */

  /*---------------------------------------------------------------------------*/

  //
  // +++lakun: IAD(Interface Association Descriptor),用于指示CDC
  //
  0X08,  // bLength: Interface Descriptor size,固定值
  0X0B,  // bDescriptorType: IAD,固定值
  0X00,  // bFirstInterface,第一个接口的起始序号,从0开始
  0X02,  // bInterfaceCount,本IAD下的接口数量
  0X02,  // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备
  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 */                   /* +++lakun:接口编号  */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x01,   /* bNumEndpoints: One endpoints used */
  0x02,   /* bInterfaceClass: Communication Interface Class */          /* +++lakun:表明这是一个通信接口 */
  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 */           /* +++lakun:这里是用来指示CDC通信接口的编号的 */
  0x01,   /* bSlaveInterface0: Data Class Interface */                    /* +++lakun:这里是用来指示CDC数据接口的编号的 */

  /*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),
  CDC_FS_BINTERVAL,                           /* bInterval: */
  /*---------------------------------------------------------------------------*/

  /*Data class interface descriptor*/
  0x09,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
  0x01,   /* bInterfaceNumber: Number of Interface */                     /* +++lakun:CDC数据接口的编号为1 */
  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 */
  CDC_OUT_EP,                        /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
  0x00,                              /* 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),
  0x00,                               /* bInterval: ignore for Bulk transfer */

  //
  // +++lakun: IAD(Interface Association Descriptor)
  //
  0X08,  // bLength: Interface Descriptor size,固定值
  0X0B,  // bDescriptorType: IAD,固定值
  0X02,  // bFirstInterface,接口的起始序号(第0、1编号的接口用于CDC1,所以从2开始)
  0X01,  // bInterfaceCount,本IAD下的接口数量
  0X08,  // bFunctionClass: MSC,表明该IAD是一个MSC类型的设备
  0X06,  // bFunctionSubClass:子类型,默认即可
  0X50,  // bFunctionProtocol:控制协议,默认即可
  0X05,  // iFunction

  /********************  Mass Storage interface ********************/
  0x09,   /* bLength: Interface Descriptor size */
  0x04,   /* bDescriptorType: */
  0x02,   /* bInterfaceNumber: Number of Interface */  /* +++lakun:第0和1编号的用给了CDC,所以MSC接口的编号从2开始 */
  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),
  0x00,   /*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),
  0x00     /*Polling interval in milliseconds*/
} ;


使用特权

评论回复
9
labasi|  楼主 | 2021-7-4 16:02 | 只看该作者
/* USB Standard Device Descriptor */
uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
{
  USB_LEN_DEV_QUALIFIER_DESC,
  USB_DESC_TYPE_DEVICE_QUALIFIER,
  0x00,
  0x02,
  0x00,
  0x00,
  0x00,
  0X40,
  0x01,
  0x00,
};


使用特权

评论回复
10
labasi|  楼主 | 2021-7-4 16:02 | 只看该作者
// 这个函数是修改的USBD_MSC_Init
static void USBD_Composite_MSC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
        USBD_Composite_Switch_MSC(pdev);

        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, MSC_EPOUT_ADDR, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);
        pdev->ep_out[MSC_EPOUT_ADDR & 0xFU].is_used = 1U;

        /* Open EP IN */
        USBD_LL_OpenEP(pdev, MSC_EPIN_ADDR, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);
        pdev->ep_in[MSC_EPIN_ADDR & 0xFU].is_used = 1U;

        /* Init the BOT  layer */
        MSC_BOT_Init(pdev);
}


使用特权

评论回复
11
labasi|  楼主 | 2021-7-4 16:03 | 只看该作者
// 这个函数是修改的USBD_CDC_Init
static void USBD_Composite_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
        USBD_CDC_HandleTypeDef   *hcdc = NULL;

        USBD_Composite_Switch_CDC(pdev);

        /* Open EP IN */
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, CDC_DATA_FS_IN_PACKET_SIZE);
        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;

        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,CDC_DATA_FS_OUT_PACKET_SIZE);
        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;

        /* Open Command IN EP */
        USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
        pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;

        hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;

        /* Init  physical Interface components */
        ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();

        /* Init Xfer states */
        hcdc->TxState = 0U;
        hcdc->RxState = 0U;

        /* Prepare Out endpoint to receive next packet */
        USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_FS_OUT_PACKET_SIZE);
}


使用特权

评论回复
12
labasi|  楼主 | 2021-7-4 16:03 | 只看该作者
uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
        USBD_Composite_MSC_Init(pdev, cfgidx);  // 初始化MSC
        USBD_Composite_CDC_Init(pdev, cfgidx);  // 初始化CDC
        return USBD_OK;
}



使用特权

评论回复
13
labasi|  楼主 | 2021-7-4 16:03 | 只看该作者
uint8_t  USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx){      
  return USBD_OK;
}



使用特权

评论回复
14
labasi|  楼主 | 2021-7-4 16:04 | 只看该作者
uint8_t  USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
        switch(req->wIndex)  // wIndex是序号编号,在配置描述符里面我们第一个IAD是CDC,第二个IAD是MSC
        {

        //
        // 第一个IAD
        //
        case 0:
                USBD_Composite_Switch_CDC(pdev);
                USBD_CDC_Setup(pdev, req);
                break;

        //
        // 第二个IAD
        //
        case 2:
                USBD_Composite_Switch_MSC(pdev);
                USBD_MSC_Setup(pdev, req);
                break;

        //
        // 第三个IAD(如果有,在这里初始化)
        //
        case 4:
                break;
        default:break;
        }
        return USBD_OK;
}


使用特权

评论回复
15
labasi|  楼主 | 2021-7-4 16:04 | 只看该作者
uint8_t  USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
        switch(epnum | 0X80)
        {
        case MSC_EPIN_ADDR:
            USBD_Composite_Switch_MSC(pdev);
            USBD_MSC_DataIn(pdev, epnum);
                break;
        case CDC_IN_EP:
            USBD_Composite_Switch_CDC(pdev);
            USBD_CDC_DataIn(pdev, epnum);
                break;
        default:break;
        }

        return USBD_OK;
}


使用特权

评论回复
16
labasi|  楼主 | 2021-7-4 16:04 | 只看该作者
uint8_t  USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
        switch(epnum)
        {
        case MSC_EPOUT_ADDR:
                USBD_Composite_Switch_MSC(pdev);
                USBD_MSC_DataOut(pdev, epnum);
                break;
        case CDC_OUT_EP:
                USBD_Composite_Switch_CDC(pdev);
                USBD_CDC_DataOut(pdev, epnum);
                break;
        default:break;
        }
        return USBD_OK;
}


使用特权

评论回复
17
labasi|  楼主 | 2021-7-4 16:04 | 只看该作者
uint8_t  *USBD_Composite_GetHSCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_Composite_CfgDesc);
  return USBD_Composite_CfgDesc;
}





使用特权

评论回复
18
labasi|  楼主 | 2021-7-4 16:05 | 只看该作者
uint8_t  *USBD_Composite_GetFSCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_Composite_CfgDesc);
  return USBD_Composite_CfgDesc;
}



使用特权

评论回复
19
labasi|  楼主 | 2021-7-4 16:05 | 只看该作者
uint8_t  *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length)
{
  *length = sizeof (USBD_Composite_CfgDesc);
  return USBD_Composite_CfgDesc;
}

使用特权

评论回复
20
labasi|  楼主 | 2021-7-4 16:05 | 只看该作者
uint8_t  *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length)
{
  *length = sizeof (USBD_Composite_DeviceQualifierDesc);
  return USBD_Composite_DeviceQualifierDesc;
}




使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

52

主题

3382

帖子

2

粉丝