mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(usb): add usb wakeup from light sleep example
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(tusb_cdc_acm_wakeup)
|
||||
@@ -0,0 +1,86 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# TinyUSB CDC ACM Wakeup Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example shows how to set up an ESP chip as a USB CDC ACM device that enters light sleep when the USB bus is suspended and wakes up from USB activity.
|
||||
The wakeup source is available only on USB-OTG capable targets with High-Speed USB support. It is not supported by USB Serial/JTAG-only peripherals.
|
||||
|
||||
As a USB stack, a TinyUSB component is used.
|
||||
|
||||
## How to use example
|
||||
|
||||
The example implements a USB CDC ACM echo device. When the host suspends the USB bus, the `tud_suspend_cb` callback configures the USB peripheral suspend state and enters light sleep. USB bus activity from the host wakes the chip and the CDC ACM echo device continues running.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Any ESP board that supports High-Speed USB-OTG wakeup from light sleep.
|
||||
|
||||
#### Pin Assignment
|
||||
|
||||
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below.
|
||||
|
||||
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```bash
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
After the flashing you should see this output:
|
||||
|
||||
```
|
||||
I (285) tusb_cdc_acm_wakeup: USB initialization
|
||||
I (455) TinyUSB: TinyUSB Driver installed
|
||||
I (465) tusb_cdc_acm_wakeup: USB initialization DONE
|
||||
```
|
||||
|
||||
Connect to the serial port (e.g. on Linux, it should be `/dev/ttyACM0`) by any terminal application (e.g. `picocom /dev/ttyACM0`).
|
||||
Now you can send data strings to the device, the device will echo back the same data string.
|
||||
|
||||
To trigger USB suspend from the host, disable or suspend the CDC ACM device on the host side. On Windows, open Device Manager, find the COM port for the device, and disable it. On Linux, you can unbind the CDC ACM driver for the device or put the USB device into autosuspend. On macOS, disconnecting the terminal application and letting the host suspend the interface can also trigger suspend depending on the host power policy.
|
||||
|
||||
When the USB host suspends and resumes the bus, the monitor output should include:
|
||||
|
||||
```
|
||||
I (122142) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (122272) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (122272) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (122402) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (122402) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (122622) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (122622) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (127432) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (132402) tusb_cdc_acm_wakeup: USB suspended, entering light sleep
|
||||
I (132422) tusb_cdc_acm_wakeup: Woke up from: USB
|
||||
I (132422) tusb_cdc_acm_wakeup: USB resumed
|
||||
I (145762) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (145812) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (145812) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (145822) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (145822) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (145832) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (148762) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (148762) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (148902) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (148902) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (149032) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (149032) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (149252) tusb_cdc_acm_wakeup: Data from channel 0:
|
||||
I (149252) tusb_cdc_acm_wakeup: 0x4ff3afa4 48 65 6c 6c 6f |Hello|
|
||||
I (151382) tusb_cdc_acm_wakeup: Line state changed on channel 0: DTR:0, RTS:0
|
||||
I (155312) tusb_cdc_acm_wakeup: USB suspended, entering light sleep
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "tusb_cdc_acm_wakeup_main.c"
|
||||
INCLUDE_DIRS .
|
||||
PRIV_REQUIRES usb hal)
|
||||
@@ -0,0 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "^2.0.1~1"
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_uart.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_private/usb_phy.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tinyusb_cdc_acm.h"
|
||||
#include "tinyusb_default_config.h"
|
||||
|
||||
#if !SOC_PM_SUPPORT_USB_WAKEUP || !SOC_PM_SUPPORT_CNNT_PD
|
||||
#error This example requires a target with SOC_PM_SUPPORT_USB_WAKEUP.
|
||||
#endif
|
||||
|
||||
static const char *TAG = "tusb_cdc_acm_wakeup";
|
||||
static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
|
||||
|
||||
/**
|
||||
* @brief Application Queue
|
||||
*/
|
||||
static QueueHandle_t app_queue;
|
||||
typedef struct {
|
||||
uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; // Data buffer
|
||||
size_t buf_len; // Number of bytes received
|
||||
uint8_t itf; // Index of CDC device interface
|
||||
} app_message_t;
|
||||
|
||||
/**
|
||||
* @brief CDC device RX callback
|
||||
*
|
||||
* CDC device signals, that new data were received
|
||||
*
|
||||
* @param[in] itf CDC device index
|
||||
* @param[in] event CDC event type
|
||||
*/
|
||||
void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
|
||||
{
|
||||
(void) event;
|
||||
|
||||
size_t rx_size = 0;
|
||||
|
||||
esp_err_t ret = tinyusb_cdcacm_read(itf, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
|
||||
if (ret == ESP_OK) {
|
||||
app_message_t tx_msg = {
|
||||
.buf_len = rx_size,
|
||||
.itf = itf,
|
||||
};
|
||||
|
||||
memcpy(tx_msg.buf, rx_buf, rx_size);
|
||||
xQueueSend(app_queue, &tx_msg, 0);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Read Error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CDC device line change callback
|
||||
*
|
||||
* CDC device signals, that the DTR, RTS states changed
|
||||
*
|
||||
* @param[in] itf CDC device index
|
||||
* @param[in] event CDC event type
|
||||
*/
|
||||
void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
|
||||
{
|
||||
int dtr = event->line_state_changed_data.dtr;
|
||||
int rts = event->line_state_changed_data.rts;
|
||||
ESP_LOGI(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts);
|
||||
}
|
||||
|
||||
void tud_suspend_cb(bool remote_wakeup_en)
|
||||
{
|
||||
(void) remote_wakeup_en;
|
||||
|
||||
ESP_LOGI(TAG, "USB suspended, entering light sleep");
|
||||
usb_phy_set_otg_suspend_state(true);
|
||||
|
||||
esp_rom_output_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
|
||||
esp_err_t err = esp_light_sleep_start();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Light sleep rejected: %s", esp_err_to_name(err));
|
||||
} else {
|
||||
uint32_t causes = esp_sleep_get_wakeup_causes();
|
||||
if (causes & BIT(ESP_SLEEP_WAKEUP_UNDEFINED)) {
|
||||
ESP_LOGW(TAG, "Woke up from an unknown source: 0x%lx", causes);
|
||||
} else if (causes & BIT(ESP_SLEEP_WAKEUP_USB)) {
|
||||
ESP_LOGI(TAG, "Woke up from: USB");
|
||||
}
|
||||
}
|
||||
usb_phy_set_otg_suspend_state(false);
|
||||
usb_phy_clear_otg_wakeup_status();
|
||||
}
|
||||
|
||||
void tud_resume_cb(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "USB resumed");
|
||||
}
|
||||
|
||||
void tud_reset_cb(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "USB reset");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Create FreeRTOS primitives
|
||||
app_queue = xQueueCreate(5, sizeof(app_message_t));
|
||||
assert(app_queue);
|
||||
app_message_t msg;
|
||||
|
||||
ESP_ERROR_CHECK(esp_sleep_cpu_retention_init());
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_usb_wakeup());
|
||||
ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_CNNT, ESP_PD_OPTION_ON));
|
||||
|
||||
ESP_LOGI(TAG, "USB initialization");
|
||||
const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
tinyusb_config_cdcacm_t acm_cfg = {
|
||||
.cdc_port = TINYUSB_CDC_ACM_0,
|
||||
.callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback
|
||||
.callback_rx_wanted_char = NULL,
|
||||
.callback_line_state_changed = NULL,
|
||||
.callback_line_coding_changed = NULL
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(tinyusb_cdcacm_init(&acm_cfg));
|
||||
/* the second way to register a callback */
|
||||
ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback(
|
||||
TINYUSB_CDC_ACM_0,
|
||||
CDC_EVENT_LINE_STATE_CHANGED,
|
||||
&tinyusb_cdc_line_state_changed_callback));
|
||||
|
||||
ESP_LOGI(TAG, "USB initialization DONE");
|
||||
while (1) {
|
||||
if (xQueueReceive(app_queue, &msg, portMAX_DELAY)) {
|
||||
if (msg.buf_len) {
|
||||
/* Print received data */
|
||||
ESP_LOGI(TAG, "Data from channel %d:", msg.itf);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, msg.buf, msg.buf_len, ESP_LOG_INFO);
|
||||
|
||||
/* Write back */
|
||||
tinyusb_cdcacm_write_queue(msg.itf, msg.buf, msg.buf_len);
|
||||
esp_err_t err = tinyusb_cdcacm_write_flush(msg.itf, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
CONFIG_TINYUSB_CDC_ENABLED=y
|
||||
Reference in New Issue
Block a user