cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4R9: Switching from USB Device to USB Host fails

TJvV
Associate

Hi,

 

I'm using an STM32L4R9 on a custom board with USB.

The project has the Host Only and Device Only implementations of the CDC Device and a MSC Host stacks.

As we don't have a USB-ID pin, we can't really use the normal OTG behaviour, so the project is configured for Host Only mode.

In my default FreeRTOS task I start out in one mode, and after being disconnected for a certain timeout, I will switch to the other mode, to see if that one does find a device/host.

 

The default task looks a bit like this:

void StartDefaultTask(void *argument)
{
  appState.isUSBDEVICE = false;
  MX_USB_HOST_Init();

  for(;;){
    if (appState.isUSBDEVICE) {
      // Running CDC Device stack
      if ((hUsbDeviceFS.dev_state == USBD_STATE_DEFAULT) || ((hUsbDeviceFS.dev_state == USBD_STATE_SUSPENDED))) {
        // No active connection
        if( timeout ){
          USBD_Stop(&hUsbDeviceFS);
          USBD_DeInit(&hUsbDeviceFS);

          appState.isUSBDEVICE = false;
          MX_USB_HOST_Init();
        }
      }
    }
    else {
      // Running MSC Host stack
      if( hUsbHostFS.gState == HOST_IDLE){
        // No active connection
        if( timeout ){
          USBH_Stop(&hUsbHostFS);
          USBH_DeInit(&hUsbHostFS);

          appState.isUSBDEVICE = true;
          MX_USB_DEVICE_Init();
        }
      }
    }
  }

 

What I have noticed here, is that the CDC Device stack seems to work pretty reliably with some mode switches in between.

The MSC Host however, does not get any interrupts after the first iteration.

Digging through the stack with my debugger, I stumbled upon a call in HAL_StatusTypeDef HAL_HCD_Init(HCD_HandleTypeDef *hhcd): USB_SetCurrentMode(hhcd->Instance, USB_HOST_MODE);

This call keeps returning HAL_ERROR after the first time.

 

I saw in the Reference Manual a note about the OTG_GUSBCFG register:

This register can be used to configure the core after power-on or a changing to host mode
or device mode. It contains USB and USB-PHY related configuration parameters. The
application must program this register before starting any transactions on either the AHB or
the USB. Do not make changes to this register after the initial programming.

 

But I didn't find anything about how to reset the peripheral beyond the USB_CoreReset(USBx) call that's already done before trying to switch the mode.

 

What am I missing for properly switching back to HOST mode?

 

 

 

4 REPLIES 4
Imen.D
ST Employee

Hello @TJvV,

You need to stop ("xxxDeinit") the stack in one mode and switch the mode.

Then, start the whole stack of the other mode, when the ID pin state changes.

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen

Hi, @Imen.D 

Thank you for your reply.

I thought my calls to

USBH_Stop(&hUsbHostFS);
USBH_DeInit(&hUsbHostFS);

 and

USBD_Stop(&hUsbDeviceFS);
USBD_DeInit(&hUsbDeviceFS);

were the functions you are talking about for stopping the stack?

Are there other stop functions I'm not aware of?

 

Also, as I mentioned, I have no ID pin in use (there's also none on the connector used).

Do I need to manually emulate the ID pin (by writing GPIO)?

 

Taul
Associate

Hi,
Yep it's been a long time, but I am currently having the same issue.
Basically I can't switch from USB Device mode to USB Host mode on the STM32F401 using the code below.

 

/* Configure USB Device mode */
USBH_Stop(&hUsbHostFS);
USBH_DeInit(&hUsbHostFS);
MX_USB_DEVICE_Init();

HAL_Delay(5000);

/* Configure USB Host mode */
MX_USB_DEVICE_Deinit();
MX_USB_HOST_Init();

 


In fact, I always end up  having a Timeout error when the "USB_SetCurrentMode(USB_OTG_GlobalTypeDef *USBx, USB_OTG_ModeTypeDef mode)" is being called (see my comment at the bottom of the code snippet below):

 

/**
  * @brief  USB_SetCurrentMode Set functional mode
  *   USBx  Selected device
  *   mode  current core mode
  *          This parameter can be one of these values:
  *            @arg USB_DEVICE_MODE Peripheral mode
  *            @arg USB_HOST_MODE Host mode
  * @retval HAL status
  */
HAL_StatusTypeDef USB_SetCurrentMode(USB_OTG_GlobalTypeDef *USBx, USB_OTG_ModeTypeDef mode)
{
  uint32_t ms = 0U;

  USBx->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD);

  if (mode == USB_HOST_MODE) {
    USBx->GUSBCFG |= USB_OTG_GUSBCFG_FHMOD;

    do {
      HAL_Delay(10U);
      ms += 10U;
    } while ((USB_GetMode(USBx) != (uint32_t)USB_HOST_MODE) && (ms < HAL_USB_CURRENT_MODE_MAX_DELAY_MS));
  }
  else if (mode == USB_DEVICE_MODE) {
    USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD;

    do {
      HAL_Delay(10U);
      ms += 10U;
    } while ((USB_GetMode(USBx) != (uint32_t)USB_DEVICE_MODE) && (ms < HAL_USB_CURRENT_MODE_MAX_DELAY_MS));
  }
  else {
    return HAL_ERROR;
  }

  if (ms == HAL_USB_CURRENT_MODE_MAX_DELAY_MS) {
    return HAL_ERROR;  // <=== Where my program always ends up
  }

  return HAL_OK;
}

 


However, oddly enough, I fixed my problem by simply removing the "MX_USB_DEVICE_Deinit();" from my code, right before calling the MX_USB_HOST_Init();
Now my stm32F401 can switch seamlessly from Host to Device and vice versa and I still don't understand why as I don't Deinit the USB device low layer prior to configuring as a USB host. If someone has an explanation?

 

Also, while debugging, I noted a typo in the "USB_GetMode" function description, located in stm32f4xx_ll_usb.c

Here is what it's written:

 

/**
  * @brief  Returns USB core mode
  *   USBx  Selected device
  * @retval return core mode : Host or Device
  *          This parameter can be one of these values:
  *           0 : Host
  *           1 : Device
  */
uint32_t USB_GetMode(const USB_OTG_GlobalTypeDef *USBx)
{
  return ((USBx->GINTSTS) & 0x1U);
}

 

Actually this function returns 0 when in USB device mode and 1 in USB host mode as per written in the RM0368, page 711, CMOD bit of OTG_FS_GINTSTS register. (The function description indicates the opposite. 0 : Host and 1 : Device)

FBL
ST Employee

Hi @Taul 

First, let me thank you for sharing your experience and report the issues. However, I preferred if you started a new thread to make it more visible.

Second, you mentioned MX_USB_DEVICE_Deinit() as user code. We can't be sure what was generated. Last, the time frame HAL_USB_CURRENT_MODE_MAX_DELAY_MS seems to be not sufficient to switch mode. To poll on GINTSTS in current mode, the wait timing is required to let Core initialize the internal state machine based on provided configuration (host or device)

In general, I would prefer to measure the elapsed time more effectively with ticks.

  if (HAL_GetTick() - startTick >= HAL_USB_CURRENT_MODE_MAX_DELAY_MS) {
    return HAL_TIMEOUT; // Change HAL_ERROR to HAL_TIMEOUT for clarity

Internel ticket 184825 is reported to update the driver. 

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.