cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamic registration of USB Classes (MCU acting as USB DEVICE)

RB4020
Associate II

Hey there!
Can we allocate the USB classes dynamically?
Like, first we Initialize the USB Device stack,  just register the CDC class and add its interface and start the USB Device Core (which pulls up the D+ line)
This we need to do (atleast registering one class) for Host (PC) to recognize our device as USB Composite device with some functionality (Com port)

Then, when USB is inserted, I'll add a 5s delay and then register another class., let's say USB MSC and register its storage. Will it work? If yes, can anyone say the flow of coding?

I'm using classic stm32 usb library for STM32U0 series MCU
Currently I'm doing like this:

-> In main.c

bool isUsbInterrupt = false;

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_I2C2_Init();
MX_RTC_Init();
MX_SPI1_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */

 


/* Initialize USB peripheral*/
// Step 1: Switch the system clock frequency to USB mode
SystemClock_Config_USB();
printf("Switched the clock frequency to USB mode (48MHz)\r\n");

 

// Step 2: Call USB peripheral Initialization Function
MX_USB_PCD_Init();
printf("USB peripheral is initialized\r\n");

 

// Step 3: Call USB Device Initialization function
USB_App_Device_Init();
printf("USB App Device is initialized\r\n");

 

// Step 4: Switch the system clock frequency to Run mode
SystemClock_Config();
printf("Switched the clock frequency to Run Mode (16MHz)\r\n");

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while(1)
{
if(isUsbInterrupt)
{

SystemClock_Config_USB();
printf("Switched the clock frequency to USB mode (48MHz)\r\n");

HAL_Delay(5000):

/* Tell me what to do here? *?


}
else{
SystemClock_Config();
printf("Switched the clock frequency to Run Mode (16MHz)\r\n");
}

}
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

/**
* @brief USB Initialization Function
* @PAram None
* @retval None
*/
static void MX_USB_PCD_Init(void)
{

/* USER CODE BEGIN USB_Init 0 */

hpcd_USB_DRD_FS.pData = &hUsbDeviceFS;

/* USER CODE END USB_Init 0 */

/* USER CODE BEGIN USB_Init 1 */

/* USER CODE END USB_Init 1 */
hpcd_USB_DRD_FS.Instance = USB_DRD_FS;
hpcd_USB_DRD_FS.Init.dev_endpoints = 8;
hpcd_USB_DRD_FS.Init.speed = USBD_FS_SPEED;
hpcd_USB_DRD_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd_USB_DRD_FS.Init.Sof_enable = DISABLE;
hpcd_USB_DRD_FS.Init.low_power_enable = DISABLE;
hpcd_USB_DRD_FS.Init.lpm_enable = DISABLE;
hpcd_USB_DRD_FS.Init.battery_charging_enable = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_DRD_FS) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USB_Init 2 */

/* USER CODE END USB_Init 2 */
}

In stm32u0xx_hal_msp.c:

/**
 * @brief PCD MSP Initialization
 * This function configures the hardware resources used in this example
 * @PAram hpcd: PCD handle pointer
 * @retval None
 */
void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)
{
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if (hpcd->Instance == USB_DRD_FS)
  {
    /* USER CODE BEGIN USB_DRD_FS_MspInit 0 */

    /* USER CODE END USB_DRD_FS_MspInit 0 */

    /** Initializes the peripherals clocks
     */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
    PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_MSI;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* Enable VDDUSB */
    HAL_PWREx_EnableVddUSB();
    /* Peripheral clock enable */
    __HAL_RCC_USB_CLK_ENABLE();
    /* USB_DRD_FS interrupt Init */
    HAL_NVIC_SetPriority(USB_DRD_FS_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USB_DRD_FS_IRQn);
    /* USER CODE BEGIN USB_DRD_FS_MspInit 1 */

    /* USER CODE END USB_DRD_FS_MspInit 1 */
  }
}

In my_src.c

In my_src.c

/* Variables */
uint8_t CDC_EpAdd_Inst[3] = {CDC_IN_EP, CDC_OUT_EP, CDC_CMD_EP}; // CDC Endpoint Addresses array
uint8_t MSC_EpAdd_Inst[2] = {MSC_EPIN_ADDR, MSC_EPOUT_ADDR}; // MSC Endpoint Addresses array
USBD_HandleTypeDef hUsbDeviceFS;
uint8_t CDC_InstID = 0, MSC_InstID = 0;

/* Extern variables */
extern USBD_DescriptorsTypeDef Class_Desc;

/* Initialization of USB device */
void USB_App_Device_Init(void)
{
/* Initialize the USB Device Library */
if (USBD_Init(&hUsbDeviceFS, &Class_Desc, 0) != USBD_OK)
{
printf("USB Device Library initialization is unsuccessful\r\n");
return;
}

/* Store CDC Instance Class ID */
CDC_InstID = hUsbDeviceFS.classId;

/* Register CDC Class First Instance */
if (USBD_RegisterClassComposite(&hUsbDeviceFS, USBD_CDC_CLASS, CLASS_TYPE_CDC, CDC_EpAdd_Inst) != USBD_OK)
{
printf("CDC Class First Instance registration is unsuccessful\r\n");
return;
}

/* Store MSC Instance Class ID */
MSC_InstID = hUsbDeviceFS.classId;

/* Register the MSC Class */
if (USBD_RegisterClassComposite(&hUsbDeviceFS, USBD_MSC_CLASS, CLASS_TYPE_MSC, MSC_EpAdd_Inst) != USBD_OK)
{
printf("MSC Class registration is unsuccessful\r\n");
return;
}

/* Add CDC Interface Class */
if (USBD_CMPSIT_SetClassID(&hUsbDeviceFS, CLASS_TYPE_CDC, 0) != 0xFF)
{
if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_CDC_Template_fops) != USBD_OK)
{
printf("CDC Interface Class is not added successfully\r\n");
return;
}
}
else
{
printf("Composite Class ID for CDC is not set successfully\r\n");
return;
}

/* Add MSC Storage Class */
if (USBD_CMPSIT_SetClassID(&hUsbDeviceFS, CLASS_TYPE_MSC, 0) != 0xFF)
{
if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_MSC_Template_fops) != USBD_OK)
{
printf("MSC Storage Class is not added successfully\r\n");
return;
}
}
else
{
printf("Composite Class ID for MSC is not set successfully\r\n");
return;
}

/* Start the USB Device Core */
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
printf("USB Device core is not started successfully\r\n");
return;
}

return;
}

In usbd_conf.c

void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
{
  // printf("USB interrupt occurred (inserted)\r\n");
  isUsbInterrupt = true;
  USBD_LL_DevConnected((USBD_HandleTypeDef*)hpcd->pData);
}

void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
{
  if(USBD_LL_DevDisconnected((USBD_HandleTypeDef*)hpcd->pData) != USBD_OK)
  {
    printf("Error occurred in HAL_PCD_DisconnectCallback()\r\n");
    return;
  }
}

void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
{
  // printf("USB interrupt occurred (removed)\r\n");
  isUsbInterrupt = false;
  USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData);
}

void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
{
  // printf("USB interrupt occurred (inserted)\r\n");
  isUsbInterrupt = true;
  USBD_LL_Resume((USBD_HandleTypeDef*)hpcd->pData);
}

In usbd_core.c

/**
* @brief USBD_LL_Suspend
* Handle Suspend event
* @PAram pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev)
{
if (pdev->dev_state != USBD_STATE_SUSPENDED)
{
pdev->dev_old_state = pdev->dev_state;
}

pdev->dev_state = USBD_STATE_SUSPENDED;

return USBD_OK;
}

/**
* @brief USBD_LL_Resume
* Handle Resume event
* @PAram pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev)
{
if (pdev->dev_state == USBD_STATE_SUSPENDED)
{
pdev->dev_old_state = pdev->dev_state;
//pdev->dev_state = pdev->dev_old_state;
}

return USBD_OK;
}

/**
* @brief USBD_LL_DevConnected
* Handle device connection event
* @PAram pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev)
{
/* Prevent unused argument compilation warning */
// UNUSED(pdev);

pdev->dev_old_state = pdev->dev_state;

return USBD_OK;
}

The above functions are modified by me. I mean, previously just return USBD_OK; used to be there. But I modified them Like this. (donno whether I modified correctly)

Now, these below functions, I didn't modify
/**
* @brief USBD_LL_DevDisconnected
* Handle device disconnection event
* @PAram pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev)
{
USBD_StatusTypeDef ret = USBD_OK;

/* Free Class Resources */
pdev->dev_state = USBD_STATE_DEFAULT;

#ifdef USE_USBD_COMPOSITE
/* Parse the table of classes in use */
for (uint32_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++)
{
/* Check if current class is in use */
if ((pdev->tclasslist[i].Active) == 1U)
{
if (pdev->pClass[i] != NULL)
{
pdev->classId = i;
/* Clear configuration and De-initialize the Class process*/
if (pdev->pClass[i]->DeInit(pdev, (uint8_t)pdev->dev_config) != 0U)
{
ret = USBD_FAIL;
}
}
}
}
#else
if (pdev->pClass[0] != NULL)
{
if (pdev->pClass[0]->DeInit(pdev, (uint8_t)pdev->dev_config) != 0U)
{
ret = USBD_FAIL;
}
}
#endif /* USE_USBD_COMPOSITE */

return ret;
}

/**
* @brief USBD_LL_Reset
* Handle Reset event
* @PAram pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev)
{
USBD_StatusTypeDef ret = USBD_OK;

/* Upon Reset call user call back */
pdev->dev_state = USBD_STATE_DEFAULT;
pdev->ep0_state = USBD_EP0_IDLE;
pdev->dev_config = 0U;
pdev->dev_remote_wakeup = 0U;
pdev->dev_test_mode = 0U;

#ifdef USE_USBD_COMPOSITE
/* Parse the table of classes in use */
for (uint32_t i = 0U; i < USBD_MAX_SUPPORTED_CLASS; i++)
{
/* Check if current class is in use */
if ((pdev->tclasslist[i].Active) == 1U)
{
if (pdev->pClass[i] != NULL)
{
pdev->classId = i;
/* Clear configuration and De-initialize the Class process*/

if (pdev->pClass[i]->DeInit != NULL)
{
if (pdev->pClass[i]->DeInit(pdev, (uint8_t)pdev->dev_config) != USBD_OK)
{
ret = USBD_FAIL;
}
}
}
}
}
#else

if (pdev->pClass[0] != NULL)
{
if (pdev->pClass[0]->DeInit != NULL)
{
if (pdev->pClass[0]->DeInit(pdev, (uint8_t)pdev->dev_config) != USBD_OK)
{
ret = USBD_FAIL;
}
}
}
#endif /* USE_USBD_COMPOSITE */

/* Open EP0 OUT */
(void)USBD_LL_OpenEP(pdev, 0x00U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE);
pdev->ep_out[0x00U & 0xFU].is_used = 1U;

pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE;

/* Open EP0 IN */
(void)USBD_LL_OpenEP(pdev, 0x80U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE);
pdev->ep_in[0x80U & 0xFU].is_used = 1U;

pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE;

return ret;
}






1 REPLY 1
FBL
ST Employee

Hi @RB4020 

You can use composite builder to register 2 classes within 2 instances.  Your device will be enumerated with two interfaces CDC and MSC classes. Check this example How to implement a dual CDC ACM USB device using t... - STMicroelectronics Community

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.


I'm out of offce with limited access to my emails.
Happy New Year!