学习
- 1 实验环境与说明
- 2 USB CDC
- 2.1 CDC代码生成
- 2.2 通信设备(CDC)描述符
- 2.2.1 设备描述符
- 2.2.2 配置描述符
- 2.3 调试
- 3 USB MSC
- 3.1 MSC代码生成
- 3.2 大容量存储设备(MSC)描述符
- 3.2.1 设备描述符
- 2.2.2 配置描述符
- 3.3 调试
- 4 USB复合设备(CDC+MSC)
- 4.1 复合设备描述符
- 4.2 代码修改
- 4.3 调试
- 5 后记
最近对usb有点兴趣,感觉挺好玩的,于是买了本圈圈大神的经典著作-<<圈圈教你玩USB>>,里面使用51单片机+usb芯片对usb的基本知识潺潺道来,做了十个左右的常用案例实验,很有趣,建议大家看看。
趁热打铁,拿身边的开发板来练练手,探索一下复合设备的好玩方便的地方。
1 实验环境与说明
实验环境:
- 硬件: STM32F407
- 编译器: Keil MDK 5.17
- 软件生成工具: STM32CubeMX 6.8.1
- 调试工具: Jlink
说明: 本实验所使用的代码基本都是使用CubeMX生成,只有小部分做了些修改和调整来适配当前的实验,关于USB的部分,如描述符、枚举、协议等不会做详细说明,感兴趣的话可以看OO的著作,或者可以跟笔者讨论,后期如果有时间的话,可以将usb的各个章节总结出来。
下面会有三个实验循序渐进,实验1是USB CDC部分,实验2是USB MSC部分,实验3是将前两个实验结合起来。STM32CubeMX版本的不同,生成的代码可能也不同,请注意不一样的地方,根据实际进行调整。
usb官网:https://usb.org/
2 USB CDC
使用usb的通信设备类,即
此设备类会在设备描述符和接口描述符中体现。
下面使用STM32CubeMX来生成此实验所需要的代码。
2.1 CDC代码生成
打开CubeMX,选择控制器芯片STM32F407。
1、时钟选择
2、usb设备选择
3、usb通信设备类选择
4、时钟配置
OK,上述CubeMX配置如此,接下来生成Keil MDK的工程代码即可。
2.2 通信设备(CDC)描述符
2.2.1 设备描述符
标准设备描述符如下:
/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{0x12, /*bLength */USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
#if (USBD_LPM_ENABLED == 1)0x01, /*bcdUSB */ /* changed to USB version 2.01in order to support LPM L1 suspendresume test of USBCV3.0*/
#else0x00, /*bcdUSB */
#endif /* (USBD_LPM_ENABLED == 1) */0x02,0x02, /*bDeviceClass*/0x02, /*bDeviceSubClass*/0x00, /*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*/
};
注意bDeviceClass字段必须使用通信设备类0x2。
2.2.2 配置描述符
配置描述符组成如下图:
对应的配置描述符代码段如下:
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{/* Configuration Descriptor */0x09, /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength */0x00,0x02, /* 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) *//*---------------------------------------------------------------------------*//* 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 */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 *//* 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 */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 */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 *//* 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 */
};
2.3 调试
上述代码生成之后,修改一下代码,在main.c的主函数中修改如下:
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USB_DEVICE_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */uint32_t cur_time, pre_time = 0;uint32_t count = 0;char buf[256];while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */cur_time = HAL_GetTick();if ((cur_time - pre_time) >=1000 ) {pre_time = cur_time;int length = snprintf(buf, sizeof(buf)-1, "你是个第%d个沙雕\r\n", count++);CDC_Transmit_FS((uint8_t*)buf, length);}}/* USER CODE END 3 */
}
编译,通过Jlink下载到控制器芯片中,将usb口连到pc上,查看对应的设备管理器,发现了增加了一个端口,如下:
网上找个免费的串口调试助手,打开对应的端口COM9,则根据上述修改的main.c文件中的内容,控制器会通过usb发送指定的内容出来显示到pc上的串口调试助手上,如下:
以上为USB CDC的实验,完成。
3 USB MSC
使用usb的Mass Storage类,即
此设备类会在接口描述符中体现。
下面使用STM32CubeMX来生成此实验所需要的代码。
3.1 MSC代码生成
打开CubeMX,选择控制器芯片STM32F407。
1、时钟选择
2、usb设备选择
3、usb大容量存储类选择
4、SDIO接口选择
选择sdio接口时需注意自己使用的sd卡是支持多少数据线的,本人的是1 bit的,大部分可能是4bit的,请注意一下。
5、fatfs文件系统选择
OK,上述CubeMX配置如此,接下来生成Keil MDK的工程代码即可。
3.2 大容量存储设备(MSC)描述符
3.2.1 设备描述符
标准设备描述符如下:
/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{0x12, /*bLength */USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
#if (USBD_LPM_ENABLED == 1)0x01, /*bcdUSB */ /* changed to USB version 2.01in order to support LPM L1 suspendresume test of USBCV3.0*/
#else0x00, /*bcdUSB */
#endif /* (USBD_LPM_ENABLED == 1) */0x02,0x00, /*bDeviceClass*/0x00, /*bDeviceSubClass*/0x00, /*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*/
};
2.2.2 配置描述符
配置描述符组成如下图:
对应的配置描述符代码段如下:
/* USB Mass storage device Configuration Descriptor */
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
__ALIGN_BEGIN static uint8_t USBD_MSC_CfgDesc[USB_MSC_CONFIG_DESC_SIZ] __ALIGN_END =
{0x09, /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */USB_MSC_CONFIG_DESC_SIZ,0x00,0x01, /* bNumInterfaces: 1 interface */0x01, /* bConfigurationValue */0x04, /* iConfiguration */
#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) *//******************** Mass Storage interface ********************/0x09, /* bLength: Interface Descriptor size */0x04, /* bDescriptorType: */0x00, /* 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),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 */
};
3.3 调试
上述代码生成之后,修改一下代码,在usb与sdio通信的接口文件usbd_storage_if.c中对以后几个函数进行补充,很关键:
具体补充的内容如下:
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{/* USER CODE BEGIN 3 */UNUSED(lun);*block_num = hsd.SdCard.BlockNbr;*block_size = hsd.SdCard.BlockSize;return (USBD_OK);/* USER CODE END 3 */
}
int8_t STORAGE_IsReady_FS(uint8_t lun)
{/* USER CODE BEGIN 4 */uint8_t state = 0;state = HAL_SD_GetState(&hsd) ;if(HAL_SD_STATE_READY != state){return USBD_FAIL ;}return (USBD_OK);/* USER CODE END 4 */
}
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{/* USER CODE BEGIN 6 */if(HAL_OK != HAL_SD_ReadBlocks(&hsd,(uint8_t *)buf, blk_addr , blk_len, 1000)){return USBD_FAIL ;}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 */if(HAL_OK != HAL_SD_WriteBlocks(&hsd, (uint8_t *)buf, blk_addr , blk_len, 1000)){return USBD_FAIL ;}return (USBD_OK);/* USER CODE END 7 */
}
在main.c中增加如下内容,会对底层的sd卡和文件系统进行初始化:
/* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_SDIO_SD_Init();MX_USB_DEVICE_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */retSD=f_mount(&SDFatFS, (TCHAR const*)SDPath, 0);retSD = f_open(&SDFile,(const char*)"1.txt",FA_CREATE_ALWAYS|FA_WRITE);retSD = f_close(&SDFile);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}
编译,通过Jlink下载到控制器芯片中,将usb口连到pc上,查看pc的资源管理器,发现了增加了一个磁盘并且具有我们新增的文件,如下:
以上为USB MSC的实验,完成。
4 USB复合设备(CDC+MSC)
理论上好像应该不能叫复合设备,应该称为组合设备,只是习惯这个叫法了。
以最近的这个MSC实验为基础,结合第一个CDC实验来修改代码变成我们的复合设备。
4.1 复合设备描述符
4.2 代码修改
1、将CDC实验使用的usb cdc类代码拷贝过来,新增复合设备类(composite)代码,这两个文件是从官网的F407例程中抠出来的,可官网搜索下载。
2、修改复合设备类(composite)代码
usbd_composite_builder.c文件中增加配置描述符,此配置描述是按照上述给出的图来进行内容填充的。
/* The generic configuration descriptor buffer that will be filled by builderSize of the buffer is the maximum possible configuration descriptor size. */
__ALIGN_BEGIN static uint8_t USBD_CMPSIT_FSCfgDesc[USBD_CMPST_MAX_CONFDESC_SZ] __ALIGN_END = {/* Configuration Descriptor */0x09, /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */USBD_CMPST_MAX_CONFDESC_SZ, /* 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) *//*---------------------------------------------------------------------------*//* 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 */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 endpoint 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 *//* 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 */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 */0x00, /* bAlternateSetting: Alternate setting */0x02, /* bNumEndpoints: Two endpoints used */0x0A, /* bInterfaceClass: CDC */0x00, /* bInterfaceSubClass */0x00, /* bInterfaceProtocol */0x06, /* 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 *//* 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: */0x00, /* 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 */0x00, /* 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 */
};static uint8_t *pCmpstFSConfDesc = USBD_CMPSIT_FSCfgDesc;
/* Variable that dynamically holds the current size of the configuration descriptor */
static __IO uint32_t CurrFSConfDescSz = USBD_CMPST_MAX_CONFDESC_SZ;
usbd_composite_builder.h修改配置描述符数组长度。
/* This is the maximum supported configuration descriptor sizeUser may define this value in usbd_conf.h in order to optimize footprint */
#ifndef USBD_CMPST_MAX_CONFDESC_SZ
#define USBD_CMPST_MAX_CONFDESC_SZ 106U
#endif /* USBD_CMPST_MAX_CONFDESC_SZ */
3、修改各端点地址
CDC类,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 */
MSC类,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 */
3、启动复合设备编译
main.h中增加复合设备编译宏USE_USBD_COMPOSITE 。
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);/* USER CODE BEGIN EFP *//* USER CODE END EFP *//* Private defines -----------------------------------------------------------*/
#define USE_USBD_COMPOSITE 1
4、修改复合设备类的类添加函数
usbd_composite_builder.c中修改设备类的添加函数,内部涉及接口、端点、类型等。
/*** @brief USBD_CMPSIT_AddClass* Register a class in the class builder* @param pdev: device instance* @param pclass: pointer to the class structure to be added* @param class: type of the class to be added (from USBD_CompositeClassTypeDef)* @param cfgidx: configuration index* @retval status*/
uint8_t USBD_CMPSIT_AddClass(USBD_HandleTypeDef *pdev,USBD_ClassTypeDef *pclass,USBD_CompositeClassTypeDef class,uint8_t cfgidx)
{
#if 1switch(class){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;UNUSED(cfgidx);return (uint8_t)USBD_OK;#elseif ((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->tclasslist[pdev->classId].Active == 0U)){/* Store the class parameters in the global tab */pdev->pClass[pdev->classId] = pclass;pdev->tclasslist[pdev->classId].ClassId = pdev->classId;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].ClassType = class;/* Call configuration descriptor builder and endpoint configuration builder */if (USBD_CMPSIT_AddToConfDesc(pdev) != (uint8_t)USBD_OK){return (uint8_t)USBD_FAIL;}}UNUSED(cfgidx);return (uint8_t)USBD_OK;
#endif
}
5、修改usb端点发送和接收的fifo设置
关于三个批量端点的发送fifo相关的设置可参考手册,此处必须修改。
usbd_conf.c文件中修改端点发送fifo配置。
#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)/* Register USB PCD CallBacks */HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback);HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_FS, PCD_DataOutStageCallback);HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_FS, PCD_DataInStageCallback);HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOOUTIncompleteCallback);HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOINIncompleteCallback);
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x40);HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40);}return USBD_OK;
}
6、添加cdc接口文件
将cdc实验的接口文件添加过来。
7、注册复合设备类
将cdc和msc的类注册到复合设备类中,注意注册的接口和类顺序,否则会出问题,usb_device.c中增加如下。
void MX_USB_DEVICE_Init(void)
{/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment *//* USER CODE END USB_DEVICE_Init_PreTreatment usbCmpsitFS_Desc*//* Init Device Library, add supported class and start the library. */if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK){Error_Handler();}if(USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK){Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_CDC,CLASS_TYPE_CDC,0) != USBD_OK){Error_Handler();}if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK){Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_MSC,CLASS_TYPE_MSC,0) != 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 */
}
8、修改cdc发送函数
因代码生成的cdc发送函数是单个设备的,若使用复合设备发送,则需要修改,usbd_cdc_if.c增加如下函数:
uint8_t CDC_Transmit(uint8_t* Buf, uint16_t Len)
{uint8_t result = USBD_OK;USBD_HandleTypeDef *pdev = &hUsbDeviceFS;/* Get the class index relative to this endpoint */uint8_t idx = USBD_CoreFindEP(pdev, CDC_IN_EP);if (((uint8_t)idx != 0xFFU) && (idx < USBD_MAX_SUPPORTED_CLASS)){pdev->classId = idx;USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassDataCmsit[pdev->classId];if (hcdc->TxState != 0){return USBD_BUSY;}USBD_CDC_SetTxBuffer(pdev, Buf, Len);result = USBD_CDC_TransmitPacket(pdev);return result;}return USBD_FAIL;
}
9,修改设备描述符
因现在是复合设备,则需要修改usbd_desc.c中的设备描述符:
/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{0x12, /*bLength */USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
#if (USBD_LPM_ENABLED == 1)0x01, /*bcdUSB */ /* changed to USB version 2.01in order to support LPM L1 suspendresume test of USBCV3.0*/
#else0x00, /*bcdUSB */
#endif /* (USBD_LPM_ENABLED == 1) */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*/
};
10,修改usb配置文件
因现在支持3个接口,则需要修改usbd_conf.h中的支持最大接口数,同时修改此文件中的malloc和free函数:
/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES 3U/* Memory management macros make sure to use static memory allocation */
/** Alias for memory allocation. */#define USBD_malloc malloc//(void *)USBD_static_malloc/** Alias for memory release. */
#define USBD_free free//USBD_static_free
11、修改内存堆大小
因10中使用了动态内存管理,而usb比较耗内存,则需要修改内存的堆大小,如下:
4.3 调试
编译代码,下载,可以同时出现了cdc设备,msc设备:
5 后记
都已经这么详细了,你那里出现这样的设备了吗?如果还没出现,欢迎随时来打我。
2023.08.09-01:12于shenzhen