feat(usb/host): Modifiy usb_host_lib example

The example keeps handling connections/disconnections indefinitely.
Added kconfig.projbuild for GPIO selection.
GPIO0 press does terdown/uninstall.
This commit is contained in:
Peter Marcisovsky
2023-10-19 14:05:49 +02:00
parent 5e1826678d
commit 546b76befa
5 changed files with 206 additions and 69 deletions

View File

@@ -5,19 +5,21 @@
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates the basic usage of the [USB Host Library API](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html) by implementing a pseudo class driver and a Host Library daemon task. The example does the following: This example demonstrates the basic usage of the [USB Host Library API](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html) by implementing a pseudo class driver and a Host Library task. The example does the following:
1. Install Host Library and register a client 1. Install Host Library and register a client
2. Waits for a device connection 2. Waits for a device connection
3. Prints the device's information (such as device/configuration/string descriptors) 3. Prints the device's information (such as device/configuration/string descriptors)
4. Waits for the device to disconnect 4. Waits for the device to disconnect
5. Deregister the client and uninstall the Host Library 5. Repeats steps 2 to 4 until a user pressess a button, which quits the `app`
6. If the button has been pressed, while a USB device is still connected, the user will be prompted to remove the device and push the button again to quit the `app`
7. Deregister the client, uninstall the Host Library and quit the `app`
The example demonstrates the following aspects of the USB Host Library API: The example demonstrates the following aspects of the USB Host Library API:
- How to use the Library API to: - How to use the Library API to:
- Install and uninstall the USB Host Library - Install and uninstall the USB Host Library
- Run the library event handler function a daemon task - Run the library event handler function and usb host library task
- How to handle library events - How to handle library events
- How to use the Client API from a client task to: - How to use the Client API from a client task to:
- Register and deregister a client of the USB Host Library - Register and deregister a client of the USB Host Library
@@ -30,7 +32,7 @@ The example demonstrates the following aspects of the USB Host Library API:
### Hardware Required ### Hardware Required
An ESP board that supports USB-OTG. The example uses the ESP's internal USB PHY, however the internal USB PHY's pins will need to be connected to a USB port (i.e., a USB breakout board) as follows: An ESP board that has a push button and supports USB-OTG. The example uses the ESP's internal USB PHY, however the internal USB PHY's pins will need to be connected to a USB port (i.e., a USB breakout board) as follows:
- GND and 5V signals of the ESP board to the GND and 5V lines of the USB port - GND and 5V signals of the ESP board to the GND and 5V lines of the USB port
- GPIO 19 to D- - GPIO 19 to D-
@@ -43,6 +45,7 @@ idf.py menuconfig
``` ```
* The USB Host Stack has a maximum supported transfer size for control transfer during device enumeration. This size is specified via the USB_HOST_CONTROL_TRANSFER_MAX_SIZE configuration option and has a default value of 256 bytes. Therefore, if devices with length config/string descriptors are used, users may want to increase the size of this configuration. * The USB Host Stack has a maximum supported transfer size for control transfer during device enumeration. This size is specified via the USB_HOST_CONTROL_TRANSFER_MAX_SIZE configuration option and has a default value of 256 bytes. Therefore, if devices with length config/string descriptors are used, users may want to increase the size of this configuration.
* Push button GPIO selection
### Build and Flash ### Build and Flash
@@ -61,14 +64,17 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
## Example Output ## Example Output
``` ```
I (261) cpu_start: Starting scheduler on PRO CPU. I (305) main_task: Started on CPU0
I (267) DAEMON: Installing USB Host Library I (315) main_task: Calling app_main()
I (297) CLASS: Registering Client I (315) USB host lib: USB host library example
I (5067) CLASS: Opening device at address 1 I (315) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (5067) CLASS: Getting device information I (325) USB host lib: Installing USB Host Library
I (5067) CLASS: Full speed I (365) CLASS: Registering Client
I (5067) CLASS: bConfigurationValue 1 I (745) CLASS: Opening device at address 1
I (5067) CLASS: Getting device descriptor I (745) CLASS: Getting device information
I (745) CLASS: Full speed
I (745) CLASS: bConfigurationValue 1
I (745) CLASS: Getting device descriptor
*** Device descriptor *** *** Device descriptor ***
bLength 18 bLength 18
bDescriptorType 1 bDescriptorType 1
@@ -84,7 +90,7 @@ iManufacturer 1
iProduct 2 iProduct 2
iSerialNumber 3 iSerialNumber 3
bNumConfigurations 1 bNumConfigurations 1
I (5097) CLASS: Getting config descriptor I (775) CLASS: Getting config descriptor
*** Configuration descriptor *** *** Configuration descriptor ***
bLength 9 bLength 9
bDescriptorType 2 bDescriptorType 2
@@ -153,12 +159,20 @@ bMaxPower 500mA
bmAttributes 0x2 BULK bmAttributes 0x2 BULK
wMaxPacketSize 64 wMaxPacketSize 64
bInterval 1 bInterval 1
I (5227) CLASS: Getting Manufacturer string descriptor I (855) CLASS: Getting Manufacturer string descriptor
Espressif Espressif
I (5237) CLASS: Getting Product string descriptor I (855) CLASS: Getting Product string descriptor
USB JTAG/serial debug unit USB JTAG/serial debug unit
I (5247) CLASS: Getting Serial Number string descriptor I (865) CLASS: Getting Serial Number string descriptor
7C:DF:A1:E0:10:50 7C:DF:A1:E0:10:50
W (2855) USB host lib: To shutdown example, remove all USB devices and press button again.
E (6135) USBH: Device 1 gone
I (9545) CLASS: Deregistering Client
I (9545) USB host lib: No more clients
I (9545) USB host lib: All devices marked as free
I (9545) USB host lib: No more clients and devices
I (9645) USB host lib: End of the example
I (9645) main_task: Returned from app_main()
``` ```
## Troubleshooting ## Troubleshooting

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "usb_host_lib_main.c" "class_driver.c" idf_component_register(SRCS "usb_host_lib_main.c" "class_driver.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
PRIV_REQUIRES usb PRIV_REQUIRES usb driver
) )

View File

@@ -0,0 +1,12 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config APP_QUIT_PIN
int "APP Quit button GPIO pin"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 0
help
GPIO pin number to be used as APP_QUIT button.
endmenu

View File

@@ -12,13 +12,16 @@
#define CLIENT_NUM_EVENT_MSG 5 #define CLIENT_NUM_EVENT_MSG 5
#define ACTION_OPEN_DEV 0x01 typedef enum {
#define ACTION_GET_DEV_INFO 0x02 ACTION_OPEN_DEV = 0x01,
#define ACTION_GET_DEV_DESC 0x04 ACTION_GET_DEV_INFO = 0x02,
#define ACTION_GET_CONFIG_DESC 0x08 ACTION_GET_DEV_DESC = 0x04,
#define ACTION_GET_STR_DESC 0x10 ACTION_GET_CONFIG_DESC = 0x08,
#define ACTION_CLOSE_DEV 0x20 ACTION_GET_STR_DESC = 0x10,
#define ACTION_EXIT 0x40 ACTION_CLOSE_DEV = 0x20,
ACTION_EXIT = 0x40,
ACTION_RECONNECT = 0x80,
} action_t;
typedef struct { typedef struct {
usb_host_client_handle_t client_hdl; usb_host_client_handle_t client_hdl;
@@ -28,6 +31,7 @@ typedef struct {
} class_driver_t; } class_driver_t;
static const char *TAG = "CLASS"; static const char *TAG = "CLASS";
static class_driver_t *s_driver_obj;
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
{ {
@@ -122,24 +126,20 @@ static void action_get_str_desc(class_driver_t *driver_obj)
driver_obj->actions &= ~ACTION_GET_STR_DESC; driver_obj->actions &= ~ACTION_GET_STR_DESC;
} }
static void aciton_close_dev(class_driver_t *driver_obj) static void action_close_dev(class_driver_t *driver_obj)
{ {
ESP_ERROR_CHECK(usb_host_device_close(driver_obj->client_hdl, driver_obj->dev_hdl)); ESP_ERROR_CHECK(usb_host_device_close(driver_obj->client_hdl, driver_obj->dev_hdl));
driver_obj->dev_hdl = NULL; driver_obj->dev_hdl = NULL;
driver_obj->dev_addr = 0; driver_obj->dev_addr = 0;
//We need to exit the event handler loop //We need to connect a new device
driver_obj->actions &= ~ACTION_CLOSE_DEV; driver_obj->actions &= ~ACTION_CLOSE_DEV;
driver_obj->actions |= ACTION_EXIT; driver_obj->actions |= ACTION_RECONNECT;
} }
void class_driver_task(void *arg) void class_driver_task(void *arg)
{ {
SemaphoreHandle_t signaling_sem = (SemaphoreHandle_t)arg;
class_driver_t driver_obj = {0}; class_driver_t driver_obj = {0};
//Wait until daemon task has installed USB Host Library
xSemaphoreTake(signaling_sem, portMAX_DELAY);
ESP_LOGI(TAG, "Registering Client"); ESP_LOGI(TAG, "Registering Client");
usb_host_client_config_t client_config = { usb_host_client_config_t client_config = {
.is_synchronous = false, //Synchronous clients currently not supported. Set this to false .is_synchronous = false, //Synchronous clients currently not supported. Set this to false
@@ -150,6 +150,7 @@ void class_driver_task(void *arg)
}, },
}; };
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &driver_obj.client_hdl)); ESP_ERROR_CHECK(usb_host_client_register(&client_config, &driver_obj.client_hdl));
s_driver_obj = &driver_obj;
while (1) { while (1) {
if (driver_obj.actions == 0) { if (driver_obj.actions == 0) {
@@ -171,18 +172,29 @@ void class_driver_task(void *arg)
action_get_str_desc(&driver_obj); action_get_str_desc(&driver_obj);
} }
if (driver_obj.actions & ACTION_CLOSE_DEV) { if (driver_obj.actions & ACTION_CLOSE_DEV) {
aciton_close_dev(&driver_obj); action_close_dev(&driver_obj);
} }
if (driver_obj.actions & ACTION_EXIT) { if (driver_obj.actions & ACTION_EXIT) {
break; break;
} }
if (driver_obj.actions & ACTION_RECONNECT) {
driver_obj.actions = 0;
}
} }
} }
ESP_LOGI(TAG, "Deregistering Client"); ESP_LOGI(TAG, "Deregistering Client");
ESP_ERROR_CHECK(usb_host_client_deregister(driver_obj.client_hdl)); ESP_ERROR_CHECK(usb_host_client_deregister(driver_obj.client_hdl));
//Wait to be deleted
xSemaphoreGive(signaling_sem);
vTaskSuspend(NULL); vTaskSuspend(NULL);
} }
void class_driver_client_deregister(void)
{
if (s_driver_obj->dev_hdl != NULL) {
s_driver_obj->actions = ACTION_CLOSE_DEV;
}
s_driver_obj->actions |= ACTION_EXIT;
// Unblock, exit the loop and proceed to deregister client
ESP_ERROR_CHECK(usb_host_client_unblock(s_driver_obj->client_hdl));
}

View File

@@ -1,27 +1,78 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "usb/usb_host.h" #include "usb/usb_host.h"
#include "driver/gpio.h"
#define DAEMON_TASK_PRIORITY 2 #define HOST_LIB_TASK_PRIORITY 2
#define CLASS_TASK_PRIORITY 3 #define CLASS_TASK_PRIORITY 3
#define APP_QUIT_PIN CONFIG_APP_QUIT_PIN
extern void class_driver_task(void *arg); extern void class_driver_task(void *arg);
extern void class_driver_client_deregister(void);
static const char *TAG = "DAEMON"; static const char *TAG = "USB host lib";
static void host_lib_daemon_task(void *arg) QueueHandle_t app_event_queue = NULL;
/**
* @brief APP event group
*
* APP_EVENT - General event, which is APP_QUIT_PIN press event in this example.
*/
typedef enum {
APP_EVENT = 0,
} app_event_group_t;
/**
* @brief APP event queue
*
* This event is used for delivering events from callback to a task.
*/
typedef struct {
app_event_group_t event_group;
} app_event_queue_t;
/**
* @brief BOOT button pressed callback
*
* Signal application to exit the Host lib task
*
* @param[in] arg Unused
*/
static void gpio_cb(void *arg)
{ {
SemaphoreHandle_t signaling_sem = (SemaphoreHandle_t)arg; const app_event_queue_t evt_queue = {
.event_group = APP_EVENT,
};
BaseType_t xTaskWoken = pdFALSE;
if (app_event_queue) {
xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken);
}
if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/**
* @brief Start USB Host install and handle common USB host library events while app pin not low
*
* @param[in] arg Not used
*/
static void usb_host_lib_task(void *arg)
{
ESP_LOGI(TAG, "Installing USB Host Library"); ESP_LOGI(TAG, "Installing USB Host Library");
usb_host_config_t host_config = { usb_host_config_t host_config = {
.skip_phy_setup = false, .skip_phy_setup = false,
@@ -29,9 +80,8 @@ static void host_lib_daemon_task(void *arg)
}; };
ESP_ERROR_CHECK(usb_host_install(&host_config)); ESP_ERROR_CHECK(usb_host_install(&host_config));
//Signal to the class driver task that the host library is installed //Signalize the app_main, the USB host library has been installed
xSemaphoreGive(signaling_sem); xTaskNotifyGive(arg);
vTaskDelay(10); //Short delay to let client task spin up
bool has_clients = true; bool has_clients = true;
bool has_devices = true; bool has_devices = true;
@@ -39,52 +89,101 @@ static void host_lib_daemon_task(void *arg)
uint32_t event_flags; uint32_t event_flags;
ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags)); ESP_ERROR_CHECK(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_NO_CLIENTS) {
ESP_LOGI(TAG, "No more clients");
has_clients = false; has_clients = false;
if (ESP_OK == usb_host_device_free_all()) {
ESP_LOGI(TAG, "All devices marked as free");
} else {
ESP_LOGI(TAG, "Wait for the ALL FREE EVENT");
}
} }
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGI(TAG, "No more devices");
has_devices = false; has_devices = false;
} }
} }
ESP_LOGI(TAG, "No more clients and devices"); ESP_LOGI(TAG, "No more clients and devices");
//Uninstall the USB Host Library //Uninstall the USB Host Library
ESP_ERROR_CHECK(usb_host_uninstall()); ESP_ERROR_CHECK(usb_host_uninstall());
//Wait to be deleted
xSemaphoreGive(signaling_sem);
vTaskSuspend(NULL); vTaskSuspend(NULL);
} }
void app_main(void) void app_main(void)
{ {
SemaphoreHandle_t signaling_sem = xSemaphoreCreateBinary(); ESP_LOGI(TAG, "USB host library example");
TaskHandle_t daemon_task_hdl; // Init BOOT button: Pressing the button simulates app request to exit
TaskHandle_t class_driver_task_hdl; // It will uninstall the class driver and USB Host Lib
//Create daemon task const gpio_config_t input_pin = {
xTaskCreatePinnedToCore(host_lib_daemon_task, .pin_bit_mask = BIT64(APP_QUIT_PIN),
"daemon", .mode = GPIO_MODE_INPUT,
4096, .pull_up_en = GPIO_PULLUP_ENABLE,
(void *)signaling_sem, .intr_type = GPIO_INTR_NEGEDGE,
DAEMON_TASK_PRIORITY, };
&daemon_task_hdl, ESP_ERROR_CHECK(gpio_config(&input_pin));
0); ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1));
//Create the class driver task ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_cb, NULL));
xTaskCreatePinnedToCore(class_driver_task,
"class",
4096,
(void *)signaling_sem,
CLASS_TASK_PRIORITY,
&class_driver_task_hdl,
0);
app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t));
app_event_queue_t evt_queue;
TaskHandle_t host_lib_task_hdl, class_driver_task_hdl;
//Create usb host lib task
BaseType_t task_created;
task_created = xTaskCreatePinnedToCore(usb_host_lib_task,
"usb_host",
4096,
xTaskGetCurrentTaskHandle(),
HOST_LIB_TASK_PRIORITY,
&host_lib_task_hdl,
0);
assert(task_created == pdTRUE);
//Wait unit the USB host library is installed
ulTaskNotifyTake(false, 1000);
//Create class driver task
task_created = xTaskCreatePinnedToCore(class_driver_task,
"class",
4096,
NULL,
CLASS_TASK_PRIORITY,
&class_driver_task_hdl,
0);
assert(task_created == pdTRUE);
vTaskDelay(10); //Add a short delay to let the tasks run vTaskDelay(10); //Add a short delay to let the tasks run
//Wait for the tasks to complete while (1) {
for (int i = 0; i < 2; i++) { if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) {
xSemaphoreTake(signaling_sem, portMAX_DELAY); if (APP_EVENT == evt_queue.event_group) {
// User pressed button
usb_host_lib_info_t lib_info;
ESP_ERROR_CHECK(usb_host_lib_info(&lib_info));
if (lib_info.num_devices == 0) {
// End while cycle
break;
} else {
ESP_LOGW(TAG, "To shutdown example, remove all USB devices and press button again.");
// Keep polling
}
}
}
} }
//Deregister client
class_driver_client_deregister();
vTaskDelay(10);
//Delete the tasks //Delete the tasks
vTaskDelete(class_driver_task_hdl); vTaskDelete(class_driver_task_hdl);
vTaskDelete(daemon_task_hdl); vTaskDelete(host_lib_task_hdl);
// Delete interrupt and queue
gpio_isr_handler_remove(APP_QUIT_PIN);
xQueueReset(app_event_queue);
vQueueDelete(app_event_queue);
ESP_LOGI(TAG, "End of the example");
} }