最近对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.01
in order to support LPM L1 suspend
resume test of USBCV3.0*/
#else
0x00, /*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 descriptor
describing the configuration */
#if (USBD_SELF_POWERED == 1U)
0xC0, /* bmAttributes: Bus Powered according to user configuration */
#else
0x80, /* 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.01
in order to support LPM L1 suspend
resume test of USBCV3.0*/
#else
0x00, /*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 */
#else
0x80, /* 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 builder
Size 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 descriptor
describing the configuration */
#if (USBD_SELF_POWERED == 1U)
0xC0, /* bmAttributes: Bus Powered according to user configuration */
#else
0x80, /* 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 size
User 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 1
switch(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;
#else
if ((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.01
in order to support LPM L1 suspend
resume test of USBCV3.0*/
#else
0x00, /*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 后记
都已经这么详细了,你那里出现这样的设备了吗?如果还没出现,欢迎随时来打我。文章来源:https://www.toymoban.com/news/detail-645024.html
2023.08.09-01:12于shenzhen文章来源地址https://www.toymoban.com/news/detail-645024.html
到了这里,关于手把手教你使用USB的CDC+MSC复合设备(基于stm32f407)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!