mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
usb: Update host CDC examples
This commit is contained in:
parent
ef4b1b7704
commit
842e8a9889
@ -364,8 +364,8 @@ CDC-ACM
|
||||
|
||||
* A host class driver for the Communication Device Class (Abstract Control Model) is deployed to `IDF component registry <https://components.espressif.com/component/espressif/usb_host_cdc_acm>`__.
|
||||
* 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 <https://github.com/espressif/esp-protocols/tree/master/components/esp_modem/examples>`__, where it is used for communication with cellular modems.
|
||||
|
||||
MSC
|
||||
"""
|
||||
|
@ -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)
|
@ -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
|
||||
...
|
||||
|
||||
```
|
@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "cdc_acm_host_bg96.cpp"
|
||||
INCLUDE_DIRS ".")
|
@ -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;
|
||||
};
|
@ -1,208 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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));
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
igrr/libnmea: "^0.1.1"
|
||||
usb_host_cdc_acm: "1.*"
|
@ -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:
|
||||
|
@ -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")
|
||||
|
@ -1,4 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
usb_host_cdc_acm: "1.*"
|
||||
usb_host_cdc_acm: "2.*"
|
||||
idf: ">=4.4"
|
||||
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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!");
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
||||
|
@ -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 "."
|
||||
)
|
||||
|
@ -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
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#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<FT23x>();
|
||||
VCP::register_driver<CP210x>();
|
||||
VCP::register_driver<CH34x>();
|
||||
|
||||
// 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<CdcAcmDevice>(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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
@ -1,175 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#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
|
@ -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
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user