基于STM32实现USB组合设备CDC+MSC正确打开方式
创始人
2024-02-21 18:06:10
0

摘要:

前一段时间对无刷电机的驱动有了兴趣,移植了odrive和simpleFOC代码,里面有关于stm32实现USB复合的实例,最近也有打算在electronbot里实现U盘+通讯来实现bootloader和语音文件的拷贝和管理。看了网上也有相关实现文章,比较HAL原代码框架,无论是odrive里,还是网上其它实现案例,都是通过ep_addr进行switch ,而原代码框架里有USBD_RegisterClassComposite函数,阅读HAL库USB相关代码后,决定以符合原代码框架的姿势打开USB组合设备CDC+MSC。


目录

摘要:

编译环境

 一、基本工程建立

二、描述符修改

1.设备层

2.配置描述符

3.端点

三.关键代码

 四.大功告成 

总结


编译环境

编译环境使用了STM32CubeMX生成makefile工程,使用gcc编译,环境搭建材料如下,基本可参照odrive环境搭建。

1、VSCodeUserSetup-x64-1.63.0.exe

2、gcc-arm-none-eabi-10.3-2021.10-win32.exe

3、openocd-20211118.zip

4、xpack-windows-build-tools-4.2.1-2-win32-x64.zip

可自行百度 Windows ODrive  编译环境搭建


 一、基本工程建立

使用STM32CubeMX建立两个独立的工程,一个是CDC工程,一个是MSC工程。然后以一个工程为母版,本例程是以CDC为母版,将MSC工程路径Middlewares\ST\STM32_USB_Device_Library\Class下的MSC文件夹拷贝到CDC工程该路径下,如图

二、描述符修改

描述符修改基本遵循设备层,配置、接口、端点依次更改。

1.设备层

无论是CDC的还是MSC的设备描述符不符合要求了,并且看到代码里有USE_USBD_COMPOSITE宏判断:

 即可理解为如果定义了USE_USBD_COMPOSITE宏,即不再使用MSC和CDC里的设备描述符和相关配置。那么就应该在MSC和CDC两个关系之上,实现这一部分。于是新建两个文件分别是usbd_composite.c 和 usbd_composite.h,关键部分如下:

上图下红框即是告诉主机,下行设备即是复合设备。

USBD_ClassTypeDef USBD_CMPSIT=
{NULL,NULL,NULL,NULL,                 /* EP0_TxSent */NULL,NULL,NULL,NULL,NULL,NULL,USBD_CMPSIT_GetHSCfgDesc,USBD_CMPSIT_GetFSCfgDesc,  USBD_CMPSIT_GetOtherSpeedCfgDesc,USBD_CMPSIT_GetDeviceQualifierDescriptor  
};

2.配置描述符

配置主要信息内容:使用了几个接口,每个接口实现什么设备功能(CDC、HID、MSC...),使用了什么样的端点等,本例程主要用了三个接口,CDC使用了两个,index为0和1,MSC使用了一个,index为2。配置描述符如下: 

__ALIGN_BEGIN static uint8_t USBD_CMPSIT_CfgDesc[USB_CMPSIT_CONFIG_DESC_SIZ] __ALIGN_END =
{/* Configuration Descriptor *///00x09,                                       /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION,                /* bDescriptorType: Configuration */USB_CMPSIT_CONFIG_DESC_SIZ,                    /* wTotalLength */0x00,0x03,                                       /* bNumInterfaces: 2 interfaces */0x01,                                       /* bConfigurationValue: Configuration value */0x00,                                       /* iConfiguration: Index of string descriptordescribing the configuration */
#if (USBD_SELF_POWERED == 1U)0xC0,                                       /* bmAttributes: Bus Powered according to user configuration */
#else0x80,                                       /* bmAttributes: Bus Powered according to user configuration */
#endif /* USBD_SELF_POWERED */USBD_MAX_POWER,                             /* MaxPower (mA) *//*---------------------------------------------------------------------------*///9/* Interface Association Descriptor: CDC device (virtual com port) */0x08,   /* bLength: IAD size */0x0B,   /* bDescriptorType: Interface Association Descriptor */0x00,   /* bFirstInterface */0x02,   /* bInterfaceCount */0x02,   /* bFunctionClass: Communication Interface Class */0x02,   /* bFunctionSubClass: Abstract Control Model */0x01,   /* bFunctionProtocol: Common AT commands */0x06,   /* iFunction *///17/* 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 endpoint used */0x02,                                       /* bInterfaceClass: Communication Interface Class */0x02,                                       /* bInterfaceSubClass: Abstract Control Model */0x01,                                       /* bInterfaceProtocol: Common AT commands */0x06,                                       /* iInterface *///26/* Header Functional Descriptor */0x05,                                       /* bLength: Endpoint Descriptor size */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x00,                                       /* bDescriptorSubtype: Header Func Desc */0x10,                                       /* bcdCDC: spec release number */0x01,//31/* Call Management Functional Descriptor */0x05,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x01,                                       /* bDescriptorSubtype: Call Management Func Desc */0x00,                                       /* bmCapabilities: D0+D1 */0x01,                                       /* bDataInterface *///36/* ACM Functional Descriptor */0x04,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x02,                                       /* bDescriptorSubtype: Abstract Control Management desc */0x02,                                       /* bmCapabilities *///40/* Union Functional Descriptor */0x05,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x06,                                       /* bDescriptorSubtype: Union func desc */0x00,                                       /* bMasterInterface: Communication class interface */0x01,                                       /* bSlaveInterface0: Data Class Interface *///45/* 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 *//*---------------------------------------------------------------------------*///52/* 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 */0x06,                                       /* iInterface *///61/* 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 *///68/* 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 *///75/* Interface Association Descriptor: Mass Storage device */0x08,   /* bLength: IAD size */0x0B,   /* bDescriptorType: Interface Association Descriptor */0x02,   /* bFirstInterface */0x01,   /* bInterfaceCount */0x08,   /* bFunctionClass: */0x06,   /* bFunctionSubClass: */0x50,   /* bFunctionProtocol: */0x07,   /* iFunction *///83/********************  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 */0x07,                                            /* iInterface: *//********************  Mass Storage Endpoints ********************///920x07,                                            /* 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 *///990x07,                                            /* 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 *///106
};

 usbd_conf.h修改部分

/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES     3U
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION     1U
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ     512U
/*---------- -----------*/
#define USBD_DEBUG_LEVEL     0U
/*---------- -----------*/
#define USBD_LPM_ENABLED     0U
/*---------- -----------*/
#define USBD_SELF_POWERED     1U#define MSC_MEDIA_PACKET     512U
#define CDC_FS_BINTERVAL     0x20U#define USBD_SUPPORT_USER_STRING_DESC  1U

3.端点

本例程的端点分配规则:CDC使用了0x81、0x01、0x82,MSC使用了0x83、0x03

usbd_cdc.h相关如下:

#ifndef CDC_IN_EP
#define CDC_IN_EP                                   0x81U  /* EP1 for data IN */
#endif /* CDC_IN_EP */
#ifndef CDC_OUT_EP
#define CDC_OUT_EP                                  0x01U  /* EP1 for data OUT */
#endif /* CDC_OUT_EP */
#ifndef CDC_CMD_EP
#define CDC_CMD_EP                                  0x82U  /* EP2 for CDC commands */
#endif /* CDC_CMD_EP  */

usbd_msc.h相关如下:

#ifndef MSC_EPIN_ADDR
#define MSC_EPIN_ADDR                0x83U
#endif /* MSC_EPIN_ADDR */#ifndef MSC_EPOUT_ADDR
#define MSC_EPOUT_ADDR               0x03U
#endif /* MSC_EPOUT_ADDR */

三.关键代码

 底层初始化部分修改,usbd_conf.c中USBD_LL_Init

 要使用USBD_RegisterClassComposite来复合 usb设备,即要对它的功能实现有所理解,然后函数内部还要实现一个USBD_CMPSIT_AddClass函数。阅读了该部分相关的代码,基本知道了它复合设备的思想了,即可逆推USBD_CMPSIT_AddClass要实现的功能。我实现该部分的代码如下:

#ifdef USE_USBD_COMPOSITE
void USBD_CMPSIT_AddClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass, USBD_CompositeClassTypeDef classtype, uint8_t *EpAddr)
{//printf("classId=%d  : %d\r\n",pdev->classId,classtype);switch(classtype){case CLASS_TYPE_CDC:{pdev->tclasslist[pdev->classId].ClassType = CLASS_TYPE_CDC;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].NumEps = 3;pdev->tclasslist[pdev->classId].Eps[0].add = CDC_CMD_EP;pdev->tclasslist[pdev->classId].Eps[0].type = USBD_EP_TYPE_INTR;pdev->tclasslist[pdev->classId].Eps[0].size = CDC_CMD_PACKET_SIZE;pdev->tclasslist[pdev->classId].Eps[0].is_used = 1U;pdev->tclasslist[pdev->classId].Eps[1].add = CDC_OUT_EP;pdev->tclasslist[pdev->classId].Eps[1].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[1].size = CDC_DATA_FS_MAX_PACKET_SIZE;pdev->tclasslist[pdev->classId].Eps[1].is_used = 1U;pdev->tclasslist[pdev->classId].Eps[2].add = CDC_IN_EP;pdev->tclasslist[pdev->classId].Eps[2].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[2].size = CDC_DATA_FS_MAX_PACKET_SIZE;pdev->tclasslist[pdev->classId].Eps[2].is_used = 1U;pdev->tclasslist[pdev->classId].NumIf = 2;pdev->tclasslist[pdev->classId].Ifs[0] = 0;pdev->tclasslist[pdev->classId].Ifs[1] = 1;	}break;case CLASS_TYPE_MSC:{pdev->tclasslist[pdev->classId].ClassType = CLASS_TYPE_MSC;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].NumEps = 2;pdev->tclasslist[pdev->classId].Eps[0].add = MSC_EPIN_ADDR;pdev->tclasslist[pdev->classId].Eps[0].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[0].size = MSC_MAX_FS_PACKET;pdev->tclasslist[pdev->classId].Eps[0].is_used = 1U;pdev->tclasslist[pdev->classId].Eps[1].add = MSC_EPOUT_ADDR;pdev->tclasslist[pdev->classId].Eps[1].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[1].size = MSC_MAX_FS_PACKET;pdev->tclasslist[pdev->classId].Eps[1].is_used = 1U;pdev->tclasslist[pdev->classId].NumIf = 1;pdev->tclasslist[pdev->classId].Ifs[0] = 2;						}break;default:break;}pdev->tclasslist[pdev->classId].CurrPcktSze = 0U;}#endif

然后就在usbd_device.c里使用USBD_RegisterClassComposite函数来注册复合设备了,如下代码段:

if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_CDC,CLASS_TYPE_CDC,0) != USBD_OK){Error_Handler();}if(USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK){Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_MSC,CLASS_TYPE_MSC,0) != USBD_OK){Error_Handler();}if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK){Error_Handler();}

 四.大功告成

 经过推理和设想,最后编译下载,功能完美实现。

 CDC实现数据传输:

 MSC部分由于没有使用flash实例化,即在电脑上发现U盘即为成功了:

总结

阅读原代码,想必作者USBD_RegisterClassComposite函数有实现该功能的最初设想,但STM32CubeMX没有例程来教导大家,实在遗憾,本人也是本着吹毛求疵的强迫症,尽量符合原框架不大改的思想去实现该功能。特与大家分享。

原码已上传到github, branch 为fw-v1.0

GitHub - wenyezhong/usb_composite

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...