diff --git a/docs/en/api-reference/peripherals/usb_host.rst b/docs/en/api-reference/peripherals/usb_host.rst index 4a819ffa66..261be0b75c 100644 --- a/docs/en/api-reference/peripherals/usb_host.rst +++ b/docs/en/api-reference/peripherals/usb_host.rst @@ -364,8 +364,8 @@ CDC-ACM * A host class driver for the Communication Device Class (Abstract Control Model) is deployed to `IDF component registry `__. * The :example:`peripherals/usb/host/cdc/cdc_acm_host` example uses the CDC-ACM host driver component to communicate with CDC-ACM devices -* The :example:`peripherals/usb/host/cdc/cdc_acm_bg96` example uses the CDC-ACM host driver component to communicate with non-compliant CDC-ACM devices (i.e., vendor-specific classes that support a subset of CDC-ACM features) such as the Quectel BG96 modem. * The :example:`peripherals/usb/host/cdc/cdc_acm_vcp` example shows how can you extend the CDC-ACM host driver to interface Virtual COM Port devices. +* The CDC-ACM driver is also used in `esp_modem examples `__, where it is used for communication with cellular modems. MSC """ diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt deleted file mode 100644 index 726b664235..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# For more information about build system see -# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html -# 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.16) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(cdc_acm_host_bg96) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md deleted file mode 100644 index 58d7c22d79..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md +++ /dev/null @@ -1,99 +0,0 @@ -| Supported Targets | ESP32-S2 | ESP32-S3 | -| ----------------- | -------- | -------- | - -# USB CDC-ACM Host Driver BG96 Example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) - -This example shows how to set up ESP chip to interface with CDC-like device by using the CDC-ACM Host Driver. CDC-like devices implement a Vendor-specific class, and support a subset of the functions of a fully compliant CDC-ACM device. - -## How to use example - -### Hardware Required - -Any ESP board with USB-OTG supported and a Quectel BG96 LTE/GPS modem. - -Connect USB_D+, USB_D-, GND and +5V signals of ESP board to BG96. - -_Note:_ Quectel BG96 modem must be started after power-up by applying low pulse on PWRKEY (pin 15). - -#### Pin Assignment - -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 the output at idf monitor: - -``` -I (276) BG96: USB Host installed -I (24446) AT: ATE0 -I (24446) AT: -OK - -I (24526) AT: -+QIND: SMS DONE - -I (24646) AT: -APP RDY - -I (25446) BG96: Sending AT -I (25446) AT: -OK - -I (26446) BG96: Enabling GNSS -I (26446) AT: -OK - -GPVTG Sentence: - Track [deg]: 0.00 - Speed [kmph]: 0.00 - Speed [knots]: 0.00 -GPGSA Sentence: - Mode: A - Fix: 1 - PDOP: 0.00 - HDOP: 0.00 - VDOP: 0.00 -GPGGA sentence -Number of satellites: 0 -Altitude: 0.000000 -GPRMC sentence -Longitude: - Degrees: 0 - Minutes: 0.000000 - Cardinal: -Latitude: - Degrees: 0 - Minutes: 0.000000 - Cardinal: -Date & Time: 00 Jan 00:00:00 1900 -Speed, in Knots: 0.000000 -Track, in degrees: 0.000000 -Magnetic Variation: - Degrees: 0.000000 - Cardinal: -Invalid Magnetic Variation Direction! -Adjusted Track (heading): 0.000000 -I (27446) BG96: Sending AT+GSN -I (27446) AT: -860517045660414 - -OK -... - -``` diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt deleted file mode 100644 index 8027d6161d..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "cdc_acm_host_bg96.cpp" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp deleted file mode 100644 index dc3cefa96c..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#pragma once - -#include "usb/cdc_acm_host.h" -#include "esp_log.h" - -#define BG96_VID (0x2C7C) -#define BG96_PID (0x0296) -#define BG96_AT_INTERFACE (2) -#define BG96_NMEA_INTERFACE (1) - -class Bg96Usb { -public: - - explicit Bg96Usb() : at_opened(false) { - }; - - esp_err_t at_start(cdc_acm_data_callback_t data_cb, void *user_arg) - { - // This driver doesn't support CDC notifications. This can lead to silent failures - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 10000, - .out_buffer_size = 64, - .event_cb = NULL, - .data_cb = data_cb, - .user_arg = user_arg, - }; - ESP_ERROR_CHECK(this->at_port.open_vendor_specific(BG96_VID, BG96_PID, BG96_AT_INTERFACE, &dev_config)); - this->at_opened = true; - - // Some FW versions have Echo enabled by default. Disable it with ATE0 command - ESP_LOGD("BG96_USB", "Turning off echo with ATE0"); - ESP_ERROR_CHECK(this->at_port.tx_blocking((uint8_t *)"ATE0\r", 5, 1000)); - vTaskDelay(100); - return ESP_OK; - } - - void at_stop() - { - this->at_port.close(); - this->at_opened = false; - } - - esp_err_t at_write(uint8_t *data, size_t len) - { - ESP_LOG_BUFFER_HEXDUMP("BG96_USB", data, len, ESP_LOG_DEBUG); - return this->at_port.tx_blocking(data, len, 1000); - } - - esp_err_t gnss_start(cdc_acm_data_callback_t data_cb) - { - if (!this->at_opened) { - return ESP_ERR_INVALID_STATE; - } - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 0, // Read-only - .event_cb = NULL, - .data_cb = data_cb, - .user_arg = this, - }; - ESP_ERROR_CHECK(this->nmea_port.open_vendor_specific(BG96_VID, BG96_PID, BG96_NMEA_INTERFACE, &dev_config)); - return this->at_port.tx_blocking((uint8_t*)"AT+QGPS=1\r", 10, 1000); - } - - esp_err_t gnss_stop() - { - esp_err_t ret = this->at_port.tx_blocking((uint8_t*)"AT+QGPSEND\r", 11, 1000); - this->nmea_port.close(); - return ret; - } - -protected: - CdcAcmDevice at_port; // Main control port for AT commands - CdcAcmDevice nmea_port; // Read only port for NMEA messages -private: - bool at_opened; -}; diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp deleted file mode 100644 index 5e7581788c..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" - -#include "usb/usb_host.h" -#include "bg96_usb.hpp" - -#include "nmea.h" -#include "gpgll.h" -#include "gpgga.h" -#include "gprmc.h" -#include "gpgsa.h" -#include "gpvtg.h" -#include "gptxt.h" -#include "gpgsv.h" - -#define EXAMPLE_USB_HOST_PRIORITY 20 - -static const char* TAG = "BG96"; - -static char fmt_buf[32]; - -/* ------------------------------- Callbacks -------------------------------- */ - -static void handle_rx(uint8_t *data, size_t data_len, void *user_arg) -{ - data[data_len] = '\0'; - ESP_LOGI("AT", "%s", data); -} - -static void handle_gps(uint8_t* data, size_t data_len, void *user_arg) -{ - // handle nmea_data - nmea_s *nmea_data = nmea_parse((char *)data, data_len, 0); - if (nmea_data == NULL) { - printf("Failed to parse the sentence!\n"); - printf(" Type: %.5s (%d)\n", data + 1, nmea_get_type((const char *)data)); - } else { - if (nmea_data->errors != 0) { - printf("WARN: The sentence struct contains parse errors!\n"); - } - - if (NMEA_GPGGA == nmea_data->type) { - printf("GPGGA sentence\n"); - nmea_gpgga_s *gpgga = (nmea_gpgga_s *)nmea_data; - printf("Number of satellites: %d\n", gpgga->n_satellites); - printf("Altitude: %f %c\n", gpgga->altitude, gpgga->altitude_unit); - } - - if (NMEA_GPGLL == nmea_data->type) { - printf("GPGLL sentence\n"); - nmea_gpgll_s *pos = (nmea_gpgll_s *)nmea_data; - printf("Longitude:\n"); - printf(" Degrees: %d\n", pos->longitude.degrees); - printf(" Minutes: %f\n", pos->longitude.minutes); - printf(" Cardinal: %c\n", (char)pos->longitude.cardinal); - printf("Latitude:\n"); - printf(" Degrees: %d\n", pos->latitude.degrees); - printf(" Minutes: %f\n", pos->latitude.minutes); - printf(" Cardinal: %c\n", (char)pos->latitude.cardinal); - strftime(fmt_buf, sizeof(fmt_buf), "%H:%M:%S", &pos->time); - printf("Time: %s\n", fmt_buf); - } - - if (NMEA_GPRMC == nmea_data->type) { - printf("GPRMC sentence\n"); - nmea_gprmc_s *pos = (nmea_gprmc_s *)nmea_data; - printf("Longitude:\n"); - printf(" Degrees: %d\n", pos->longitude.degrees); - printf(" Minutes: %f\n", pos->longitude.minutes); - printf(" Cardinal: %c\n", (char)pos->longitude.cardinal); - printf("Latitude:\n"); - printf(" Degrees: %d\n", pos->latitude.degrees); - printf(" Minutes: %f\n", pos->latitude.minutes); - printf(" Cardinal: %c\n", (char)pos->latitude.cardinal); - strftime(fmt_buf, sizeof(fmt_buf), "%d %b %T %Y", &pos->date_time); - printf("Date & Time: %s\n", fmt_buf); - printf("Speed, in Knots: %f\n", pos->gndspd_knots); - printf("Track, in degrees: %f\n", pos->track_deg); - printf("Magnetic Variation:\n"); - printf(" Degrees: %f\n", pos->magvar_deg); - printf(" Cardinal: %c\n", (char)pos->magvar_cardinal); - double adjusted_course = pos->track_deg; - if (NMEA_CARDINAL_DIR_EAST == pos->magvar_cardinal) { - adjusted_course -= pos->magvar_deg; - } else if (NMEA_CARDINAL_DIR_WEST == pos->magvar_cardinal) { - adjusted_course += pos->magvar_deg; - } else { - printf("Invalid Magnetic Variation Direction!\n"); - } - - printf("Adjusted Track (heading): %f\n", adjusted_course); - } - - if (NMEA_GPGSA == nmea_data->type) { - nmea_gpgsa_s *gpgsa = (nmea_gpgsa_s *)nmea_data; - - printf("GPGSA Sentence:\n"); - printf(" Mode: %c\n", gpgsa->mode); - printf(" Fix: %d\n", gpgsa->fixtype); - printf(" PDOP: %.2lf\n", gpgsa->pdop); - printf(" HDOP: %.2lf\n", gpgsa->hdop); - printf(" VDOP: %.2lf\n", gpgsa->vdop); - } - - if (NMEA_GPGSV == nmea_data->type) { - nmea_gpgsv_s *gpgsv = (nmea_gpgsv_s *)nmea_data; - - printf("GPGSV Sentence:\n"); - printf(" Num: %d\n", gpgsv->sentences); - printf(" ID: %d\n", gpgsv->sentence_number); - printf(" SV: %d\n", gpgsv->satellites); - printf(" #1: %d %d %d %d\n", gpgsv->sat[0].prn, gpgsv->sat[0].elevation, gpgsv->sat[0].azimuth, - gpgsv->sat[0].snr); - printf(" #2: %d %d %d %d\n", gpgsv->sat[1].prn, gpgsv->sat[1].elevation, gpgsv->sat[1].azimuth, - gpgsv->sat[1].snr); - printf(" #3: %d %d %d %d\n", gpgsv->sat[2].prn, gpgsv->sat[2].elevation, gpgsv->sat[2].azimuth, - gpgsv->sat[2].snr); - printf(" #4: %d %d %d %d\n", gpgsv->sat[3].prn, gpgsv->sat[3].elevation, gpgsv->sat[3].azimuth, - gpgsv->sat[3].snr); - } - - if (NMEA_GPTXT == nmea_data->type) { - nmea_gptxt_s *gptxt = (nmea_gptxt_s *)nmea_data; - - printf("GPTXT Sentence:\n"); - printf(" ID: %d %d %d\n", gptxt->id_00, gptxt->id_01, gptxt->id_02); - printf(" %s\n", gptxt->text); - } - - if (NMEA_GPVTG == nmea_data->type) { - nmea_gpvtg_s *gpvtg = (nmea_gpvtg_s *)nmea_data; - - printf("GPVTG Sentence:\n"); - printf(" Track [deg]: %.2lf\n", gpvtg->track_deg); - printf(" Speed [kmph]: %.2lf\n", gpvtg->gndspd_kmph); - printf(" Speed [knots]: %.2lf\n", gpvtg->gndspd_knots); - } - - nmea_free(nmea_data); - } -} - -void usb_lib_task(void *arg) -{ - while (1) { - //Start handling system events - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - printf("No more clients\n"); - ESP_ERROR_CHECK(usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - break; - } - } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - vTaskDelete(NULL); -} - -/* ---------------------------------- Main ---------------------------------- */ -extern "C" void app_main(void) -{ - //Install USB Host driver. Should only be called once in entire application - ESP_LOGI(TAG, "Installing USB Host"); - usb_host_config_t host_config = { - .skip_phy_setup = false, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - - // Create a task that will handle USB library events - xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, EXAMPLE_USB_HOST_PRIORITY, NULL); - - ESP_LOGI(TAG, "Installing CDC-ACM driver"); - ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - - Bg96Usb *bg96 = new Bg96Usb(); - bg96->at_start(handle_rx, NULL); - - static char text1[] = "AT\r"; - static char text2[] = "AT+GSN\r"; - - ESP_LOGI(TAG, "Sending AT"); - bg96->at_write((uint8_t *)text1, strlen(text1)); - vTaskDelay(100); - - ESP_LOGI(TAG, "Enabling GNSS"); - bg96->gnss_start(handle_gps); - - vTaskDelay(100); - ESP_LOGI(TAG, "Sending AT+GSN"); - bg96->at_write((uint8_t *)text2, strlen(text2)); -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml deleted file mode 100644 index 0455cf6acb..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml +++ /dev/null @@ -1,5 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - idf: ">=4.4" - igrr/libnmea: "^0.1.1" - usb_host_cdc_acm: "1.*" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md index b30ddb3d9e..c4af5faabe 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md @@ -33,15 +33,6 @@ idf.py -p PORT flash monitor See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. -### Running with dual USB CDC device -USB CDC device example [tusb_serial_device example](../../../device/tusb_serial_device) -can be configured to act as dual CDC device. - -In the device example project, enter command `idf.py menuconfig` and set Component config->TinyUSB Stack->Communication Device Class (CDC)->CDC channel Count to `2`. - -This settings also changes device's PID, so `EXAMPLE_USB_DEVICE_PID` in [usb-cdc.c](./main/usb-cdc.c) must be changed to `0x4002`. - - ## Example Output After the flashing you should see the output at idf monitor: diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt index d2e76376b6..72a9796821 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt @@ -1,3 +1,2 @@ -idf_component_register(SRCS "usb-cdc.c" +idf_component_register(SRCS "usb_cdc_example_main.c" INCLUDE_DIRS ".") -target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml index 96533b1fbc..7865f53578 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml @@ -1,4 +1,4 @@ ## IDF Component Manager Manifest File dependencies: - usb_host_cdc_acm: "1.*" + usb_host_cdc_acm: "2.*" idf: ">=4.4" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c deleted file mode 100644 index f0d831ce71..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "esp_err.h" -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" - -#define EXAMPLE_USB_HOST_PRIORITY 20 -#define EXAMPLE_USB_DEVICE_VID 0x303A // 0x303A:0x4001 (TinyUSB CDC device) -#define EXAMPLE_USB_DEVICE_PID 0x4001 // Change this to 0x4002 for dual CDC device - -static const char *TAG = "USB-CDC"; - -/* ------------------------------- Callbacks -------------------------------- */ -static void handle_rx(uint8_t *data, size_t data_len, void *arg) -{ - ESP_LOGI(TAG, "Data received"); - ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); -} - -void usb_lib_task(void *arg) -{ - while (1) { - //Start handling system events - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - ESP_LOGI(TAG, "All clients deregistered"); - ESP_ERROR_CHECK(usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - break; - } - } - - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - vTaskDelete(NULL); -} - -/* ---------------------------------- Main ---------------------------------- */ -void app_main(void) -{ - //Install USB Host driver. Should only be called once in entire application - ESP_LOGI(TAG, "Installing USB Host"); - usb_host_config_t host_config = { - .skip_phy_setup = false, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - - // Create a task that will handle USB library events - xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); - - ESP_LOGI(TAG, "Installing CDC-ACM driver"); - ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - - ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); - cdc_acm_dev_hdl_t cdc_dev; - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 5000, - .out_buffer_size = 64, - .user_arg = NULL, - .event_cb = NULL, - .data_cb = handle_rx - }; - ESP_ERROR_CHECK(cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev)); - assert(cdc_dev); - cdc_acm_host_desc_print(cdc_dev); - vTaskDelay(100); - - // Test sending and receiving: Send AT commands, responses are handled in handle_rx callback - static char text1[] = "AT\r"; - ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (uint8_t *)text1, strlen(text1), 1000)); - vTaskDelay(100); - - static char text2[] = "AT+GSN\r"; - ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (uint8_t *)text2, strlen(text2), 1000)); - vTaskDelay(100); - - // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again - ESP_LOGI(TAG, "Setting up line coding"); - - cdc_acm_line_coding_t line_coding; - ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - line_coding.dwDTERate = 9600; - line_coding.bDataBits = 7; - line_coding.bParityType = 1; - line_coding.bCharFormat = 1; - ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Set: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - - ESP_LOGI(TAG, "Example finished successfully!"); -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c new file mode 100644 index 0000000000..01a5862202 --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "usb/usb_host.h" +#include "usb/cdc_acm_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (20) +#define EXAMPLE_USB_DEVICE_VID (0x303A) +#define EXAMPLE_USB_DEVICE_PID (0x4001) // 0x303A:0x4001 (TinyUSB CDC device) +#define EXAMPLE_USB_DEVICE_DUAL_PID (0x4002) // 0x303A:0x4002 (TinyUSB Dual CDC device) +#define EXAMPLE_TX_STRING ("CDC test string!") +#define EXAMPLE_TX_TIMEOUT_MS (1000) + +static const char *TAG = "USB-CDC"; +static SemaphoreHandle_t device_disconnected_sem; + +/** + * @brief Data received callback + * + * @param[in] data Pointer to received data + * @param[in] data_len Length of received data in bytes + * @param[in] arg Argument we passed to the device open function + * @return + * true: We have processed the received data + * false: We expect more data + */ +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) +{ + ESP_LOGI(TAG, "Data received"); + ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); + return true; +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case CDC_ACM_HOST_ERROR: + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %i", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + ESP_ERROR_CHECK(cdc_acm_host_close(event->data.cdc_hdl)); + xSemaphoreGive(device_disconnected_sem); + break; + case CDC_ACM_HOST_SERIAL_STATE: + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); + break; + case CDC_ACM_HOST_NETWORK_CONNECTION: + default: + ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type); + break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} + +/** + * @brief Main application + * + * Here we open a USB CDC device and send some data to it + */ +void app_main(void) +{ + device_disconnected_sem = xSemaphoreCreateBinary(); + assert(device_disconnected_sem); + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 512, + .in_buffer_size = 512, + .user_arg = NULL, + .event_cb = handle_event, + .data_cb = handle_rx + }; + + while (true) { + cdc_acm_dev_hdl_t cdc_dev = NULL; + + // Open USB device from tusb_serial_device example example. Either single or dual port configuration. + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); + esp_err_t err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID); + err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + continue; + } + } + cdc_acm_host_desc_print(cdc_dev); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test sending and receiving: responses are handled in handle_rx callback + ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (const uint8_t *)EXAMPLE_TX_STRING, strlen(EXAMPLE_TX_STRING), EXAMPLE_TX_TIMEOUT_MS)); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again + ESP_LOGI(TAG, "Setting up line coding"); + + cdc_acm_line_coding_t line_coding; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + line_coding.dwDTERate = 9600; + line_coding.bDataBits = 7; + line_coding.bParityType = 1; + line_coding.bCharFormat = 1; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Set: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Example finished successfully! You can reconnect the device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md index 6c459fb6a0..71fdfb331d 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md @@ -1,24 +1,26 @@ | Supported Targets | ESP32-S2 | ESP32-S3 | | ----------------- | -------- | -------- | -# USB CDC-ACM Virtual Com Port example +# USB CDC-ACM Virtual COM Port example (See the README.md file in the upper level 'examples' directory for more information about examples.) -This example shows how to extend CDC-ACM driver for Virtual Communication Port devices, -such as CP210x or FTDI FT23x devices. +This example shows how to extend CDC-ACM driver for Virtual Communication Port (VCP) devices, +such as CP210x, FTDI FT23x or CH34x devices. + +The drivers are fetched from [IDF Component Registry](https://components.espressif.com/) together with VCP service that automatically loads correct driver for plugged-in device. ## How to use example -1. Pick your USB-to-UART device by executing `idf.py menuconfig` and navigating to `Example Configuration -> USB-to-UART device type` -2. Change baudrate and other line coding parameters in `cdc_acm_vcp.cpp` to match your needs -3. Now you can use the CDC-ACM to API to control the device and send data. Data are received in `handle_rx` callback +1. Connect your USB<->UART converter to ESP32-S2/S3, the device will be automatically enumerated and correct driver will be loaded +2. Change baudrate and other line coding parameters in [cdc_acm_vcp_example_main.cpp](main/cdc_acm_vcp_example_main.cpp) to match your needs +3. Now you can use the usual CDC-ACM API to control the device and send data. Data are received in `handle_rx` callback 4. Try disconnecting and then reconnecting of the USB device to experiment with USB hotplugging ### Hardware Required * ESP board with USB-OTG supported -* Silicon Labs CP210x or FTDI FT23x USB to UART converter +* Silicon Labs CP210x, FTDI FT23x or CP34x USB to UART converter Connect USB_D+, USB_D-, GND and +5V signals of your ESP chip to matching signals on USB to UART converter. diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt index bed3ab4e2a..7fefe3ecbd 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt @@ -1,3 +1,4 @@ -idf_component_register(SRCS "cdc_acm_vcp.cpp" "cp210x_usb.cpp" "ftdi_usb.cpp" - INCLUDE_DIRS ".") -target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") +idf_component_register( + SRCS "cdc_acm_vcp_example_main.cpp" + INCLUDE_DIRS "." + ) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/Kconfig.projbuild b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/Kconfig.projbuild deleted file mode 100644 index b7e335e3af..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/Kconfig.projbuild +++ /dev/null @@ -1,15 +0,0 @@ -menu "Example Configuration" - - choice - prompt "USB-to-UART device type" - default EXAMPLE_USE_CP210X - help - Type of UART converter to use in this example. - - config EXAMPLE_USE_FTDI - bool "FT232" - config EXAMPLE_USE_CP210X - bool "CP2012" - endchoice - -endmenu diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp similarity index 55% rename from examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp.cpp rename to examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp index e61b89d96f..f90620ec87 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp.cpp +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp @@ -1,20 +1,24 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include #include -#include "sdkconfig.h" -#include "cp210x_usb.hpp" -#include "ftdi_usb.hpp" -#include "usb/usb_host.h" + #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#include "usb/cdc_acm_host.h" +#include "usb/vcp_ch34x.hpp" +#include "usb/vcp_cp210x.hpp" +#include "usb/vcp_ftdi.hpp" +#include "usb/vcp.hpp" +#include "usb/usb_host.h" + using namespace esp_usb; // Change these values to match your needs @@ -23,15 +27,36 @@ using namespace esp_usb; #define EXAMPLE_PARITY (0) // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space #define EXAMPLE_DATA_BITS (8) +namespace { static const char *TAG = "VCP example"; - static SemaphoreHandle_t device_disconnected_sem; -static void handle_rx(uint8_t *data, size_t data_len, void *arg) +/** + * @brief Data received callback + * + * Just pass received data to stdout + * + * @param[in] data Pointer to received data + * @param[in] data_len Length of received data in bytes + * @param[in] arg Argument we passed to the device open function + * @return + * true: We have processed the received data + * false: We expect more data + */ +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) { printf("%.*s", data_len, data); + return true; } +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) { switch (event->type) { @@ -43,14 +68,19 @@ static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ xSemaphoreGive(device_disconnected_sem); break; case CDC_ACM_HOST_SERIAL_STATE: - ESP_LOGI(TAG, "serial state notif 0x%04X", event->data.serial_state.val); + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); break; case CDC_ACM_HOST_NETWORK_CONNECTION: default: break; } } -void usb_lib_task(void *arg) +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) { while (1) { // Start handling system events @@ -65,13 +95,19 @@ void usb_lib_task(void *arg) } } } +} +/** + * @brief Main application + * + * This function shows how you can use Virtual COM Port drivers + */ extern "C" void app_main(void) { device_disconnected_sem = xSemaphoreCreateBinary(); assert(device_disconnected_sem); - //Install USB Host driver. Should only be called once in entire application + // Install USB Host driver. Should only be called once in entire application ESP_LOGI(TAG, "Installing USB Host"); const usb_host_config_t host_config = { .skip_phy_setup = false, @@ -80,37 +116,37 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(usb_host_install(&host_config)); // Create a task that will handle USB library events - xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL); + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL); + assert(task_created == pdTRUE); ESP_LOGI(TAG, "Installing CDC-ACM driver"); ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + // Register VCP drivers to VCP service + VCP::register_driver(); + VCP::register_driver(); + VCP::register_driver(); + + // Do everything else in a loop, so we can demonstrate USB device reconnections while (true) { const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 10000, - .out_buffer_size = 64, + .connection_timeout_ms = 5000, // 5 seconds, enough time to plug the device in or experiment with timeout + .out_buffer_size = 512, + .in_buffer_size = 512, .event_cb = handle_event, .data_cb = handle_rx, .user_arg = NULL, }; -#if defined(CONFIG_EXAMPLE_USE_FTDI) - FT23x *vcp; - try { - ESP_LOGI(TAG, "Opening FT232 UART device"); - vcp = FT23x::open_ftdi(FTDI_FT232_PID, &dev_config); - } -#else - CP210x *vcp; - try { - ESP_LOGI(TAG, "Opening CP210X device"); - vcp = CP210x::open_cp210x(CP210X_PID, &dev_config); - } -#endif - catch (esp_err_t err) { - ESP_LOGE(TAG, "The required device was not opened.\nExiting..."); - return; + // You don't need to know the device's VID and PID. Just plug in any device and the VCP service will load correct (already registered) driver for the device + ESP_LOGI(TAG, "Opening any VCP device..."); + auto vcp = std::unique_ptr(VCP::open(&dev_config)); + + if (vcp == nullptr) { + ESP_LOGI(TAG, "Failed to open VCP device"); + continue; } + vTaskDelay(10); ESP_LOGI(TAG, "Setting up line coding"); cdc_acm_line_coding_t line_coding = { @@ -129,8 +165,14 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t *)"Test string", 12)); */ + // Send some dummy data + ESP_LOGI(TAG, "Sending data through CdcAcmDevice"); + uint8_t data[] = "test_string"; + ESP_ERROR_CHECK(vcp->tx_blocking(data, sizeof(data))); + ESP_ERROR_CHECK(vcp->set_control_line_state(true, true)); + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Done. You can reconnect the VCP device to run again."); xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); - delete vcp; } } diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cp210x_usb.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cp210x_usb.cpp deleted file mode 100644 index 080a76c474..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cp210x_usb.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include "cp210x_usb.hpp" -#include "usb/usb_types_ch9.h" -#include "esp_log.h" -#include "esp_check.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -#define SILICON_LABS_VID (0x10C4) -#define CP210X_READ_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_INTERFACE | USB_BM_REQUEST_TYPE_DIR_IN) -#define CP210X_WRITE_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_INTERFACE | USB_BM_REQUEST_TYPE_DIR_OUT) - -namespace esp_usb { -CP210x *CP210x::open_cp210x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) -{ - return new CP210x(pid, dev_config, interface_idx); -} - -CP210x::CP210x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) - : intf(interface_idx) -{ - esp_err_t err; - err = this->open_vendor_specific(SILICON_LABS_VID, pid, this->intf, dev_config); - if (err != ESP_OK) { - throw(err); - } - - // CP210X interfaces must be explicitly enabled - err = this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_IFC_ENABLE, 1, this->intf, 0, NULL); - if (err != ESP_OK) { - throw(err); - } -}; - -esp_err_t CP210x::line_coding_get(cdc_acm_line_coding_t *line_coding) -{ - assert(line_coding); - - ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_READ_REQ, CP210X_CMD_GET_BAUDRATE, 0, this->intf, sizeof(line_coding->dwDTERate), (uint8_t *)&line_coding->dwDTERate), "CP210X",); - - uint8_t temp_data[2]; - ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_READ_REQ, CP210X_CMD_GET_LINE_CTL, 0, this->intf, 2, temp_data), "CP210X",); - line_coding->bCharFormat = temp_data[0] & 0x0F; - line_coding->bParityType = (temp_data[0] & 0xF0) >> 4; - line_coding->bDataBits = temp_data[1]; - - return ESP_OK; -} - -esp_err_t CP210x::line_coding_set(cdc_acm_line_coding_t *line_coding) -{ - assert(line_coding); - - if (line_coding->dwDTERate != 0) { - ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_BAUDRATE, 0, this->intf, sizeof(line_coding->dwDTERate), (uint8_t *)&line_coding->dwDTERate), "CP210X",); - } - - if (line_coding->bDataBits != 0) { - const uint16_t wValue = line_coding->bCharFormat | (line_coding->bParityType << 4) | (line_coding->bDataBits << 8); - return this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_LINE_CTL, wValue, this->intf, 0, NULL); - } - return ESP_OK; -} - -esp_err_t CP210x::set_control_line_state(bool dtr, bool rts) -{ - const uint16_t wValue = (uint16_t)dtr | ((uint16_t)rts << 1) | 0x0300; - return this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_MHS, wValue, this->intf, 0, NULL); -} - -esp_err_t CP210x::send_break(uint16_t duration_ms) -{ - ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_BREAK, 1, this->intf, 0, NULL), "CP210x",); - vTaskDelay(pdMS_TO_TICKS(duration_ms)); - return this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_BREAK, 0, this->intf, 0, NULL); -} -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cp210x_usb.hpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cp210x_usb.hpp deleted file mode 100644 index a8395077d5..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cp210x_usb.hpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#pragma once - -#include "usb/cdc_acm_host.h" - -#define CP210X_PID (0xEA60) // Single i.e. CP2101 - CP2104 -#define CP2105_PID (0xEA70) // Dual -#define CP2108_PID (0xEA71) // Quad - -// @see AN571: CP210x Virtual COM Port Interface, chapter 5 -#define CP210X_CMD_IFC_ENABLE (0x00) // Enable or disable the interface -#define CP210X_CMD_SET_BAUDDIV (0x01) // Set the baud rate divisor -#define CP210X_CMD_GET_BAUDDIV (0x02) // Get the baud rate divisor -#define CP210X_CMD_SET_LINE_CTL (0x03) // Set the line control -#define CP210X_CMD_GET_LINE_CTL (0x04) // Get the line control -#define CP210X_CMD_SET_BREAK (0x05) // Set a BREAK -#define CP210X_CMD_IMM_CHAR (0x06) // Send character out of order -#define CP210X_CMD_SET_MHS (0x07) // Set modem handshaking -#define CP210X_CMD_GET_MDMSTS (0x08) // Get modem status -#define CP210X_CMD_SET_XON (0x09) // Emulate XON -#define CP210X_CMD_SET_XOFF (0x0A) // Emulate XOFF -#define CP210X_CMD_SET_EVENTMASK (0x0B) // Set the event mask -#define CP210X_CMD_GET_EVENTMASK (0x0C) // Get the event mask -#define CP210X_CMD_GET_EVENTSTATE (0x16) // Get the event state -#define CP210X_CMD_SET_RECEIVE (0x17) // Set receiver max timeout -#define CP210X_CMD_GET_RECEIVE (0x18) // Get receiver max timeout -#define CP210X_CMD_SET_CHAR (0x0D) // Set special character individually -#define CP210X_CMD_GET_CHARS (0x0E) // Get special characters -#define CP210X_CMD_GET_PROPS (0x0F) // Get properties -#define CP210X_CMD_GET_COMM_STATUS (0x10) // Get the serial status -#define CP210X_CMD_RESET (0x11) // Reset -#define CP210X_CMD_PURGE (0x12) // Purge -#define CP210X_CMD_SET_FLOW (0x13) // Set flow control -#define CP210X_CMD_GET_FLOW (0x14) // Get flow control -#define CP210X_CMD_EMBED_EVENTS (0x15) // Control embedding of events in the data stream -#define CP210X_CMD_GET_BAUDRATE (0x1D) // Get the baud rate -#define CP210X_CMD_SET_BAUDRATE (0x1E) // Set the baud rate -#define CP210X_CMD_SET_CHARS (0x19) // Set special characters -#define CP210X_CMD_VENDOR_SPECIFIC (0xFF) // Read/write latch values - -namespace esp_usb { -class CP210x : public CdcAcmDevice { -public: - /** - * @brief Factory method for this CP210x driver - * - * @note USB Host library and CDC-ACM driver must be already installed - * - * @param[in] pid PID eg. CP210X_PID - * @param[in] dev_config CDC device configuration - * @param[in] interface_idx Interface number - * @return CP210x Pointer to created and opened CP210x device - */ - static CP210x *open_cp210x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); - - /** - * @brief Get Line Coding method - * - * @see AN571: CP210x Virtual COM Port Interface chapters 5.6 and 5.8 - * @note Overrides default implementation in CDC-ACM driver - * @param[out] line_coding Line Coding structure - * @return esp_err_t - */ - esp_err_t line_coding_get(cdc_acm_line_coding_t *line_coding); - - /** - * @brief Set Line Coding method - * - * @see AN571: CP210x Virtual COM Port Interface chapters 5.5 and 5.7 - * @note Overrides default implementation in CDC-ACM driver - * @param[in] line_coding Line Coding structure - * @return esp_err_t - */ - esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding); - - /** - * @brief Set Control Line State method - * - * @see AN571: CP210x Virtual COM Port Interface chapter 5.9 - * @note Overrides default implementation in CDC-ACM driver - * @note Both signals are active low - * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. - * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. - * @return esp_err_t - */ - esp_err_t set_control_line_state(bool dtr, bool rts); - - /** - * @brief Send Break method - * - * @see AN571: CP210x Virtual COM Port Interface chapter 5.20 - * @note Overrides default implementation in CDC-ACM driver - * @param[in] duration_ms Duration of the break condition in [ms] - * @return esp_err_t - */ - esp_err_t send_break(uint16_t duration_ms); - -private: - const uint8_t intf; - - // Constructors are private, use factory method to create this object - CP210x(); - CP210x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); - - // Make open functions from CdcAcmDevice class private - using CdcAcmDevice::open; - using CdcAcmDevice::open_vendor_specific; -}; -} // namespace esp_usb diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/ftdi_usb.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/ftdi_usb.cpp deleted file mode 100644 index 1df5050010..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/ftdi_usb.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include "ftdi_usb.hpp" -#include "usb/usb_types_ch9.h" -#include "esp_log.h" -#include "esp_check.h" - -#define FTDI_VID (0x0403) -#define FTDI_READ_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_DIR_IN) -#define FTDI_WRITE_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_DIR_OUT) - -namespace esp_usb { -FT23x *FT23x::open_ftdi(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) -{ - return new FT23x(pid, dev_config, interface_idx); -} - -FT23x::FT23x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) - : intf(interface_idx), user_data_cb(dev_config->data_cb), user_event_cb(dev_config->event_cb), - user_arg(dev_config->user_arg), uart_state(0) -{ - cdc_acm_host_device_config_t ftdi_config; - memcpy(&ftdi_config, dev_config, sizeof(cdc_acm_host_device_config_t)); - // FT23x reports modem status in first two bytes of RX data - // so here we override the RX handler with our own - - if (dev_config->data_cb) { - ftdi_config.data_cb = ftdi_rx; - ftdi_config.user_arg = this; - } - - if (dev_config->event_cb) { - ftdi_config.event_cb = ftdi_event; - ftdi_config.user_arg = this; - } - - esp_err_t err; - err = this->open_vendor_specific(FTDI_VID, pid, this->intf, &ftdi_config); - if (err != ESP_OK) { - throw(err); - } - - // FT23x interface must be first reset and configured (115200 8N1) - err = this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_RESET, 0, this->intf + 1, 0, NULL); - if (err != ESP_OK) { - throw(err); - } - - cdc_acm_line_coding_t line_coding = { - .dwDTERate = 115200, - .bCharFormat = 0, - .bParityType = 0, - .bDataBits = 8, - }; - err = this->line_coding_set(&line_coding); - if (err != ESP_OK) { - throw(err); - } -}; - -esp_err_t FT23x::line_coding_set(cdc_acm_line_coding_t *line_coding) -{ - assert(line_coding); - - if (line_coding->dwDTERate != 0) { - uint16_t wIndex, wValue; - calculate_baudrate(line_coding->dwDTERate, &wValue, &wIndex); - ESP_RETURN_ON_ERROR(this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_BAUDRATE, wValue, wIndex, 0, NULL), "FT23x",); - } - - if (line_coding->bDataBits != 0) { - const uint16_t wValue = (line_coding->bDataBits) | (line_coding->bParityType << 8) | (line_coding->bCharFormat << 11); - return this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_LINE_CTL, wValue, this->intf, 0, NULL); - } - return ESP_OK; -} - -esp_err_t FT23x::set_control_line_state(bool dtr, bool rts) -{ - ESP_RETURN_ON_ERROR(this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_MHS, dtr ? 0x11 : 0x10, this->intf, 0, NULL), "FT23x",); // DTR - return this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_MHS, rts ? 0x21 : 0x20, this->intf, 0, NULL); // RTS -} - -void FT23x::ftdi_rx(uint8_t* data, size_t data_len, void *user_arg) -{ - FT23x *this_ftdi = (FT23x *)user_arg; - - // Dispatch serial state if it has changed - if (this_ftdi->user_event_cb) { - cdc_acm_uart_state_t new_state; - new_state.val = 0; - new_state.bRxCarrier = data[0] & 0x80; // DCD - new_state.bTxCarrier = data[0] & 0x20; // DSR - new_state.bBreak = data[1] & 0x10; - new_state.bRingSignal = data[0] & 0x40; - new_state.bFraming = data[1] & 0x08; - new_state.bParity = data[1] & 0x04; - new_state.bOverRun = data[1] & 0x02; - - if (this_ftdi->uart_state != new_state.val) { - cdc_acm_host_dev_event_data_t serial_event; - serial_event.type = CDC_ACM_HOST_SERIAL_STATE; - serial_event.data.serial_state = new_state; - this_ftdi->user_event_cb(&serial_event, this_ftdi->user_arg); - this_ftdi->uart_state = new_state.val; - } - } - - // Dispatch data if any - if (data_len > 2) { - this_ftdi->user_data_cb(&data[2], data_len - 2, this_ftdi->user_arg); - } -} - -void FT23x::ftdi_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) -{ - FT23x *this_ftdi = (FT23x *)user_ctx; - this_ftdi->user_event_cb(event, this_ftdi->user_arg); -} - -int FT23x::calculate_baudrate(uint32_t baudrate, uint16_t *wValue, uint16_t *wIndex) -{ - #define FTDI_BASE_CLK (3000000) - - int baudrate_real; - if (baudrate > 2000000) { - // set to 3000000 - *wValue = 0; - *wIndex = 0; - baudrate_real = 3000000; - } else if (baudrate >= 1000000) { - // set to 1000000 - *wValue = 1; - *wIndex = 0; - baudrate_real = 1000000; - } else { - const float ftdi_fractal[] = {0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1}; - const uint8_t ftdi_fractal_bits[] = {0, 0x03, 0x02, 0x04, 0x01, 0x05, 0x06, 0x07}; - uint16_t divider_n = FTDI_BASE_CLK / baudrate; // integer value - int ftdi_fractal_idx = 0; - float divider = FTDI_BASE_CLK / (float)baudrate; // float value - float divider_fractal = divider - (float)divider_n; - - // Find closest bigger FT23x fractal divider - for (ftdi_fractal_idx = 0; ftdi_fractal[ftdi_fractal_idx] <= divider_fractal; ftdi_fractal_idx++) {}; - - // Calculate baudrate errors for two closest fractal divisors - int diff1 = baudrate - (int)(FTDI_BASE_CLK / (divider_n + ftdi_fractal[ftdi_fractal_idx])); // Greater than required baudrate - int diff2 = (int)(FTDI_BASE_CLK / (divider_n + ftdi_fractal[ftdi_fractal_idx - 1])) - baudrate; // Lesser than required baudrate - - // Chose divider and fractal divider with smallest error - if (diff2 < diff1) { - ftdi_fractal_idx--; - } else { - if (ftdi_fractal_idx == 8) { - ftdi_fractal_idx = 0; - divider_n++; - } - } - - baudrate_real = FTDI_BASE_CLK / (float)((float)divider_n + ftdi_fractal[ftdi_fractal_idx]); - *wValue = ((0x3FFFF) & divider_n) | (ftdi_fractal_bits[ftdi_fractal_idx] << 14); - *wIndex = ftdi_fractal_bits[ftdi_fractal_idx] >> 2; - } - ESP_LOGD("FT23x", "wValue: 0x%04X wIndex: 0x%04X", *wValue, *wIndex); - ESP_LOGI("FT23x", "Baudrate required: %d, set: %d", baudrate, baudrate_real); - - return baudrate_real; -} -} // esp_usb diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/ftdi_usb.hpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/ftdi_usb.hpp deleted file mode 100644 index b8927ac364..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/ftdi_usb.hpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#pragma once - -#include "usb/cdc_acm_host.h" - -#define FTDI_FT232_PID (0x6001) -#define FTDI_FT231_PID (0x6015) - -#define FTDI_CMD_RESET (0x00) -#define FTDI_CMD_SET_FLOW (0x01) -#define FTDI_CMD_SET_MHS (0x02) // Modem hanshaking -#define FTDI_CMD_SET_BAUDRATE (0x03) -#define FTDI_CMD_SET_LINE_CTL (0x04) -#define FTDI_CMD_GET_MDMSTS (0x05) // Modem status - -namespace esp_usb { -class FT23x : public CdcAcmDevice { -public: - /** - * @brief Factory method for this FTDI driver - * - * @note USB Host library and CDC-ACM driver must be already installed - * - * @param[in] pid PID eg. FTDI_FT232_PID - * @param[in] dev_config CDC device configuration - * @param[in] interface_idx Interface number - * @return FT23x Pointer to created and opened FTDI device - */ - static FT23x *open_ftdi(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); - - /** - * @brief Set Line Coding method - * - * @note Overrides default implementation in CDC-ACM driver - * @param[in] line_coding Line Coding structure - * @return esp_err_t - */ - esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding); - - /** - * @brief Set Control Line State method - * - * @note Overrides default implementation in CDC-ACM driver - * @note Both signals are active low - * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. - * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. - * @return esp_err_t - */ - esp_err_t set_control_line_state(bool dtr, bool rts); - -private: - /** - * @brief FT23x's RX data handler - * - * First two bytes are status bytes, the RX data start at data[2]. - * Coding of status bytes: - * Byte 0: - * Bit 0: Full Speed packet - * Bit 1: High Speed packet - * Bit 4: CTS - * Bit 5: DSR - * Bit 6: RI - * Bit 7: DCD - * Byte 1: - * Bit 1: RX overflow - * Bit 2: Parity error - * Bit 3: Framing error - * Bit 4: Break received - * Bit 5: Transmitter holding register empty - * Bit 6: Transmitter empty - * - * @todo When CTS is asserted, this driver should stop sending data. - * - * @param[in] data Received data - * @param[in] data_len Received data length - * @param[in] user_arg Pointer to FT23x class - */ - static void ftdi_rx(uint8_t* data, size_t data_len, void *user_arg); - - // Just a wrapper to recover user's argument - static void ftdi_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx); - - /** - * @brief Construct a new calculate baudrate object - * - * A Baud rate for the FT232R, FT2232 (UART mode) or FT232B is generated using the chips - * internal 48MHz clock. This is input to Baud rate generator circuitry where it is then divided by 16 - * and fed into a prescaler as a 3MHz reference clock. This 3MHz reference clock is then divided - * down to provide the required Baud rate for the device's on chip UART. The value of the Baud rate - * divisor is an integer plus a sub-integer prescaler. - * Allowed values for the Baud rate divisor are: - * Divisor = n + 0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875; where n is an integer between 2 and - * 16384 (214). - * - * Note: Divisor = 1 and Divisor = 0 are special cases. A divisor of 0 will give 3 MBaud, and a divisor - * of 1 will give 2 MBaud. Sub-integer divisors between 0 and 2 are not allowed. - * Therefore the value of the divisor needed for a given Baud rate is found by dividing 3000000 by the - * required Baud rate. - * - * @see FTDI AN232B-05 Configuring FT232R, FT2232 and FT232B Baud Rates - * @param[in] baudrate - * @param[out] wValue - * @param[out] wIndex - */ - static int calculate_baudrate(uint32_t baudrate, uint16_t *wValue, uint16_t *wIndex); - - // Constructors are private, use factory method open_ftdi() to create this object - FT23x(); - FT23x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); - - // Make open functions from CdcAcmDevice class private - using CdcAcmDevice::open; - using CdcAcmDevice::open_vendor_specific; - - const uint8_t intf; - const cdc_acm_data_callback_t user_data_cb; - const cdc_acm_host_dev_callback_t user_event_cb; - void *user_arg; - uint16_t uart_state; -}; -} // namespace esp_usb diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml index 96533b1fbc..81ded83041 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml @@ -1,4 +1,7 @@ ## IDF Component Manager Manifest File dependencies: - usb_host_cdc_acm: "1.*" - idf: ">=4.4" + usb_host_ch34x_vcp: "^2" + usb_host_cp210x_vcp: "^2" + usb_host_ftdi_vcp: "^2" + usb_host_vcp: "^1" + idf: ">=4.4.0"