Merge branch 'docs/usb-docs-link-esp-usb' into 'master'

docs(usb): Move USB docs to esp-usb and add redirects

Closes IDF-13559

See merge request espressif/esp-idf!45997
This commit is contained in:
Tomas Rezucha
2026-03-11 11:41:52 +01:00
39 changed files with 47 additions and 3746 deletions
-2
View File
@@ -177,8 +177,6 @@
/docs/**/api-reference/bluetooth/ @esp-idf-codeowners/bluetooth /docs/**/api-reference/bluetooth/ @esp-idf-codeowners/bluetooth
/docs/**/api-reference/network/ @esp-idf-codeowners/network @esp-idf-codeowners/wifi /docs/**/api-reference/network/ @esp-idf-codeowners/network @esp-idf-codeowners/wifi
/docs/**/api-reference/peripherals/ @esp-idf-codeowners/peripherals /docs/**/api-reference/peripherals/ @esp-idf-codeowners/peripherals
/docs/**/api-reference/peripherals/usb* @esp-idf-codeowners/peripherals @esp-idf-codeowners/peripherals/usb
/docs/**/api-reference/peripherals/usb*/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/peripherals/usb
/docs/**/api-reference/protocols/ @esp-idf-codeowners/network @esp-idf-codeowners/app-utilities /docs/**/api-reference/protocols/ @esp-idf-codeowners/network @esp-idf-codeowners/app-utilities
/docs/**/api-reference/provisioning/ @esp-idf-codeowners/app-utilities/provisioning /docs/**/api-reference/provisioning/ @esp-idf-codeowners/app-utilities/provisioning
/docs/**/api-reference/storage/ @esp-idf-codeowners/storage /docs/**/api-reference/storage/ @esp-idf-codeowners/storage
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

+19 -14
View File
@@ -154,19 +154,6 @@ TOUCH_SENSOR_DOCS = ['api-reference/peripherals/cap_touch_sens.rst']
SPIRAM_DOCS = ['api-guides/external-ram.rst'] SPIRAM_DOCS = ['api-guides/external-ram.rst']
USB_DOCS = [
'api-reference/peripherals/usb_device.rst',
'api-reference/peripherals/usb_host.rst',
'api-reference/peripherals/usb_host/usb_host_notes_arch.rst',
'api-reference/peripherals/usb_host/usb_host_notes_design.rst',
'api-reference/peripherals/usb_host/usb_host_notes_dwc_otg.rst',
'api-reference/peripherals/usb_host/usb_host_notes_index.rst',
'api-reference/peripherals/usb_host/usb_host_notes_usbh.rst',
'api-reference/peripherals/usb_host/usb_host_notes_enum.rst',
'api-reference/peripherals/usb_host/usb_host_notes_ext_hub.rst',
'api-reference/peripherals/usb_host/usb_host_notes_ext_port.rst',
]
I80_LCD_DOCS = ['api-reference/peripherals/lcd/i80_lcd.rst'] I80_LCD_DOCS = ['api-reference/peripherals/lcd/i80_lcd.rst']
RGB_LCD_DOCS = ['api-reference/peripherals/lcd/rgb_lcd.rst'] RGB_LCD_DOCS = ['api-reference/peripherals/lcd/rgb_lcd.rst']
DSI_LCD_DOCS = ['api-reference/peripherals/lcd/dsi_lcd.rst'] DSI_LCD_DOCS = ['api-reference/peripherals/lcd/dsi_lcd.rst']
@@ -361,7 +348,6 @@ conditional_include_dict = {
'SOC_SDMMC_HOST_SUPPORTED': SDMMC_DOCS, 'SOC_SDMMC_HOST_SUPPORTED': SDMMC_DOCS,
'SOC_SDIO_SLAVE_SUPPORTED': SDIO_SLAVE_DOCS, 'SOC_SDIO_SLAVE_SUPPORTED': SDIO_SLAVE_DOCS,
'SOC_MCPWM_SUPPORTED': MCPWM_DOCS, 'SOC_MCPWM_SUPPORTED': MCPWM_DOCS,
'SOC_USB_OTG_SUPPORTED': USB_DOCS,
'SOC_USB_SERIAL_JTAG_SUPPORTED': USB_SERIAL_JTAG_DOCS, 'SOC_USB_SERIAL_JTAG_SUPPORTED': USB_SERIAL_JTAG_DOCS,
'SOC_DEDICATED_GPIO_SUPPORTED': DEDIC_GPIO_DOCS, 'SOC_DEDICATED_GPIO_SUPPORTED': DEDIC_GPIO_DOCS,
'SOC_LCD_I80_SUPPORTED': I80_LCD_DOCS, 'SOC_LCD_I80_SUPPORTED': I80_LCD_DOCS,
@@ -512,6 +498,21 @@ QEMU_TARGETS = ['esp32', 'esp32c3', 'esp32s3']
ESP_TEE_TARGETS = ['esp32c6', 'esp32h2', 'esp32c5', 'esp32c61'] ESP_TEE_TARGETS = ['esp32c6', 'esp32h2', 'esp32c5', 'esp32c61']
def _resolve_redirect_page_macros(redirect_pages, target, language):
replace_map = {
'{IDF_TARGET_PATH_NAME}': target,
'{IDF_DOCS_LANGUAGE}': language,
}
resolved_redirect_pages = []
for old_url, new_url in redirect_pages:
for macro, value in replace_map.items():
new_url = new_url.replace(macro, value)
resolved_redirect_pages.append((old_url, new_url))
return resolved_redirect_pages
# Callback function for user setup that needs be done after `config-init`-event # Callback function for user setup that needs be done after `config-init`-event
# config.idf_target is not available at the initial config stage # config.idf_target is not available at the initial config stage
def conf_setup(app, config): def conf_setup(app, config):
@@ -534,6 +535,10 @@ def conf_setup(app, config):
# Not for all target # Not for all target
pass pass
config.html_redirect_pages = _resolve_redirect_page_macros(
config.html_redirect_pages, config.idf_target, config.language
)
config.html_baseurl = f'https://docs.espressif.com/projects/esp-idf/{config.language}/stable/{config.idf_target}' config.html_baseurl = f'https://docs.espressif.com/projects/esp-idf/{config.language}/stable/{config.idf_target}'
-7
View File
@@ -22,13 +22,6 @@ api-reference/storage/mass_mfg.rst
api-reference/storage/fatfsgen.rst api-reference/storage/fatfsgen.rst
api-reference/storage/index.rst api-reference/storage/index.rst
api-reference/storage/nvs_partition_parse.rst api-reference/storage/nvs_partition_parse.rst
api-reference/peripherals/usb_host.rst
api-reference/peripherals/usb_host/usb_host_notes_arch.rst
api-reference/peripherals/usb_host/usb_host_notes_index.rst
api-reference/peripherals/usb_host/usb_host_notes_dwc_otg.rst
api-reference/peripherals/usb_host/usb_host_notes_usbh.rst
api-reference/peripherals/usb_host/usb_host_notes_design.rst
api-reference/peripherals/usb_device.rst
api-reference/peripherals/sd_pullup_requirements.rst api-reference/peripherals/sd_pullup_requirements.rst
api-reference/network/esp_eth.rst api-reference/network/esp_eth.rst
api-reference/network/esp_netif_driver.rst api-reference/network/esp_netif_driver.rst
-10
View File
@@ -105,8 +105,6 @@ api-reference/peripherals/cap_touch_sens.rst
api-reference/peripherals/index.rst api-reference/peripherals/index.rst
api-reference/peripherals/sdio_slave.rst api-reference/peripherals/sdio_slave.rst
api-reference/peripherals/temp_sensor.rst api-reference/peripherals/temp_sensor.rst
api-reference/peripherals/usb_device.rst
api-reference/peripherals/usb_host.rst
api-reference/peripherals/camera_driver.rst api-reference/peripherals/camera_driver.rst
api-reference/peripherals/adc_oneshot.rst api-reference/peripherals/adc_oneshot.rst
api-reference/peripherals/sdspi_share.rst api-reference/peripherals/sdspi_share.rst
@@ -115,14 +113,6 @@ api-reference/peripherals/adc_continuous.rst
api-reference/peripherals/sdspi_host.rst api-reference/peripherals/sdspi_host.rst
api-reference/peripherals/vad.rst api-reference/peripherals/vad.rst
api-reference/peripherals/isp.rst api-reference/peripherals/isp.rst
api-reference/peripherals/usb_host/usb_host_notes_usbh.rst
api-reference/peripherals/usb_host/usb_host_notes_ext_hub.rst
api-reference/peripherals/usb_host/usb_host_notes_arch.rst
api-reference/peripherals/usb_host/usb_host_notes_index.rst
api-reference/peripherals/usb_host/usb_host_notes_ext_port.rst
api-reference/peripherals/usb_host/usb_host_notes_design.rst
api-reference/peripherals/usb_host/usb_host_notes_enum.rst
api-reference/peripherals/usb_host/usb_host_notes_dwc_otg.rst
api-reference/peripherals/parlio.rst api-reference/peripherals/parlio.rst
api-reference/peripherals/adc_calibration.rst api-reference/peripherals/adc_calibration.rst
api-reference/peripherals/lp_i2s.rst api-reference/peripherals/lp_i2s.rst
+2 -2
View File
@@ -50,7 +50,7 @@ Peripherals API
:SOC_TOUCH_SENSOR_SUPPORTED: cap_touch_sens :SOC_TOUCH_SENSOR_SUPPORTED: cap_touch_sens
:SOC_TWAI_SUPPORTED: twai :SOC_TWAI_SUPPORTED: twai
uart uart
:SOC_USB_OTG_SUPPORTED: usb_device :SOC_USB_OTG_SUPPORTED: USB Device Stack <https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_device.html>
:SOC_USB_OTG_SUPPORTED: usb_host :SOC_USB_OTG_SUPPORTED: USB Host <https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host.html>
Code examples for this API section are provided in the :example:`peripherals` directory of ESP-IDF examples. Code examples for this API section are provided in the :example:`peripherals` directory of ESP-IDF examples.
@@ -1,470 +0,0 @@
USB Device Stack
=================
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_USB_DP_GPIO_NUM:default="20", esp32h4="22"}
{IDF_TARGET_USB_DM_GPIO_NUM:default="19", esp32h4="21"}
{IDF_TARGET_USB_EP_NUM: default="6", esp32p4="15"}
{IDF_TARGET_USB_EP_NUM_INOUT:default="5", esp32p4="8"}
{IDF_TARGET_USB_EP_NUM_IN:default="1", esp32p4="7"}
Overview
--------
The ESP-IDF USB Device Stack (hereinafter referred to as the Device Stack) enables USB Device support on {IDF_TARGET_NAME}. By using the Device Stack, {IDF_TARGET_NAME} can be programmed with any well defined USB device functions (e.g., keyboard, mouse, camera), a custom function (aka vendor-specific class), or a combination of those functions (aka a composite device).
The Device Stack is built around the TinyUSB stack, but extends TinyUSB with some minor features and modifications for better integration with ESP-IDF. The Device stack is distributed as a managed component via the `ESP Component Registry <https://components.espressif.com/components/espressif/esp_tinyusb>`__.
Features
--------
- Multiple supported device classes (CDC, HID, MIDI, MSC)
- Composite devices
- Vendor specific classes
- Maximum of {IDF_TARGET_USB_EP_NUM} endpoints
- {IDF_TARGET_USB_EP_NUM_INOUT} IN/OUT endpoints
- {IDF_TARGET_USB_EP_NUM_IN} IN endpoints
- VBUS monitoring for self-powered devices
.. Todo: Refactor USB hardware connect into a separate guide
Hardware Connection
-------------------
.. only:: esp32s2 or esp32s3 or esp32h4
The {IDF_TARGET_NAME} routes the USB D+ and D- signals to GPIOs {IDF_TARGET_USB_DP_GPIO_NUM} and {IDF_TARGET_USB_DM_GPIO_NUM} respectively. For USB device functionality, these GPIOs should be connected to the bus in some way (e.g., via a Micro-B port, USB-C port, or directly to standard-A plug).
.. only:: esp32p4
The {IDF_TARGET_NAME} routes the USB D+ and D- signals to their dedicated pins. For USB device functionality, these pins should be connected to the bus in some way (e.g., via a Micro-B port, USB-C port, or directly to standard-A plug).
.. figure:: ../../../_static/usb-board-connection.png
:align: center
:alt: Connection of an USB GPIOs directly to a USB standard-A plug
:figclass: align-center
.. only:: esp32s2 or esp32s3 or esp32h4
.. note::
If you are using an {IDF_TARGET_NAME} development board with two USB ports, the port labeled "USB" will already be connected to the D+ and D- GPIOs.
.. note::
Self-powered devices must also connect VBUS through a voltage divider or comparator. For more details, please refer to :ref:`self-powered-device`.
.. only:: esp32s3
External PHY Configuration
--------------------------
The {IDF_TARGET_NAME} contains two USB controllers: USB-OTG and USB-Serial-JTAG. However, both controllers share a **single PHY**, which means only one can operate at a time. To use USB Device functionality while the USB-Serial-JTAG is active (e.g., for debugging or flashing), an **external PHY** is required, since the PHY is used by USB-Serial-JTAG.
.. note::
An external PHY is not the only way to enable debugging alongside USB Host or Device functionality. It is also possible to switch the debugging interface from USB-Serial-JTAG to plain JTAG by burning the appropriate eFuses. For details, refer to document :doc:`JTAG Debugging <../../api-guides/jtag-debugging/index>` in ESP-IDF Programming Guide for your target.
{IDF_TARGET_NAME} supports connecting external PHY ICs. This becomes especially relevant when full-speed USB device functionality is needed while the USB-Serial-JTAG controller is also in use. Various external PHY ICs may require different hardware modifications. Please refer to each IC's datasheet for specifics. A general connection diagram below is provided for reference. For more information, please refer to `Use an external PHY <https://docs.espressif.com/projects/esp-iot-solution/en/latest/usb/usb_overview/usb_phy.html#use-an-external-phy>`__.
.. figure:: ../../../_static/usb_device/usb_fs_phy_sp5301.png
:align: center
:alt: usb_fs_phy_sp5301
A typical circuit diagram for an external PHY
**List of Tested External PHY ICs:**
- **SP5301** — Directly supported by {IDF_TARGET_NAME}. See the guide above for schematic and routing information.
- **TUSB1106** — Directly supported by {IDF_TARGET_NAME}. Works with the external-PHY driver via GPIO mapping. Follow the reference wiring in the TUSB1106 datasheet (power-supply options and recommended series resistors on D+/D).
- **STUSB03E** — Requires signal routing using an analog switch. See example below.
.. figure:: ../../../_static/usb_device/ext_phy_schematic_stusb03e.png
:align: center
:alt: External PHY with Analog Switch Schematic (Device mode)
Example connection using STUSB03E and analog switch (Device mode)
.. note::
This schematic is a minimal example intended only to demonstrate the external PHY connection. It omits other essential components and signals (e.g., VCC, GND, RESET) required for a complete, functional {IDF_TARGET_NAME} design.
The schematic includes both a +5 V rail (usually from USB VBUS) and a VCC rail. VCC should match the chip supply voltage (usually 3.3 V). Ensure that the external PHY and the chip are powered from the same voltage domain. If designing a self-powered USB device, connect VBUSDET signal from the external PHY to {IDF_TARGET_NAME} for mandatory VBUS monitoring.
Hardware configuration is handled via GPIO mapping to the PHY's pins. Any unused pins (e.g., :cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num`, :cpp:member:`usb_phy_ext_io_conf_t::fs_edge_sel_io_num`) **must be set to -1**.
.. note::
The :cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num` pin is **currently not supported** and does not need to be connected.
The :cpp:member:`usb_phy_ext_io_conf_t::fs_edge_sel_io_num` pin is optional and only required if switching between low-speed and full-speed modes is needed.
Starting from version 2.0, the ESP TinyUSB Device Stack supports external PHY usage. To use an external PHY in device mode:
1. Configure the GPIO mapping and PHY using :cpp:type:`usb_phy_config_t`.
2. Create the PHY using :cpp:func:`usb_new_phy()`.
3. Use :cpp:func:`TINYUSB_DEFAULT_CONFIG()` to initialize :cpp:type:`tinyusb_config_t`.
4. Set the `phy.skip_setup` field of :cpp:type:`tinyusb_config_t` to ``true`` to bypass PHY reinitialization and use the externally configured PHY.
**Example Code:**
.. code-block:: c
// GPIO configuration for external PHY
const usb_phy_ext_io_conf_t ext_io_conf = {
.vp_io_num = 8,
.vm_io_num = 5,
.rcv_io_num = 11,
.oen_io_num = 17,
.vpo_io_num = 4,
.vmo_io_num = 46,
.suspend_n_io_num = -1,
.fs_edge_sel_io_num = -1,
};
// Configuration and initialization of external PHY for OTG controller (Device mode)
const usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_EXT,
.otg_mode = USB_OTG_MODE_DEVICE,
.otg_speed = USB_PHY_SPEED_FULL,
.ext_io_conf = &ext_io_conf
};
usb_phy_handle_t phy_hdl;
ESP_ERROR_CHECK(usb_new_phy(&phy_config, &phy_hdl));
// Initialize TinyUSB with default configuration (event handler can be set if needed)
tinyusb_config_t config = TINYUSB_DEFAULT_CONFIG();
config.phy.skip_setup = true;
tinyusb_driver_install(&config);
This setup ensures that the USB Device stack uses the **external PHY** instead of attempting to configure the internal one.
Device Stack Structure
----------------------
The basis of the Device Stack is TinyUSB, where the Device Stack implements the following features on top of TinyUSB:
- Customization of USB descriptors
- Serial device support
- Redirecting of standard streams through the Serial device
- Storage Media (SPI-Flash and SD-Card) for USB Device MSC Class.
- A task within the encapsulated device stack that handles TinyUSB servicing
Component Dependency
^^^^^^^^^^^^^^^^^^^^
The Device Stack is distributed via the `ESP Component Registry <https://components.espressif.com/components/espressif/esp_tinyusb>`__. Thus, to use it, please add the Device Stack component as dependency using the following command:
.. code:: bash
idf.py add-dependency esp_tinyusb
Configuration Options
^^^^^^^^^^^^^^^^^^^^^
Multiple aspects of the Device Stack can be configured using menuconfig. These include:
- The verbosity of the TinyUSB's log
- Device Stack task related options
- Default device/string descriptor options
- Class specific options
.. _descriptors-configuration:
Descriptor Configuration
^^^^^^^^^^^^^^^^^^^^^^^^
The :cpp:type:`tinyusb_config_t` structure provides USB descriptor related fields that should be initialized.
The following descriptors should be initialized for both full-speed and high-speed devices:
- :cpp:member:`device_descriptor`
- :cpp:member:`string_descriptor`
Full-speed devices should initialize the following field to provide their configuration descriptor:
- :cpp:member:`configuration_descriptor`
.. only:: esp32p4
High-speed devices should initialize the following fields to provide configuration descriptors at each speed:
- :cpp:member:`fs_configuration_descriptor`
- :cpp:member:`hs_configuration_descriptor`
- :cpp:member:`qualifier_descriptor`
.. note::
Both :cpp:member:`fs_configuration_descriptor` and :cpp:member:`hs_configuration_descriptor` must be present to comply with USB 2.0 specification.
The Device Stack will instantiate a USB device based on the descriptors provided in the fields described above when :cpp:func:`tinyusb_driver_install` is called.
The Device Stack also provides default descriptors that can be installed by setting the corresponding field in :cpp:func:`tinyusb_driver_install` to ``NULL``. Default descriptors include:
- Default device descriptor: Enabled by setting :cpp:member:`device_descriptor` to ``NULL``. Default device descriptor will use the values set by the corresponding menuconfig options (e.g., PID, VID, bcdDevice etc).
- Default string descriptor: Enabled by setting :cpp:member:`string_descriptor` to ``NULL``. Default string descriptors will use the value set by corresponding menuconfig options (e.g., manufacturer, product, and serial string descriptor options).
- Default configuration descriptor. Some classes that rarely require custom configuration (such as CDC and MSC) will provide default configuration descriptors. These can be enabled by setting associated configuration descriptor field to ``NULL``:
- :cpp:member:`configuration_descriptor`: full-speed descriptor for full-speed devices only
- :cpp:member:`fs_configuration_descriptor`: full-speed descriptor for high-speed devices
- :cpp:member:`hs_configuration_descriptor`: high-speed descriptor for high-speed devices
.. note::
For backward compatibility, when Device Stack supports high-speed, the field :cpp:member:`configuration_descriptor` could be used instead of :cpp:member:`fs_configuration_descriptor` for full-speed configuration descriptor.
Installation
------------
To install the Device Stack, please call :cpp:func:`tinyusb_driver_install`. The Device Stack's configuration is specified in a :cpp:type:`tinyusb_config_t` structure that is passed as an argument to :cpp:func:`tinyusb_driver_install`.
.. note::
The :cpp:type:`tinyusb_config_t` structure can be zero-initialized (e.g., ``const tinyusb_config_t tusb_cfg = { 0 };``) or partially (as shown below). For any member that is initialized to ``0`` or ``NULL``, the stack uses its default configuration values for that member, see example below.
.. code-block:: c
const tinyusb_config_t partial_init = {
.device_descriptor = NULL, // Use the default device descriptor specified in Menuconfig
.string_descriptor = NULL, // Use the default string descriptors specified in Menuconfig
.external_phy = false, // Use internal PHY
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = NULL, // Use the default full-speed configuration descriptor according to settings in Menuconfig
.hs_configuration_descriptor = NULL, // Use the default high-speed configuration descriptor according to settings in Menuconfig
.qualifier_descriptor = NULL, // Use the default qualifier descriptor, with values from default device descriptor
#else
.configuration_descriptor = NULL, // Use the default configuration descriptor according to settings in Menuconfig
#endif // TUD_OPT_HIGH_SPEED
};
.. _self-powered-device:
Self-Powered Device
-------------------
USB specification mandates self-powered devices to monitor voltage levels on USB's VBUS signal. As opposed to bus-powered devices, a self-powered device can be fully functional even without a USB connection. The self-powered device detects connection and disconnection events by monitoring the VBUS voltage level. VBUS is considered valid if it rises above 4.75 V and invalid if it falls below 4.35 V.
On the {IDF_TARGET_NAME}, this will require using a GPIO to act as a voltage sensing pin to detect when VBUS goes above/below the prescribed thresholds. However, {IDF_TARGET_NAME} pins are 3.3 V tolerant. Thus, even if VBUS rises/falls above/below the thresholds mentioned above, it would still appear as a logic HIGH to the {IDF_TARGET_NAME}. Thus, in order to detect the VBUS valid condition, users can do one of the following:
- Connect VBUS to a voltage comparator chip/circuit that detects the thresholds described above (i.e., 4.35 V and 4.75 V), and outputs a 3.3 V logic level to the {IDF_TARGET_NAME} indicating whether VBUS is valid or not.
- Use a resistor voltage divider that outputs (0.75 x Vdd) if VBUS is 4.4 V (see figure below).
.. note::
In either case, the voltage on the sensing pin must be logic low within 3 ms after the device is unplugged from the USB host.
.. figure:: ../../../_static/diagrams/usb/usb_vbus_voltage_monitor.png
:align: center
:alt: Simple voltage divider for VBUS monitoring
:figclass: align-center
Simple voltage divider for VBUS monitoring
To use this feature, in :cpp:type:`tinyusb_config_t`, you must set :cpp:member:`self_powered` to ``true`` and :cpp:member:`vbus_monitor_io` to GPIO number that is used for VBUS monitoring.
USB Serial Device (CDC-ACM)
---------------------------
If the CDC option is enabled in Menuconfig, the USB Serial Device can be initialized with :cpp:func:`tusb_cdc_acm_init` according to the settings from :cpp:type:`tinyusb_config_cdcacm_t`, see example below.
.. code-block:: c
const tinyusb_config_cdcacm_t acm_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_0,
.rx_unread_buf_sz = 64,
.callback_rx = NULL,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL
};
tusb_cdc_acm_init(&acm_cfg);
To specify callbacks, you can either set the pointer to your :cpp:type:`tusb_cdcacm_callback_t` function in the configuration structure or call :cpp:func:`tinyusb_cdcacm_register_callback` after initialization.
USB Serial Console
^^^^^^^^^^^^^^^^^^
The USB Serial Device allows the redirection of all standard input/output streams (stdin, stdout, stderr) to USB. Thus, calling standard library input/output functions such as ``printf()`` will result into the data being sent/received over USB instead of UART.
Users should call :cpp:func:`esp_tusb_init_console` to switch the standard input/output streams to USB, and :cpp:func:`esp_tusb_deinit_console` to switch them back to UART.
USB Mass Storage Device (MSC)
-----------------------------
If the MSC ``CONFIG_TINYUSB_MSC_ENABLED`` option is enabled in Menuconfig, the ESP Chip can be used as USB MSC Device. The storage media (SPI-Flash or SD-Card) can be initialized as shown below.
- SPI-Flash
.. code-block:: c
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
{
***
esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
***
wl_mount(data_partition, wl_handle);
***
}
storage_init_spiflash(&wl_handle);
const tinyusb_msc_spiflash_config_t config_spi = {
.wl_handle = wl_handle
};
tinyusb_msc_storage_init_spiflash(&config_spi);
- SD-Card
.. code-block:: c
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
{
***
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
// For SD Card, set bus width to use
slot_config.width = 4;
slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
(*host.init)();
sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config);
sdmmc_card_init(&host, sd_card);
***
}
storage_init_sdmmc(&card);
const tinyusb_msc_sdmmc_config_t config_sdmmc = {
.card = card
};
tinyusb_msc_storage_init_sdmmc(&config_sdmmc);
MSC Performance Optimization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Single-Buffer Approach**
The single-buffer approach improves performance by using a dedicated buffer to temporarily store incoming write data instead of processing it immediately in the callback.
- **Configurable buffer size**: The buffer size is set via ``CONFIG_TINYUSB_MSC_BUFSIZE``, allowing users to balance performance and memory usage.
This approach ensures that USB transactions remain fast while avoiding potential delays caused by storage operations.
**USB MSC Drive Performance**
.. only:: esp32s3
.. list-table::
:header-rows: 1
:widths: 20 20 20
* - FIFO Size
- Read Speed
- Write Speed
* - 512 B
- 0.566 MB/s
- 0.236 MB/s
* - 8192 B
- 0.925 MB/s
- 0.928 MB/s
.. only:: esp32p4
.. list-table::
:header-rows: 1
:widths: 20 20 20
* - FIFO Size
- Read Speed
- Write Speed
* - 512 B
- 1.174 MB/s
- 0.238 MB/s
* - 8192 B
- 4.744 MB/s
- 2.157 MB/s
* - 32768 B
- 5.998 MB/s
- 4.485 MB/s
.. only:: esp32s2
.. note::
SD card support is not available for {IDF_TARGET_NAME} in MSC device mode.
**SPI Flash Performance:**
.. list-table::
:header-rows: 1
:widths: 20 20
* - FIFO Size
- Write Speed
* - 512 B
- 5.59 KB/s
* - 8192 B
- 21.54 KB/s
.. only:: esp32h4
.. note::
SD card support is not available for {IDF_TARGET_NAME} in MSC device mode.
**SPI Flash Performance:**
.. list-table::
:header-rows: 1
:widths: 20 20
* - FIFO Size
- Write Speed
* - 512 B
- 4.48 KB/s
* - 8192 B
- 22.33 KB/s
Performance Limitations:
- **Internal SPI Flash performance** is constrained by architectural limitations where program execution and storage access share the same flash chip. This results in program execution being **suspended during flash writes**, significantly impacting performance.
- **Internal SPI Flash usage is intended primarily for demonstration purposes.** For practical use cases requiring higher performance, it is recommended to use **external storage such as an SD card or an external SPI flash chip, where supported.**
.. only:: esp32s3 or esp32p4
SD cards are not affected by this constraint, explaining their higher performance gains.
Application Examples
--------------------
The examples can be found in the directory :example:`peripherals/usb/device`.
- :example:`peripherals/usb/device/tusb_console` demonstrates how to set up {IDF_TARGET_NAME} to get log output via a Serial Device connection using the TinyUSB component, applicable for any Espressif boards that support USB-OTG.
- :example:`peripherals/usb/device/tusb_serial_device` demonstrates how to set up {IDF_TARGET_NAME} to function as a USB Serial Device using the TinyUSB component, with the ability to be configured as a double serial device.
- :example:`peripherals/usb/device/tusb_midi` demonstrates how to set up {IDF_TARGET_NAME} to function as a USB MIDI Device, outputting a MIDI note sequence via the native USB port using the TinyUSB component.
- :example:`peripherals/usb/device/tusb_hid` demonstrates how to implement a USB keyboard and mouse using the TinyUSB component, which sends 'key a/A pressed & released' events and moves the mouse in a square trajectory upon connection to a USB host.
- :example:`peripherals/usb/device/tusb_msc` demonstrates how to use the USB capabilities to create a Mass Storage Device that can be recognized by USB-hosts, allowing access to its internal data storage, with support for SPI Flash and SD MMC Card storage media.
- :example:`peripherals/usb/device/tusb_composite_msc_serialdevice` demonstrates how to set up {IDF_TARGET_NAME} to function simultaneously as both a USB Serial Device and an MSC device (SPI-Flash as the storage media) using the TinyUSB component.
.. only:: not esp32p4 and not esp32h4
- :example:`peripherals/usb/device/tusb_ncm` demonstrates how to transmit Wi-Fi data to a Linux or Windows host via USB using the Network Control Model (NCM), a sub-class of Communication Device Class (CDC) USB Device for Ethernet-over-USB applications, with the help of a TinyUSB component.
@@ -1,736 +0,0 @@
USB Host
========
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_OTG_NUM_HOST_CHAN: default="8", esp32p4="16"}
The document provides information regarding the USB Host Library. This document is split into the following sections:
.. contents:: Sections
:depth: 2
.. ---------------------------------------------------- Overview -------------------------------------------------------
Overview
--------
The USB Host Library (hereinafter referred to as the Host Library) is the lowest layer of the USB Host stack that exposes a public facing API. In most cases, applications that require USB Host functionality do not need to interface with the Host Library directly. Instead, most applications use the API provided by a host class driver that is implemented on top of the Host Library.
However, you may want to use the Host Library directly for some of (but not limited to) the following reasons:
- Implementation of a custom host class driver
- Usage of lower level USB Host API
Features & Limitations
^^^^^^^^^^^^^^^^^^^^^^
The Host Library has the following features:
.. list::
:esp32s2 or esp32s3 or esp32h4: - Supports Full Speed (FS) and Low Speed (LS) Devices.
:esp32p4: - Supports High Speed (HS), Full Speed (FS) and Low Speed (LS) Devices.
- Supports all four transfer types: Control, Bulk, Interrupt, and Isochronous.
:esp32p4: - Supports High-Bandwidth Isochronous endpoints.
:esp32p4: - {IDF_TARGET_NAME} includes two USB 2.0 OTG peripherals: one High-Speed and one Full-Speed. Both support USB Host functionality. However, due to a current software limitation, only one can operate as a USB Host at a time. Support for dual USB Host operation is planned for a future update.
- Allows multiple class drivers to run simultaneously, i.e., multiple clients of the Host Library.
- A single device can be used by multiple clients simultaneously, e.g., composite devices.
- The Host Library itself and the underlying Host Stack does not internally instantiate any OS tasks. The number of tasks is entirely controlled by how the Host Library interface is used. However, a general rule of thumb regarding the number of tasks is ``(the number of host class drivers running + 1)``.
- Allows single Hub support (If option `CONFIG_USB_HOST_HUBS_SUPPORTED` is enabled).
- Allows multiple Hubs support (If option `CONFIG_USB_HOST_HUB_MULTI_LEVEL` is enabled).
- Supports global suspend and resume, implemented by suspending or resuming the entire bus.
- Supports automatic global resume by submitting a transfer.
Currently, the Host Library and the underlying Host Stack has the following limitations:
.. list::
- Only supports Asynchronous transfers.
- Only supports using one configuration. Changing to other configurations after enumeration is not supported yet.
- Transfer timeouts are not supported yet.
- Selective (per-device/per-port) suspend/resume is not supported yet.
- Remote Wakeup initiated by a USB device is not supported yet.
- The External Hub Driver: Remote Wakeup feature is not supported (External Hubs are active, even if there are no devices inserted).
- The External Hub Driver: Doesn't handle error cases (overcurrent handling, errors during initialization etc. are not implemented yet).
- The External Hub Driver: No Interface selection. The Driver uses the first available Interface with Hub Class code (09h).
- The External Port Driver: No downstream port debounce mechanism (not implemented yet).
:esp32p4: - The External Hub Driver: No Transaction Translator layer (No FS/LS Devices support when a Hub is attached to HS Host).
.. -------------------------------------------------- Architecture -----------------------------------------------------
Architecture
------------
.. figure:: ../../../_static/usb_host_lib_entities.png
:align: center
:alt: Diagram of the Key Entities of USB Host Functionality
:figclass: align-center
Diagram of the key entities involved in USB Host functionality
The diagram above shows the key entities that are involved when implementing USB Host functionality. These entities are:
- The **Host Library**
- **Clients** of the Host Library
- **Devices**
- Host Library **Daemon Task**
Host Library
^^^^^^^^^^^^
The Host Library is the lowest public-facing API layer of the ESP-IDF USB Host Stack. Any other ESP-IDF component (such as a class driver or a user component) that needs to communicate with a connected USB device can only do so using the Host Library API either directly or indirectly.
The Host Library's API is split into two sub-sets, namely the **Library API** and **Client API**.
- The Client API handles the communication between a client of the Host Library and one or more USB devices. The Client API should only be called by registered clients of the Host Library.
- The Library API handles all of the Host Library processing that is not specific to a single client, such as device enumeration. Usually, the library API is called by a Host Library Daemon Task.
Clients
^^^^^^^
A client of the Host Library is a software component, such as a host class driver or user component, which utilizes the Host Library to establish communication with a USB device. Generally, each client has a one-to-one relation with a task. This implies that all Client API calls pertaining to a specific client must originate from the context of the same task.
By organizing the software components that use the Host Library's into clients, the Host Library can delegate the handling of all events specific to that client to the client's task. In other words, each client task is responsible for all the required processing and event handling associated with the USB communication that the client initiates.
Daemon Task
^^^^^^^^^^^
Although the Host Library delegates the handling of client events to the clients themselves, there are still Library events events that are not specific to any particular client that need to be handled. Library event handling can include things such as:
- Handling USB device connection, enumeration, and disconnection
- Rerouting control transfers to/from clients
- Forwarding events to clients
Therefore, in addition to the client tasks, the Host Library also requires a task, which is usually the Host Library Daemon Task, to handle all of the library events.
Devices
^^^^^^^
The Host Library shields clients from the details of device handling, encompassing details such as connection, memory allocation, and enumeration. The clients are provided only with a list of already connected and enumerated devices to choose from. By default during enumeration, each device is automatically configured to use the first configuration found, namely, the first configuration descriptor returned on a Get Configuration Descriptor request. For most standard devices, the first configuration will have a ``bConfigurationValue`` of ``1``. If option `CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK` is enabled, a different ``bConfigurationValue`` can be selected, see `Multiple Configuration Support`_ for more details.
It is possible for two or more clients to simultaneously communicate with the same device as long as they are not communicating to the same interface. However, multiple clients can simultaneously communicate with the same device's default endpoint (i.e., EP0), which will result in their control transfers being serialized.
For a client to communicate with a device, the client must:
#. Open the device using the device's address. This lets the Host Library know that the client is using that device.
#. Claim the interface(s) that will be used for communication. This prevents other clients from claiming the same interface(s).
#. Send transfers to the endpoints of claimed interfaces. The client's task is responsible for handling its own processing and events related to USB device communication.
.. ------------------------------------------------------ Usage --------------------------------------------------------
Usage
-----
The Host Library and the underlying Host Stack will not create any tasks. All tasks, namely the client tasks and the Daemon Task, need to be created by the class drivers or the user. Instead, the Host Library provides two event handler functions that handle all of the required Host Library processing, thus these functions should be called repeatedly from the client tasks and the Daemon Task. Therefore, the implementation of client tasks and the Daemon Task will be largely centered around the invocation of these event handler functions.
Host Library & Daemon Task
^^^^^^^^^^^^^^^^^^^^^^^^^^
Basic Usage
"""""""""""
The Host Library API provides :cpp:func:`usb_host_lib_handle_events` to handle library events. This function should be called repeatedly, typically from the Daemon Task. Some notable features regarding :cpp:func:`usb_host_lib_handle_events` are:
- The function can block until a library event needs handling.
- Event flags are returned on each invocation. These event flags are useful for knowing when the Host Library can be uninstalled.
A bare-bones Daemon Task would resemble something like the following code snippet:
.. code-block:: c
#include "usb/usb_host.h"
void daemon_task(void *arg)
{
...
bool exit = false;
while (!exit) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
...
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
...
}
...
}
...
}
.. note::
See the :example:`peripherals/usb/host/usb_host_lib` example for full implementation of the Daemon Task.
Lifecycle
"""""""""
.. figure:: ../../../_static/usb_host_lib_lifecycle.png
:align: center
:alt: Graph of Typical USB Host Library Lifecycle
:figclass: align-center
Graph of Typical USB Host Library Lifecycle
The graph above illustrates the typical lifecycle of the Host Library with multiple clients and devices. Specifically, the example involves:
- Two registered clients (Client 1 and Client 2).
- Two connected devices (Device 1 and Device 2), where Client 1 communicates with Device 1 and Client 2 communicates with Device 2.
With reference to the graph above, the typical lifecycle involves the following key stages.
1. The Host Library is installed by calling :cpp:func:`usb_host_install`.
- Installation must be done before any other Host Library API is called.
- Where :cpp:func:`usb_host_install` is called (e.g., from the Daemon Task or another task) depends on the synchronization logic between the Daemon Task, client tasks, and the rest of the system.
2. Once the Host Library is installed, the clients can be registered by calling :cpp:func:`usb_host_client_register`.
- This is typically called from the client task, where the client task waits for a signal from the Daemon Task.
- This can be called elsewhere if necessary as long it is called after :cpp:func:`usb_host_install`.
3. Device 1 connects and is then enumerated.
- Each registered client (in this case Client 1 and Client 2) is notified of the new device by way of the :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event.
- Client 1 opens Device 1 and begins communication with it.
4. Similarly Device 2 connects and is enumerated.
- Client 1 and 2 are notified of a new device via a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event.
- Client 2 opens Device 2 and begins communication with it.
5. Device 1 suddenly disconnects.
- Client 1 is notified by way of :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` and begins its cleanup.
- Client 2 is not notified as it has not opened Device 1.
6. Client 1 completes its cleanup and deregisters by calling :cpp:func:`usb_host_client_deregister`.
- This is typically called from the client task before the task exits.
- This can be called elsewhere if necessary as long as Client 1 has already completed its cleanup.
7. Client 2 completes its communication with Device 2. Client 2 then closes Device 2 and deregisters itself.
- The Daemon Task is notified of the deregistration of all clients by way the :c:macro:`USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS` event flag as Client 2 is the last client to deregister.
- Device 2 is still allocated (i.e., not freed), as it is still connected albeit not currently opened by any client.
8. The Daemon Task decides to clean up as there are no more clients.
- The Daemon Task must free Device 2 first by calling :cpp:func:`usb_host_device_free_all`.
- If :cpp:func:`usb_host_device_free_all` was able to free all devices, the function will return `ESP_OK` indicating that all devices have been freed.
- If :cpp:func:`usb_host_device_free_all` was unable to free all devices for reasons like the device is still opened by a client, the function will return `ESP_ERR_NOT_FINISHED`.
- The Daemon Task must wait for :cpp:func:`usb_host_lib_handle_events` to return the :c:macro:`USB_HOST_LIB_EVENT_FLAGS_ALL_FREE` event flag in order to know when all devices have been freed.
9. Once the Daemon Task has verified that all clients have deregistered and all devices have been freed, it can now uninstall the Host Library by calling :cpp:func:`usb_host_uninstall`.
Clients & Class Driver
^^^^^^^^^^^^^^^^^^^^^^
Basic Usage
"""""""""""
The Host Library API provides :cpp:func:`usb_host_client_handle_events` to handle a particular client's events. This function should be called repeatedly, typically from the client's task. Some notable features regarding :cpp:func:`usb_host_client_handle_events` are:
- The function can block until a client event needs handling.
- The function's primary purpose is to call the various event handling callbacks when a client event occurs.
The following callbacks are called from within :cpp:func:`usb_host_client_handle_events` thus allowing the client task to be notified of events.
- The client event callback of type :cpp:type:`usb_host_client_event_cb_t` delivers client event messages to the client. Client event messages indicate events such as the addition or removal of a device.
- The USB transfer completion callback of type :cpp:type:`usb_transfer_cb_t` indicates that a particular USB transfer previously submitted by the client has been completed.
.. note::
Given that the callbacks are called from within :cpp:func:`usb_host_client_handle_events`, users should avoid blocking from within the callbacks as this will result in :cpp:func:`usb_host_client_handle_events` being blocked as well, thus preventing other pending client events from being handled.
The following code snippet demonstrates a bare-bones host class driver and its client task. The code snippet contains:
- A simple client task function ``client_task`` that calls :cpp:func:`usb_host_client_handle_events` in a loop.
- Implementations of a client event callback and transfer completion callbacks.
- Implementation of a simple state machine for the class driver. The class driver simply opens a device, sends an OUT transfer to EP1, then closes the device.
.. code-block:: c
#include <string.h>
#include "usb/usb_host.h"
#define CLASS_DRIVER_ACTION_OPEN_DEV 0x01
#define CLASS_DRIVER_ACTION_TRANSFER 0x02
#define CLASS_DRIVER_ACTION_CLOSE_DEV 0x03
struct class_driver_control {
uint32_t actions;
uint8_t dev_addr;
usb_host_client_handle_t client_hdl;
usb_device_handle_t dev_hdl;
};
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
{
// This function is called from within usb_host_client_handle_events(). Do not block and try to keep it short
struct class_driver_control *class_driver_obj = (struct class_driver_control *)arg;
switch (event_msg->event) {
case USB_HOST_CLIENT_EVENT_NEW_DEV:
class_driver_obj->actions |= CLASS_DRIVER_ACTION_OPEN_DEV;
class_driver_obj->dev_addr = event_msg->new_dev.address; //Store the address of the new device
break;
case USB_HOST_CLIENT_EVENT_DEV_GONE:
class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV;
break;
default:
break;
}
}
static void transfer_cb(usb_transfer_t *transfer)
{
// This function is called from within usb_host_client_handle_events(). Do not block and try to keep it short
struct class_driver_control *class_driver_obj = (struct class_driver_control *)transfer->context;
printf("Transfer status %d, actual number of bytes transferred %d\n", transfer->status, transfer->actual_num_bytes);
class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV;
}
void client_task(void *arg)
{
... // Wait until Host Library is installed
// Initialize class driver objects
struct class_driver_control class_driver_obj = {0};
// Register the client
usb_host_client_config_t client_config = {
.is_synchronous = false,
.max_num_event_msg = 5,
.async = {
.client_event_callback = client_event_cb,
.callback_arg = &class_driver_obj,
}
};
usb_host_client_register(&client_config, &class_driver_obj.client_hdl);
//Allocate a USB transfer
usb_transfer_t *transfer;
usb_host_transfer_alloc(1024, 0, &transfer);
//Event handling loop
bool exit = false;
while (!exit) {
// Call the client event handler function
usb_host_client_handle_events(class_driver_obj.client_hdl, portMAX_DELAY);
// Execute pending class driver actions
if (class_driver_obj.actions & CLASS_DRIVER_ACTION_OPEN_DEV) {
// Open the device and claim interface 1
usb_host_device_open(class_driver_obj.client_hdl, class_driver_obj.dev_addr, &class_driver_obj.dev_hdl);
usb_host_interface_claim(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1, 0);
}
if (class_driver_obj.actions & CLASS_DRIVER_ACTION_TRANSFER) {
// Send an OUT transfer to EP1
memset(transfer->data_buffer, 0xAA, 1024);
transfer->num_bytes = 1024;
transfer->device_handle = class_driver_obj.dev_hdl;
transfer->bEndpointAddress = 0x01;
transfer->callback = transfer_cb;
transfer->context = (void *)&class_driver_obj;
usb_host_transfer_submit(transfer);
}
if (class_driver_obj.actions & CLASS_DRIVER_ACTION_CLOSE_DEV) {
// Release the interface and close the device
usb_host_interface_release(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1);
usb_host_device_close(class_driver_obj.client_hdl, class_driver_obj.dev_hdl);
exit = true;
}
... // Handle any other actions required by the class driver
}
// Cleanup class driver
usb_host_transfer_free(transfer);
usb_host_client_deregister(class_driver_obj.client_hdl);
... // Delete the client task. Signal the Daemon Task if necessary.
}
.. note::
An actual host class driver is likely to support many more features, thus will have a much more complex state machine. A host class driver is also likely to need to:
- Be able to open multiple devices
- Parse an opened device's descriptors to identify if the device is of the target class
- Communicate with multiple endpoints of an interface in a particular order
- Claim multiple interfaces of a device
- Handle various errors
Lifecycle
"""""""""
The typical life cycle of a client task and class driver will go through the following stages:
#. Wait for some signal regarding the Host Library being installed.
#. Register the client via :cpp:func:`usb_host_client_register` and allocate any other class driver resources, such as allocating transfers using :cpp:func:`usb_host_transfer_alloc`.
#. For each new device that the class driver needs to communicate with:
a. Check if the device is already connected via :cpp:func:`usb_host_device_addr_list_fill`.
b. If the device is not already connected, wait for a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event from the client event callback.
c. Open the device via :cpp:func:`usb_host_device_open`.
d. Parse the device and configuration descriptors via :cpp:func:`usb_host_get_device_descriptor` and :cpp:func:`usb_host_get_active_config_descriptor` respectively.
e. Claim the necessary interfaces of the device via :cpp:func:`usb_host_interface_claim`.
#. Submit transfers to the device via :cpp:func:`usb_host_transfer_submit` or :cpp:func:`usb_host_transfer_submit_control`.
#. Once an opened device is no longer needed by the class driver, or has disconnected, as indicated by a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` event:
a. Stop any previously submitted transfers to the device's endpoints by calling :cpp:func:`usb_host_endpoint_halt` and :cpp:func:`usb_host_endpoint_flush` on those endpoints.
b. Release all previously claimed interfaces via :cpp:func:`usb_host_interface_release`.
c. Close the device via :cpp:func:`usb_host_device_close`.
#. Deregister the client via :cpp:func:`usb_host_client_deregister` and free any other class driver resources.
#. Delete the client task. Signal the Daemon Task if necessary.
.. ---------------------------------------------------- Power Management -----------------------------------------------
Power Management
----------------
Global Suspend/Resume
^^^^^^^^^^^^^^^^^^^^^
USB Host library supports global Suspend/Resume, which suspends and resumes the entire USB bus. The global Suspend/Resume is implemented through the root port(s). When suspended, the USB Host stops sending SOFs, which effectively puts all the devices connected to the USB bus to a suspended state.
Note that the global Suspend/Resume does **not** suspend/resume the USB-OTG peripheral on the USB Host side, and therefore does **not** reduce power consumption on the host controller itself.
Events
^^^^^^
Client events
"""""""""""""
When a device is suspended or resumed, all clients that have opened the affected device are notified via the following events. Each event includes the handle of the suspended or resumed device:
- :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_SUSPENDED` — The device has entered a suspended state.
- :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_RESUMED` — The device has resumed operation.
USB Host library event
""""""""""""""""""""""
The following event is associated with the automatic suspend timer. When a timer expires, its callback unblocks the USB Host library handler and delivers the event:
- :cpp:enumerator:`USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND` — Indicates that the automatic suspend timer has expired.
Auto Suspend Timer
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Users can configure an automatic suspend timer using :cpp:func:`usb_host_lib_set_auto_suspend`. The auto suspend timer is reset every time the USB Host library client processing function handles an event from any client, or when USB Host library itself is processing any event.
The auto suspend timer actively monitors USB bus activity and USB Host library activity. Specifically:
- If USB traffic is detected (i.e., any transfer or client-handled event occurs), the timer is automatically reset.
- If USB Host library activity is detected (i.e., new device connection occurs), the timer is automatically reset.
- If the USB bus and USB Host library remain idle (no traffic, client or library events), the timer continues counting down.
- Once the timer reaches the specified timeout (in milliseconds), the suspend event is triggered.
This mechanism ensures that the USB Host enters suspend mode only when the bus is truly idle, preventing unintended suspension during active communication.
Important considerations for the auto suspend timer:
- The timer can be configured and will start counting down even if no device is connected.
- The timer stops when all devices are disconnected.
- The timer automatically resets upon any device connection or disconnection.
- When the timer expires, the USB Host library event is delivered only if:
- A device is currently connected to the root port, and
- The root port is not already in a suspended state
The auto suspend timer can be configured in the following modes:
- **One-shot** (:cpp:enumerator:`USB_HOST_LIB_AUTO_SUSPEND_ONE_SHOT`) — The timer expires once and then stops.
- **Periodic** (:cpp:enumerator:`USB_HOST_LIB_AUTO_SUSPEND_PERIODIC`) — The timer automatically restarts after each expiration, repeating indefinitely.
Auto Resume by Transfer Submit
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to the automatic suspend timer, the USB Host library also supports automatic resume of a suspended root port when a transfer (control or non-control) is submitted. This feature allows developers to initiate a resume simply by calling a transfer API (e.g., :cpp:func:`usb_host_transfer_submit` or :cpp:func:`usb_host_transfer_submit_control`), without needing to explicitly call :cpp:func:`usb_host_lib_root_port_resume`.
When the root port is in suspended state and a client submits a transfer to a suspended device, the library will:
1. Automatically initiate a resume on the root port.
2. Delay the transfer until the resume signaling is complete.
3. Submit the transfer once the bus is in the active (resumed) state.
This mechanism simplifies client implementation and ensures that resume behavior is consistent and safe, avoiding race conditions that may occur if resume is triggered manually in parallel with transfer submission.
.. note::
Auto-resume via transfer submission is only applicable when the root port is in a suspended state. If the port is already active, transfer submission proceeds as usual.
The following code snippet demonstrates how to cycle between suspended and resumed states using the auto suspend timer and the auto resume by transfer submit functionality.
.. code-block:: c
#include "usb/usb_host.h"
static void client_task(void *arg)
{
while(true) {
// Submit transfer to the suspended device, which automatically resumes the device
// Device will then automatically enter suspended mode after 1 second of inactivity thanks to the auto suspend timer
usb_host_transfer_submit(xfer_out);
// Switch context for 10s. The device will be automatically suspended after ~1s of inactivity
vTaskDelay(pdMS_TO_TICKS(10000));
}
}
void usb_host_lib_task(void *arg)
{
...
// Set the auto suspend timer to periodic mode and period of 1s,
// to automatically suspend the device after 1 second of inactivity and to restart automatically after expiration
usb_host_lib_set_auto_suspend(USB_HOST_LIB_AUTO_SUSPEND_PERIODIC, 1000);
while (1) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND) {
// Auto suspend timer expired, suspend the root port
usb_host_lib_root_port_suspend();
}
...
}
...
}
.. note::
For more details regarding suspend and resume, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 11.9 *Suspend and Resume*.
.. ---------------------------------------------------- Examples -------------------------------------------------------
Examples
--------
Host Library Examples
^^^^^^^^^^^^^^^^^^^^^
:example:`peripherals/usb/host/usb_host_lib` demonstrates how to use the USB Host Library API to install and register a client, wait for a device connection, print the device's information, handle disconnection, and repeat these steps until a user quits the application.
Class Driver Examples
^^^^^^^^^^^^^^^^^^^^^
The USB Host Stack provides a number of examples that implement host class drivers using the Host Library's API.
CDC-ACM
"""""""
* A host class driver for the Communication Device Class (Abstract Control Model) is distributed as a managed component via the `ESP Component Registry <https://components.espressif.com/component/espressif/usb_host_cdc_acm>`__.
* :example:`peripherals/usb/host/cdc` demonstrates how to use the CDC-ACM Host Driver to enable communication between {IDF_TARGET_NAME} and a USB CDC-ACM device, including vendor-specific devices such as CP210x, FTDI FT23x or CH34x.
* The CDC-ACM driver is also used in `esp_modem examples <https://github.com/espressif/esp-protocols/tree/master/components/esp_modem/examples>`__, where it is used for communication with cellular modems.
MSC
"""
* A host class driver for the Mass Storage Class (Bulk-Only Transport) is deployed to `ESP Component Registry <https://components.espressif.com/component/espressif/usb_host_msc>`__.
* :example:`peripherals/usb/host/msc` demonstrates how to use USB Mass Storage Class to access, read, write, and perform operations on a USB flash drive, including handling USB reconnections and deinitializing the USB Host Stack.
HID
"""
* A host class driver for the HID (Human interface device) is distributed as a managed component via the `ESP Component Registry <https://components.espressif.com/components/espressif/usb_host_hid>`__.
* :example:`peripherals/usb/host/hid` demonstrates how to implement a basic USB Host HID Class Driver on {IDF_TARGET_NAME}, enabling communication with USB HID devices like keyboards and mice, and continuously scans for their connection, fetching HID reports once connected.
UVC
"""
* A host class driver for the USB Video Device Class is distributed as a managed component via the `ESP Component Registry <https://components.espressif.com/component/espressif/usb_host_uvc>`__.
* :example:`peripherals/usb/host/uvc` demonstrates how to capture video frames from a USB camera using the UVC driver.
.. ---------------------------------------------- USB Host Menuconfig --------------------------------------------------
.. only:: esp32s3
External PHY Configuration
--------------------------
The {IDF_TARGET_NAME} contains two USB controllers—the USB-OTG and USB-Serial-JTAG. However, both controllers share a **single PHY**, which means only one can operate at a time. To use USB Host functionality while the USB-Serial-JTAG is active (e.g., for debugging or flashing), an **external PHY** is required, since the PHY is used by USB-Serial-JTAG.
.. note::
An external PHY is not the only way to enable debugging alongside USB Host or Device functionality. It is also possible to switch the debugging interface from USB-Serial-JTAG to plain JTAG by burning the appropriate eFuses. For details, refer to the `JTAG Debugging <https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/jtag-debugging/index.html>`_ in the ESP-IDF Programming Guide for your target.
{IDF_TARGET_NAME} supports connecting external PHY ICs. This allows independent operation of both USB-OTG and USB-Serial-JTAG controllers. Various external PHY ICs may require different hardware configurations. Please refer to the respective IC datasheets for details. A general connection diagram is available in the official ESP documentation: `Use an external PHY <https://docs.espressif.com/projects/esp-iot-solution/en/latest/usb/usb_overview/usb_phy.html#use-an-external-phy>`__.
**List of Tested External PHY ICs:**
- **SP5301** — Directly supported by {IDF_TARGET_NAME}. See the guide above for schematic and routing details.
- **TUSB1106** — Directly supported by {IDF_TARGET_NAME}. Works with the external-PHY driver via GPIO mapping. Follow the reference wiring in the TUSB1106 datasheet (power-supply options and recommended series resistors on D+/D).
- **STUSB03E** — Requires signal routing using an analog switch. See example below.
.. figure:: ../../../_static/usb_host/ext_phy_schematic_stusb03e.png
:align: center
:alt: External PHY with Analog Switch Schematic (Host mode)
Example connection using STUSB03E and analog switch (Host mode)
.. note::
This schematic is a minimal example intended only to demonstrate the external PHY connection. It omits other essential components and signals (e.g., VCC, GND, RESET) required for a complete, functional {IDF_TARGET_NAME} design.
The schematic includes both a +5 V rail (used to power USB devices) and a VCC rail (typically 3.3 V). VCC should match the chip supply voltage. Ensure that +5 V for the USB bus is appropriately sourced and protected (e.g., with a power switch and current limiting). Always comply with USB host power requirements, particularly when supporting USB bus-powered devices.
Hardware configuration is handled via GPIO mapping to the PHY's pins. Any unused pins (e.g., :cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num`) **must be set to -1**.
.. note::
The :cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num` pin is **currently not supported** and does not need to be connected.
**Example Code:**
.. code-block:: c
// GPIO configuration for external PHY
const usb_phy_ext_io_conf_t ext_io_conf = {
.vp_io_num = 8,
.vm_io_num = 5,
.rcv_io_num = 11,
.oen_io_num = 17,
.vpo_io_num = 4,
.vmo_io_num = 46,
.fs_edge_sel_io_num = 38,
.suspend_n_io_num = -1,
};
// Configuration and initialization of external PHY for OTG controller (Host mode)
const usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_EXT,
.otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_FULL,
.ext_io_conf = &ext_io_conf
};
usb_phy_handle_t phy_hdl;
ESP_ERROR_CHECK(usb_new_phy(&phy_config, &phy_hdl));
// Configure USB Host to use the externally initialized PHY
usb_host_config_t host_config = {
.skip_phy_setup = true,
// Add other host configuration fields as needed
};
ESP_ERROR_CHECK(usb_host_install(&host_config));
This setup ensures that the USB Host stack uses the **external PHY** and bypasses PHY setup.
Host Stack Configuration
------------------------
Non-Compliant Device Support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To support USB devices that are non-compliant in various scenarios or exhibit specific behaviors, it is possible to configure the USB Host stack.
As a USB device may be hot-plugged, it is essential to have configurable delays between power switching and device attachment, and when the device's internal power has stabilized.
Enumeration Configuration
"""""""""""""""""""""""""
During the process of enumerating connected USB devices, several delay values ensure the proper functioning of the device.
.. figure:: ../../../_static/usb_host/poweron-timings.png
:align: center
:alt: USB Root Hub Power-on and Connection Events Timing
USB Root Hub Power-on and Connection Events Timing
The figure above shows all the delay values associated with both turning on port power with a device connected and hot-plugging a device.
* After a port is reset or resumed, the USB system software is expected to provide a "recovery" interval of 10 ms before the device attached to the port is expected to respond to data transfers.
* After the reset/resume recovery interval, if a device receives a ``SetAddress()`` request, the device must be able to complete processing of the request and be able to successfully complete the Status stage of the request within 50 ms.
* After successful completion of the Status stage, the device is allowed a ``SetAddress()`` recovery interval of 2 ms.
.. note::
For more details regarding connection event timings, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 7.1.7.3 *Connect and Disconnect Signaling*.
Configurable parameters of the USB host stack can be configured with multiple options via Menuconfig.
* For debounce delay, refer to `CONFIG_USB_HOST_DEBOUNCE_DELAY_MS`.
* For reset hold interval, refer to `CONFIG_USB_HOST_RESET_HOLD_MS`.
* For reset recovery interval, refer to `CONFIG_USB_HOST_RESET_RECOVERY_MS`.
* For ``SetAddress()`` recovery interval, refer to `CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS`.
Downstream Port Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When external Hubs feature is supported, there are several parameters which could be configured for the external Hubs port.
Each external Hub has a Hub Descriptor which describes the device characteristics.
.. note::
For detailed information about Hub Descriptor, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 11.23.2.1 *Hub Descriptor*.
Configurable parameters of the downstream port can be configured with multiple options via Menuconfig.
* For custom value to stabilize the power after powering on the port (PwrOn2PwrGood value), refer to `CONFIG_USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_MS`.
* For reset recovery interval, refer to `CONFIG_USB_HOST_EXT_PORT_RESET_RECOVERY_DELAY_MS`.
.. note::
The specification claims, that for a hub with no power switches, PwrOn2PwrGood must be set to zero. Meanwhile, for some devices, this value could be increased to give extra time for device to power-up. To enable this feature, refer to `CONFIG_USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_ENABLE`.
Host Channels
"""""""""""""
When external Hubs support feature is enabled (`CONFIG_USB_HOST_HUBS_SUPPORTED`), the amount of Host channels plays important role, as each downstream device requires vacant channel.
To handle each attached device, different amount of channels are required. This amount does depend on the device class (EPs number).
Supported amount of channels for {IDF_TARGET_NAME} is {IDF_TARGET_OTG_NUM_HOST_CHAN}.
.. note::
- One free channel is required to enumerate the device.
- From 1 to N (when N - number of EPs) free channels are required to claim the interface.
- When there are no more free Host channels available, the device could not be enumerated and its interface cannot be claimed.
Multiple Configuration Support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To support USB devices that have more than one configuration, it is possible to specify the desired configuration number during a device's enumeration process.
Enumeration Filter
""""""""""""""""""
The enumeration filter is a callback function of type :cpp:type:`usb_host_enum_filter_cb_t` called at the beginning of the enumeration process once a device descriptor is read from a newly attached USB device. Consequently, the user is provided with the obtained device descriptor. Through this callback, the user can:
* Select the configuration of the USB device.
* Filter which USB devices should be enumerated.
To use the enumeration filter, users should enable the `CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK` option using menuconfig. Users can specify the callback by setting :cpp:member:`usb_host_config_t::enum_filter_cb` which is then passed to the Host Library when calling :cpp:func:`usb_host_install`.
.. -------------------------------------------------- API Reference ----------------------------------------------------
API Reference
-------------
The API of the USB Host Library is separated into the following header files. However, it is sufficient for applications to only ``#include "usb/usb_host.h"`` and all USB Host Library headers will also be included.
- `usb/include/usb/usb_host.h` contains the functions and types of the USB Host Library.
- `usb/include/usb/usb_helpers.h` contains various helper functions that are related to the USB protocol such as descriptor parsing.
- `usb/include/usb/usb_types_stack.h` contains types that are used across multiple layers of the USB Host stack.
- `usb/include/usb/usb_types_ch9.h` contains types and macros related to Chapter 9 of the USB2.0 specification, i.e., descriptors and standard requests.
- `usb/include/usb/usb_types_ch11.h` contains types and macros related to Chapter 11 of the USB2.0 specification, i.e., hub specifications.
Header File
^^^^^^^^^^^
- ``usb_host.h`` can be included with:
.. code:: c
#include "usb/usb_host.h"
- This header file is a part of the API provided by the ``usb`` component. The ``usb`` component is distributed via the `ESP Component Registry <https://components.espressif.com/components/espressif/usb>`__. Thus, to use it, please add the Host Stack component as dependency using the following command:
.. code:: bash
idf.py add-dependency usb
.. ------------------------------------------------ Maintainers Notes --------------------------------------------------
Maintainers Notes
-----------------
.. note::
For more details regarding the internal implementation details of the USB Host stack, please refer to :doc:`/api-reference/peripherals/usb_host/usb_host_notes_index`.
.. toctree::
:hidden:
:maxdepth: 0
usb_host/usb_host_notes_index
@@ -1,60 +0,0 @@
USB Host Maintainers Notes (Architecture)
=========================================
:link_to_translation:`zh_CN:[中文]`
The Host Stack is roughly split into multiple layers of abstraction, with each layer representing different USB concepts and a different level of USB Host operation. For example, a higher layer may present an abstraction of devices and application data transfers, whereas a lower layer may present an abstraction of endpoints and USB transfers.
Layer Descriptions
------------------
The layers of the Host Stack are described in the following table. The layers are ordered from lowest layer (i.e, furthest away from the user) to highest layer (i.e., closest to the user).
.. list-table:: Host Stack Layers
:widths: 20 10 70
:header-rows: 1
* - Layer
- Files
- Description
* - Host Controller (DWC_OTG)
- N/A
- This layer represents the USB Controller Hardware of the {IDF_TARGET_NAME}. The API presented by this layer is the register interface of the controller.
* - LL
- ``usbh_ll.h``
- The LL (Low Level) layer abstracts the basic register access of the USB controller according to ESP-IDF's :doc:`Hardware Abstraction API Guidelines <../../../api-guides/hardware-abstraction>`. In other words, this layer provides APIs to access the controller's registers and format/parse the controller's DMA descriptors.
* - HAL
- ``usbh_hal.h``, ``usbh_hal.c``
- The HAL (Hardware Abstraction Layer) abstracts the operating steps of the USB controller into functions according to ESP-IDF's :doc:`Hardware Abstraction API Guidelines <../../../api-guides/hardware-abstraction>`. This layer also abstracts the controller's host port and host channels, and provides APIs to operate the them.
* - HCD
- ``hcd.h``, ``hcd.c``
- The HCD (Host Controller Driver) acts as hardware agnostic API for all USB controllers (i.e., an API that can theoretically be used with any USB controller implementation). This layer also abstracts the root port (i.e., root hub) and USB pipes.
* - USBH and Hub Driver
- ``usbh.h``, ``usbh.c``
- The USBH (USB Host Driver) layer is equivalent to the USBD layer described in chapter 10 of the USB2.0 specification. The USBH presents an abstraction of USB devices, internally manages a list of connected devices (i.e., device pool), and also arbitrates device sharing between clients (i.e., tracks which endpoints are in use and also presents a shared endpoint 0).
* - Hub Driver
- ``hub.h``, ``hub.c``
- The Hub Driver layer acts as a special client of the USBH that is responsible for handling device attachment/detachment, and notifying the USBH of such events. For device attachment, the Hub Driver also handles the enumeration process as well.
* - USB Host Library
- ``usb_host.h``, ``usb_host.c``
- The USB Host Library layer is the lowest public API layer of the Host Stack and presents the concept of USB Host Clients. The abstraction of clients allows for multiple class drivers to coexist simultaneously (where each class roughly maps to a single client) and also acts as a mechanism for division of labor (where each client is responsible for its own processing and event handling).
* - Host Class Drivers
- See the `ESP-USB repository <https://github.com/espressif/esp-usb>`_ or the USB Host examples in ESP-IDF (via :example:`peripherals/usb/host`).
- The Host Class Drivers implement the host side of a particular device class (e.g., CDC, MSC, HID). The exposed API is specific to each class driver.
Layer Dependencies
------------------
The Host Stack roughly follows a top to bottom hierarchy with inter-layer dependencies. Given layers A (highest), B, and C (lowest), the Host Stack has the following inter-layer dependency rules:
- a particular layer can use the API of any layer directly below (Layer A using layer B is allowed).
- a particular layer can use the API of any layer indirectly below (Layer A using layer C is allowed) i.e., skipping layers.
- a particular layer must not use the API of any layer above (Layer C using layer A/B is forbidden).
.. note::
Layer skipping is permitted in order to circumvent the need to repeat the same abstraction across multiple layers. For example, the abstraction of pipes are presented at the HCD layer but are used by multiple layers above.
.. todo::
Add diagram of API dependencies between layers
@@ -1,144 +0,0 @@
USB Host Maintainers Notes (Design Guidelines)
==============================================
:link_to_translation:`zh_CN:[中文]`
Design Considerations
---------------------
The design of the Host Stack takes into account the following design considerations:
**Limited Hardware Resources:**
The embedded nature of Host Stack means limited hardware resources (such as memory and processing power) when compared to larger host systems.
**USB 2.0 Chapter 10:**
Chapter 10 of the USB 2.0 specification specifies certain requirements of USB Host systems, in particular the required layers of the USB Host's system software.
**Varying Complexity of Different Use Cases:**
The embedded nature of the Host Stack also means a wide range of use cases with differing complexities. Some USB Host applications aim to only support a single vendor specific device, whereas other applications require support for a wide range of devices of different classes.
Requirements
------------
Given the design considerations above, the Host Stack was designed with the following set of requirements:
DMA Support
^^^^^^^^^^^
**Requirement: The Host Stack must support DMA.**
The Host Stack must support DMA in order to reduce CPU's workload. DMA support allows the automatic copying of USB transfer data to and from the Host Controller without CPU intervention. This is especially critical given the embedded nature of the CPU (i.e., lower CPU frequencies) and large maximum data payloads of USB transfers (e.g., 1023 bytes for isochronous transfers).
Minimize Memory Copies
^^^^^^^^^^^^^^^^^^^^^^
**Requirement: The Host Stack should minimize the amount of memory copies when passing data between layers.**
Various data and objects (e.g., USB transfers) need to be passed between multiple layers of the Host Stack. The Host Stack should minimize the amount of memory copies that occur between layers by allocating the data's or object's memory once, and simply passing a pointer to that data or object between the layers. Therefore, the Host Stack requires some standardized data types shared across multiple layers (see USB 2.0 Section 10.3.4).
Event Driven
^^^^^^^^^^^^
**Requirement: The Host Stack must allow for event driven operation (i.e., the Host Stack's API must not be polling).**
The Host Stack needs to support some CPU intensive application scenarios such as video streaming (i.e., UVC class). Therefore, the Host Stack should minimize CPU usage by supporting completely event driven operation, thus reserving the majority of CPU time for the application itself (i.e., video encoding or decoding in this case).
The Host Stack needs to communicate events across the layers using interrupts, callbacks, and FreeRTOS synchronization primitives (e.g., queues and semaphores).
No Task Creation
^^^^^^^^^^^^^^^^
**Requirement: All layers of the Host Stack below (and including) the Host Library layer must not create any tasks.**
Task stacks are generally one of the most memory intensive parts of an ESP-IDF applications. Given the wide range of applications scenarios, the number of tasks created (and their stack sizes) can vary greatly. For example:
- applications that require low latency or high throughput (such as isochronous transfers) may choose to create a dedicated task to handle those transfers in order to minimize latency.
- applications that do not have strict latency requirements (such as bulk transfers) may choose to handle those transfers from a shared task in order to save some memory.
Therefore, all layers of the Host Stack below (and including) the Host Library layer **must not** create any tasks. Instead, these layers should expose handlers functions to be called from tasks created by the upper layers. Task creation will be delegated to the class driver or application layer.
Operable at Different Layers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Given the wide range of use case complexities, the Host Stack must be operable at different layers, allowing users to use the Host Stack at a lower layer (e.g., the HCD or HAL) or at a higher layer (e.g., a class driver).
Being operable at different layers allows the users to decide on the appropriate trade-off between convenience, control, and optimization for their application when using the Host Stack. For example:
- Host Stack applications that support a dedicated custom device may want to use a lower level of abstraction for better optimization, control, and simpler API.
- Host Stack applications that need to support a wide range of device classes requires the full Host Stack so that device enumeration is automatically handled.
Coding Conventions
------------------
The Host Stack follows the following set of coding conventions for better code readability and maintainability:
Symbols Use Layer Name As Prefix
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For each layer of the Host Stack, the symbols exposed by that layer (i.e., functions, types, macros) must be prefixed with that layer's name. For example, the symbols exposed by the HCD layer will be prefixed ``hcd_...`` or ``HCD_...``.
However, internal symbols (e.g., static functions) **should not** be prefixed with their layer's name. This makes it easier to differentiate between internal and external symbols when modifying that layer's source code.
Critical Section Functions Prefixed With ``_``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In each layer of the Host Stack, there are various static functions that must be called inside a critical section. The names of these functions are prefixed with ``_`` (e.g., ``_func_called_from_crit()``) to make it easier for maintainers to differentiate which functions should be called from critical sections. For example:
.. code-block:: c
some_func(); // Called outside critical section
taskENTER_CRITICAL(&some_lock);
_some_func_crit(); // Called inside critical section. _ prefix makes it easier to differentiate
taskEXIT_CRITICAL(&some_lock);
Grouping Structure Members by Locking Mechanism
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some layers of the Host Stack utilize multiple locking schemes (e.g., critical sections and task mutexes) to ensure thread safety, where each locking scheme offers a different level of protection. However, member variables of the same object can be protected by different locking scheme. Therefore, to clearly demarcate the different locking schemes and their associated variables, structure members are grouped by locking scheme as nested structures.
.. list-table:: Locking Scheme
:widths: 10 10 80
:header-rows: 1
* - Locking Scheme
- Nested Structure
- Description
* - Critical Sections
- ``dynamic``
- Shared data accessed from both a task context and ISR context are protected by a critical section.
* - Task Mutexes
- ``mux_protected``
- Shared data accessed from only a task context are protected by a FreeRTOS Mutex
* - Single Thread
- ``single_thread``
- Data that is only ever accessed by the same task do not require the use of any locks.
* - Constant
- ``constant``
- Constant data is set once during the object's instantiation and never changed again. Thus, any task or ISR can freely access the constant data without the use of locks, so long as the variable is never written to.
Grouping structure members by locking scheme makes the code more maintainable as it makes clear which locking scheme is required when accessing a particular member variable, as demonstrated in the code snippet below:
.. code-block:: c
typedef struct some_obj some_obj_t;
some_obj_t obj;
// Accessing dynamic members requires critical section
taskENTER_CRITICAL(&some_lock);
obj.dynamic.varA = 1;
taskEXIT_CRITICAL(&some_lock);
// Accessing mutex protected members requires taking the mutex
xSemaphoreTake(&some_mux, portMAX_DELAY);
obj.mux_protected.varB = 1;
xSemaphoreGive(&some_mux);
// Accessing single thread members does not require locking so long as this is the only task to access it
obj.single_thread.varC = 1;
// Accessing constant members requires no locking. But only read access is allowed
int local_var = obj.constant.varD;
@@ -1,224 +0,0 @@
USB Host Maintainers Notes (DWC_OTG Controller)
===============================================
:link_to_translation:`zh_CN:[中文]`
The {IDF_TARGET_NAME} uses a DesignWare USB 2.0 On-the-Go Controller (henceforth referred to as DWC_OTG in this document) as its underlying hardware controller, where the DWC_OTG operates in Host Mode with Scatter/Gather DMA enabled.
.. note::
This section only summarizes the operation of the DWC_OTG in Host Mode at a high level. For full details of the DWC_OTG, please refer to the DWC_OTG Databook and Programming Guide.
Host Mode Operating Model
-------------------------
A simplified version of the operating model of the DWC_OTG in Host Mode is illustrated in the diagram below. The diagram contains some of the key concepts and terms regarding DWC_OTG Host Mode.
.. figure:: ../../../../_static/usb_host/dwc-otg-operation.png
:align: center
:alt: DWC_OTG Host Mode Operating Model
:figclass: align-center
.. note::
Refer to the Databook section 2.1.4 (Host Architecture) for more details.
Host Port
^^^^^^^^^
The Host Port represents the single USB port provided by the DWC_OTG (in USB terms, this can be thought of as a single USB port of the root hub of the bus). The Host Port can only connect to a single device, though more devices can be connected via hub devices.
The Host Port is responsible for:
- detecting direct device connections/disconnections.
- detecting the speed of the directly connected device.
- issuing various bus signals (such as suspend, resume, reset).
Host Channels
^^^^^^^^^^^^^
In Host Mode, the DWC_OTG uses channels to communicate with device endpoints, where one channel maps to a particular endpoint (in USB terms, channels are the hardware representation of pipes). For example, a channel will map to EP 1 OUT. Each channel has its own set of CSRs (Control and Status Registers) so that they can be configured and controlled independently. A channel's CSRs are used to:
- specify the details of the channel's target endpoint (e.g., device address, EP number, transfer type, direction).
- start a transfer on the channel (e.g., by setting up DMA descriptors).
When using Scatter/Gather DMA, transfers on Host Channels are completely event driven. Users simply fill out the appropriate DMA descriptors, fill in the channel's CSRs, then enable the channel. The channel will then generate an interrupt when the transfer completes.
Data FIFOs
^^^^^^^^^^
In Host Mode, the DWC_OTG uses multiple FIFOs as a staging area for the data payloads of USB transfers. When using DMA, the DMA engine will copy data between the TX/RX FIFOs and {IDF_TARGET_NAME}'s internal memory:
- For an OUT transfer, the transfer's data payload is copied from main memory to one of the TX FIFOs by DMA. The MAC Layer will then transmit that data payload in accordance to USB packet formatting.
- For an IN transfer, the MAC Layer will parse the received USB packet and store the received data payload in the RX FIFO. The data is then copied to main memory by DMA.
The destination FIFO depends on the direction and transfer type of the channel:
- All IN channel data goes to the RX FIFO.
- All non-periodic (i.e., Control and Bulk) OUT channel data goes to the Non-periodic TX (NPTX) FIFO.
- All periodic (i.e., Interrupt and Isochronous) OUT channel data goes to the Periodic TX (PTX) FIFO.
.. note::
The separation of non-periodic and periodic OUT channels to the NPTX and PTX FIFOs is due to the periodic nature of Interrupt and Isochronous endpoints (specified by the ``bInterval`` value of the endpoint). The DWC_OTG automatically schedules these periodic transfers, thus a separate PTX FIFO allows these periodic transfers to be staged separately.
DMA Engine
^^^^^^^^^^
The DMA engine is responsible for copying data between the FIFOs and main memory. In Host Mode Scatter/Gather DMA, a particular channel can carry out multiple transfers automatically without software intervention. The following diagram illustrates the DWC_OTG Host Mode Scatter/Gather DMA Memory Structures.
.. figure:: ../../../../_static/usb_host/dwc-otg-scatter-gather.png
:align: center
:alt: DWC_OTG Host Mode Scatter/Gather DMA Memory Structures
:figclass: align-center
- Each USB transfer is described by a Queue Transfer Descriptor (QTD). Each QTD consists of:
- A 32-bit Buffer Status Quadlet specifying details of the transfer, and also reports the status of the transfer on completion. The Buffer Status Quadlet has bits to specify whether the QTD should generate an interrupt and/or halt the channel on completion.
- A 32-bit pointer to the data buffer containing the data payload for OUT transfers, or an empty buffer used to store the data payload for IN transfers.
- The data payload of each QTD can be larger than the MPS (Maximum Packet Size) of its target endpoint. The DWC_OTG hardware automatically handles the split of transfer into multiple transactions.
- Multiple QTDs can be placed into a single QTD List. A channel will then execute each QTD in the list automatically, and optionally loop back around via configuration.
- Before a channel starts data transfer, it is configured with a QTD list (and QTD list length). Once the channel is enabled, USB transfers are executed automatically by the hardware.
- A channel can generate interrupts (configurable) on completion of a particular QTD, or an entire QTD list.
.. note::
Refer to Programming Guide section 6.2.1 (Descriptor Memory Structures) for more details.
Hardware Configuration
----------------------
The DWC_OTG IP is configurable. The notable host related configurations of the {IDF_TARGET_NAME}'s DWC_OTG are listed below:
.. only:: esp32p4
.. list-table:: {IDF_TARGET_NAME}'s DWC_OTG Configuration
:widths: 70 30
:header-rows: 1
* - Description
- Configuration
* - Host and Device Mode support with OTG
- ``OTG_MODE = 0``
* - High Speed (HS), Full Speed (FS) and Low Speed (LS) support
- ``OTG_FSPHY_INTERFACE = 2``, ``OTG_HSPHY_INTERFACE = 3``
* - Internal DMA controller with Scatter/Gather DMA
- ``OTG_ARCHITECTURE = 2``, ``OTG_EN_DESC_DMA = 1``
* - Split transfers not supported
- ``OTG_SINGLE_POINT = 1``
* - 16 Host Mode channels
- ``OTG_NUM_HOST_CHAN = 16``
* - All transfer types supported, including ISOC and INTR OUT transfers
- ``OTG_EN_PERIO_HOST = 1``
* - Dynamically sized Data FIFO of 4096 bytes (1024 lines)
- ``OTG_DFIFO_DYNAMIC = 1``, ``OTG_DFIFO_DEPTH = 1024``
* - Only 4 periodic and 4 non-periodic transactions per microframe
- ``OTG_NPERIO_TX_QUEUE_DEPTH = 4``, ``OTG_PERIO_TX_QUEUE_DEPTH = 4``
.. only:: esp32s2 or esp32s3 or esp32h4
.. list-table:: {IDF_TARGET_NAME}'s DWC_OTG Configuration
:widths: 70 30
:header-rows: 1
* - Description
- Configuration
* - Host and Device Mode support with OTG
- ``OTG_MODE = 0``
* - Full Speed (FS) and Low Speed (LS) support
- ``OTG_FSPHY_INTERFACE = 1``, ``OTG_HSPHY_INTERFACE = 0``
* - Internal DMA controller with Scatter/Gather DMA
- ``OTG_ARCHITECTURE = 2``, ``OTG_EN_DESC_DMA = 1``
* - 8 Host Mode channels
- ``OTG_NUM_HOST_CHAN = 8``
* - All transfer types supported, including ISOC and INTR OUT transfers
- ``OTG_EN_PERIO_HOST = 1``
* - Dynamically sized Data FIFO of 1024 bytes (256 lines)
- ``OTG_DFIFO_DYNAMIC = 1``, ``OTG_DFIFO_DEPTH = 256``
Scatter/Gather DMA Transfer
---------------------------
The basic operating procedure for Host Channels' transfers consists of the following steps:
#. Prepare data buffers, QTDs, and a QTD list. In particular, ensure which QTDs should halt the channel and generate an interrupt on completion.
#. Set channel and endpoint characteristics via CSRs (such as EP address, transfer type, EP MPS etc).
#. Set channel's QTD list related CSRs (such as QTD list pointer and QTD list length) and channel interrupt CSRs
#. Enable the channel. Transfers are now handled automatically by hardware using DMA.
#. The Channel generates an interrupt on a channel event (e.g., QTD completion or channel error).
#. Parse the channel interrupt to determine what event occurred.
#. Parse the QTDs to determine the result of each individual transfer.
However, there are some minor differences in channel operation and QTD list usage depending on the transfer type.
Bulk
^^^^
Bulk transfers are the simplest transfers. Each QTD represents a bulk transfer of a particular direction, where the DWC_OTG automatically splits a particular QTD into multiple MPS sized transactions. Thus it is possible to fill a QTD list with multiple bulk transfers, and have the entire list executed automatically (i.e., only interrupt on completion of the last QTD).
Control
^^^^^^^
Control transfers are more complicated as they are bi-directional (i.e., each control transfer stage can have a different direction). Thus, a separate QTD is required for each stage, and each QTD must halt the channel on completion. Halting the channel after each QTD allows the channel's direction to be changed by reconfiguring the channel's CSRs. Thus a typical control transfer consists of 3 QTDs (one for each stage).
Interrupt
^^^^^^^^^
In accordance with the USB 2.0 specification, interrupt transfers executes transactions at the endpoints specified service period (i.e., ``bInterval``). A particular interrupt endpoint may not execute more than one interrupt transaction within a service period. The service period is specified in number of microframes or frames, thus a particular interrupt endpoint will generally execute one transaction every Nth microframe or frame until the transfer is complete. For interrupt channels, the service period of a particular channel (i.e., ``bInterval``) is specified via the Host Frame List (see section 6.5 of Programming Guide for more details).
.. note::
HS USB allows an interrupt endpoint to have three interrupt transactions in a single microframe. See USB 2.0 specification section 5.7.3 (Interrupt Transfer Packet Size Constraints) for more details.
Thus, interrupt transfers in Host Mode Scatter/Gather DMA have the following peculiarities:
- If a QTD payload is larger than the endpoint's MPS, the channel will automatically split the transfer into multiple MPS sized transactions (similar to bulk transfers). However, each transaction **is executed at endpoint's specified service period** (i.e., one transaction per ``bInterval``) until the transfer completes.
- For Interrupt IN transfers, if a short packet is received (i.e., transaction's data payload is < MPS), this indicates that the endpoint has no more data to send. In this case:
- the channel generates an extra channel interrupt even if the transfer's QTD did not set the IOC (interrupt on complete) bit.
- however, the channel is not halted even if this extra channel interrupt is generated.
- software must then use this extra interrupt to manually halt the interrupt channel, thus canceling any remaining QTDs in the QTD list.
.. note::
Due to the interrupt transfer peculiarities, it may be easier for software to allocate a QTD for each transaction instead of an entire transfer.
Isochronous
^^^^^^^^^^^
In accordance with the USB 2.0 specification, isochronous transfers executes transactions at the endpoints specified service period (i.e., ``bInterval``) in order to achieve a constant rate of data transfer. A particular isochronous endpoint may not execute more than one isochronous transaction within a service period. The service period is specified in number of microframes or frames, thus a particular isochronous endpoint will generally execute one transaction every Nth microframe or frame until the transfer is complete. For isochronous channels, the service period of a particular channel (i.e., ``bInterval``) is specified via the Host Frame List (see section 6.5 of programming guide for more details).
However, unlike interrupt transactions, isochronous transactions are not retried on failure (or NAK), due to the need to maintain the constant data rate.
.. note::
HS USB allows an isochronous endpoint to have three isochronous transactions in a single microframe. See USB 2.0 specification section 5.6.3 (Isochronous Transfer Packet Size Constraints) for more details.
Thus, isochronous transfers in Host Mode Scatter/Gather DMA have the following peculiarities:
- A QTD must be allocated for each microframe or frame. However, non-service period QTDs should be left blank (i.e., only every Nth QTD should be filled if the channel's service period is every Nth microframe or frame).
- **Each filled QTD must represent a single transaction instead of the entire transfer**.
- Because isochronous transactions are not retried on failure, the status of each completed QTD must be checked.
Supplemental Notes
------------------
Some of the DWC_OTG's behaviors are not mentioned in the Databook or Programming Guide. This section describes some of those behaviors that are relevant to the Host Stack's implementation.
Port Errors Do Not Trigger a Channel Interrupt
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If a port error occurs (such as a sudden disconnection or port over-current) while there are one or more active channels,
- the active channels remains active (i.e., ``HCCHAR.ChEna`` remains set) and no channel interrupts are generated.
- channels could in theory be disabled by setting ``HCCHAR.ChDis``, but this does not work for Isochronous channels as the channel disabled interrupt is never generated.
Therefore, on port errors, a controller soft reset should be used to ensure all channels are disabled.
Port Reset Interrupts
^^^^^^^^^^^^^^^^^^^^^
- When the DWC_OTG issues a reset signal on its port, and during the reset signal the device disconnects, the disconnection interrupt (i.e., ``HPRT.PrtConnDet``) is not generated until the reset is deasserted.
- When resetting an already enabled port (i.e., ``HPRT.PrtEna``) such as a second reset during enumeration or a run-time reset, a Port Enable/Disable Change interrupt (i.e., ``HPRT.PrtEnChng``) is generated both on the assertion and deassertion of the reset signal.
@@ -1,121 +0,0 @@
.. Translation not required: According to the USB developers, all of the ``usb_host_notes_*`` files are just internal notes for ESP-IDF developers, not for our end users. So we don't need to translate them at all.
USB Host Enumeration Driver (Enum)
==================================
Introduction
------------
The USB Host Enumeration Driver (henceforth referred to as Enum Driver) provides a software interface which abstracts away the USB device enumeration process. The Enum Driver provides a simple API to start, proceed, complete and cancel the enumeration of a particular device. Internally, the Enum Driver will handle all stages of the enumeration process such as requesting various descriptors and setting the device's configuration.
Requirements
------------
USB Specification Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Chapter 9.1.2 of the USB 2.0 specification outlines some actions, when a USB device is attached to a powered port.
The design of the Enum Driver takes into consideration following actions:
- **The hub performs the required reset processing for the port.** ``enum_config_t.enum_event_cb`` call with ``ENUM_EVENT_RESET_REQUIRED`` event used for Hub Driver notification.
- **The host assigns a unique address to the USB device.** The Enum Driver keeps the device address value and assigns a unique address to the USB device, moving the device to the Address state.
- **The host reads the device descriptor to determine what actual maximum data payload size this USB device's default pipe can use**. The Enum Driver reads the device descriptor to determine what actual maximum data payload size this USB device's default pipe can use.
- **The host reads the configuration information from the device by reading each configuration zero to n-1, where n is the number of configurations.** This requirement simplified to reading only one configuration information from the device by reading configuration with default number, which could be selected by ``enum_config_t.enum_filter_cb`` call.
- **The host assigns a configuration value to the device.** The Enum Driver assigns a configuration value to the device.
.. note::
Typically, most USB devices only contain a single configuration. Thus, the Enum Driver defaults to selecting the configuration with `bConfigurationValue = 1`.
If users would like to select a different configuration, the Enum Driver provides an enumeration filter callback feature (enabled via `CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK`). This callback is called during the enumeration process and allows users to decide what devices to enumerate, and which ``bConfigurationValue`` to use.
.. note::
For more detailed information about Bus enumeration, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 9.1.2 **Bus Enumeration**.
Host Stack Requirements
^^^^^^^^^^^^^^^^^^^^^^^
In addition to the USB 2.0 specification requirements, the Enum Driver also takes into consideration the requirements set for the overall Host Stack (see :doc:`./usb_host_notes_design`):
- Enum Driver must not instantiate any tasks/threads
- Enum Driver must be event driven, providing event callbacks and an event processing function
- Enum Driver must use only API from underlying layer (USBH)
Implementation & Usage
----------------------
Host Stack Interaction
^^^^^^^^^^^^^^^^^^^^^^
The Enum Driver takes place between USB Host layer and USBH layer with a possibility to select configuration with ``enum_config_t.enum_event_cb`` callback provided on Enum Driver installation.
Events & Processing
^^^^^^^^^^^^^^^^^^^
The Enum Driver is completely event driven and all event handling is done via the ``enum_process()`` function. The ``enum_config_t.proc_req_cb`` callback provided on Enum Driver installation will be called when processing is required. Typically, ``enum_process()`` will be called from a shared USB Host stack thread/task.
The Enum Driver exposes the following event callbacks:
- ``enum_event_cb_t`` used to indicate various events regarding an enumeration process. This callback is called from the context of ``enum_process()``
The Enum Driver exposes the following events:
- ``ENUM_EVENT_STARTED`` Enumeration process has been started
- ``ENUM_EVENT_RESET_REQUIRED`` Enumeration process requires device reset
- ``ENUM_EVENT_COMPLETED`` Enumeration process has been completed
- ``ENUM_EVENT_CANCELED`` Enumeration process has been canceled (due to internal error or via ``enum_cancel()`` function call)
Device Enumeration
^^^^^^^^^^^^^^^^^^
The USB device enumeration process implemented in the Enum Driver is mostly based same process in the `Windows USB stack <https://techcommunity.microsoft.com/t5/microsoft-usb-blog/how-does-usb-stack-enumerate-a-device/ba-p/270685>`__. The Enum Driver's enumeration process involves the following steps:
#. First device descriptor request (to obtain the MPS of EP0)
#. Second device reset (to workaround some non-compliant devices)
#. Set device address
#. Second device descriptor request (to obtain and store the full device descriptor)
#. Configuration descriptor request (to obtain the full configuration descriptor of selected configuration)
#. Language ID Table request (checks to see if en-US is supported)
#. Manufacturer string descriptor request
#. Product string descriptor request
#. Serial number string descriptor request
#. Set configuration request (sets the device to target configuration number)
.. note::
String descriptors are optional. If a device does not support string descriptors, these stages could be omitted.
Enumeration Stages
^^^^^^^^^^^^^^^^^^
The Enum Driver splits the enumeration process into multiple stages which are executed linearly. Depending on the connected device, some stages (such as fetching the string descriptors) can be skipped. When a stage completes, a call to the ``enum_config_t.proc_req_cb`` callback must be made to trigger a subsequent call of ``enum_process()``.The subsequent call of ``enum_process()`` will then select and execute the next stage of enumeration. Stage completion can trigger the ``enum_config_t.proc_req_cb`` callback in one of the following ways:
- Inside the control transfer completion callback (for stages that send a control transfer)
- Direct call to ``enum_config_t.proc_req_cb`` (for stages that don't need to wait for any event)
- Inside ``enum_proceed()`` (for stages that require some action to be carried out outside the Enum Driver)
Any control transfer made during enumeration is split into two stages, where the first stage executes the transfer and the second stage (suffixed with ``_CHECK``) will check the results of the transfers.
When requesting a variable length descriptors (e.g., configuration or string descriptors), the request is split into two control transfers. The first control transfer is fixed in length which only reads the header of the descriptor. The ``bLength`` field of the descriptor's header indicates the full length of the entire descriptor and is used to set the size of the second transfer which fetches the entire descriptor. As a result, any request for a variable length descriptor is split into four stages:
- Get short **ANY** descriptor (prefixed with ``GET_SHORT_...``)
- Check short **ANY** descriptor (prefixed with ``CHECK_SHORT_...``)
- Get full **ANY** descriptor (prefixed with ``GET_FULL_...``)
- Check full **ANY** descriptor (prefixed with ``CHECK_FULL_...``)
.. note::
Retrieving the Device Descriptor is an exception here because the second reset is taken place after retrieving short Device Descriptor.
Cancel Enumeration
^^^^^^^^^^^^^^^^^^
In some cases (such as a device disconnection), an ongoing enumeration process may need to be cancelled. An ongoing enumeration can be cancelled (regardless of its current stage) by calling ``enum_cancel()`` which will change the enumeration process's current stage to ``ENUM_STAGE_CANCEL``.
On the next call to ``enum_process``, the Enum Driver will execute the ``ENUM_STAGE_CANCEL`` which does the following:
- releases the device's enumeration lock.
- frees all resources related to the current device.
- propagates the ``ENUM_EVENT_CANCELED`` event.
@@ -1,65 +0,0 @@
USB Host External Hub Driver (Ext Hub)
======================================
Introduction
------------
The External Hub Driver (henceforth referred to as Ext Hub Driver)
Requirements
------------
USB Specification Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Chapter 11 of the USB 2.0 specification outlines some aspects, when a USB Hub device is attached to a powered port.
The design of the Ext Driver takes into consideration the following:
- **Connectivity behavior**
- **Power management**
- **Device connect/disconnect detection**
- **Bus fault detection and recovery**
- **High-, full-, and low-speed device support**
.. note::
For more detailed information, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 11.1 **Overview**.
Host Stack Requirements
^^^^^^^^^^^^^^^^^^^^^^^
In addition to the USB 2.0 specification requirements, the Ext Hub Driver also takes into consideration the requirements set for the overall Host Stack (see :doc:`./usb_host_notes_design`):
- Ext Hub Driver must not instantiate any tasks/threads
- Ext Hub Driver must be event driven, providing event callbacks and an event processing function
- Ext Hub Driver must use only API from underlying layer (USBH)
Implementation & Usage
----------------------
Host Stack Interaction
^^^^^^^^^^^^^^^^^^^^^^
The Ext Hub Driver takes place between USB Host layer and USBH layer, next to the Hub Driver. The Hub Driver and the Ext Hub Driver were split into two Drivers to achieve the goal of logic distinguishing between root Hub and external Hub.
Device handling
^^^^^^^^^^^^^^^
The Ext Hub Driver can be installed via ``ext_hub_install()`` call and uninstalled via ``ext_hub_uninstall()`` call. After installation the Ext Hub provides the following APIs for external Hub addition and removal:
- ``ext_hub_new_dev()`` which will verify the device class (`HUB_CLASSCODE (09H)`) and, if the device has the Hub class, the Ext Hub Driver:
- allocates a new device object
- adds it to the external device pool
- starts the process of Hub configuration (retrieving Hub Descriptor, Device status and Hub status)
- ``ext_hub_dev_gone()`` which will verify the device in the Ext Hub Driver list and start the process of external Hub device removing.
Events & Processing
^^^^^^^^^^^^^^^^^^^
The Ext Hub Driver is completely event driven and all event handling is done via the ``ext_hub_process()`` function. The ``ext_hub_config_t.proc_req_cb`` callback provided on the Ext Hub Driver installation will be called when processing is required. Typically, ``ext_hub_process()`` will be called from the Hub Driver ``hub_process()`` processing function.
The Ext Hub Driver does not expose any event callback.
@@ -1,85 +0,0 @@
USB Host External Port Driver (Ext Port)
========================================
Introduction
------------
The External Port Driver (henceforth referred to as Ext Port Driver) isolates the handling process for downstream facing ports, which are provided by the Ext Hub Driver.
.. note::
For more detailed information, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 11.5 **Downstream Facing Ports**.
Requirements
------------
Host Stack Requirements
^^^^^^^^^^^^^^^^^^^^^^^
The Ext Port Driver takes into consideration the requirements set for the overall Host Stack (see :doc:`./usb_host_notes_design`):
- The Ext Port Driver must not instantiate any tasks/threads
- The Ext Port Driver must be event driven, providing event callbacks and an event processing function
- The Ext Port Driver must use only API from underlying layer (The Ext Hub Driver)
Implementation & Usage
----------------------
Host Stack Interaction
^^^^^^^^^^^^^^^^^^^^^^
The Ext Port Driver is a part of The Ext Hub Driver, so the interaction and hierarchical place in USB Host Stack is the same as for the Ext Hub Driver. The Ext Hub and the Ext Port Drivers were split into two Drivers to achieve the goal of logic distinguishing between external Hubs and Downstream Facing Ports handling.
Ports handling
^^^^^^^^^^^^^^^
The Ext Port Driver can be installed via ``ext_port_install()`` call and uninstalled via ``ext_port_uninstall()`` call.
After installation, the Ext Port Driver API could be requested via ``ext_port_get_driver()`` call.
The Ext Port Driver API
-----------------------
The Ext Port Driver provides an API, which could be split into three groups: object control, device control and general.
The Ext Port Driver: Object Control
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Create object
- Delete object
The Ext Port Driver: Port Control
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Reset
- Disable
- Recycle
- Activate
- Get Speed
- Get status
- Set status
- Gone
The Ext Port Driver: General Purpose
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Request processing
Events & Processing
-------------------
The Ext Port Driver is completely event driven and all event handling is done via the ``ext_port_process()`` function. The ``ext_port_driver_config_t.proc_req_cb`` callback provided on the Ext Port Driver installation will be called when processing is required. Typically, ``ext_port_process()`` will be called from the Hub Driver ``hub_process()`` processing function.
The Ext Port Driver exposes the following events via ``ext_port_driver_config_t.event_cb``:
- ``EXT_PORT_CONNECTED`` Downstream facing port has a device connection event
- ``EXT_PORT_RESET_COMPLETED`` Downstream facing port has a device and completed the port reset
- ``EXT_PORT_DISCONNECTED`` Downstream facing port has a device disconnection event
The Ext Port Driver ports processing is based on the Hub class-specific request ``Get Port Status``.
After successful completion of the class-specific request ``Get Port Status`` and setting the new port status, the Ext Port Driver continues the port handling while it is required by ports' state and status.
.. note::
For more detailed information, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 11.24.2.7 **Get Port Status**
@@ -1,43 +0,0 @@
USB Host Maintainers Notes (Introduction)
=========================================
:link_to_translation:`zh_CN:[中文]`
This document contains information regarding the implementation details of the USB Host stack. This document is intended for the maintainers and third-party contributors of the USB Host stack. Users of the USB Host stack should refer to :doc:`../usb_host` instead.
.. warning::
The implementations details of the USB Host stack is categorized as private API. Thus, all layers (other than the USB Host Library) do not adhere to :ref:`ESP-IDF's versioning scheme <versioning-scheme>` (i.e., breaking changes are permitted).
.. figure:: ../../../../_static/usb_host/stack-overview.png
:align: center
:alt: Diagram of Host Stack Layers
This document is split into the following sections:
.. toctree::
:maxdepth: 1
usb_host_notes_design
usb_host_notes_arch
usb_host_notes_dwc_otg
usb_host_notes_usbh
usb_host_notes_enum
usb_host_notes_ext_hub
usb_host_notes_ext_port
Todo:
- USB Host Maintainers Notes (HAL & LL)
- USB Host Maintainers Notes (HCD)
- USB Host Maintainers Notes (Hub)
- USB Host Maintainers Notes (USB Host Library)
.. -------------------------------------------------- Introduction -----------------------------------------------------
Introduction
------------
The ESP-IDF USB Host Stack allows the {IDF_TARGET_NAME} to operate as a USB Host. Operating as a USB Host allows the {IDF_TARGET_NAME} to communicate with a wide range of USB devices. However, most USB Host Stack implementations do not run on embedded hardware (i.e., runs on PCs and smartphones), thus have comparatively more resources (i.e., memory and CPU speed).
The implementation of the ESP-IDF USB Host Stack (henceforth referred to as the Host Stack) takes into account the embedded nature of the {IDF_TARGET_NAME} which is reflected in various aspects of the Host Stack's design.
@@ -1,117 +0,0 @@
.. Translation not required: According to the USB developers, all of the ``usb_host_notes_*`` files are just internal notes for ESP-IDF developers, not for our end users. So we don't need to translate them at all.
USB Host Driver (USBH)
======================
Introduction
------------
The USB Host Driver (henceforth referred to as USBH) provides a USB Host software interface which abstracts USB devices. The USBH interface provides APIs to...
- manage the device pool (i.e., adding and removing devices)
- address and configure a device (i.e., setting device and configuration descriptors)
- submit transfers to a particular endpoint of a device
Requirements
------------
USB Specification Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Chapter 10 of the USB 2.0 specification outlines some requirements of the USBH (referred to as USBD in the specification). The design of the USBH takes into consideration these requirements from the specification.
- Default pipe of a device is owned by the USBH
- All other pipes are owned and managed by clients of the USBH
- USBH interface must provide the following services
- Configuration and command mechanism
- Transfer services via both command and pipe mechanisms
- Event notification
- Status reporting and error recovery
Host Stack Requirements
^^^^^^^^^^^^^^^^^^^^^^^
In addition to the USB 2.0 specification requirements, the USBH also takes into consideration the requirements set for the overall Host Stack (see :doc:`./usb_host_notes_design`):
- USBH must not instantiate any tasks/threads
- USBH must be event driven, providing event callbacks and an event processing function
Implementation & Usage
----------------------
Events & Processing
^^^^^^^^^^^^^^^^^^^
The USBH is completely event driven and all event handling is done via then ``usbh_process()`` function. The ``usbh_config_t.proc_req_cb`` callback provided on USBH installation will be called when processing is required. Typically, ``usbh_process()`` will be called from a dedicated thread/task.
The USBH exposes the following event callbacks:
- ``usbh_event_cb_t`` used to indicate various events regarding a particular device and control transfers to EP0. This callback is called from the context of ``usbh_process()``
- ``usbh_ep_cb_t`` used to indicate events for all other endpoints. This callback is not called from the ``usbh_process()`` context (currently called from an HCD interrupt context).
Device Pool
^^^^^^^^^^^
The USBH keeps track of all currently connected devices by internally maintaining a device pool (simply a linked list) where each device is represented by a device object.
The USB 2.0 specification "assumes a specialized client of the USBD, called a hub driver, that acts as a clearinghouse for the addition and removal of devices from a particular hub". As a result, the USBH is completely reliant on an external client(s) (typically a Hub Driver) to inform the USBH of device addition and removal. The USBH provides the following APIs for device addition and removal:
- ``usbh_devs_add()`` which will allocate a new device object and add it to the device pool. The newly added device will be unenumerated, meaning the device object will...
- be assigned to address 0
- have no device and configuration descriptor
- ``usbh_devs_remove()`` which will indicate to the USBH that a device has been removed (such as due to a disconnection or a port error).
- If the device is not currently opened (i.e., used by one or more clients), the USBH will free the underlying device object immediately.
- If the device is currently opened, a ``USBH_EVENT_DEV_GONE`` event will be propagated and the device will be flagged for removal. The last client to close the device will free the device object.
- When a device object is freed, a ``USBH_EVENT_DEV_FREE`` event will be propagated. This event is used to indicate that the device's upstream port can be recycled.
Device Enumeration
^^^^^^^^^^^^^^^^^^
Newly added devices will need to be enumerated. The USBH provides various ``usbh_dev_set_...()`` functions to enumerate the device, such as assigning the device's address and setting device/configuration/string descriptors. Given that USBH devices can be shared by multiple clients, attempting to enumerate a device while another client has opened the device can cause issues.
Thus, before calling any ``usbh_dev_set_...()`` enumeration function, a device must be locked for enumeration by calling ``usbh_dev_enum_lock()``. This prevents the device from being opened by any other client but the enumerating client.
After enumeration is complete, the enumerating client can call ``usbh_devs_trigger_new_dev_event()`` to propagate a ``USBH_EVENT_NEW_DEV`` event.
Device Usage
^^^^^^^^^^^^
Clients that want to use a device must open the device by calling ``usbh_devs_open()`` and providing the device's address. The device's address can either be obtained from a ``USBH_EVENT_NEW_DEV`` event or by calling ``usbh_devs_addr_list_fill()``.
Opening a device will do the following:
- Return a ``usb_device_handle_t`` device handle which can be used to refer to the device in various USBH functions
- Increment the device's internal ``open_count`` which indicates how many clients have opened the device. As long as ``open_count > 0``, the underlying device object will not be freed, thus guaranteeing that the device handle refers to a valid device object.
Once a client no longer needs to use a device, the client should call ``usbh_devs_close()`` thus invalidating the device handle.
.. note::
Most device related APIs accept ``usb_device_handle_t`` as an argument, which means that the calling client must have previously opened the device to obtain the device handle beforehand. This design choice is intentional in order to enforce an "open before use" pattern.
However, a limited set of APIs (e.g., ``usbh_devs_remove()``) refer to devices using a Unique Identifier (``uid``) which is assigned on device addition (see ``usbh_devs_add()``). The use of ``uid`` in these functions allows their callers to refer to a device **without needing to open it** due to the lack of a ``usb_device_handle_t``.
As a result, it is possible that a caller of a ``uid`` function may refer to a device that has already been freed. Thus, callers should account for a fact that these functions may return :c:macro:`ESP_ERR_NOT_FOUND`.
Endpoints & Transfers
^^^^^^^^^^^^^^^^^^^^^
USBH supports transfer to default (i.e., EP0) and non-default endpoints.
For non-default endpoints:
- A client must first allocate the endpoint by calling ``usbh_ep_alloc()`` which assigns a ``usbh_ep_cb_t`` callback and returns a ``usbh_ep_handle_t`` endpoint handle so that the endpoint can be referred to.
- A client can then enqueue a ``urb_t`` transfer to the endpoint by calling ``usbh_ep_enqueue_urb()``.
- The ``usbh_ep_cb_t`` callback is called to indicate transfer completion
- The client must then dequeue the transfer using ``usbh_ep_dequeue_urb()``
Default endpoints are owned and managed by the USBH, thus API for control transfers are different:
- EP0 is always allocated for each device, thus clients do no need to allocate EP0 or provide an endpoint callback.
- Clients call should call ``usbh_dev_submit_ctrl_urb()`` to submit a control transfer to a device's EP0.
- A ``USBH_EVENT_CTRL_XFER`` event will be propagated when the transfer is complete
- Control transfers do not need to be dequeued
+11 -1
View File
@@ -16,7 +16,17 @@ api-reference/ethernet/esp_eth api-reference/network/esp_eth
api-reference/mesh/index api-reference/network/index api-reference/mesh/index api-reference/network/index
api-reference/mesh/esp_mesh api-reference/network/esp_mesh api-reference/mesh/esp_mesh api-reference/network/esp_mesh
api-reference/peripherals/can api-reference/peripherals/twai api-reference/peripherals/can api-reference/peripherals/twai
api-reference/peripherals/usb api-reference/peripherals/usb_device api-reference/peripherals/usb "https://docs.espressif.com/projects/esp-usb/{IDF_DOCS_LANGUAGE}/latest/{IDF_TARGET_PATH_NAME}/usb_device.html"
api-reference/peripherals/usb_device "https://docs.espressif.com/projects/esp-usb/{IDF_DOCS_LANGUAGE}/latest/{IDF_TARGET_PATH_NAME}/usb_device.html"
api-reference/peripherals/usb_host "https://docs.espressif.com/projects/esp-usb/{IDF_DOCS_LANGUAGE}/latest/{IDF_TARGET_PATH_NAME}/usb_host.html"
api-reference/peripherals/usb_host/usb_host_notes_index "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_index.html"
api-reference/peripherals/usb_host/usb_host_notes_arch "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_arch.html"
api-reference/peripherals/usb_host/usb_host_notes_design "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_design.html"
api-reference/peripherals/usb_host/usb_host_notes_dwc_otg "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_dwc_otg.html"
api-reference/peripherals/usb_host/usb_host_notes_usbh "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_usbh.html"
api-reference/peripherals/usb_host/usb_host_notes_enum "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_enum.html"
api-reference/peripherals/usb_host/usb_host_notes_ext_hub "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_ext_hub.html"
api-reference/peripherals/usb_host/usb_host_notes_ext_port "https://docs.espressif.com/projects/esp-usb/en/latest/{IDF_TARGET_PATH_NAME}/usb_host/usb_host_notes_ext_port.html"
api-reference/wifi/index api-reference/network/index api-reference/wifi/index api-reference/network/index
api-reference/wifi/esp_now api-reference/network/esp_now api-reference/wifi/esp_now api-reference/network/esp_now
api-reference/wifi/esp_smartconfig api-reference/network/esp_smartconfig api-reference/wifi/esp_smartconfig api-reference/network/esp_smartconfig
@@ -50,7 +50,7 @@
:SOC_TOUCH_SENSOR_SUPPORTED: cap_touch_sens :SOC_TOUCH_SENSOR_SUPPORTED: cap_touch_sens
:SOC_TWAI_SUPPORTED: twai :SOC_TWAI_SUPPORTED: twai
uart uart
:SOC_USB_OTG_SUPPORTED: usb_device :SOC_USB_OTG_SUPPORTED: USB 设备栈 <https://docs.espressif.com/projects/esp-usb/zh_CN/latest/{IDF_TARGET_PATH_NAME}/usb_device.html>
:SOC_USB_OTG_SUPPORTED: usb_host :SOC_USB_OTG_SUPPORTED: USB 主机 <https://docs.espressif.com/projects/esp-usb/zh_CN/latest/{IDF_TARGET_PATH_NAME}/usb_host.html>
本部分的 API 示例代码存放在 ESP-IDF 示例项目的 :example:`peripherals` 目录下。 本部分的 API 示例代码存放在 ESP-IDF 示例项目的 :example:`peripherals` 目录下。
@@ -1,470 +0,0 @@
USB 设备栈
=================
:link_to_translation:`en:[English]`
{IDF_TARGET_USB_DP_GPIO_NUM:default="20", esp32h4="22"}
{IDF_TARGET_USB_DM_GPIO_NUM:default="19", esp32h4="21"}
{IDF_TARGET_USB_EP_NUM: default="6", esp32p4="15"}
{IDF_TARGET_USB_EP_NUM_INOUT:default="5", esp32p4="8"}
{IDF_TARGET_USB_EP_NUM_IN:default="1", esp32p4="7"}
概述
--------
USB 设备栈(以下简称设备栈)支持在 {IDF_TARGET_NAME} 上启用 USB 设备支持。通过使用设备栈,可以为 {IDF_TARGET_NAME} 烧录任意具有明确定义的 USB 设备功能(如键盘、鼠标、摄像头)、自定义功能(也称特定供应商类别)或上述功能的组合(也称复合设备)。
设备栈基于 TinyUSB 栈构建,但对 TinyUSB 进行了一些小的功能扩展和修改,使其更好地集成到 ESP-IDF。设备栈通过 `乐鑫组件注册表 <https://components.espressif.com/components/espressif/esp_tinyusb>`__ 作为托管组件分发。
功能列表
--------
- 支持多种设备类别 (CDC, HID, MIDI, MSC)
- 支持复合设备
- 支持特定供应商类别
- 最多支持 {IDF_TARGET_USB_EP_NUM} 个端点
- {IDF_TARGET_USB_EP_NUM_INOUT} 个输入/输出端点
- {IDF_TARGET_USB_EP_NUM_IN} 个输入端点
- 自供电设备的 VBUS 监测
.. Todo: Refactor USB hardware connect into a separate guide
硬件连接
--------
.. only:: esp32s2 or esp32s3 or esp32h4
{IDF_TARGET_NAME} 将 USB D+ 和 D- 信号分别路由到 GPIO {IDF_TARGET_USB_DP_GPIO_NUM} 和 {IDF_TARGET_USB_DM_GPIO_NUM}。为了实现 USB 设备功能,这些 GPIO 应通过某种方式连接到总线(例如,通过 Micro-B 端口、USB-C 端口或直接连接到标准-A 插头)。
.. only:: esp32p4
{IDF_TARGET_NAME} 将 USB D+ 和 D- 信号路由到其专用管脚。为了实现 USB 设备功能,这些管脚应通过某种方式连接到总线(例如,通过 Micro-B 端口、USB-C 端口或直接连接到标准-A 插头)。
.. figure:: ../../../_static/usb-board-connection.png
:align: center
:alt: 将 USB GPIO 直接接连至 USB 标准-A 插头
:figclass: align-center
.. only:: esp32s2 or esp32s3 or esp32h4
.. note::
如果你使用带有两个 USB 端口的 {IDF_TARGET_NAME} 开发板,标有 "USB" 的端口已经连接到 D+ 和 D- GPIO。
.. note::
自供电设备还必须通过电压分压器或比较器连接 VBUS,详情请参阅 :ref:`self-powered-device`
.. only:: esp32s3
外部 PHY 配置
-------------
{IDF_TARGET_NAME} 内部集成了两个 USB 控制器:USB-OTG 与 USB-Serial-JTAG。这两个控制器 **共用同一个 PHY**,因此同一时间只能有一个控制器工作。如果在 USB-Serial-JTAG 工作时(例如调试或烧录)时仍需要使用 USB 设备功能,必须使用 **外部 PHY**,因为此时内部 PHY 已被 USB-Serial-JTAG 占用。
.. note::
使用外部 PHY 并不是在 USB 主机或设备功能开启时同时实现调试的唯一办法。也可以通过烧录对应的 eFuse,将调试接口从 USB-Serial-JTAG 切换为传统的 JTAG 接口。具体步骤请参考 ESP-IDF 编程指南中针对你的芯片的 :doc:`JTAG 调试 <../../api-guides/jtag-debugging/index>` 章节。
{IDF_TARGET_NAME} 支持连接外部 PHY 芯片。当需要在使用 USB-Serial-JTAG 控制器的同时提供全速 USB 设备功能时,这一点尤其重要。不同的外部 PHY 芯片可能需要不同的硬件配置,请参考各自芯片的规格书。乐鑫官方文档提供了如下的通用连接示意图供参考。如需了解更多内容,请参阅 `使用外部 PHY <https://docs.espressif.com/projects/esp-iot-solution/zh_CN/latest/usb/usb_overview/usb_phy.html#external-phy>`__
.. figure:: ../../../_static/usb_device/usb_fs_phy_sp5301.png
:align: center
:alt: usb_fs_phy_sp5301
连接外部 PHY 芯片的典型电路图
**已测试的外部 PHY 芯片如下:**
- **SP5301** — {IDF_TARGET_NAME} 原生支持此芯片。原理图与布线方法请参考上文链接。
- **TUSB1106** — {IDF_TARGET_NAME} 原生支持。可通过 GPIO 映射与外部 PHY 驱动配合使用。请遵循 TUSB1106 数据手册中的参考连接(供电方案以及在 D+/D– 上建议的串联电阻)。
- **STUSB03E** — 需要通过模拟开关进行信号切换,详情参考下方示例。
.. figure:: ../../../_static/usb_device/ext_phy_schematic_stusb03e.png
:align: center
:alt: 使用模拟开关的外部 PHY 原理图(设备模式)
使用 STUSB03E 与模拟开关的连接示例(设备模式)
.. note::
此原理图仅为简化示例,用于展示外部 PHY 的连接方式,未包含完整 {IDF_TARGET_NAME} 设计所需的所有元件和信号(如 VCC、GND、RESET 等)。
图中包含 +5 V 电源轨(通常来自 USB VBUS)和 VCC 电源轨。VCC 电压应与芯片供电电压一致(通常为 3.3 V),并确保外部 PHY 与芯片使用同一电压域供电。如果设计自供电 USB 设备,请将外部 PHY 的 VBUSDET 信号接入 {IDF_TARGET_NAME},以便实现对 VBUS 电压状态的监控。
硬件配置通过将 GPIO 映射到 PHY 管脚实现。任何未使用的管脚(如 :cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num`:cpp:member:`usb_phy_ext_io_conf_t::fs_edge_sel_io_num` **必须设置为 -1**
.. note::
:cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num` 管脚 **当前不受支持**,无需连接。
:cpp:member:`usb_phy_ext_io_conf_t::fs_edge_sel_io_num` 管脚为可选管脚,仅需在低速和全速模式间切换时使用。
ESP TinyUSB 设备栈从 2.0 版本开始支持外部 PHY。要在设备模式下使用外部 PHY,需执行以下步骤:
1. 使用 :cpp:type:`usb_phy_config_t` 配置 GPIO 映射与 PHY。
2. 调用 :cpp:func:`usb_new_phy()` 创建 PHY。
3. 使用 :cpp:func:`TINYUSB_DEFAULT_CONFIG()` 初始化 :cpp:type:`tinyusb_config_t`
4. 将 :cpp:type:`tinyusb_config_t``phy.skip_setup` 字段设为 ``true``,从而跳过 PHY 的重新初始化,直接使用已配置的外部 PHY。
**示例代码:**
.. code-block:: c
// 外部 PHY 的 GPIO 配置
const usb_phy_ext_io_conf_t ext_io_conf = {
.vp_io_num = 8,
.vm_io_num = 5,
.rcv_io_num = 11,
.oen_io_num = 17,
.vpo_io_num = 4,
.vmo_io_num = 46,
.suspend_n_io_num = -1,
.fs_edge_sel_io_num = -1,
};
// 针对 OTG 控制器(设备模式)的外部 PHY 配置与初始化
const usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_EXT,
.otg_mode = USB_OTG_MODE_DEVICE,
.otg_speed = USB_PHY_SPEED_FULL,
.ext_io_conf = &ext_io_conf
};
usb_phy_handle_t phy_hdl;
ESP_ERROR_CHECK(usb_new_phy(&phy_config, &phy_hdl));
// 使用默认配置初始化 TinyUSB(可根据需要设置事件处理函数)
tinyusb_config_t config = TINYUSB_DEFAULT_CONFIG();
config.phy.skip_setup = true;
tinyusb_driver_install(&config);
通过上述配置,USB 设备栈会直接使用 **外部 PHY**,而不会尝试配置内部 PHY。
设备栈结构
----------
设备栈以 TinyUSB 栈为基础,在此基础上,该设备栈实现了以下功能:
- 自定义 USB 描述符
- 支持串行设备
- 通过串行设备重定向标准流
- 提供用于 USB 设备 MSC 类的存储介质(SPI-Flash 和 SD 卡)
- 封装设备栈中处理 TinyUSB 服务的任务
组件依赖项
-------------
设备栈通过 `乐鑫组件注册表 <https://components.espressif.com/components/espressif/esp_tinyusb>`__ 分发,使用前,请使用以下命令将设备栈组件添加为依赖项:
.. code:: bash
idf.py add-dependency esp_tinyusb
配置选项
^^^^^^^^^
通过 menuconfig 选项,可以对设备栈进行以下多方面配置:
- TinyUSB 日志的详细程度
- 设备栈任务相关选项
- 默认设备/字符串描述符选项
- 特定类别的选项
.. _descriptors-configuration:
配置描述符
^^^^^^^^^^^^^^^^^^^^^^^^^
结构体 :cpp:type:`tinyusb_config_t` 提供了与 USB 描述符相关的字段,应进行初始化。
无论是全速 USB 设备还是高速 USB 设备,都应初始化以下描述符:
- :cpp:member:`device_descriptor`
- :cpp:member:`string_descriptor`
全速 USB 设备应初始化以下字段,以提供相应的配置描述符:
- :cpp:member:`configuration_descriptor`
.. only:: esp32p4
高速 USB 设备应初始化以下字段,以提供不同速度下的配置描述符:
- :cpp:member:`fs_configuration_descriptor`
- :cpp:member:`hs_configuration_descriptor`
- :cpp:member:`qualifier_descriptor`
.. note::
为符合 USB 2.0 协议规范,需同时初始化 :cpp:member:`fs_configuration_descriptor`:cpp:member:`hs_configuration_descriptor`
调用 :cpp:func:`tinyusb_driver_install` 时,设备栈将基于上述字段中提供的描述符实现 USB 设备。
设备栈还提供了默认描述符,将 :cpp:func:`tinyusb_driver_install` 中的相应字段设置为 ``NULL`` 即可安装。默认描述符包括:
- 默认设备描述符:如需启用,将 :cpp:member:`device_descriptor` 设置为 ``NULL``。默认设备描述符将使用相应的 menuconfig 选项设置的值(如 PID、VID、bcdDevice 等)。
- 默认字符串描述符:如需启用,将 :cpp:member:`string_descriptor` 设置为 ``NULL``。默认字符串描述符将使用相应的 menuconfig 选项设置的值(如制造商、产品和序列字符串描述符选项)。
- 默认配置描述符。某些很少需要自定义配置的类别(如 CDC 和 MSC)将提供默认配置描述符。如需启用,将相应的配置描述符字段设置为 ``NULL``
- :cpp:member:`configuration_descriptor`:全速描述符,仅适用于全速设备
- :cpp:member:`fs_configuration_descriptor`:全速描述符,适用于高速设备
- :cpp:member:`hs_configuration_descriptor`:高速描述符,适用于高速设备
.. note::
为实现向后兼容性,若设备栈支持高速,可使用 :cpp:member:`configuration_descriptor` 代替 :cpp:member:`fs_configuration_descriptor` 来设置全速配置描述符。
安装设备栈
----------
请调用 :cpp:func:`tinyusb_driver_install` 安装设备栈。结构体 :cpp:type:`tinyusb_config_t` 指定了设备栈的配置,而 :cpp:type:`tinyusb_config_t` 作为参数传递给 :cpp:func:`tinyusb_driver_install`
.. note::
结构体 :cpp:type:`tinyusb_config_t` 可以实现零初始化(如 ``const tinyusb_config_t tusb_cfg = { 0 };``)或部分初始化(如下所示)。对于结构体中任何初始化为 ``0````NULL`` 的成员,设备栈将使用其默认配置,请参阅如下示例。
.. code-block:: c
const tinyusb_config_t partial_init = {
.device_descriptor = NULL, // 使用在 menuconfig 中指定的默认设备描述符
.string_descriptor = NULL, // 使用在 menuconfig 中指定的默认字符串描述符
.external_phy = false, // 使用内部 USB PHY
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = NULL, // 使用在 menuconfig 中根据设置指定的默认全速配置描述符
.hs_configuration_descriptor = NULL, // 使用在 menuconfig 中根据设置指定的默认高速配置描述符
.qualifier_descriptor = NULL, // 使用默认限定描述符,值取自默认设备描述符
#else
.configuration_descriptor = NULL, // 使用在 menuconfig 中根据设置指定的默认配置描述符
#endif // TUD_OPT_HIGH_SPEED
};
.. _self-powered-device:
自供电设备
-------------------
USB 规范要求自供电设备监测 USB 的 VBUS 信号的电压水平。与总线供电设备相反,即使没有 USB 连接,自供电设备也可以正常工作。通过监测 VBUS 电压水平,自供电设备可以检测连接和断开事件。当 VBUS 电压升高到 4.75 V 以上时视为有效;当 VBUS 电压下降到 4.35 V 以下时视为无效。
在 {IDF_TARGET_NAME} 上,需要使用一个 GPIO 作为电压感测管脚,检测 VBUS 处于在规定阈值之上/之下。然而,由于 {IDF_TARGET_NAME} 管脚具有 3.3 V 容差,即使 VBUS 上升/下降到高于/低于上述规定阈值,{IDF_TARGET_NAME} 仍会显示为逻辑高电平。因此,为了检测 VBUS 是否有效,可以采用以下方法:
- 将 VBUS 连接至电压比较器芯片/电路,该芯片/电路可检测上述阈值(即 4.35 V 和 4.75 V),并向 {IDF_TARGET_NAME} 输出 3.3 V 逻辑电平,指示 VBUS 是否有效。
- 如果 VBUS 为 4.4 V,则使用电阻分压器输出 (0.75 x Vdd)(见下图)。
.. note::
在这两种情况下,设备从 USB 主机拔出后 3 毫秒内,传感管脚上的电压必须为逻辑低电平。
.. figure:: ../../../_static/diagrams/usb/usb_vbus_voltage_monitor.png
:align: center
:alt: 用于 VBUS 监测的简易分压器
:figclass: align-center
用于 VBUS 监测的简易分压器
请在结构体 :cpp:type:`tinyusb_config_t` 中将 :cpp:member:`self_powered` 设置为 ``true``,并将 :cpp:member:`vbus_monitor_io` 设置为用于 VBUS 监测的 GPIO 管脚编号以使用此功能。
USB 串行设备 (CDC-ACM)
---------------------------
如果在 menuconfig 中启用了 CDC 选项,则可以根据 :cpp:type:`tinyusb_config_cdcacm_t` 的设置,使用 :cpp:func:`tusb_cdc_acm_init` 初始化 USB 串行设备,请参阅如下示例:
.. code-block:: c
const tinyusb_config_cdcacm_t acm_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_0,
.rx_unread_buf_sz = 64,
.callback_rx = NULL,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL
};
tusb_cdc_acm_init(&acm_cfg);
可以在配置结构体中设置指向 :cpp:type:`tusb_cdcacm_callback_t` 函数的指针指定回调函数,或在初始化 USB 串行设备后,调用 :cpp:func:`tinyusb_cdcacm_register_callback` 指定回调函数。
USB 串行控制台
^^^^^^^^^^^^^^^^^^
USB 串行设备支持将所有标准输入/输出流 (stdin、stdout、stderr) 重定向到 USB。因此,调用如 ``printf()`` 等标准库输入/输出函数将导致通过 USB 而不是 UART 发送/接收数据。
建议调用 :cpp:func:`esp_tusb_init_console` 将标准输入/输出流切换到 USB,并调用 :cpp:func:`esp_tusb_deinit_console` 将其切换回 UART。
USB 大容量存储设备 (MSC)
-----------------------------
在 menuconfig 中启用 MSC ``CONFIG_TINYUSB_MSC_ENABLED`` 选项时,可以将 ESP 芯片作为 USB 大容量存储设备使用。按如下示例,可以初始化存储媒介(SPI-Flash 或 SD 卡)。
- SPI-Flash
.. code-block:: c
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
{
***
esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
***
wl_mount(data_partition, wl_handle);
***
}
storage_init_spiflash(&wl_handle);
const tinyusb_msc_spiflash_config_t config_spi = {
.wl_handle = wl_handle
};
tinyusb_msc_storage_init_spiflash(&config_spi);
- SD 卡
.. code-block:: c
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
{
***
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
// 对于 SD 卡,设置要使用的总线宽度
slot_config.width = 4;
slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
(*host.init)();
sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config);
sdmmc_card_init(&host, sd_card);
***
}
storage_init_sdmmc(&card);
const tinyusb_msc_sdmmc_config_t config_sdmmc = {
.card = card
};
tinyusb_msc_storage_init_sdmmc(&config_sdmmc);
MSC 性能优化
^^^^^^^^^^^^^^
**single-buffer 方案**
single-buffer 方案通过使用专用 buffer 临时存储接收到的写入数据,而不是在回调中立即处理,从而提升性能。
- **可配置的 buffer 大小** 通过 ``CONFIG_TINYUSB_MSC_BUFSIZE`` 参数设置 buffer 大小,用户可以根据实际需要,灵活调整性能与存储占用的平衡点。
该方案确保了 USB 传输的效率,同时避免因存储操作可能带来的延迟。
**USB MSC 驱动器性能**
.. only:: esp32s3
.. list-table::
:header-rows: 1
:widths: 20 20 20
* - FIFO 大小
- 读取速度
- 写入速度
* - 512 B
- 0.566 MB/s
- 0.236 MB/s
* - 8192 B
- 0.925 MB/s
- 0.928 MB/s
.. only:: esp32p4
.. list-table::
:header-rows: 1
:widths: 20 20 20
* - FIFO 大小
- 读取速度
- 写入速度
* - 512 B
- 1.174 MB/s
- 0.238 MB/s
* - 8192 B
- 4.744 MB/s
- 2.157 MB/s
* - 32768 B
- 5.998 MB/s
- 4.485 MB/s
.. only:: esp32s2
.. note::
{IDF_TARGET_NAME} 在 MSC 设备模式下不支持 SD 卡功能。
**SPI flash 性能:**
.. list-table::
:header-rows: 1
:widths: 20 20
* - FIFO 大小
- 写入速度
* - 512 B
- 5.59 KB/s
* - 8192 B
- 21.54 KB/s
.. only:: esp32h4
.. note::
{IDF_TARGET_NAME} 不支持在 MSC 设备模式下使用 SD 卡。
**SPI Flash 性能:**
.. list-table::
:header-rows: 1
:widths: 20 20
* - FIFO 大小
- 写入速度
* - 512 B
- 4.48 KB/s
* - 8192 B
- 22.33 KB/s
性能限制:
- **内部 SPI flash 性能** 受架构限制影响。程序执行和存储访问共享同一 flash 芯片,导致写入 flash 时必须暂停程序执行,会显著影响性能。
- **内部 SPI flash 主要适用于演示场景**。在需要更高性能的实际应用中,在支持的情况下使用 **外部存储设备**,如 SD 卡或外部 SPI flash 芯片。
.. only:: esp32s3 or esp32p4
SD 卡不受此限制影响,因此能获得更高的性能提升。
应用示例
--------------------
如需查看相关示例,请前往目录 :example:`peripherals/usb/device`
- :example:`peripherals/usb/device/tusb_console` 演示了如何使用 TinyUSB 组件配置 {IDF_TARGET_NAME},以通过串行设备连接获取和输出日志,适用于任何支持 USB-OTG 的乐鑫开发板。
- :example:`peripherals/usb/device/tusb_serial_device` 演示了如何使用 TinyUSB 组件将 {IDF_TARGET_NAME} 配置为 USB 串行设备,还支持配置为双串行设备。
- :example:`peripherals/usb/device/tusb_midi` 演示了如何使用 TinyUSB 组件将 {IDF_TARGET_NAME} 配置为 USB MIDI 设备,从而通过本地 USB 端口输出 MIDI 音符序列。
- :example:`peripherals/usb/device/tusb_hid` 演示了如何使用 TinyUSB 组件实现 USB 键盘和鼠标,在连接到 USB 主机时发送 “按下和释放 key a/A” 事件,并使鼠标沿方形轨迹移动。
- :example:`peripherals/usb/device/tusb_msc` 演示了如何使用 USB 功能创建一个可以被 USB 主机识别的大容量存储设备,允许访问其内部数据存储,支持 SPI Flash 和 SD MMC 卡存储介质。
- :example:`peripherals/usb/device/tusb_composite_msc_serialdevice` 演示了如何使用 TinyUSB 组件将 {IDF_TARGET_NAME} 同时配置为 USB 串行设备和 MSC 设备(存储介质为 SPI-Flash)运行。
.. only:: not esp32p4 and not esp32h4
- :example:`peripherals/usb/device/tusb_ncm` 演示了使用 TinyUSB 组件,借助网络控制模型 (NCM) 将 Wi-Fi 数据通过 USB 传输到 Linux 或 Windows 主机。NCM 是通信设备类 (CDC) USB 设备的一个子类,专用于 Ethernet-over-USB 应用。
@@ -1,736 +0,0 @@
USB 主机
========
:link_to_translation:`en:[English]`
{IDF_TARGET_OTG_NUM_HOST_CHAN: default="8", esp32p4="16"}
本文档提供了 USB 主机库的相关信息,按以下章节展开:
.. contents:: 章节
:depth: 2
.. ---------------------------------------------------- Overview -------------------------------------------------------
概述
--------
USB 主机库(以下简称主机库)是 USB 主机栈的最底层,提供面向公众开放的 API。应用程序使用 USB 主机功能时,通常无需与主机库直接交互,而是使用某个主机 Class 驱动提供的 API,这些主机 Class 驱动构建在主机库之上。
然而,由于以下的某些原因(但不仅限于此),有时你可能需要直接使用主机库:
- 需要实现自定义主机 Class 驱动程序
- 需要更低级别的 USB 主机 API
特性和限制
^^^^^^^^^^^^^^^^^^^^^^
主机库具有以下特性:
.. list::
:esp32s2 or esp32s3 or esp32h4: - 支持全速 (FS) 和低速 (LS) 设备。
:esp32p4: - 支持高速 (HS)、全速 (FS) 和低速 (LS) 设备。
- 支持四种传输类型,即控制传输、块传输、中断传输和同步传输。
:esp32p4: - 支持高带宽等时性端点。
:esp32p4: - {IDF_TARGET_NAME} 包含两个 USB 2.0 OTG 外设:USB 2.0 OTG 高速和 USB 2.0 OTG 全速,二者均支持 USB 主机功能。但由于当前软件的限制,同一时间仅能有一个作为 USB 主机工作。未来版本计划支持两个 USB 主机同时运行。
- 支持多个 Class 驱动程序同时运行,即主机的多个客户端同时运行。
- 单个设备可以由多个客户端同时使用,如复合设备。
- 主机库及其底层主机栈不会在内部自动创建操作系统任务,任务数量完全由主机库接口的使用方式决定。一般来说,任务数量为 ``(运行中的主机 Class 驱动程序数量 + 1)``
- 支持单个 Hub(启用选项 `CONFIG_USB_HOST_HUBS_SUPPORTED`)。
- 支持多个 Hub(启用选项 `CONFIG_USB_HOST_HUB_MULTI_LEVEL`)。
- 支持全局挂起与恢复,通过挂起或恢复整个总线来实现。
- 支持通过提交传输自动触发全局恢复。
目前,主机库及其底层主机栈存在以下限制:
.. list::
- 仅支持异步传输。
- 仅支持使用发现的首个配置,尚不支持变更为其他配置。
- 尚不支持传输超时。
- 尚未支持选择性(按设备/按端口)挂起/恢复。
- 尚不支持由 USB 设备发起的远程唤醒。
- 外部 Hub 驱动:不支持远程唤醒功能(即使没有设备插入,外部 Hub 也处于工作状态)。
- 外部 Hub 驱动:不处理错误用例(尚未实现过流处理、初始化错误等功能)。
- 外部 Hub 驱动:不支持接口选择。驱动程序使用具有 Hub 类代码 (09h) 的第一个可用接口。
- 外部端口驱动:无下游端口去抖动机制(尚未实现)。
:esp32p4: - 外部 Hub 驱动:无事务转换层(当 Hub 连接到高速主机时,不支持全速/低速设备)。
.. -------------------------------------------------- Architecture -----------------------------------------------------
架构
------------
.. figure:: ../../../_static/usb_host_lib_entities.png
:align: center
:alt: USB 主机功能的关键实体
:figclass: align-center
USB 主机功能涉及的关键实体
上图展示了使用 USB 主机功能时涉及的关键实体,包括:
- **主机库**
- 主机库的 **客户端**
- **设备**
- 主机库的 **守护进程任务**
主机库
^^^^^^^^^^^^
主机库是 ESP-IDF USB 主机栈中面向公众开放的最底层的 API 层。任何其他 ESP-IDF 组件(如 Class 驱动程序或用户组件),如果需要与连接的 USB 设备通信,只能直接或间接使用主机库 API。
主机库 API 分为两类,即 **库 API****客户端 API**
- 客户端 API 负责主机库的客户端与一或多个 USB 设备间的通信,该 API 只能由主机库的注册客户端调用。
- 库 API 负责主机库处理的通信中不特定于单个客户端的通信,如设备枚举。该 API 通常由主机库的守护进程任务调用。
客户端
^^^^^^^
主机库的客户端指使用主机库与 USB 设备通信的软件组件,如主机 Class 驱动程序或用户组件。每个客户端通常与任务间存在一对一关系,这表明,对特定客户端而言,其所有客户端 API 都应该在同一任务的上下文中调用。
通过将使用主机库的软件组件进行分类,划分为独立的客户端,主机库可以将所有客户端特定事件的处理委托给客户端对应的任务。换句话说,每个客户端任务负责管理与其对应的客户端之间的所有 USB 通信操作和事件处理,无需关心其他客户端的事件。
守护进程任务
^^^^^^^^^^^^
尽管主机库将客户端事件的处理委托给客户端本身,但仍然需要处理主机库事件,即不特定于客户端的事件。主机库事件处理可能涉及以下内容:
- 处理 USB 设备的连接、枚举和断连
- 将控制传输从/向客户端进行重定向
- 将事件转发给客户端
因此,除客户端任务外,主机库也需要一个任务来处理所有的库事件,这个任务通常是主机库守护进程任务。
设备
^^^^^^^
主机库隔离了客户端与设备处理的细节,包括连接、内存分配和枚举等,客户端只需提供已连接且已枚举的设备列表供选择。默认情况下,在枚举过程中,每个设备都会自动配置为使用找到的第一个配置,即通过获取配置描述符请求返回的第一个配置描述符。对于大多数标准设备,通常将第一个配置的 ``bConfigurationValue`` 设置为 ``1``。启用选项 `CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK` 后,可以选择不同的 ``bConfigurationValue``。获取更多详细信息,请参阅 `多项配置支持`_。
只要不与相同接口通信,两个及以上的客户端可以同时与同一设备通信。然而,多个客户端同时与相同设备的默认端点(即 EP0)通信,将导致它们的控制传输序列化。
要与设备通信,客户端必须满足以下条件:
#. 使用设备地址打开设备,告知主机库,客户端正在使用该设备。
#. 获取将用于通信的接口,防止其他客户端获取相同的接口。
#. 通过已经获取了使用权的设备接口的端点通信通道发送数据传输。客户端的任务负责处理其与 USB 设备通信相关的操作和事件。
.. ------------------------------------------------------ Usage --------------------------------------------------------
用法
-----
主机库及底层主机栈不会创建任何任务,客户端任务和守护进程任务等均需由 Class 驱动程序或用户自行创建。然而,主机库提供了两个事件处理函数,可以处理所有必要的主机库操作,这些函数应从客户端任务和守护进程任务中重复调用。因此,客户端任务和守护进程任务的使用将主要集中于调用这些事件处理函数。
主机库与守护进程任务
^^^^^^^^^^^^^^^^^^^^^^^^^^
基本用法
"""""""""""
主机库 API 提供了 :cpp:func:`usb_host_lib_handle_events`,可以处理库事件。该函数需要反复调用,通常是从守护进程任务中调用。函数 :cpp:func:`usb_host_lib_handle_events` 有以下特点:
- 该函数会持续阻塞,直至需要处理库事件。
- 每次调用该函数都会返回事件标志,有助于了解卸载主机库的时机。
最基础的守护进程任务通常类似以下代码片段:
.. code-block:: c
#include "usb/usb_host.h"
void daemon_task(void *arg)
{
...
bool exit = false;
while (!exit) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
...
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
...
}
...
}
...
}
.. note::
了解守护进程任务的完整示例,请前往 :example:`peripherals/usb/host/usb_host_lib`
生命周期
"""""""""
.. figure:: ../../../_static/usb_host_lib_lifecycle.png
:align: center
:alt: USB 主机库典型生命周期
:figclass: align-center
USB 主机库典型生命周期
上图展示了 USB 主机库的典型生命周期,其中涉及多个客户端和设备。具体而言,示例涉及以下内容:
- 两个已注册的客户端(客户端 1 和客户端 2)。
- 两个已连接的设备(设备 1 和设备 2),其中客户端 1 与设备 1 通信,客户端 2 与设备 2 通信。
参考上图可知,典型 USB 主机库生命周期包括以下关键阶段:
1. 调用 :cpp:func:`usb_host_install`,安装主机库。
- 调用任意主机库 API 前,请确保已完成主机库安装。
- 调用 :cpp:func:`usb_host_install` 的位置(如从守护进程任务或其他任务中调用)取决于守护系统任务、客户端任务和系统其余部分间的同步逻辑。
2. 安装完主机库后,调用 :cpp:func:`usb_host_client_register` 注册客户端。
- 该注册函数通常从客户端任务调用,而客户端任务需等待来自守护进程任务的信号。
- 调用过 :cpp:func:`usb_host_install` 后,如有需要,也可以在其他地方调用该注册函数。
3. 设备 1 连接,并进行枚举。
- 每个已注册的客户端(本案例中为客户端 1 和客户端 2)都会通过 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` 事件得到新设备的通知。
- 客户端 1 开启设备 1,并与之通信。
4. 设备 2 连接,并进行枚举。
- 客户端 1 和 2 通过 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` 事件得到新设备的通知。
- 客户端 2 开启设备 2,并与之通信。
5. 设备 1 突然断开连接。
- 客户端 1 通过 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` 得到通知,并开始清理,关闭和释放客户端 1 与设备 1 之间的关联资源。
- 客户端 2 不会收到通知,因为它并未开启设备 1。
6. 客户端 1 完成清理,调用 :cpp:func:`usb_host_client_deregister` 注销客户端。
- 该注销函数通常在任务退出前,从客户端任务中调用。
- 如有需要,只要客户端 1 已完成清理,也可以在其他地方调用该注销函数。
7. 客户端 2 完成与设备 2 的通信,随后关闭设备 2,并自行注销。
- 由于客户端 2 是最后一个注销的客户端,通过 :c:macro:`USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS` 事件标志,守护进程任务可以得知,所有客户端已注销。
- 设备 2 未释放,仍会进行分配,因为虽然没有任何客户端打开设备 2,但它仍处于连接状态。
8. 所有客户端注销后,守护进程任务开始清理。
- 守护进程任务需要先调用 :cpp:func:`usb_host_device_free_all`,释放设备 2。
- 如果 :cpp:func:`usb_host_device_free_all` 能够成功释放所有设备,函数将返回 `ESP_OK`,表明已释放所有设备。
- 如果 :cpp:func:`usb_host_device_free_all` 无法成功释放所有设备,例如因为设备仍由某个客户端开启,函数将返回 `ESP_ERR_NOT_FINISHED`
- 守护进程任务必须等待 :cpp:func:`usb_host_lib_handle_events` 返回事件标志 :c:macro:`USB_HOST_LIB_EVENT_FLAGS_ALL_FREE`,方知何时所有设备均已释放。
9. 一旦守护进程任务确认所有客户端均已注销,且所有设备均已释放,便可调用 :cpp:func:`usb_host_uninstall`,卸载主机库。
客户端与 Class 驱动程序
^^^^^^^^^^^^^^^^^^^^^^^^^^
基本用法
"""""""""""
主机库 API 提供函数 :cpp:func:`usb_host_client_handle_events`,可以处理特定客户端事件。该函数需要反复调用,通常是从客户端任务中调用。函数 :cpp:func:`usb_host_client_handle_events` 有以下特点:
- 该函数可以持续阻塞,直至需要处理客户端事件。
- 该函数的主要目的是发生客户端事件时,调用多个事件处理回调函数。
以下回调函数均从 :cpp:func:`usb_host_client_handle_events` 中调用,因此客户端任务能够获取事件通知。
- 类型为 :cpp:type:`usb_host_client_event_cb_t` 的客户端事件回调函数会将客户端事件消息传递给客户端,提示添加、移除设备等事件。
- 类型为 :cpp:type:`usb_transfer_cb_t` 的 USB 传输完成回调函数表明,先前由客户端提交的特定 USB 传输已完成。
.. note::
考虑到上述回调函数从 :cpp:func:`usb_host_client_handle_events` 中调用,应避免在回调函数内部阻塞,否则将导致 :cpp:func:`usb_host_client_handle_events` 阻塞,阻止其他待处理的客户端事件得到处理。
以下代码片段展示了一个基础的主机 Class 驱动程序及其客户端任务,代码片段中包括:
- 一个简单的客户端任务函数 ``client_task``,它会在循环中调用 :cpp:func:`usb_host_client_handle_events`
- 使用客户端事件回调函数和传输完成回调函数。
- 一个用于 Class 驱动程序的简单状态机。该 Class 驱动程序仅支持打开设备,发送 OUT 传输到 EP1,然后关闭设备。
.. code-block:: c
#include <string.h>
#include "usb/usb_host.h"
#define CLASS_DRIVER_ACTION_OPEN_DEV 0x01
#define CLASS_DRIVER_ACTION_TRANSFER 0x02
#define CLASS_DRIVER_ACTION_CLOSE_DEV 0x03
struct class_driver_control {
uint32_t actions;
uint8_t dev_addr;
usb_host_client_handle_t client_hdl;
usb_device_handle_t dev_hdl;
};
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
{
//该函数从 usb_host_client_handle_events() 中调用,请勿在此阻塞,并尽量保持简洁
struct class_driver_control *class_driver_obj = (struct class_driver_control *)arg;
switch (event_msg->event) {
case USB_HOST_CLIENT_EVENT_NEW_DEV:
class_driver_obj->actions |= CLASS_DRIVER_ACTION_OPEN_DEV;
class_driver_obj->dev_addr = event_msg->new_dev.address; //存储新设备的地址
break;
case USB_HOST_CLIENT_EVENT_DEV_GONE:
class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV;
break;
default:
break;
}
}
static void transfer_cb(usb_transfer_t *transfer)
{
//该函数从 usb_host_client_handle_events() 中调用,请勿在此阻塞,并尽量保持简洁
struct class_driver_control *class_driver_obj = (struct class_driver_control *)transfer->context;
printf("Transfer status %d, actual number of bytes transferred %d\n", transfer->status, transfer->actual_num_bytes);
class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV;
}
void client_task(void *arg)
{
... //等待主机库安装
//初始化 Class 驱动程序对象
struct class_driver_control class_driver_obj = {0};
//注册客户端
usb_host_client_config_t client_config = {
.is_synchronous = false,
.max_num_event_msg = 5,
.async = {
.client_event_callback = client_event_cb,
.callback_arg = &class_driver_obj,
}
};
usb_host_client_register(&client_config, &class_driver_obj.client_hdl);
//分配一个 USB 传输
usb_transfer_t *transfer;
usb_host_transfer_alloc(1024, 0, &transfer);
//事件处理循环
bool exit = false;
while (!exit) {
//调用客户端事件处理函数
usb_host_client_handle_events(class_driver_obj.client_hdl, portMAX_DELAY);
//执行待处理的 Class 驱动程序操作
if (class_driver_obj.actions & CLASS_DRIVER_ACTION_OPEN_DEV) {
//开启设备,声明接口 1
usb_host_device_open(class_driver_obj.client_hdl, class_driver_obj.dev_addr, &class_driver_obj.dev_hdl);
usb_host_interface_claim(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1, 0);
}
if (class_driver_obj.actions & CLASS_DRIVER_ACTION_TRANSFER) {
//发送一个 OUT 传输到 EP1
memset(transfer->data_buffer, 0xAA, 1024);
transfer->num_bytes = 1024;
transfer->device_handle = class_driver_obj.dev_hdl;
transfer->bEndpointAddress = 0x01;
transfer->callback = transfer_cb;
transfer->context = (void *)&class_driver_obj;
usb_host_transfer_submit(transfer);
}
if (class_driver_obj.actions & CLASS_DRIVER_ACTION_CLOSE_DEV) {
//释放接口,关闭设备
usb_host_interface_release(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1);
usb_host_device_close(class_driver_obj.client_hdl, class_driver_obj.dev_hdl);
exit = true;
}
... //处理其他 Class 驱动程序要求的行为
}
//清理 Class 驱动程序
usb_host_transfer_free(transfer);
usb_host_client_deregister(class_driver_obj.client_hdl);
... //删除客户端任务。如有需要,向守护进程任务发送信号。
}
.. note::
在实际应用中,主机 Class 驱动程序还能支持更多功能,因此也存在更复杂的状态机。主机 Class 驱动程序可能需要:
- 能够开启多个设备
- 解析已开启设备的描述符,确定设备是否是目标 Class
- 按特定顺序,与接口的多个端点通信
- 声明设备的多个接口
- 处理各种错误情况
生命周期
"""""""""
客户端任务与 Class 驱动程序的典型生命周期包括以下关键阶段:
#. 等待与主机库有关的信号完成安装。
#. 通过 :cpp:func:`usb_host_client_register` 注册客户端,并分配其他的 Class 驱动程序资源,如使用 :cpp:func:`usb_host_transfer_alloc` 分配传输。
#. 对于 Class 驱动程序需要与之通信的各个新设备:
a. 调用 :cpp:func:`usb_host_device_addr_list_fill`,检查设备是否已连接。
b. 如果设备尚未连接,则等待客户端事件回调函数的 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` 事件。
c. 调用 :cpp:func:`usb_host_device_open` 开启设备。
d. 分别调用 :cpp:func:`usb_host_get_device_descriptor`:cpp:func:`usb_host_get_active_config_descriptor`,解析设备和配置描述符。
e. 调用 :cpp:func:`usb_host_interface_claim`,声明设备的必要接口。
#. 调用 :cpp:func:`usb_host_transfer_submit`:cpp:func:`usb_host_transfer_submit_control`,向设备提交传输。
#. 一旦 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` 事件表示,Class 驱动程序不再需要已打开的设备,或者设备断开连接:
a. 在这些端点上调用 :cpp:func:`usb_host_endpoint_halt`:cpp:func:`usb_host_endpoint_flush`,停止先前提交的传输。
b. 调用 :cpp:func:`usb_host_interface_release`,释放先前声明的所有接口。
c. 调用 :cpp:func:`usb_host_device_close`,关闭设备。
#. 调用 :cpp:func:`usb_host_client_deregister` 注销客户端,并释放其他 Class 驱动程序资源。
#. 删除客户端任务。如有需要,向守护进程任务发送信号。
.. ---------------------------------------------------- 电源管理 -----------------------------------------------
电源管理
----------------
全局挂起/恢复
^^^^^^^^^^^^^^^^^^^^^
USB 主机库支持全局挂起/恢复,也就是挂起或恢复整个 USB 总线。全局挂起恢复通过根端口实现。挂起时,USB 主机会停止发送 SOF 包,从而使连接到 USB 总线的所有设备进入挂起状态。
请注意,全局挂起/恢复并 **不会** 在 USB 主机端挂起/恢复 USB-OTG 外设,因此并 **不会** 降低主机控制器本身的功耗。
事件
^^^^^^
客户端事件
"""""""""""""
当设备被挂起或恢复时,所有已打开该受影响设备的客户端都会通过以下事件收到通知。每个事件都包含该被挂起或已恢复设备的句柄:
- :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_SUSPENDED` — 设备已进入挂起状态。
- :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_RESUMED` — 设备已恢复运行。
USB 主机库事件
""""""""""""""""""""
下列事件与自动挂起定时器相关联。定时器超时后,其回调函数会解除 USB 主机库处理程序的阻塞并传递该事件:
- :cpp:enumerator:`USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND` — 表示自动挂起定时器已超时。
自动挂起定时器
^^^^^^^^^^^^^^^^^^^^^^^^^^^
可使用 :cpp:func:`usb_host_lib_set_auto_suspend` 配置自动挂起定时器。每当 USB 主机库的客户端处理函数处理来自任意客户端的事件时,或当 USB 主机库本身在处理任何事件时,自动挂起定时器都会被重置。
自动挂起定时器会主动监测 USB 总线活动以及 USB 主机库的活动情况。具体如下:
- 如果检测到 USB 流量(即发生任何传输或由客户端处理的事件),定时器会自动重置。
- 如果检测到 USB 主机库的活动(即有新设备连接),定时器会自动重置。
- 如果 USB 总线和 USB 主机库保持空闲(无流量、客户端事件或库事件),定时器继续倒计时。
- 一旦定时器达到指定的超时时间(以毫秒为单位),将触发挂起。
这种机制确保 USB 主机仅在总线真正空闲时才进入挂起模式,避免在活跃通信期间出现意外挂起。
自动挂起定时器的重要注意事项:
- 定时器可配置,即使未连接任何设备也会开始倒计时。
- 所有设备断开连接后,定时器停止。
- 设备连接或断开时,定时器自动重置。
- 定时器超时后,只有在以下情况下才会派发 USB 主机库事件:
- 当前有设备连接到根端口,并且
- 根端口尚未处于挂起状态
自动挂起定时器可以配置为以下几种模式:
- **单次** (:cpp:enumerator:`USB_HOST_LIB_AUTO_SUSPEND_ONE_SHOT`):定时器超时一次后停止。
- **周期性** (:cpp:enumerator:`USB_HOST_LIB_AUTO_SUSPEND_PERIODIC`):定时器在每次超时后自动重启,无限重复。
由传输提交触发自动恢复
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
除了自动挂起定时器之外,USB 主机库还支持在传输提交(控制或非控制)时对已挂起的根端口进行自动恢复。通过此功能,开发人员只需调用传输 API(例如 :cpp:func:`usb_host_transfer_submit`:cpp:func:`usb_host_transfer_submit_control`)即可发起恢复,无需显式调用 :cpp:func:`usb_host_lib_root_port_resume`
当根端口处于挂起状态,且有客户端向已挂起的设备提交传输请求时,USB 主机库将执行以下流程:
1. 自动向根端口发起恢复信号。
2. 暂停处理该传输请求,直到恢复信号完成。
3. 待总线进入活动(已恢复)状态后提交传输。
这一机制简化了客户端实现,避免了因手动触发恢复与提交传输并行进行而可能产生的竞态条件,确保恢复行为一致且安全。
.. note::
仅在根端口处于挂起状态时,才能通过传输提交进行自动恢复。若根端口已处于活动状态,传输提交将照常进行。
下面的代码片段演示了如何使用自动挂起定时器和传输提交自动恢复功能,在挂起与恢复状态之间循环切换。
.. code-block:: c
#include "usb/usb_host.h"
static void client_task(void *arg)
{
while(true) {
// 向挂起的设备提交传输,以自动恢复设备
// 借助自动挂起定时器,设备在无操作 1 秒后自动进入挂起模式
usb_host_transfer_submit(xfer_out);
// 切换上下文 10 秒。设备将在空闲约 1 秒后自动挂起
vTaskDelay(pdMS_TO_TICKS(10000));
}
}
void usb_host_lib_task(void *arg)
{
...
// 将自动挂起定时器设置为周期模式,周期为 1 秒,
// 用于在无操作 1 秒后自动挂起设备,并在超时后自动重启
usb_host_lib_set_auto_suspend(USB_HOST_LIB_AUTO_SUSPEND_PERIODIC, 1000);
while (1) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND) {
// 自动挂起定时器超时,挂起根端口
usb_host_lib_root_port_suspend();
}
...
}
...
}
.. note::
关于挂起与恢复的更多细节,请参阅 `USB 2.0 规范 <https://www.usb.org/document-library/usb-20-specification>`_ > 第 11.9 章 *Suspend and Resume*
.. ---------------------------------------------------- Examples -------------------------------------------------------
示例
--------
主机库示例
^^^^^^^^^^^^^^^^^^^^^
:example:`peripherals/usb/host/usb_host_lib` 演示了如何使用 USB 主机库 API 来安装和注册客户端、等待设备连接、打印设备信息和处理断开连接,并重复这些步骤,直到退出应用程序。
Class 驱动程序示例
^^^^^^^^^^^^^^^^^^^^^^^^
USB 主机栈提供了大量示例,展示了如何通过使用主机库 API 创建主机 Class 驱动程序。
CDC-ACM
"""""""
* 通信设备 Class(抽象控制模型)的主机 Class 驱动程序通过 `乐鑫组件注册表 <https://components.espressif.com/component/espressif/usb_host_cdc_acm>`__ 作为受管理的组件分发。
* 示例 :example:`peripherals/usb/host/cdc` 演示了如何使用 CDC-ACM 主机驱动程序,让 {IDF_TARGET_NAME} 与 USB CDC-ACM 设备通信,包括 CP210x、FTDI FT23x 或 CH34x 等厂商设备。
* 示例 `esp_modem <https://github.com/espressif/esp-protocols/tree/master/components/esp_modem/examples>`__ 中也使用了 CDC-ACM 驱动程序,该程序在这些示例中与蜂窝模块通信。
MSC
"""
* 大容量存储 Class(仅支持批量传输)的主机 Class 驱动程序已部署到 `乐鑫组件注册表 <https://components.espressif.com/component/espressif/usb_host_msc>`__
* 示例 :example:`peripherals/usb/host/msc` 演示了如何使用 USB 大容量存储类来访问、读取、写入和操作 USB 闪存驱动器,包括处理 USB 重新连接和反初始化 USB 主机堆栈。
HID
"""
* HID(人机接口设备)的主机 class 驱动作为托管组件通过 `乐鑫组件注册表 <https://components.espressif.com/components/espressif/usb_host_hid>`__ 分发。
* 示例 :example:`peripherals/usb/host/hid` 演示了如何在 {IDF_TARGET_NAME} 上实现基本的 USB 主机 HID 类驱动,以便与 USB HID 设备(如键盘和鼠标)进行通信,并持续扫描设备的连接状态。一旦连接成功,即获取 HID 报告。
UVC
"""
* USB 视频设备 Class 的主机 Class 驱动程序作为托管组件通过 `乐鑫组件注册表 <https://components.espressif.com/component/espressif/usb_host_uvc>`__ 分发。
* 示例 :example:`peripherals/usb/host/uvc` 演示了如何使用 UVC 驱动程序从 USB 摄像头捕获视频帧。
.. ---------------------------------------------- USB Host Menuconfig --------------------------------------------------
.. only:: esp32s3
外部 PHY 配置
-------------
{IDF_TARGET_NAME} 内部集成了两个 USB 控制器 —— USB-OTG 和 USB-Serial-JTAG。这两个控制器 **共用同一个 PHY**,因此同一时间只能有一个控制器工作。如果在 USB-Serial-JTAG 工作时(如调试或烧录)时仍需使用 USB 主机功能,必须使用 **外部 PHY**,因为此时内部 PHY 已被 USB-Serial-JTAG 占用。
.. note::
使用外部 PHY 并不是在 USB 主机或设备功能开启时同时实现调试的唯一办法。也可以通过烧录对应的 eFuse,将调试接口从 USB-Serial-JTAG 切换为传统的 JTAG 接口。具体步骤请参考 ESP-IDF 编程指南中针对你的芯片的 `JTAG 调试 <https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-guides/jtag-debugging/index.html>`_ 章节。
{IDF_TARGET_NAME} 支持连接外部 PHY 芯片,从而实现 USB-OTG 和 USB-Serial-JTAG 控制器的独立工作。不同的外部 PHY 芯片可能需要不同的硬件配置,具体请参阅各芯片的规格书。乐鑫官方文档提供了通用的连接示意图用于参考:`使用外部 PHY <https://docs.espressif.com/projects/esp-iot-solution/en/latest/usb/usb_overview/usb_phy.html#use-an-external-phy>`__
**已测试的外部 PHY 芯片如下:**
- **SP5301** — {IDF_TARGET_NAME} 原生支持此芯片。原理图与布线方法请参考上文链接。
- **TUSB1106** — {IDF_TARGET_NAME} 原生支持。可通过 GPIO 映射与外部 PHY 驱动配合使用。请遵循 TUSB1106 数据手册中的参考连接(供电方案以及在 D+/D– 上建议的串联电阻)。
- **STUSB03E** — 需要通过模拟开关进行信号路由。请参考下方示例。
.. figure:: ../../../_static/usb_host/ext_phy_schematic_stusb03e.png
:align: center
:alt: 使用模拟开关的外部 PHY 原理图(主机模式)
使用 STUSB03E 与模拟开关的连接示例(主机模式)
.. note::
此原理图为简化示例,用于演示外部 PHY 连接方式,未包含完整 {IDF_TARGET_NAME} 设计所需的所有元器件和信号(如 VCC、GND、RESET 等)。
图中包含 +5 V 电源轨(用于为 USB 设备供电)和 VCC 电源轨(通常为 3.3 V)。VCC 电压应与芯片供电电压保持一致。确保为 USB 总线提供的 +5 V 电源可靠,并具备必要的保护措施(如电源开关和限流设计)在支持 USB 总线供电设备时,务必遵守 USB 主机的供电规范。
硬件配置通过将 GPIO 映射到 PHY 引脚实现。任何未使用的引脚(如 :cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num` **必须设置为 -1**
.. note::
:cpp:member:`usb_phy_ext_io_conf_t::suspend_n_io_num` 引脚 **当前不支持**,无需连接。
**示例代码:**
.. code-block:: c
// 外部 PHY 的 GPIO 配置
const usb_phy_ext_io_conf_t ext_io_conf = {
.vp_io_num = 8,
.vm_io_num = 5,
.rcv_io_num = 11,
.oen_io_num = 17,
.vpo_io_num = 4,
.vmo_io_num = 46,
.fs_edge_sel_io_num = 38,
.suspend_n_io_num = -1,
};
// 针对 OTG 控制器(Host 模式)的外部 PHY 配置与初始化
const usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_EXT,
.otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_FULL,
.ext_io_conf = &ext_io_conf
};
usb_phy_handle_t phy_hdl;
ESP_ERROR_CHECK(usb_new_phy(&phy_config, &phy_hdl));
// 配置 USB 主机使用外部初始化的 PHY
usb_host_config_t host_config = {
.skip_phy_setup = true,
// 根据需求添加其他 host 配置字段
};
ESP_ERROR_CHECK(usb_host_install(&host_config));
该配置确保 USB 主机协议栈使用 **外部 PHY**,并跳过 PHY 初始化步骤。
主机栈配置
----------
非兼容设备支持
^^^^^^^^^^^^^^
为了支持某些非兼容或具有特定行为的 USB 设备,可以对 USB 主机栈进行配置。
USB 设备可能是热插拔的,因此必须配置电源开关和设备连接之间的延迟,以及设备内部电源稳定后的延迟。
枚举配置
""""""""
在枚举已连接 USB 设备的过程中,需要给一些事件配置合适的间隔时间以确保设备正常运行。
.. figure:: ../../../_static/usb_host/poweron-timings.png
:align: center
:alt: USB 根集线器上电和连接事件时序
USB 根集线器上电和连接事件时序
上图展示了与连接设备时开启端口电源和热插拔设备相关的所有间隔时间。
* 端口复位或恢复运行后,USB 系统软件应提供 10 毫秒的恢复时间,此后连接到端口的设备才会响应数据传输。
* 恢复时间结束后,如果设备收到 ``SetAddress()`` 请求,设备必须能够完成对该请求的处理,并能在 50 毫秒内成功完成请求的状态 (Status) 阶段。
* 状态阶段结束后,设备允许有 2 毫秒的 ``SetAddress()`` 恢复时间。
.. note::
有关连接事件时序的更多信息,请参阅 `通用串行总线 2.0 规范 <https://www.usb.org/document-library/usb-20-specification>`_ > 第 7.1.7.3 章 *连接和断开信令*
可通过 Menuconfig 选项设置 USB 主机栈的可配置参数。
* `CONFIG_USB_HOST_DEBOUNCE_DELAY_MS` 用于配置防抖延迟。
* `CONFIG_USB_HOST_RESET_HOLD_MS` 用于配置重置保持时间。
* `CONFIG_USB_HOST_RESET_RECOVERY_MS` 用于配置重置恢复时间。
* `CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS` 用于配置 ``SetAddress()`` 恢复时间。
下游端口配置
^^^^^^^^^^^^
当支持外部 Hub 功能时,可以为外部 Hub 端口配置多个参数。
每个外部 Hub 都有一个 Hub 描述符,用于描述设备特性。
.. note::
有关 Hub 描述符的详细信息,请参考 `USB 2.0 规范 <https://www.usb.org/document-library/usb-20-specification>`_ > 章节 11.23.2.1 *Hub Descriptor*
可以通过 Menuconfig 配置下游端口的可配置参数。
* 对于在端口上电后稳定电源的自定义值(PwrOn2PwrGood 值),请参阅 `CONFIG_USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_MS`
* 对于复位恢复间隔,请参阅 `CONFIG_USB_HOST_EXT_PORT_RESET_RECOVERY_DELAY_MS`
.. note::
规范规定,对于没有电源开关的 Hub,PwrOn2PwrGood 必须设置为零。同时,对于某些设备,可以增加此值以提供额外的上电时间。如需启用此功能,请参考 `CONFIG_USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_ENABLE`
主机通道
"""""""""""""
当启用外部 Hub 支持功能(`CONFIG_USB_HOST_HUBS_SUPPORTED`)时,主机通道的数量非常重要,因为每个下游设备都需要空闲通道。
每个连接的设备需要不同数量的通道,而所需通道数则取决于设备类别(EP 数量)。
对于 {IDF_TARGET_NAME},支持的通道数量为 {IDF_TARGET_OTG_NUM_HOST_CHAN}。
.. note::
- 需要一个空闲通道来枚举设备。
- 需要 1 到 N(N 为 EP 数量)个空闲通道来占用接口。
- 如果所有的主机通道都已经被占用,则设备无法进行枚举,也无法获取接口。
多项配置支持
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
对于具有多项配置的 USB 设备,可以在设备枚举过程中指定所需的配置编号。
枚举过滤器
""""""""""""""""""
枚举过滤器是类型为 :cpp:type:`usb_host_enum_filter_cb_t` 的回调函数。从新连接的 USB 设备上读取设备描述符后,USB 主机栈会在枚举过程开始时调用枚举过滤器,从而为用户提供读取的设备描述符。借助此回调,用户得以:
* 选择 USB 设备的配置。
* 过滤应该进行枚举的 USB 设备。
在 menuconfig 中启用 `CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK` 选项即可启用枚举过滤器。可以通过设置 :cpp:member:`usb_host_config_t::enum_filter_cb` 来指定回调函数,该函数会在调用 :cpp:func:`usb_host_install` 时传递至主机库。
.. -------------------------------------------------- API Reference ----------------------------------------------------
API 参考
--------
USB 主机库的 API 包含以下头文件,但应用程序调用该 API 时只需 ``#include "usb/usb_host.h"``,就可以包含所有 USB 主机库的头文件。
- `usb/include/usb/usb_host.h` 包含 USB 主机库的函数和类型。
- `usb/include/usb/usb_helpers.h` 包含与 USB 协议相关的各种辅助函数,如描述符解析等。
- `usb/include/usb/usb_types_stack.h` 包含在 USB 主机栈的多个层次中使用的类型。
- `usb/include/usb/usb_types_ch9.h` 包含了与 USB 2.0 规范中第 9 章相关的类型和宏,即描述符和标准请求。
- `usb/include/usb/usb_types_ch11.h` 包含与 USB2.0 规范第 11 章相关的类型和宏,即集线器规范。
头文件
^^^^^^^
- ``usb_host.h`` 可以通过以下方式包含:
.. code:: c
#include "usb/usb_host.h"
- 该头文件是 ``usb`` 组件提供的 API 的一部分。``usb`` 组件通过 `乐鑫组件注册表 <https://components.espressif.com/components/espressif/usb>`__ 分发。因此,若要使用该组件,请通过以下命令将 Host Stack 组件添加为依赖项:
.. code:: bash
idf.py add-dependency usb
.. ------------------------------------------------ Maintainers Notes --------------------------------------------------
维护注意事项
-----------------
.. note::
有关 USB 主机栈内部实现的更多细节,请参阅 :doc:`/api-reference/peripherals/usb_host/usb_host_notes_index`
.. toctree::
:hidden:
:maxdepth: 0
usb_host/usb_host_notes_index
@@ -1,60 +0,0 @@
USB 主机维护者注释(架构)
===========================
:link_to_translation:`en:[English]`
主机协议栈大致分为多个抽象层,每个层代表不同的 USB 概念和不同级别的 USB 主机操作。例如,较高的层可能呈现为设备和应用数据传输的抽象,而较低的层则为端点和 USB 传输的抽象。
层描述
------
从最低层(离用户最远的层)到最高层(离用户最近的层),主机协议栈的层描述如下表所示:
.. list-table:: 主机协议栈的各层功能简述
:widths: 20 10 70
:header-rows: 1
* - 层
- 文件
- 描述
* - 主机控制器 (DWC_OTG)
- N/A
- 此层代表 {IDF_TARGET_NAME} 的 USB 控制器硬件,所提供的 API 是控制器的寄存器接口。
* - LL
- ``usbh_ll.h``
- LL(低级)层根据 ESP-IDF 的 :doc:`硬件抽象 API 指南 <../../../api-guides/hardware-abstraction>` 抽象了 USB 控制器的基本寄存器访问。换句话说,此层提供的 API 能访问控制寄存器,也可以格式化/解析控制器的 DMA 描述符。
* - HAL
- ``usbh_hal.h`` ``usbh_hal.c``
- HAL(硬件抽象层)根据 ESP-IDF 的 :doc:`硬件抽象 API 指南 <../../../api-guides/hardware-abstraction>`,将 USB 控制器的操作步骤抽象成函数。此层还将控制器的主机端口和主机通道抽象化,并提供操作它们的 API。
* - HCD
- ``hcd.h`` ``hcd.c``
- HCD(主机控制器驱动程序)是一个与硬件无关的 API,适用于任何 USB 控制器(理论上,此 API 可以与任何 USB 控制器实现一同使用)。此层还抽象了根端口(根集线器)和 USB 管道。
* - USBH 和集线器驱动程序
- ``usbh.h`` ``usbh.c``
- USBH(USB 主机驱动程序)层等效于 USB2.0 规范第 10 章中描述的 USBD 层。USBH 提供 USB 设备的抽象,在内部管理已连接设备的列表(即,设备池),还仲裁客户端之间的设备共享(即,跟踪正在使用的端点并提供一个共享端点 0)。
* - 集线器驱动程序
- ``hub.h`` ``hub.c``
- 集线器驱动程序层是 USBH 的特殊客户端,负责处理设备的连接/断开,并通知 USBH 此类事件。对于设备的连接,集线器驱动程序还会处理枚举过程。
* - USB 主机库
- ``usb_host.h`` ``usb_host.c``
- USB 主机库层是主机协议栈的最低公共 API 层,并呈现 USB 主机客户端的概念。客户端的抽象允许多个 class 驱动程序同时存在(其中每个类大致映射到一个单独的客户端),这也是一种分工机制(其中每个客户端负责各自的处理以及事件处理)。
* - 主机 Class 驱动程序
- 请参阅 `ESP-USB 存储库 <https://github.com/espressif/esp-usb>`_ 或是 ESP-IDF 中的 USB 主机示例 :example:`peripherals/usb/host`
- 主机 Class 驱动程序能实现特定设备类(例如,CDC、MSC、HID)的主机端。每个 class 驱动程序具有特定的公开 API 接口。
层依赖关系
----------
主机协议栈大致遵循自顶向下的层次结构,具有层间依赖关系。假设存在层 A(最高层)、B 、 C(最低层),则主机协议栈具有以下层间依赖规则:
- 特定层可以使用其直接下层的 API(层 A 可以使用层 B 的 API)。
- 特定层可以使用其间接下层的 API(层 A 可以使用层 C 的 API),即可以隔层调用。
- 特定层不能使用其任何上层的 API(层 C 无法使用层 A/B 的 API)。
.. note::
允许跳过层次以避免在多个层次之间重复相同的抽象。例如,管道的抽象都位于 HCD 层,但被上面的多个层使用。
.. todo::
添加图示以说明不同层之间的 API 依赖关系
@@ -1,144 +0,0 @@
USB 主机维护者注意事项(设计指南)
=====================================
:link_to_translation:`en:[English]`
设计考虑
--------
设计主机协议栈时应考虑以下要点:
**硬件资源有限:**
与较大的主机系统相比,嵌入式主机协议栈的硬件资源(如内存和处理能力)有限。
**USB 2.0 第 10 章:**
USB 2.0 规范的第 10 章中规定了 USB 主机系统的某些要求,特别是 USB 主机系统软件所需的必要层次。
**用例复杂性广泛:**
嵌入式主机协议栈用例广泛,包含不同的复杂性。一些 USB 主机应用只支持单个厂商的特定设备,而其他应用则需要支持不同类别的各种设备。
要求
----
考虑到以上设计要素,在设计主机协议栈时满足了以下要求:
支持 DMA
^^^^^^^^^^
**要求:主机协议栈必须支持 DMA。**
为降低 CPU 负载,主机协议栈必须支持 DMA。通过 DMA,无需 CPU 干预,即可自动复制 USB 传输数据到主机控制器中,以及自动从主机控制器中复制 USB 传输数据。这一点对于嵌入式 CPU(即,较低的 CPU 频率)和较大的最大 USB 传输数据负载(例如,同步传输的 1023 字节)来说尤为重要。
减少内存拷贝
^^^^^^^^^^^^
**要求:在不同层之间传递数据时主机协议栈应尽量减少内存拷贝次数。**
各种数据和对象(例如 USB 传输)需要在主机协议栈的多个层之间传递。主机协议栈应一次性为数据或对象分配内存,并在各层之间传递指向该数据或对象的指针,从而减少各层间的内存拷贝次数。因此,主机协议栈需要能够在不同层之间共享的标准化数据类型(参见 USB 2.0 规范的第 10.3.4 节)。
支持事件驱动
^^^^^^^^^^^^
**要求:主机协议栈必须支持事件驱动(即,主机协议栈的 API 不是轮询的方式)。**
主机协议栈应支持 CPU 密集型应用场景,例如视频流传输(即,UVC Class)。因此,主机协议栈应支持事件驱动操作,减少 CPU 占用,从而将大部分 CPU 时间留给应用本身(如,视频编码或解码)。
主机协议栈需要通过中断、回调和 FreeRTOS 同步原语(例如队列和信号量)在各层之间传递事件。
不得创建任务
^^^^^^^^^^^^
**要求:主机库层及其下层的所有主机协议栈层都不得创建任何任务。**
任务栈通常是 ESP-IDF 应用程序中最占内存的部分之一。由于应用场景广泛,创建的任务数量(以及任务的栈大小)差别可能很大。例如:
- 需要低延迟或高吞吐量(例如同步传输)的应用可能会创建一个专门的任务来处理传输数据,确保延迟最小化。
- 对延迟要求不高(例如批量传输)的应用可能会通过共享任务来处理传输数据,以节省内存。
因此,主机库层及其下层的所有主机协议栈层都不得创建任何任务,而应公开处理函数,供上层创建的任务调用。任务创建将委托给 Class 驱动程序或应用程序层。
可在不同层操作
^^^^^^^^^^^^^^
主机协议栈用例广泛,情况复杂,应确保其可在不同层操作。因此无论是在较低层(例如 HCD 或 HAL),还是在较高层(例如 Class 驱动程序),用户都可以使用主机协议栈。
用户可以在不同的层上使用主机协议栈,并根据应用程序的特点,权衡便利性、可控性和优化度,例如:
- 支持专用定制设备的主机协议栈应用可能会使用较低级别的抽象,从而调用优化度高、可控性强、简单便利的 API。
- 支持各种设备 Class 的主机协议栈应用需要使用完整的主机协议栈,以便自动处理设备枚举。
编码约定
--------
主机协议栈遵循下列编码规范,以提高代码可读性与可维护性:
符号应使用层名作为前缀
^^^^^^^^^^^^^^^^^^^^^^
主机协议栈每层公开的符号(即函数、类型、宏)必须以该层的名称作为前缀。例如,HCD 层公开的符号将以 ``hcd_...````HCD_...`` 为前缀。
但内部符号(例如静态函数)**不应** 以其所在层的名称作为前缀,以便修改该层源代码时能更好地区分内部和外部符号。
临界区函数应以 ``_`` 为前缀
^^^^^^^^^^^^^^^^^^^^^^^^^^^
主机协议栈的每层中都有各种必须在临界区内调用的静态函数。这些函数的名称以 ``_`` 为前缀(例如,``_func_called_from_crit()``),以便维护者更好地区分哪些函数应该从临界区内调用。例如:
.. code-block:: c
some_func(); // 从临界区外调用
taskENTER_CRITICAL(&some_lock);
_some_func_crit(); // 从临界区内调用。使用 _ 前缀,使其便于区分
taskEXIT_CRITICAL(&some_lock);
根据锁机制对结构体成员进行分组
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
主机协议栈的某些层使用多种锁机制(例如临界区和任务互斥锁)来确保线程安全。每种锁机制能提供不同级别的保护,而同一对象的成员变量受不同锁机制保护。因此,为了清晰地区分不同的锁机制及其相关变量,结构体成员按锁机制被分组为嵌套结构体。
.. list-table:: 锁机制
:widths: 20 10 80
:header-rows: 1
* - 锁机制
- 嵌套结构体
- 描述
* - 临界区
- ``dynamic``
- 从任务上下文和 ISR 上下文访问的共享数据受临界区保护。
* - 任务互斥锁
- ``mux_protected``
- 仅从任务上下文访问的共享数据受 FreeRTOS 互斥锁保护。
* - 单线程
- ``single_thread``
- 只由同一任务访问的数据无需锁保护。
* - 常量
- ``constant``
- 在对象实例化时设置常量数据,之后常量将不再改变。因此,只要变量不被写入,任何任务或 ISR 都可以自由访问常量数据。
根据锁机制对结构体成员进行分组,能清晰地显示访问特定成员变量时需要哪种锁机制,使得代码易于维护,如下所示:
.. code-block:: c
typedef struct some_obj some_obj_t;
some_obj_t obj;
// 访问动态成员需要临界区
taskENTER_CRITICAL(&some_lock);
obj.dynamic.varA = 1;
taskEXIT_CRITICAL(&some_lock);
// 访问受互斥锁保护的成员需要获取互斥锁
xSemaphoreTake(&some_mux, portMAX_DELAY);
obj.mux_protected.varB = 1;
xSemaphoreGive(&some_mux);
// 只由某一任务访问的单线程成员数据无需锁保护
obj.single_thread.varC = 1;
// 访问常量成员无需锁保护,但此种访问为只读
int local_var = obj.constant.varD;
@@ -1,224 +0,0 @@
USB 主机维护者注意事项(DWC_OTG 控制器)
=========================================
:link_to_translation:`en:[English]`
{IDF_TARGET_NAME} 使用 DesignWare USB 2.0 On-the-Go 控制器(后文简称为 DWC_OTG)作为其底层硬件控制器。启用分散/聚集式 DMA 功能后,DWC_OTG 将以主机模式运行。
.. note::
本节高度概括了 DWC_OTG 在主机模式下的操作。有关 DWC_OTG 的详细信息,请参阅 DWC_OTG 技术规格书和编程指南。
主机模式操作模型
----------------
下图展示了 DWC_OTG 在主机模式下的简化操作模型。该图包含了 DWC_OTG 主机模式下的一些关键概念及术语。
.. figure:: ../../../../_static/usb_host/dwc-otg-operation.png
:align: center
:alt: DWC_OTG 主机模式操作模型
:figclass: align-center
.. note::
详情请参阅技术规格书第 2.1.4 节(主机架构)。
主机端口
^^^^^^^^^
主机端口指的是 DWC_OTG 提供的单个 USB 端口(在 USB 术语中,也被视为总线根集线器的单个 USB 端口)。主机端口通常只能连接一个设备,但可以通过集线器连接更多设备。
主机端口具有以下功能:
- 检测直连设备的连接/断开情况。
- 检测直连设备的速度。
- 发送各种总线信号(如挂起、恢复、复位)。
主机通道
^^^^^^^^
在主机模式下,DWC_OTG 使用通道与设备端点通信,每个通道会映射到特定的端点(在 USB 术语中,通道是管道的硬件表示)。例如,有个通道将映射到 EP 1 OUT。每个通道都有一组控制和状态寄存器 (CSR),可以进行独立配置和控制,主要用于:
- 指定通道目标端点的详细信息(例如,设备地址、端点编号、传输类型、通道方向)。
- 启动通道传输(如设置 DMA 描述符)。
使用分散/聚集式 DMA 功能时,主机通道上的传输完全由事件驱动,用户只需填写恰当的 DMA 描述符,填充通道的 CSR,然后启用通道即可。传输完成后,通道将生成中断。
数据 FIFO
^^^^^^^^^^
主机模式下,DWC_OTG 使用多个 FIFO 作为 USB 传输数据负载的暂存区。使用 DMA 时,DMA 引擎将在 TX/RX FIFO 和 {IDF_TARGET_NAME} 的内部存储器之间复制数据:
- 对于 OUT 传输,数据负载将由 DMA 从主内存复制到某个 TX FIFO 中。MAC 层将按照 USB 数据包格式传输该数据负载。
- 对于 IN 传输,MAC 层会解析接收到的 USB 数据包,并将接收的数据负载存储在 RX FIFO 中。之后,由 DMA 将数据复制到主内存中。
目标 FIFO 由通道的方向和传输类型决定:
- 所有 IN 通道数据进入 RX FIFO。
- 所有非周期性(即控制和批量)OUT 通道数据进入非周期性 TX (NPTX) FIFO。
- 所有周期性(即中断和同步)OUT 通道数据进入周期性 TX (PTX) FIFO。
.. note::
非周期性和周期性 OUT 通道数据应分别进入 NPTX 和 PTX FIFO,因为中断和同步端点具有周期性特性(由端点的 ``bInterval`` 值指定)。DWC_OTG 会自动调度周期性传输,因此 PTX FIFO 能单独暂存这些周期性传输。
DMA 引擎
^^^^^^^^
DMA 引擎负责在 FIFO 和主内存之间复制数据。启用主机模式分散/聚集式 DMA 功能,无需软件干预,特定通道即可自动执行多个传输。下图展示了 DWC_OTG 主机模式分散/聚集式 DMA 内存结构。
.. figure:: ../../../../_static/usb_host/dwc-otg-scatter-gather.png
:align: center
:alt: DWC_OTG 主机模式分散/聚集式 DMA 内存结构
:figclass: align-center
- USB 传输由队列传输描述符 (QTD) 描述。每个 QTD 包含:
- 一个 32 位的 buffer 状态四元组,用来指定有关传输的详细信息,且会在传输完成后报告传输状态。buffer 状态四元组可以指定 QTD 在传输完成后生成中断或是停止通道,也指定上述两种情况同时发生。
- 一个 32 位指针,指向一个包含 OUT 传输数据负载的 buffer,或是指向一个空的 buffer,这个空 buffer 将用来存储 IN 传输的数据负载。
- 每个 QTD 的数据负载可以大于其目标端点的最大数据包大小 (MPS)。DWC_OTG 能够自动拆分传输。
- 多个 QTD 可以放入一个 QTD 列表中。通道将自动执行列表中的每个 QTD,并可设置是否要循环执行。
- 在通道开始传输数据之前,会为其配置 QTD 列表(以及 QTD 列表长度)。一旦启用通道,DWC_OTG 将自动开始 USB 传输。
- 在特定 QTD 或整个 QTD 列表结束任务后,通道将生成中断(可配置)。
.. note::
详情请参阅编程指南第 6.2.1 节(描述符内存结构)。
硬件配置
--------
DWC_OTG IP 是可配置的。有关 {IDF_TARGET_NAME} 的 DWC_OTG 的重要主机配置,请参阅下表:
.. only:: esp32p4
.. list-table:: {IDF_TARGET_NAME} 的 DWC_OTG 配置
:widths: 70 30
:header-rows: 1
* - 描述
- 配置
* - 支持 OTG 的主机和设备模式
- ``OTG_MODE = 0``
* - 支持高速 (HS)、全速 (FS) 和低速 (LS)
- ``OTG_FSPHY_INTERFACE = 2````OTG_HSPHY_INTERFACE = 3``
* - 支持分散/聚集式 DMA 功能的内部 DMA 控制器
- ``OTG_ARCHITECTURE = 2````OTG_EN_DESC_DMA = 1``
* - 不支持分割传输
- ``OTG_SINGLE_POINT = 1``
* - 16 个主机模式通道
- ``OTG_NUM_HOST_CHAN = 16``
* - 支持包括 ISOC 和 INTR OUT 传输在内的所有传输类型
- ``OTG_EN_PERIO_HOST = 1``
* - 动态大小的 4096 字节(1024 行)数据 FIFO
- ``OTG_DFIFO_DYNAMIC = 1````OTG_DFIFO_DEPTH = 1024``
* - 每个微帧仅支持 4 个周期性和 4 个非周期性事务
- ``OTG_NPERIO_TX_QUEUE_DEPTH = 4````OTG_PERIO_TX_QUEUE_DEPTH = 4``
.. only:: esp32s2 or esp32s3 or esp32h4
.. list-table:: {IDF_TARGET_NAME} 的 DWC_OTG 配置
:widths: 70 30
:header-rows: 1
* - 描述
- 配置
* - 支持 OTG 的主机和设备模式
- ``OTG_MODE = 0``
* - 支持全速 (FS) 和低速 (LS)
- ``OTG_FSPHY_INTERFACE = 1````OTG_HSPHY_INTERFACE = 0``
* - 支持分散/聚集式 DMA 功能的内部 DMA 控制器
- ``OTG_ARCHITECTURE = 2````OTG_EN_DESC_DMA = 1``
* - 8 个主机模式通道
- ``OTG_NUM_HOST_CHAN = 8``
* - 支持包括 ISOC 和 INTR OUT 传输在内的所有传输类型
- ``OTG_EN_PERIO_HOST = 1``
* - 动态大小的 1024 字节(256 行)数据 FIFO
- ``OTG_DFIFO_DYNAMIC = 1``, ``OTG_DFIFO_DEPTH = 256``
分散/聚集式 DMA 传输
---------------------
主机通道传输的基本操作步骤如下:
#. 准备好数据 buffer、QTD 及 QTD 列表,确保有 QTD 能在完成任务后停止通道并生成中断。
#. 通过 CSR 设置通道和端点的特性(如 EP 地址、传输类型、EP MPS 等)。
#. 设置有关通道 QTD 列表的 CSR(如 QTD 列表指针和 QTD 列表长度)及通道中断 CSR。
#. 启用通道。硬件将使用 DMA 自动处理传输。
#. 在发生通道事件(如 QTD 完成任务或通道报错)时,通道将生成中断。
#. 解析通道中断,确定通道事件类型。
#. 解析 QTD,确定每个单独传输的结果。
若传输类型不同,在通道操作和 QTD 列表使用上也会有一些细微差别。
批量传输
^^^^^^^^
批量传输最为简单。每个 QTD 代表特定方向的批量传输,DWC_OTG 会自动将特定 QTD 拆分为多个 MPS 大小的传输。因此,可以用多次批量传输填充一个 QTD 列表,并自动执行整个列表(即,只在最后一个 QTD 完成任务时发送中断)。
控制传输
^^^^^^^^
控制传输是双向的,因而较为复杂(即,每个控制传输阶段可以有不同的方向)。每个阶段需要一个单独的 QTD,并且每个 QTD 必须在完成任务后停止传输通道,从而确保能通过重新配置通道的 CSR 来改变通道的方向。通常来说,控制传输需要 3 个 QTD(每个阶段一个)。
中断传输
^^^^^^^^
根据 USB 2.0 规范,中断传输在端点指定的服务周期(即 ``bInterval``)执行传输事务。特定中断端点在一个服务周期内不得执行多次中断传输。服务周期由微帧或帧的数量指定,因此特定中断端点通常每隔 N 个微帧或帧执行一次传输,直至完成传输。特定中断通道的服务周期(即 ``bInterval``)由主机帧列表指定(详见编程指南第 6.5 节)。
.. note::
HS USB 允许一个中断端点在一个微帧内执行三次中断传输。详情请参阅 USB 2.0 规范第 5.7.3 节(中断传输数据包大小限制)。
总的来说,主机模式分散/聚集式 DMA 中的中断传输具有以下特点:
- 如果 QTD 数据负载大于端点的 MPS,通道会自动将传输拆分为多个 MPS 大小的传输事务(类似于批量传输)。但每次传输将在端点指定的服务周期内执行(即每个 ``bInterval`` 时间段内执行一次传输),直至完成传输。
- 对于中断 IN 传输,若收到短包(即传输的数据负载小于 MPS),则表明端点已无需要发送的数据,此时:
- 即便 QTD 未设置 IOC(完成时中断)位,通道也会生成额外的通道中断。
- 即使生成了额外的通道中断,通道也不会停止。
- 软件必须使用这个额外的中断手动停止中断通道,从而取消 QTD 列表中剩余的 QTD。
.. note::
基于上述中断传输的特点,对于软件来说,为每次传输分配一个 QTD 可能比为整个传输分配一个 QTD 更加容易。
同步传输
^^^^^^^^
根据 USB 2.0 规范,同步传输在端点指定的服务周期(即 ``bInterval``)执行传输事务,以实现恒定的数据传输速率。特定同步端点在一个服务周期内不得执行多次中断传输。服务周期由微帧或帧的数量指定,因此特定同步端点通常每隔 N 个微帧或帧执行一次传输,直至完成传输。特定同步通道的服务周期(即 ``bInterval``)由主机帧列表指定(详见编程指南第 6.5 节)。
但与中断传输不同,由于同步传输需要保持恒定的数据传输速率,即便传输失败(或收到 NAK),同步传输也不会重试,
.. note::
HS USB 允许一个同步端点在一个微帧内执行三次同步传输。详情请参阅 USB 2.0 规范第 5.6.3 节(同步传输数据包大小限制)。
总的来说,主机模式分散/聚集式 DMA 中的同步传输具有以下特点:
- 必须为每微帧或帧分配一个 QTD,但非服务周期的 QTD 应保持空白(即,如果通道的服务周期是每 N 个微帧或帧,则只需填充每第 N 个 QTD)。
- **每个已填充的 QTD 只能代表一次传输事务,而非整个传输过程**
- 同步传输不会在失败时重试,因此必须检查每个完成任务的 QTD 的状态。
补充说明
--------
某些 DWC_OTG 行为在技术规格书或编程指南中均未提及。本节解释了一些与主机协议栈的实现相关的行为。
端口错误不会触发通道中断
^^^^^^^^^^^^^^^^^^^^^^^^
一个或多个通道运行时,若发生端口错误(例如突然断连或端口过流),则:
- 通道仍可运行(即,保持设置 ``HCCHAR.ChEna``)且不会生成通道中断。
- 理论上可以通过设置 ``HCCHAR.ChDis`` 来禁用通道,但这对同步通道无效,因为通道禁用中断未能生成。
因此,发生端口错误时,应使用控制器软复位以确保禁用所有通道。
端口复位中断
^^^^^^^^^^^^
- DWC_OTG 在其端口上发出复位信号,如果设备在此期间断开连接,则断连中断(即 ``HPRT.PrtConnDet``)不会在取消复位前生成。
- 在复位已经启用的端口(即 ``HPRT.PrtEna``)时,如枚举期间的二次复位或是设备运行期间的复位,无论是在复位信号生效还是失效时,都会生成端口启用/禁用更改中断(即 ``HPRT.PrtEnChng``)。
@@ -1,3 +0,0 @@
.. Translation not required: According to the USB developers, all of the ``usb_host_notes_*`` files are just internal notes for ESP-IDF developers, not for our end users. So we don't need to translate them at all.
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_enum.rst
@@ -1 +0,0 @@
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_ext_hub.rst
@@ -1 +0,0 @@
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_ext_port.rst
@@ -1 +0,0 @@
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_index.rst
@@ -1,3 +0,0 @@
.. Translation not required: According to the USB developers, all of the ``usb_host_notes_*`` files are just internal notes for ESP-IDF developers, not for our end users. So we don't need to translate them at all.
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_usbh.rst
+13
View File
@@ -150,8 +150,21 @@ KNOWN_MISSING = {
'peripherals/spi_slave_hd/segment_mode/seg_slave', 'peripherals/spi_slave_hd/segment_mode/seg_slave',
'peripherals/twai/twai_network/twai_listen_only', 'peripherals/twai/twai_network/twai_listen_only',
'peripherals/twai/twai_network/twai_sender', 'peripherals/twai/twai_network/twai_sender',
# TODO: remove once the checker can also account for the ESP-USB programming guide.
'peripherals/usb/device/cherryusb_serial_device', 'peripherals/usb/device/cherryusb_serial_device',
'peripherals/usb/device/tusb_composite_msc_serialdevice',
'peripherals/usb/device/tusb_console',
'peripherals/usb/device/tusb_hid',
'peripherals/usb/device/tusb_midi',
'peripherals/usb/device/tusb_msc',
'peripherals/usb/device/tusb_ncm',
'peripherals/usb/device/tusb_serial_device',
'peripherals/usb/host/cdc',
'peripherals/usb/host/cherryusb_host', 'peripherals/usb/host/cherryusb_host',
'peripherals/usb/host/hid',
'peripherals/usb/host/msc',
'peripherals/usb/host/usb_host_lib',
'peripherals/usb/host/uvc',
# TODO IDF-15383: add :example: references for protocols examples # TODO IDF-15383: add :example: references for protocols examples
'protocols/dns_over_https', 'protocols/dns_over_https',
'protocols/http_request', 'protocols/http_request',