¿Cómo _deshabilitar_ USB en STM32F1xx?

Tengo un dispositivo alimentado por batería que usa un STM32F107 . El dispositivo funciona y actualmente estoy trabajando para minimizar su consumo de energía.

Uno de los mayores consumos de corriente es el propio subsistema USB, que utiliza alrededor de 8 mA cuando está inactivo. El USB solo se utiliza para fines de configuración; casi siempre es innecesario tenerlo habilitado.

Estoy usando la biblioteca USB de ST v2.1.0 . Se comunica como un puerto COM virtual de clase CDC ("VCP"). Estoy usando el PHY de velocidad completa incorporado.

Al encender, quiero que el USB se inicialice y esté activo durante, digamos, 30 segundos. Después de esto, suponiendo que no haya conexión a un host, simplemente quiero cerrar la funcionalidad USB. Como si nunca lo hubiera habilitado en primer lugar.

No veo nada expuesto en la API que lo haga posible. Hay algunas funciones notables, pero no parecen ser lo que necesito:

  • de la plantilla API:usbDeInit()

    Esto está destinado al código de usuario personalizado. Sin embargo, es llamado por la función del controlador del dispositivo usbd_cdc_DeInit(), que en realidad nunca se llama, por lo que puedo ver.

  • en la biblioteca cdc_core:usbd_cdc_DeInit()

    Este es el archivo que nunca se llama en la fuente del controlador. Cierra los puntos finales, luego llama a la usbDeInit()función (mencionada anteriormente). Parece prometedor; sin embargo, se declara como staticen la fuente del controlador. Dudo en cambiar el código de la biblioteca del controlador directamente, ya que se sobrescribirá al actualizar el controlador de ST en el futuro.

  • de la biblioteca device_core:USBD_DeInit()

    Esto lo llama el conductor en algunos lugares, pero está vacío. Es decir, la función no contiene código en absoluto. Nuevamente, prefiero no cambiar la fuente del controlador de ST.

Mi enrutamiento de inicialización se ve así:

// 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);

Por supuesto, si simplemente desactivo la fuente de reloj USB, el microcontrolador se cuelga.

Me aseguré de que el USB_OTG_FS_LOW_PWR_MGMT_SUPPORTinterruptor esté habilitado en el archivo usb_conf.h, pero el dibujo actual permanece. Supongo que no entra en modo de suspensión (que, como dice Ali Chen, sería la solución adecuada).

  1. ¿Alguien sabe cómo lograr lo que estoy buscando?
  2. ¿Hay algo especial que deba hacer en el lado del Host para decirle al dispositivo que se suspenda?
¿Por qué no desea utilizar la función SUSPENDER? Debe haber una API de administración de energía estándar. Cada USB en estos días está diseñado desde la perspectiva de la administración de energía, en primer lugar. La función de suspensión es exactamente la función que necesita, debe detener el reloj PHY y el reloj del controlador correctamente.
Los periféricos STM32 suelen tener un bit de administración de energía y un bit de habilitación de reloj. Desactive cualquier componente de software que se queje, anule la afirmación de estas habilitaciones según el manual del programador o cualquier HAL que esté utilizando, y si puede deshabilitar la detección de presencia/la extracción de velocidad. Charlar con el anfitrión sobre lo que planeas hacer es bastante opcional... podría parecer que estás desconectado. Por supuesto, también puede iniciar el bloque USB solo si ve Vbus.
Gracias, @ChrisStratton, me gusta la idea de "buscar Vbus".

Respuestas (2)

Deshabilitar bloques funcionales como USB en un SoC moderno no es algo que se recomiende hacer a nivel de registro por parte del usuario. El bloque USB IP contiene varios subbloques e interfaces entre ellos: tejido de acceso, controlador central, FIFO/SIPO, PHY. Todas las interfaces generalmente funcionan con algunos protocolos de negociación que generalmente requieren la presencia de relojes en ambos lados de la interfaz, por lo que la burla arbitraria con los registros de control del reloj puede provocar interbloqueos. Por lo tanto, se recomienda encarecidamente utilizar funciones prediseñadas (como SUSPEND) del software IP para gestionar el bloqueo de USB. La documentación dice, en parte:

La interfaz OTG_HS tiene las siguientes características: ... - Funciones de ahorro de energía, como la parada del reloj del sistema durante la suspensión del USB, el apagado de los dominios del reloj interno del núcleo digital, la administración de energía PHY y DFIFO...

y

Suspensión del dispositivo Cuando el dispositivo detecta una condición de suspensión en el USB, la biblioteca detiene todas las operaciones y pone el sistema en estado de suspensión (si el modo de bajo consumo está habilitado en el archivo usb_conf.h).

Por lo tanto, para lograr la tarea de ahorro de energía, recomendaría encarecidamente utilizar funciones ya desarrolladas de la biblioteca STM USB.

ADICIÓN: El paquete STM parece tener la limitación de que el firmware del usuario no puede forzar los modos de suspensión o desconexión. Aquí hay una idea: después de que finalice la función del dispositivo, cambie el controlador OTG al MODO HOST. Pero luego no continúe con la inicialización de la función HOST, ni apague la función HOST. Podría resultar en un bloqueo USB completamente deshabilitado.

Gracias, y estoy de acuerdo contigo. No veo ningún lugar en la API para forzar el modo de suspensión; parece ser impulsado por el host. Habilité el USB_OTG_FS_LOW_PWR_MGMT_SUPPORTinterruptor en el archivo usb_conf.h, pero el sorteo actual aún existe. ¿Debería el usuario poder forzar el modo Suspender?
Tiene razón, la función de suspensión normal debe ocurrir automáticamente en 3 ms después de que el enlace ascendente deje de tener actividad USB (flujo de SOF). Sin embargo, debe haber una función de desconexión forzada en el lado del dispositivo. No soy un tipo de software, solo quiero advertirle sobre las complejidades generales del reloj y la activación de energía en el bloque USB IP.
Sí, tampoco veo la función "forzar desconexión" en el paquete. Demasiado. Por lo general, la función de desconexión forzada se usa cuando un desarrollador necesita cambiar las funciones del dispositivo, por ejemplo, del modo de depuración al modo normal, o después de cargar/actualizar el firmware del dispositivo, con cambios en VID/PID. Luego, debe crear esta función, que será un desafío, ya que los detalles finos de las interfaces de registro generalmente están protegidos y asegurados por el fabricante.
Algunos diseños implementan "desconexión forzada" con interruptores analógicos que abren las líneas de datos fuera del chip.

Probablemente necesite deshabilitar todo en orden inverso. Intentar:

USBD_DeInit();
RCC_AHBPeriphClockCmd(USB_AHB_CLK, ENABLE);

O tal vez al revés.

La API para STM32 contiene funciones especiales para poner el controlador USB y PHY en SUSPENDER . Esta es precisamente la función que "deshabilita todo" en el orden correcto, sin "o" o "tal vez". Hackear esta funcionalidad a nivel de registro es muy imprudente.