Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions device/esp_tinyusb/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [Unreleased]

- Fixed forward compatibility with TinyUSB 0.19
- Added VBUS monitoring feature for ESP32P4 USB OTG 2.0 (HS)

## 2.0.1

Expand Down
9 changes: 9 additions & 0 deletions device/esp_tinyusb/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
idf_build_get_property(target IDF_TARGET)

set(srcs
"descriptors_control.c"
"tinyusb.c"
Expand Down Expand Up @@ -43,6 +45,13 @@ if(CONFIG_TINYUSB_NET_MODE_NCM)
)
endif() # CONFIG_TINYUSB_NET_MODE_NCM

# Software VBUS monitoring is available only on esp32p4
if(${target} STREQUAL "esp32p4")
list(APPEND srcs
"tinyusb_vbus_monitor.c"
)
endif() # esp32p4

idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "include_private"
Expand Down
84 changes: 79 additions & 5 deletions device/esp_tinyusb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ The default installation automatically configures the port (High-speed if suppor

Default descriptors are provided for the following USB classes: CDC, MSC, and NCM.

> **⚠️ Important:** For demonstration purposes, all error handling logic has been removed from the code examples. Do not ignore proper error handling in actual development.
> ⚠️ For demonstration purposes, all error handling logic has been removed from the code examples. Do not ignore proper error handling in actual development.

```c
#include "tinyusb_default_config.h"
Expand Down Expand Up @@ -208,10 +208,12 @@ Values of default descriptors could be configured via `menuconfig`.

### USB PHY configuration & Self-Powered Device

For self-powered devices, monitoring the VBUS voltage is required. To do this:
For self-powered USB devices, the peripheral must be able to detect the VBUS voltage to know when it is connected to, or disconnected from, a USB host.

- Configure a GPIO pin as an input, using an external voltage divider or comparator to detect the VBUS state.
- Set `self_powered = true` and assign the VBUS monitor GPIO in the `tinyusb_config_t` structure.
To enable this:

- Connect VBUS to a GPIO input (typically through a voltage divider or external comparator).
- Set `self_powered = true` and assign the VBUS monitor GPIO in `tinyusb_config_t`.

```c
#include "tinyusb_default_config.h"
Expand All @@ -226,7 +228,79 @@ For self-powered devices, monitoring the VBUS voltage is required. To do this:
tinyusb_driver_install(&tusb_cfg);
}
```
If external PHY is used:

#### ESP32-P4 (USB OTG 2.0, High-Speed)

> **⚠️ Important:**
>
> On **USB OTG 1.1** (`TINYUSB_PORT_FULL_SPEED_0`), the VBUS **BVALID** signal is handled in hardware.
>
> On **USB OTG 2.0** (`TINYUSB_PORT_HIGH_SPEED_0`), there is no hardware **BVALID** detection – it is implemented in the driver.

For the high-speed port, the driver uses a combination of ISR-driven GPIO state detection plus a software debounce timer on the VBUS monitor GPIO. This filters glitches and cable plug/unplug noise.

- The debounce interval is controlled by `vbus_monitor_debounce_ms` in `tinyusb_config_t`.
- If not set explicitly, the default debounce time is 250 ms.

> **Note:**
>
> The `vbus_monitor_debounce_ms` member also exists in `tinyusb_config_t` when using `TINYUSB_PORT_FULL_SPEED_0`, but it is not used by the driver in that mode because VBUS is already handled in hardware.

You can override the debounce interval in the driver configuration:

```c
#include "tinyusb_default_config.h"

void app_main(void)
{
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();

tusb_cfg.phy.self_powered = true;
tusb_cfg.phy.vbus_monitor_io = GPIO_NUM_0;
tusb_cfg.phy.vbus_monitor_debounce_ms = 350; // new debounce value

tinyusb_driver_install(&tusb_cfg);
}
```

> **🔧 GPIO ISR requirement**:
>
> When VBUS monitoring is enabled with `TINYUSB_PORT_HIGH_SPEED_0` (ESP32-P4 USB OTG 2.0), the GPIO driver ISR service **must be installed before** calling `tinyusb_driver_install()`.

To install GPIO's driver's ISR service:

```c
#include "tinyusb_default_config.h"
#include "driver/gpio.h"
#include "sdkconfig.h"

void app_main(void)
{
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();

tusb_cfg.phy.self_powered = true;
tusb_cfg.phy.vbus_monitor_io = GPIO_NUM_0;

#if (CONFIG_IDF_TARGET_ESP32P4)
// TINYUSB_PORT_HIGH_SPEED_0 is used by default
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
#endif // CONFIG_IDF_TARGET_ESP32P4

tinyusb_driver_install(&tusb_cfg);

// application code

tinyusb_driver_uninstall();

#if (CONFIG_IDF_TARGET_ESP32P4)
gpio_uninstall_isr_service();
#endif // CONFIG_IDF_TARGET_ESP32P4
}
```

The driver can also **skip PHY configuration** if you need to initialize and manage the USB PHY externally (for example, when using a custom or external PHY).

To do this, set `skip_setup = true`:

```c
#include "tinyusb_default_config.h"
Expand Down
4 changes: 3 additions & 1 deletion device/esp_tinyusb/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ targets:
dependencies:
idf: '>=5.0' # IDF 4.x contains TinyUSB as submodule
tinyusb:
version: '>=0.17.0~2' # 0.17.0~2 is the first version that supports deinit
# TODO: Temporary pin to 0.18.0, revert after IEC-403
# version: '>=0.17.0~2' # 0.17.0~2 is the first version that supports deinit
version: '^0.18.0'
public: true
1 change: 1 addition & 0 deletions device/esp_tinyusb/include/tinyusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ typedef struct {
The voltage divider output should be (0.75 * Vdd) if VBUS is 4.4V (lowest valid voltage at device port).
The comparator thresholds should be set with hysteresis: 4.35V (falling edge) and 4.75V (raising edge). */
int vbus_monitor_io; /*!< GPIO for VBUS monitoring, 3.3 V tolerant (use a comparator or a resistior divider to detect the VBUS valid condition). Ignored if not self_powered. */
uint32_t vbus_monitor_debounce_ms; /*!< Debounce delay for VBUS monitoring in milliseconds. Default is 250 ms. Relevant only for ESP32P4 and ignored if not self_powered. */
} tinyusb_phy_config_t;

/**
Expand Down
90 changes: 47 additions & 43 deletions device/esp_tinyusb/include/tinyusb_default_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,54 +63,58 @@ extern "C" {
#define TINYUSB_DEFAULT_TASK_SIZE 4096
// Default priority for task used in TinyUSB task creation
#define TINYUSB_DEFAULT_TASK_PRIO 5
// Default VBUS debounce time in milliseconds
#define TINYUSB_DEFAULT_DEBOUNCE_MS 250

#define TINYUSB_CONFIG_FULL_SPEED(event_hdl, arg) \
(tinyusb_config_t) { \
.port = TINYUSB_PORT_FULL_SPEED_0, \
.phy = { \
.skip_setup = false, \
.self_powered = false, \
.vbus_monitor_io = -1, \
}, \
.task = TINYUSB_TASK_DEFAULT(), \
.descriptor = { \
.device = NULL, \
.qualifier = NULL, \
.string = NULL, \
.string_count = 0, \
.full_speed_config = NULL, \
.high_speed_config = NULL, \
}, \
.event_cb = (event_hdl), \
.event_arg = (arg), \
#define TINYUSB_CONFIG_FULL_SPEED(event_hdl, arg) \
(tinyusb_config_t) { \
.port = TINYUSB_PORT_FULL_SPEED_0, \
.phy = { \
.skip_setup = false, \
.self_powered = false, \
.vbus_monitor_io = -1, \
.vbus_monitor_debounce_ms = TINYUSB_DEFAULT_DEBOUNCE_MS, \
}, \
.task = TINYUSB_TASK_DEFAULT(), \
.descriptor = { \
.device = NULL, \
.qualifier = NULL, \
.string = NULL, \
.string_count = 0, \
.full_speed_config = NULL, \
.high_speed_config = NULL, \
}, \
.event_cb = (event_hdl), \
.event_arg = (arg), \
}

#define TINYUSB_CONFIG_HIGH_SPEED(event_hdl, arg) \
(tinyusb_config_t) { \
.port = TINYUSB_PORT_HIGH_SPEED_0, \
.phy = { \
.skip_setup = false, \
.self_powered = false, \
.vbus_monitor_io = -1, \
}, \
.task = TINYUSB_TASK_DEFAULT(), \
.descriptor = { \
.device = NULL, \
.qualifier = NULL, \
.string = NULL, \
.string_count = 0, \
.full_speed_config = NULL, \
.high_speed_config = NULL, \
}, \
.event_cb = (event_hdl), \
.event_arg = (arg), \
#define TINYUSB_CONFIG_HIGH_SPEED(event_hdl, arg) \
(tinyusb_config_t) { \
.port = TINYUSB_PORT_HIGH_SPEED_0, \
.phy = { \
.skip_setup = false, \
.self_powered = false, \
.vbus_monitor_io = -1, \
.vbus_monitor_debounce_ms = TINYUSB_DEFAULT_DEBOUNCE_MS, \
}, \
.task = TINYUSB_TASK_DEFAULT(), \
.descriptor = { \
.device = NULL, \
.qualifier = NULL, \
.string = NULL, \
.string_count = 0, \
.full_speed_config = NULL, \
.high_speed_config = NULL, \
}, \
.event_cb = (event_hdl), \
.event_arg = (arg), \
}

#define TINYUSB_TASK_DEFAULT() \
(tinyusb_task_config_t) { \
.size = TINYUSB_DEFAULT_TASK_SIZE, \
.priority = TINYUSB_DEFAULT_TASK_PRIO, \
.xCoreID = TINYUSB_DEFAULT_TASK_AFFINITY, \
#define TINYUSB_TASK_DEFAULT() \
(tinyusb_task_config_t) { \
.size = TINYUSB_DEFAULT_TASK_SIZE, \
.priority = TINYUSB_DEFAULT_TASK_PRIO, \
.xCoreID = TINYUSB_DEFAULT_TASK_AFFINITY, \
}

/**
Expand Down
6 changes: 5 additions & 1 deletion device/esp_tinyusb/include_private/tinyusb_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "esp_err.h"
#include "tinyusb.h"
#include "tinyusb_vbus_monitor.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -42,7 +43,10 @@ esp_err_t tinyusb_task_check_config(const tinyusb_task_config_t *task_cfg);
* - ESP_ERR_NO_MEM if memory allocation failed
* - ESP_OK if TinyUSB Task initialized successfully
*/
esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *task_cfg, const tinyusb_desc_config_t *desc_cfg);
esp_err_t tinyusb_task_start(tinyusb_port_t port,
const tinyusb_task_config_t *task_cfg,
const tinyusb_desc_config_t *desc_cfg,
const tinyusb_vbus_monitor_config_t *vbus_monitor_cfg);

/**
* @brief Stops TinyUSB Task
Expand Down
44 changes: 44 additions & 0 deletions device/esp_tinyusb/include_private/tinyusb_vbus_monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include "esp_err.h"
#include "tinyusb.h"
#include "driver/gpio.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
gpio_num_t gpio_num; /*!< GPIO number used for VBUS monitoring, 3.3 V tolerant */
uint32_t debounce_delay_ms; /*!< Debounce delay in milliseconds */
} tinyusb_vbus_monitor_config_t;

/**
* @brief Initialize VBUS monitoring on the specified GPIO
*
* Note:
* - This function should be called after tusb_init() when GOTGCTL register is initialized
* - This is a single-threaded implementation, so only one instance of VBUS monitoring is supported
*
* @param config VBUS monitoring configuration
*
* @return
* - ESP_ERR_INVALID_ARG if config is NULL
* - ESP_OK if VBUS monitoring was initialized successfully
*/
esp_err_t tinyusb_vbus_monitor_init(tinyusb_vbus_monitor_config_t *config);

/**
* @brief Deinitialize VBUS monitoring
*/
void tinyusb_vbus_monitor_deinit(void);

#ifdef __cplusplus
}
#endif
Loading
Loading