Electronic – How to _disable_ USB on STM32F1xx

stm32stm32f10xusbusb device

I have a battery-powered device using an STM32F107. The device works and I am currently working on minimizing its power consumption.

One of the bigger current draws is the USB subsystem itself, using about 8mA when idle. The USB is only used for configuration purposes; it is almost always unnecessary to have it enabled.

I am using ST's USB Library v2.1.0. It communicates as a CDC class Virtual COM Port ("VCP"). I'm using the embedded Full Speed PHY.

Upon power-up I want the USB to initialize and be active for, say, 30 seconds. After this, assuming there is no connection to a host, I want to simply shut down the USB functionality. As if I had never enabled it in the first place.

I don't see anything exposed in the API which would make this possible. There are a few noteworthy functions but they don't seem to be what I need:

  • from the API template: usbDeInit()

    This is intended for custom user code. However, it is called by the device driver function usbd_cdc_DeInit(), which is never actually called, as far as I can see.

  • in the cdc_core library: usbd_cdc_DeInit()

    This is the file that never is called in the driver source. It closes the endpoints, then calls the (previously mentioned) usbDeInit() function. It seems promising; however, it is declared as static in the driver source. I'm hesitant to change the driver library code directly as this will get overwritten when updating ST's driver in the future.

  • from the device_core library: USBD_DeInit()

    This is called by the driver in a few places, but is empty. That is, the function contains no code at all. Again, I'd rather not change ST's driver source.

My initialization routing looks like this:

// Configure USB Clock Source
RCC_OTGFSCLKConfig(RCC_OTGFSCLKSource_PLLVCO_Div3);

// Enable USB Clock
RCC_AHBPeriphClockCmd(USB_AHB_CLK, ENABLE);

// Initialize USB
USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_CDC_cb, &USR_cb);

Of course, if I simply disable the USB clock source then the microcontroller hangs.

I've made sure that the USB_OTG_FS_LOW_PWR_MGMT_SUPPORT switch is enabled in the usb_conf.h file, but the current draw remains. I'm assuming it doesn't go into suspend mode (which, as Ali Chen says, would be the proper solution).

  1. Anybody know how to achieve what I'm looking for?
  2. Is there anything special I need to do at the Host side to tell the device to suspend?

Best Answer

Disabling functional blocks as USB in a modern SoC is not something that is advised to be done on register level by user. The USB IP block contains several sub-blocks and interfaces between them: access fabric, core controller, FIFOs/SIPOs, PHY. All interfaces usually work with some handshake protocols that usually require some presence of clocks on both sides of the interface, so arbitrary mocking with clock control registers might result in deadlocks. Therefore it is strongly advised to use pre-designed functions (as SUSPEND) from software IP to manage USB block. The documentation says, in part:

The OTG_HS interface has the following features: ... - Powersaving features such as system clock stop during USB suspend, switching off of the digital core internal clock domains, PHY and DFIFO power management ...

and

Device suspend When the device detects a suspend condition on the USB, the library stops all the operations and puts the system to suspend state (if low power mode is enabled by in the usb_conf.h file).

Therefore, to accomplish the power savings task, I would strongly advise to use already developed functions from STM USB library.

ADDITION: The STM package seems to have a limitation that the suspend or disconnect modes cannot be forced by user firmware. Here is an idea: After the device function is finished, switch the OTG controller into HOST MODE. But then don't proceed with initialization of the HOST function, or shut the HOST function down. It might result in fully disabled USB block.