前言
本意是做一个CDC+MSC的复合设备,就是类似于ST-LINK2的那种设备,
在平时使用串口功能通讯,偶尔可以通过拖拉文件进行IAP升级,但是在网上
找了好多资料和教程后,却发现基本都是同一个教程,而且效果都不尽人意,
总会出现一堆奇奇怪怪的效果,但是USB这个东西本身又是一个很复杂的协议
,我又不想去学习,所以就贼心不死,去google了一些资料,但发现按照
他们的流程走一遍还是有问题,大概记录一下移植过程中遇到的问题。
1、准备内容
- USB_CDC(VCP)工程一份
- USB_MSC工程一份(确保可以从PC端看到的内存并且可以进行读写操作)
2、我是个辛勤的搬运工
-
在你的CDC工程中找到如下四个文件:
usbd_cdc.c usbd_cdc.h usbd_cdc_if.c usbd_cdc_if.h
-
拷贝到MSC工程下,在KEIL里添加头文件路径。
-
新建usbd_composite.c和usbd_composite.h文件。
-
在usbd_composite.c文件中添加如下内容。
/**
******************************************************************************
* @file usbd_MC.c
* @author MCD Application Team
* @version V2.4.2
* @date 11-December-2015
* @brief This file provides all the MC core functions.
*
* @verbatim
*
* ===================================================================
* MC Class Description
* ===================================================================
* This module manages the MC class V1.0 following the "Universal
* Serial Bus Mass Storage Class (MC) Bulk-Only Transport (BOT) Version 1.0
* Sep. 31, 1999".
* This driver implements the following aspects of the specification:
* - Bulk-Only Transport protocol
* - Subclass : SCSI transparent command set (ref. SCSI Primary Commands - 3 (SPC-3))
*
* @endverbatim
*
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT 2015 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.st.com/software_license_agreement_liberty_v2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_composite.h"
#include "usbd_def.h"
#include "usbd_msc.h"
#include "usbd_cdc.h"
#include "usbd_storage_if.h"
#include "usbd_cdc_if.h"
/** @defgroup MC_CORE_Private_FunctionPrototypes
* @{
*/
uint8_t USBD_MC_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx);
uint8_t USBD_MC_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx);
uint8_t USBD_MC_Setup (USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req);
uint8_t USBD_MC_DataIn (USBD_HandleTypeDef *pdev,
uint8_t epnum);
uint8_t USBD_MC_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum);
uint8_t *USBD_MC_GetHSCfgDesc (uint16_t *length);
uint8_t *USBD_MC_GetFSCfgDesc (uint16_t *length);
uint8_t *USBD_MC_GetOtherSpeedCfgDesc (uint16_t *length);
uint8_t *USBD_MC_GetDeviceQualifierDescriptor (uint16_t *length);
static uint8_t USBD_MC_RxReady (USBD_HandleTypeDef *pdev);
static void MC_Switch_MSC(USBD_HandleTypeDef *pdev);
static void MC_Switch_CDC(USBD_HandleTypeDef *pdev);
/**
* @}
*/
extern USBD_HandleTypeDef hUsbDeviceFS;
/** @defgroup MC_CORE_Private_Variables
* @{
*/
USBD_ClassTypeDef USBD_MC =
{
USBD_MC_Init,
USBD_MC_DeInit,
USBD_MC_Setup,
NULL, /*EP0_TxSent*/
USBD_MC_RxReady, /*EP0_RxReady*/
USBD_MC_DataIn,
USBD_MC_DataOut,
NULL, /*SOF */
NULL,
NULL,
USBD_MC_GetHSCfgDesc,
USBD_MC_GetFSCfgDesc,
USBD_MC_GetOtherSpeedCfgDesc,
USBD_MC_GetDeviceQualifierDescriptor,
};
/* USB Mass storage device Configuration Descriptor */
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
uint8_t USBD_MC_CfgDesc[USB_MC_CONFIG_DESC_SIZ] =
{
/*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 */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xC0, /* bmAttributes: self powered */
0x32, /* MaxPower 0 mA */
/*---------------------------------------------------------------------------*/
// IAD
0x08, //ÃèÊö·û´óС
0x0B, //IADÃèÊö·ûÀàÐÍ
0x00, // bFirstInterface
0x02, // bInterfaceCount
0x02, // bFunctionClass: CDC Class
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 */
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints 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: 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 */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
MC_CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
0x10, /* 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 */
MC_CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(MC_MAX_FS_PACKET), /* wMaxPacketSize: */
HIBYTE(MC_MAX_FS_PACKET),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
MC_CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(MC_MAX_FS_PACKET), /* wMaxPacketSize: */
HIBYTE(MC_MAX_FS_PACKET),
0x00, /* bInterval: ignore for Bulk transfer */
/*---------------------------------------------------------------------------*/
// IAD
0x08, //ÃèÊö·û´óС
0x0B, //IADÃèÊö·ûÀàÐÍ
0x02, // bFirstInterface
0x01, // bInterfaceCount
0x08, // bFunctionClass: MASS STORAGE Class
0x06, // bFunctionSubClass
0x50, // bFunctionProtocol
0x01, // 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 */
0x05, /* iInterface: */
/******************** Mass Storage Endpoints ********************/
0x07, /*Endpoint descriptor length = 7*/
0x05, /*Endpoint descriptor type */
MC_MSC_EPIN_ADDR, /*Endpoint address (IN, address 1) */
0x02, /*Bulk endpoint type */
LOBYTE(MC_MAX_FS_PACKET),
HIBYTE(MC_MAX_FS_PACKET),
0x00, /*Polling interval in milliseconds */
0x07, /*Endpoint descriptor length = 7 */
0x05, /*Endpoint descriptor type */
MC_MSC_EPOUT_ADDR, /*Endpoint address (OUT, address 1) */
0x02, /*Bulk endpoint type */
LOBYTE(MC_MAX_FS_PACKET),
HIBYTE(MC_MAX_FS_PACKET),
0x00 /*Polling interval in milliseconds*/
};
/* USB Standard Device Descriptor */
uint8_t USBD_MC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
{
USB_LEN_DEV_QUALIFIER_DESC,
USB_DESC_TYPE_DEVICE_QUALIFIER,
0x00,
0x02,
0x00,
0x00,
0x00,
MC_MAX_FS_PACKET,
0x01,
0x00,
};
/**
* @}
*/
/** @defgroup MC_CORE_Private_Functions
* @{
*/
/**
* @brief USBD_MC_Init
* Initialize the mass storage configuration
* @param pdev: device instance
* @param cfgidx: configuration index
* @retval status
*/
uint8_t USBD_MC_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
{
USBD_CDC_HandleTypeDef * hcdc;
MC_Switch_CDC(pdev);
USBD_LL_OpenEP(pdev,
MC_CDC_IN_EP,
USBD_EP_TYPE_BULK,
MC_MAX_FS_PACKET);
USBD_LL_OpenEP(pdev,
MC_CDC_OUT_EP,
USBD_EP_TYPE_BULK,
MC_MAX_FS_PACKET);
USBD_LL_OpenEP(pdev,
MC_CDC_CMD_EP,
USBD_EP_TYPE_INTR,
CDC_CMD_PACKET_SIZE);
hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
hcdc->TxState =0;
hcdc->RxState =0;
USBD_LL_PrepareReceive(pdev,
MC_CDC_OUT_EP,
hcdc->RxBuffer,
MC_MAX_FS_PACKET);
}
{
MC_Switch_MSC(pdev);
USBD_LL_OpenEP(pdev,
MC_MSC_EPOUT_ADDR,
USBD_EP_TYPE_BULK,
MC_MAX_FS_PACKET);
USBD_LL_OpenEP(pdev,
MC_MSC_EPIN_ADDR,
USBD_EP_TYPE_BULK,
MC_MAX_FS_PACKET);
MSC_BOT_Init(pdev);
}
return USBD_OK;
}
/**
* @brief USBD_MC_DeInit
* DeInitilaize the mass storage configuration
* @param pdev: device instance
* @param cfgidx: configuration index
* @retval status
*/
uint8_t USBD_MC_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
return USBD_OK;
}
/**
* @brief USBD_MC_Setup
* Handle the MC specific requests
* @param pdev: device instance
* @param req: USB request
* @retval status
*/
uint8_t USBD_MC_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
if(req->wIndex == 0x0002)
{
MC_Switch_MSC(pdev);
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef*) pdev->pClassData;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
/* Class request */
case USB_REQ_TYPE_CLASS :
switch (req->bRequest)
{
case BOT_GET_MAX_LUN :
if((req->wValue == 0) &&
(req->wLength == 1) &&
((req->bmRequest & 0x80) == 0x80))
{
hmsc->max_lun = ((USBD_StorageTypeDef *)pdev->pUserData)->GetMaxLun();
USBD_CtlSendData (pdev,
(uint8_t *)&hmsc->max_lun,
1);
}
else
{
USBD_CtlError(pdev , req);
return USBD_FAIL;
}
break;
case BOT_RESET :
if((req->wValue == 0) &&
(req->wLength == 0) &&
((req->bmRequest & 0x80) != 0x80))
{
MSC_BOT_Reset(pdev);
}
else
{
USBD_CtlError(pdev , req);
return USBD_FAIL;
}
break;
default:
USBD_CtlError(pdev , req);
return USBD_FAIL;
}
break;
/* Interface & Endpoint request */
case USB_REQ_TYPE_STANDARD:
switch (req->bRequest)
{
case USB_REQ_GET_INTERFACE :
USBD_CtlSendData (pdev,
(uint8_t *)&hmsc->interface,
1);
break;
case USB_REQ_SET_INTERFACE :
hmsc->interface = (uint8_t)(req->wValue);
break;
case USB_REQ_CLEAR_FEATURE:
/* Flush the FIFO and Clear the stall status */
USBD_LL_FlushEP(pdev, (uint8_t)req->wIndex);
/* Reactivate the EP */
USBD_LL_CloseEP (pdev , (uint8_t)req->wIndex);
if((((uint8_t)req->wIndex) & 0x80) == 0x80)
{
if(pdev->dev_speed == USBD_SPEED_HIGH )
{
/* Open EP IN */
USBD_LL_OpenEP(pdev,
MC_MSC_EPIN_ADDR,
USBD_EP_TYPE_BULK,
MSC_MAX_HS_PACKET);
}
else
{
/* Open EP IN */
USBD_LL_OpenEP(pdev,
MC_MSC_EPIN_ADDR,
USBD_EP_TYPE_BULK,
MSC_MAX_FS_PACKET);
}
}
else
{
if(pdev->dev_speed == USBD_SPEED_HIGH )
{
/* Open EP IN */
USBD_LL_OpenEP(pdev,
MC_MSC_EPOUT_ADDR,
USBD_EP_TYPE_BULK,
MSC_MAX_HS_PACKET);
}
else
{
/* Open EP IN */
USBD_LL_OpenEP(pdev,
MC_MSC_EPOUT_ADDR,
USBD_EP_TYPE_BULK,
MSC_MAX_FS_PACKET);
}
}
/* Handle BOT error */
MSC_BOT_CplClrFeature(pdev, (uint8_t)req->wIndex);
break;
}
break;
default:
break;
}
}
else
{
MC_Switch_CDC(pdev);
static uint8_t ifalt = 0;
USBD_CDC_HandleTypeDef * hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
case USB_REQ_TYPE_CLASS :
if (req->wLength)
{
if (req->bmRequest & 0x80)
{
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest,
(uint8_t *)hcdc->data,
req->wLength);
USBD_CtlSendData (pdev,
(uint8_t *)hcdc->data,
req->wLength);
}
else
{
hcdc->CmdOpCode = req->bRequest;
hcdc->CmdLength = req->wLength;
USBD_CtlPrepareRx (pdev,
(uint8_t *)hcdc->data,
req->wLength);
}
}
else
{
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest,
(uint8_t*)req,
0);
}
break;
case USB_REQ_TYPE_STANDARD:
switch (req->bRequest)
{
case USB_REQ_GET_INTERFACE :
USBD_CtlSendData (pdev,
&ifalt,
1);
break;
case USB_REQ_SET_INTERFACE :
break;
}
default:
break;
}
}
return USBD_OK;
}
/**
* @brief USBD_MC_DataIn
* handle data IN Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
uint8_t USBD_MC_DataIn (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
if(epnum == (MC_MSC_EPIN_ADDR & 0x7f))
{
MC_Switch_MSC(pdev);
MSC_BOT_DataIn(pdev , epnum);
}
else if(epnum == (MC_CDC_IN_EP & 0x7f))
{
USBD_CDC_HandleTypeDef *hcdc;
MC_Switch_CDC(pdev);
hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
hcdc->TxState = 0;
}
return USBD_OK;
}
/**
* @brief USBD_MC_DataOut
* handle data OUT Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
uint8_t USBD_MC_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
if(epnum == MC_MSC_EPOUT_ADDR)
{
MC_Switch_MSC(pdev);
MSC_BOT_DataOut(pdev , epnum);
}
else if(epnum == MC_CDC_OUT_EP)
{
USBD_CDC_HandleTypeDef *hcdc;
MC_Switch_CDC(pdev);
hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
hcdc->RxLength = USBD_LL_GetRxDataSize (pdev, epnum);
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength);
}
return USBD_OK;
}
/**
* @brief USBD_MC_GetHSCfgDesc
* return configuration descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_MC_GetHSCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_MC_CfgDesc);
return USBD_MC_CfgDesc;
}
/**
* @brief USBD_MC_GetFSCfgDesc
* return configuration descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_MC_GetFSCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_MC_CfgDesc);
return USBD_MC_CfgDesc;
}
uint8_t *USBD_MC_GetOtherSpeedCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_MC_CfgDesc);
return USBD_MC_CfgDesc;
}
uint8_t *USBD_MC_GetDeviceQualifierDescriptor (uint16_t *length)
{
*length = sizeof (USBD_MC_DeviceQualifierDesc);
return USBD_MC_DeviceQualifierDesc;
}
static uint8_t USBD_MC_RxReady (USBD_HandleTypeDef *pdev)
{
USBD_CDC_HandleTypeDef *hcdc;
MC_Switch_CDC(pdev);
hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
if((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFF))
{
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(hcdc->CmdOpCode,
(uint8_t *)hcdc->data,
hcdc->CmdLength);
hcdc->CmdOpCode = 0xFF;
}
return USBD_OK;
}
static void MC_Switch_MSC(USBD_HandleTypeDef *pdev)
{
static USBD_MSC_BOT_HandleTypeDef msc_handle;
USBD_MSC_RegisterStorage(pdev, &USBD_Storage_Interface_fops_FS);
pdev->pClassData = &msc_handle;
}
static void MC_Switch_CDC(USBD_HandleTypeDef *pdev)
{
static USBD_CDC_HandleTypeDef cdc_handle;
USBD_CDC_RegisterInterface(pdev, &USBD_Interface_fops_FS);
pdev->pClassData = &cdc_handle;
}
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
- 在usbd_composite.h文件中添加如下内容。
#ifndef __USBD_COMPOSITE_H
#define __USBD_COMPOSITE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "usbd_ioreq.h"
#include "usbd_cdc.h"
#include "usbd_msc.h"
#define MC_MAX_FS_PACKET 0x40
#define USB_MC_CONFIG_DESC_SIZ 106
#define MC_MSC_EPIN_ADDR MSC_EPIN_ADDR
#define MC_MSC_EPOUT_ADDR MSC_EPOUT_ADDR
#define MC_CDC_IN_EP CDC_IN_EP
#define MC_CDC_OUT_EP CDC_OUT_EP
#define MC_CDC_CMD_EP CDC_CMD_EP
extern USBD_ClassTypeDef USBD_MC;
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __USBD_MSC_H */
/**
* @}
*/
/************************ (C) COPYRIGHT WEYNE *****END OF FILE****/
- 修改usbd_cdc.h和usbd_msc.h中的部分内容。
修改usbd_cdc.h文件内容。
#define CDC_IN_EP 0x81U /* EP1 for data IN */
#define CDC_OUT_EP 0x01U /* EP1 for data OUT */
#define CDC_CMD_EP 0x83U /* EP2 for CDC commands */
修改usbd_msc.h文件内容
#define MSC_EPIN_ADDR 0x82U
#define MSC_EPOUT_ADDR 0x02U
- 修改usbd_conf.h文件内部分内容。
从1修改为3
#define USBD_MAX_NUM_INTERFACES 3
- 修改usbd_conf.c文件部分内容。
找到USBD_LL_Init函数,修改为如下内容
#include "usbd_cdc.h"
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
/* Init USB Ip. */
/* 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, 0x18);
// HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
// /* USER CODE END EndPoint_Configuration */
// /* USER CODE BEGIN EndPoint_Configuration_MSC */
// HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x98);
// HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0xD8);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0x98);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xD8);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPIN_ADDR , PCD_SNG_BUF, 0x118);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPOUT_ADDR , PCD_SNG_BUF, 0x158);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP , PCD_SNG_BUF, 0x198);
/* USER CODE END EndPoint_Configuration_MSC */
return USBD_OK;
}
- 修改usbd_device.c文件中部分内容。
#include "usbd_composite.h"
void MX_USB_DEVICE_Init(void)
{
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MC) != USBD_OK)
{
Error_Handler();
}
// if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK)
// {
// Error_Handler();
// }
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}
}
- 修改usbd_des.c
主要是修改bDeviceClass及后续两个设备子类和设备控制参数。
#define USBD_SERIALNUMBER_STRING_FS "00000000001A"
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
0x12, /*bLength */
USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
0x00, /*bcdUSB */
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*/
};
uint8_t * USBD_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
UNUSED(speed);
*length = USB_SIZ_STRING_SERIAL;
/* Update the serial number string descriptor with the data from the unique
* ID */
Get_SerialNum();
/* USER CODE BEGIN USBD_FS_SerialStrDescriptor */
USBD_GetString((uint8_t *)USBD_SERIALNUMBER_STRING_FS, USBD_StrDesc, length);
/* USER CODE END USBD_FS_SerialStrDescriptor */
return (uint8_t *) USBD_StrDesc;
}
这里USBD_FS_SerialStrDescriptor我是修改后才成功的,当然后来成功后还没来得及验证不修改该函数是否有效。后续继续测试。
理论上来说,这个函数只是回传串行字符串描述符,只是对USB枚举阶段有影响,对后续的传输应该是无任何影响的。
3、总结
总体来说,所有的32HAL库的移植步骤大概都分为下面几步,以CDC+MSC举例。
1. 先把类文件较少的一个类的文件移植到类文件较多的工程中,一般该路径在
USB_MSC\Middlewares\ST\STM32_USB_Device_Library\Class下。
2. 找到该类的usbd_xxxx_if.c和头文件,一并添加到工程。(上述流程的1-2)
3. 在你想复合的设备中找到端点号,进行端点号分配。(上述流程中的第7步)
4. 添加一个新的c文件和头文件,名字随便。该文件的主要目的如下
①. 整合你想移植类的一些函数(一般就是找到要整合的类的USBD_XXX_if.c文件进行合并).
②. 定义一个USBD_ClassTypeDef类型变量,将所有函数名按顺序存放到该变量。
③.定义复合设备的配置描述符和USB标准描述符。
5. 在usbd_conf.c文件的USBD_LL_Init函数中添加你新增端点的PMA
6. 在usbd_conf.h文件中修改最大接口数,因为是复合设备,具体数量根据你
的实际使用情况来填写。
7. 修改USBD_device.c文件中的注册接口,将原有的变量修改为你自定义的
USBD_ClassTypeDef类型的变量,注释掉,USBD_XXX_RegisterXXXX等字样的函数。
只有第5步是只涉及到STM32本身的,在32里面是由一段内存专门用于USB数据缓冲的
4、问题总结
大概分为两个阶段的问题
1. 枚举阶段
- 枚举失败
- 只识别一个设备,另外一个设备枚举失败
2. 使用阶段
- U盘正常工作,可读写,但是CDC只有在打开串口的时候会输出两条语句
- 使用U盘读写时CDC没有数据输出,但在弹出U盘后CDC正常工作
- 使用U盘读写后,CDC直接挂掉!!!无论如何都没有数据
- CDC也没数据,MSC出现请插入U盘
反正是问题一大堆,然后在网上翻来覆去的找,也没找到一个好的解决办法(太菜了,也懒,实在不想去看USB协议,里面的分包机制和应答机制,最关键的是CDC和MSC的通讯机制),最后在国外论坛找到了一个工程,打开了他的工程,然后参照着修修改改,最终才勉强能用,但还是会存在以下问题。
- U盘进行较多的数据读写时,会照成CDC堵塞,但在文件读写完后,也可以正常工作。
- 在弹出U盘后,CDC也会停止工作,需要手动点击一下打开串口才可以正常工作。
4.1、解决办法
对于问题一对于目前我的需求来说并没有什么影响,主要在于问题二。
经过测试,使用不同的串口工具最终会进入**CDC_Control_FS**函数的几
个分支,但具体是通过setup还是EP0_RxReady进入我还没有测试,后续测试后在
补上。如果知道了如何模拟打开串口这一操作,那么这一问题就好解决了。
说白了还是对CDC和MSC的通讯机制不是很熟,英文的文档看着确实慢。 慢慢看吧,一个人摸索一个陌生的东西实在太难了,还好网上有大量技术人员的博客,感谢他们的笔记和一些开源的代码资料,万分感谢。
8/27补充
- 读写大文件的时候,CDC会堵塞
st的USB库里面所有的处理都是在中断中进行的,在进行大数据吞吐的时候,所有数据的处理也都是在中断进行,我在测试时候所用的存储介质是SPI_FLASH,本身读写的时候需要等待时间,所以堵塞的时间基本都是在对spiflash进行操作。
- 在弹出U盘后,CDC也会停止工作,需要手动点击一下打开串口才可以正常工作
在弹出U盘的时候,主机会发处三条指令,两次SCSI_READ_CAPACITY10和一次SCSI_READ10,这个时候最终才会弹出U盘.
说到这个,还要说一下ST的USB库的分层,我们需要完善的就是usbd_xxxx_if.c等类文件.USB库的所有操作都是针对USBD_HandleTypeDef这个数据结构进行的操作,在库里面具体的表现是定义了一个hUsbDeviceFS的变量。
在进行复合设备的操作的时候,我目前在网上看到的所有的基于ST的USB开发的例子全都是将官方的类模板进行整合,然后通过对于pClassData和pUserData的切换进行工作.这个是前提然后接着第一段说, 这个时候会通过USBD_MC_DataOut函数内的MC_Switch_MSC将pClassData指针指向了MSC的数据结构体(USBD_MSC_BOT_HandleTypeDef),但在CDC发送的时候,会判断pClassData指向的结构体变量的一个成员变量,在正常情况下使用CDC发送应该是指向USBD_CDC_HandleTypeDef的变量,但这个时候指向的却是(USBD_MSC_BOT_HandleTypeDef),导致判断对象出错,一直被误判为USBD_BUSY.
之所以使用串口调试助手重现打开串口可以继续使用,原因也同上,在进行 打开/关闭 串口操作时,根据串口调试助手软件的不同,会下发不同的协议帧,但共同点是最终会通过USBD_MC_Setup里面的
MC_Switch_CDC(dev)将pClassData切换到USBD_CDC_HandleTypeDef类型的变量,这个时候就正常了.
补充一下第二个问题的解决办法,将MC_Switch_CDC函数的静态修饰去掉,修改CDC_Transmit_FS函数如下即可
uint8_t result = USBD_OK;
/* USER CODE BEGIN 7 */
extern void MC_Switch_CDC(USBD_HandleTypeDef *pdev);
MC_Switch_CDC(&hUsbDeviceFS);
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
if (hcdc->TxState != 0){
return USBD_BUSY;
}
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
/* USER CODE END 7 */
return result;
现在还有一个问题,在不对U盘进行操作的时候,每秒会有一条SCSI_TEST_UNIT_READY指令,这条指令会导致每秒进行一次进行一次MC_Switch_MSC(pdev)函数,但为什么这个时候CDC还能用,是在哪个地方进行的CDC的切换的.我还没找到,-.-,后续填坑吧
找到CDC能用的原因了,如下图,数据回传的时候会通过USBD_MC_DataIn函数进行切换,如下图所.
那么问题又来了,在设备主动上传信息的时候,主机为什么知道我要上传信息,USB通讯里面所有的信息传输都是由主机发起的,那么主机如何知道我要发送数据呢,这个就不填了,不影响使用,后续有机会可以再去学习一下CDC的协议.
推荐参考
- USB Made Simple
- 官方培训资料
- STM32复合设备编写
- STM32F1XX参考手册
- TeenyDT
2021/09/01记录
最近做了一个CDC+MSC的USB HOST,对USB所有工作机制有了更全面的了解,补充一下第一个问题的解决思路.
- 问题:读写大文件的时候,CDC会堵塞. 原因在上面的笔记中也记录了.
解决思路:
- 首先要有一个RTOS
- 在usbd_composite.c中将真正执行收发数据的地方改为信号量(邮箱、消息队列都可以)通知
- 如果使用了N个类做复合设备,则创建N个任务线程做等待对应的通知,根据通知的不同做对应的动作处理.
这个思路就是RTTTHREAD的USBD内核的处理思路.
具体可以参考RTT的USBD的实现,中断里接收完之后,通过消息队列通知USBD_CORE_THREAD_ENTRY(忘了名字了,好像是这个名字)进行逻辑处理,之后该线程根据接受数据不同通过初始化注册好的一套套链表去找到对应的类去执行对应的处理.