完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
工程试验环境
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函数,注册成我们自己的组合设备 下面进行分布修改。 第二步:USB设备描述符的修改 这一步很简单的,就是修改u***d_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型: 第三步:修改PMA端点分布 首先修改一下CDC所用到的端点地址,CDC的输入输出端点不动,将命令端点成0X83: 在改一下MSC的输入输出端点,改成0X82和0X02: 然后进入u***d_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化: 这里的修改非常关键,为什么addr要从0X38开始呢? 因为我们目前用到了: 默认的两个端点(0X80、0X00) CDC的三个端点(0X81、0X01、0X83) MSC的两个端点(0X82、0X02) 共7个端点,每个端点占用8字节,总共56字节,十六进制就是0X38! 好多人修改自己的USB应用时,就是卡在了这里!如果起始地址修改不对,那么USB端点缓冲就会覆盖PMA头部的端点描述! 有关PMA的详细描述及使用,请看文档头部的《STM32 USB相关知识扫盲》链接文章! 第四步:编写我们自己的组合设备配置 这里我为了方便修改,新建了一个u***d_composite.c文件,用于编写组合设备驱动,文件内容如下: #include “u***d_def.h” #include “u***d_msc.h” #include “u***d_cdc.h” #include “u***d_storage_if.h” #include “u***d_cdc_if.h” #define USB_MC_CONFIG_DESC_SIZ 106 extern USBD_HandleTypeDef hU***DeviceFS; 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, }; /* 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*/ } ; /* 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, }; // 这个函数是修改的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); } // 这个函数是修改的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); } 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; } uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) { return USBD_OK; } uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { switch(req-》wIndex) // wIndex是接口编号,在配置描述符里面我们第一个IAD是CDC使用的是编号0和1两个接口,第二个IAD是MSC使用的是编号2的接口 { // // 第一个IAD // case 0: // CDC的命令接口编号为0 case 1: // CDC的数据接口编号为1 USBD_Composite_Switch_CDC(pdev); USBD_CDC_Setup(pdev, req); break; // // 第二个IAD // case 2: // MSC只有一个接口,编号为2 USBD_Composite_Switch_MSC(pdev); USBD_MSC_Setup(pdev, req); break; // // 第三个IAD(如果有,在这里初始化) // case 3: break; default:break; } return USBD_OK; } 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; } 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; } uint8_t *USBD_Composite_GetHSCfgDesc (uint16_t *length) { *length = sizeof (USBD_Composite_CfgDesc); return USBD_Composite_CfgDesc; } uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length) { *length = sizeof (USBD_Composite_CfgDesc); return USBD_Composite_CfgDesc; } uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length) { *length = sizeof (USBD_Composite_CfgDesc); return USBD_Composite_CfgDesc; } uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length) { *length = sizeof (USBD_Composite_DeviceQualifierDesc); return USBD_Composite_DeviceQualifierDesc; } static uint8_t USBD_Composite_RxReady (USBD_HandleTypeDef *pdev) { uint8_t ret = 0; switch(pdev-》request.wIndex) // wIndex是接口编号,这里我们通过接口编号确定CDC还是其他设备的EP0接收 { case 0: // CDC的命令接口编号是0 case 1: // CDC的数据接口编号是1 USBD_Composite_Switch_CDC(pdev); ret = USBD_CDC_EP0_RxReady(pdev); break; // 如果有其他设备还用到了EP0接收,在这里加入 case 2: break; default:break; } return ret; } static void USBD_Composite_Switch_MSC(USBD_HandleTypeDef *pdev) { static USBD_MSC_BOT_HandleTypeDef USBD_MSC_Handle; USBD_MSC_RegisterStorage(pdev, &USBD_Storage_Interface_fops_FS); pdev-》pClassData = (void *)&USBD_MSC_Handle; } static void USBD_Composite_Switch_CDC(USBD_HandleTypeDef *pdev) { static USBD_CDC_HandleTypeDef USBD_CDC_Handle; USBD_CDC_RegisterInterface(pdev, &USBD_Interface_fops_FS); pdev-》pClassData = (void *)&USBD_CDC_Handle; } 其中有关设备描述符的修改,我都用+++lakun进行了标注,并写上注释,方便查看。 第五步:MX_DEVICE_USB_Init修改 上面我们已经完成了统一接口的编写,现在就可以修改MX_DEVICE_USB_Init函数了,屏蔽掉接口注册函数USBD_CDC_RegisterInterface,然后将类注册换成我们自己的组合设备,如下图: 到这里就已经改完了,运行程序之后设备管理器会出现:一个组合设备、一个虚拟的串口、一个USB大容量存储设备,如下图: 这样就是成功了! !!如果修改过后发现设备管理器的MSC或者CDC显示感叹号,那么在回头检查PMA的配置以及组合设备描述符!! 遇到的问题 我修改完毕之后,将板子接入电脑,虚拟出来的串口没问题,但是就是死活认不出U盘,后来找到问题的原因是因为我先使用的CDC串口这个单独的工程,我的win10已经默认枚举成了CDC,所以认不出U盘。 解决办法: 卸载识别出来的串口,重新拔插 在程序里修改一下PID 我是用的第二种办法解决的! |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1777 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1080 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1678 浏览 2 评论
1937浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
595浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
553浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 01:12 , Processed in 0.591011 second(s), Total 76, Slave 57 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号