本文主要是介绍WinUSB与MSC复合设备--最佳搭配!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本文中采用stm32f103核心板,自带USB FS接口,板载了Flash W25Q16,将该Flash配置为MSC的存储区,最终实现的效果为:将设备插入电脑,可以识别到WinUSB免驱设备,高速通信,同时也能识别出一个大容量存储区域,方便存放一些设备相关的资料、文档等,非常实用!
本文的方法是在WinUSB工程的基础上增加MSC功能。
首先准备好一个WinUSB工程,配置方法见之前的文章:
https://blog.csdn.net/bingwueyi/article/details/121622001
在准备WinUSB工程的时候,将SPI也配置好,后期增加MSC的时候就不用再去设置SPI相关功能,省去一些麻烦。
1 工程配置
1、通过CubeMX生成一个MSC的工程,设置如下:
2、将下图中框出的几个文件及其头文件拷贝到WinUSB工程中的对应目录中
3、将Flash的操作文件W25QXX.c和复合设备文件usbd_composite.c也放到工程目录中,并且将上述文件添加到Keil工程目录,添加完后的工程目录如下:
Keil工程配置中添加相关路径
Flash操作函数参见:STM32CubeMX系列教程11:串行外设接口SPI(二) - STM32CubeMX系列教程 微雪课堂
头文件中将引用改为
#include "main.h"
同时增加如下定义:
#define SPI_FLASH_REBUILD 1 // 1:使能格式化串行Flash,0:禁用格式化串行Flash
#define SPI_FLASH_SECTOR_SIZE 4096 // 串行Flash扇区大小
#define SPI_FLASH_START_SECTOR 256 // 串行Flash文件系统FatFS偏移量
#define SPI_FLASH_SECTOR_COUNT 256 // 串行Flash文件系统FatFS占用扇区个数
extern SPI_HandleTypeDef hspi1; // 添加SPI定义
usbd_composite.c,usbd_composite.h基于这篇文章修改:https://www.bbsmax.com/A/kjdwBXA5Np/
修改后的文件如下
//usbd_composite.c
/*** @file usbd_composite.c* @author Weyne* @version V01* @date 2023.09.10* @brief MSC + WinUSB 复合设备* @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);//-----------------ADD GUOXUAN-----------------extern uint8_t* USBD_WinUSBOSStrDescriptor(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,//-----------------ADD GUOXUAN----------------USBD_WinUSBOSStrDescriptor,//--------------------------------------------};/* 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: */0x00, /* iConfiguration: */0xC0, /* bmAttributes: */0x96, /* MaxPower 300 mA *//****************************CDC************************************//* Interface Association Descriptor */USBD_IAD_DESC_SIZE, // bLengthUSBD_IAD_DESCRIPTOR_TYPE, // bDescriptorTypeUSBD_CDC_FIRST_INTERFACE, // bFirstInterfaceUSBD_CDC_INTERFACE_NUM, // bInterfaceCount0x02, // bFunctionClass0x02, // bFunctionSubClass0x01, // bInterfaceProtocol0x02, // iFunction ??/*Interface Descriptor *//* Data class interface descriptor */0x09, /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */0x00, /* bInterfaceNumber: Number of Interface */0x00, /* bAlternateSetting: Alternate setting */0x02, /* bNumEndpoints: Two endpoints used */0xFF, /* 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 *//* 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 *//****************************MSC************************************//* Interface Association Descriptor */USBD_IAD_DESC_SIZE, // bLengthUSBD_IAD_DESCRIPTOR_TYPE, // bDescriptorTypeUSBD_MSC_FIRST_INTERFACE, // bFirstInterfaceUSBD_MSC_INTERFACE_NUM, // bInterfaceCount0x08, // bFunctionClass0x06, // bFunctionSubClass0x50, // bInterfaceProtocol0x05,/******************** 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_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_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:pdev->pClassData = pCDCData;pdev->pUserData = &USBD_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_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_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_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****/
//usbd_composite.h
/*** @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 __cplusplusextern "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 1 /* winusb Interface NUM */#define USBD_CDC_DATA_INTERFACE 0#define USBD_MSC_FIRST_INTERFACE 1 /* MSC FirstInterface */#define USBD_MSC_INTERFACE_NUM 1 /* MSC Interface NUM */#define USBD_MSC_INTERFACE 1#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 (71)extern USBD_ClassTypeDef USBD_COMPOSITE;/*** @}*//*** @}*/#ifdef __cplusplus}#endif#endif /* __USBD_MSC_H *//*** @}*//************************ (C) COPYRIGHT WEYNE *****END OF FILE****/
2 修改工程文件
2.1 修改usbd_storage_if.c
1、添加Flash操作函数头文件
#include "W25QXX.h"
2、根据Flash容量修改block数量,例如此处我用的时2MB的Flash,Flash需要划分一部分空间存文件系统信息,此处Block大小为4K,数量为256,总容量为1M,修改如下
#define STORAGE_LUN_NBR 1#define STORAGE_BLK_NBR 256#define STORAGE_BLK_SIZ 0x1000
3、在STORAGE_Init_FS中加入Flash初始化函数BSP_W25Qx_Init(),在初始化中可以执行唤醒、读写使能、三四字节模式切换等操作。
int8_t STORAGE_Init_FS(uint8_t lun){/* USER CODE BEGIN 2 */BSP_W25Qx_Init();return (USBD_OK);/* USER CODE END 2 */}
4、在读写函数中分别加入对Flash的操作
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len){/* USER CODE BEGIN 6 */blk_addr+=SPI_FLASH_START_SECTOR; BSP_W25Qx_Read(buf, blk_addr*SPI_FLASH_SECTOR_SIZE, blk_len*SPI_FLASH_SECTOR_SIZE);return (USBD_OK);/* USER CODE END 6 */}int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len){/* USER CODE BEGIN 7 */uint32_t write_addr;blk_addr+=SPI_FLASH_START_SECTOR;write_addr = blk_addr*SPI_FLASH_SECTOR_SIZE; BSP_W25Qx_Erase_Block(write_addr);BSP_W25Qx_Write((uint8_t *)buf,write_addr,blk_len*SPI_FLASH_SECTOR_SIZE);return (USBD_OK);/* USER CODE END 7 */}
2.2 修改usbd_msc.h
查看MSC_EPIN_ADDR的值为0x81,与WinUSB的端点重复了,此处将MSC的端点改为0x83和0x03:
#define MSC_EPIN_ADDR 0x83U#define MSC_EPOUT_ADDR 0x03U
2.3 修改usb_device.c
1、添加复合设备头文件引用
#include "usbd_composite.h"
2、将初始化函数中的设备注册修改注册为复合设备
void MX_USB_DEVICE_Init(void){/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment *//* USER CODE END USB_DEVICE_Init_PreTreatment *//* Init Device Library, add supported class and start the library. */if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK){Error_Handler();}USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE);// if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC) != USBD_OK)// {// Error_Handler();// }// if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK)// {// Error_Handler();// }if (USBD_Start(&hUsbDeviceFS) != USBD_OK){Error_Handler();}/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment *//* USER CODE END USB_DEVICE_Init_PostTreatment */}
2.4 修改usbd_conf.c
1、添加msc的头文件引用
#include "usbd_msc.h"
2、在USBD_LL_Init函数中增加MSC的内存区间定义
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev){/* Init USB Ip. */uint32_t addr = 0x20;/* 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, addr); addr = addr + 0x40;HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, addr); addr = addr + 0x40;HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPIN_ADDR , PCD_SNG_BUF, addr); addr = addr + 0x40;HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPOUT_ADDR , PCD_SNG_BUF, addr); addr = addr + 0x40;HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, addr); addr = addr + 0x40;HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, addr); addr = addr + 0x40;HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP , PCD_SNG_BUF, addr); addr = addr + 0x40;/* USER CODE END EndPoint_Configuration_MSC */return USBD_OK;}
3、修改USBD_static_malloc函数,增加MSC的存储空间:
void *USBD_static_malloc(uint32_t size){static uint32_t mem2[(sizeof(USBD_MSC_BOT_HandleTypeDef)/4)+1];/* On 32-bit boundary */static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* */if(size == sizeof(USBD_MSC_BOT_HandleTypeDef))return mem2;else return mem;}
2.5 修改usbd_conf.h
1、将USBD_MAX_NUM_INTERFACES定义为2个接口:
#define USBD_MAX_NUM_INTERFACES 2
2、添加MSC_MEDIA_PACKET定义:
#define MSC_MEDIA_PACKET 4096
3 测试
完成上述修改(建议随意修改一个PID VID),编译-->下载-->插入电脑,将会枚举出一个usb复合设备,并且识别到一个WinUSB设备和一个大容量存储设备:
查看识别到的U盘,容量与2.1章节第2小节设置的一致,打开U盘创建文件、导入、导出等功能一切正常。
使用WinUSB通用调试软件,可以识别到刚插入的设备,并且通信正常,此处WinUSB为回环模式,收到数据原包回复。
这篇关于WinUSB与MSC复合设备--最佳搭配!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!