Unverified Commit 2fdd9016 authored by Rodrigo Garcia's avatar Rodrigo Garcia Committed by GitHub

fixes default 100ms delay with HWCDC write() is CDC is not connected (#9307)

* feat(hwcdc): fix delay

Fixes delay when CDC is disconnected. At this time is only fixes it when USB cable is unplugged.

* feat(hwcdc): fix delay

fixes delay when CDC is not connected. It was only considering when the USB cable was not plugged.

* feat(hwcdc): add 2 methods

Adds 2 new methods:
- isPlugged() will return true when USB cable is plugged, false otherwise.
- isConnected() will return true when USB CDC is connected to a application in the USB Host side and communication is stablished.

* feat(hwcdc): adjusts APIs

Fixes the example to use the new added APIs for checking if USB cable is plugged and for checking if CDC is connected.

* fixes api declaration

* fixes API declaration
parent b7af090f
...@@ -37,7 +37,7 @@ static QueueHandle_t rx_queue = NULL; ...@@ -37,7 +37,7 @@ static QueueHandle_t rx_queue = NULL;
static uint8_t rx_data_buf[64] = {0}; static uint8_t rx_data_buf[64] = {0};
static intr_handle_t intr_handle = NULL; static intr_handle_t intr_handle = NULL;
static SemaphoreHandle_t tx_lock = NULL; static SemaphoreHandle_t tx_lock = NULL;
static volatile bool isConnected = false; static volatile bool connected = false;
// timeout has no effect when USB CDC is unplugged // timeout has no effect when USB CDC is unplugged
static uint32_t requested_tx_timeout_ms = 100; static uint32_t requested_tx_timeout_ms = 100;
...@@ -79,12 +79,12 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -79,12 +79,12 @@ static void hw_cdc_isr_handler(void *arg) {
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
// Interrupt tells us the host picked up the data we sent. // Interrupt tells us the host picked up the data we sent.
if(!usb_serial_jtag_is_connected()) { if(!usb_serial_jtag_is_connected()) {
isConnected = false; connected = false;
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
// USB is unplugged, nothing to be done here // USB is unplugged, nothing to be done here
return; return;
} else { } else {
isConnected = true; connected = true;
} }
if (usb_serial_jtag_ll_txfifo_writable() == 1) { if (usb_serial_jtag_ll_txfifo_writable() == 1) {
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send. // We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
...@@ -98,7 +98,7 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -98,7 +98,7 @@ static void hw_cdc_isr_handler(void *arg) {
usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size); usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size);
usb_serial_jtag_ll_txfifo_flush(); usb_serial_jtag_ll_txfifo_flush();
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken); vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken);
if(isConnected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
//send event? //send event?
//ets_printf("TX:%u\n", queued_size); //ets_printf("TX:%u\n", queued_size);
event.tx.len = queued_size; event.tx.len = queued_size;
...@@ -122,13 +122,13 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -122,13 +122,13 @@ static void hw_cdc_isr_handler(void *arg) {
} }
event.rx.len = i; event.rx.len = i;
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
isConnected = true; connected = true;
} }
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) { if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET); usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
isConnected = false; connected = false;
} }
if (xTaskWoken == pdTRUE) { if (xTaskWoken == pdTRUE) {
...@@ -136,9 +136,36 @@ static void hw_cdc_isr_handler(void *arg) { ...@@ -136,9 +136,36 @@ static void hw_cdc_isr_handler(void *arg) {
} }
} }
bool HWCDC::isCDC_Connected()
{
static bool running = false;
// USB may be unplugged
if (usb_serial_jtag_is_connected() == false) {
connected = false;
running = false;
return false;
}
if (connected) {
running = false;
return true;
}
if (running == false && !connected) { // enables it only once!
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
// this will feed CDC TX FIFO to trigger IN_EMPTY
uint8_t c = '\0';
usb_serial_jtag_ll_write_txfifo(&c, sizeof(c));
usb_serial_jtag_ll_txfifo_flush();
running = true;
return false;
}
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) { static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
uint32_t tx_timeout_ms = 0; uint32_t tx_timeout_ms = 0;
if(usb_serial_jtag_is_connected()) { if(HWCDC::isConnected()) {
tx_timeout_ms = requested_tx_timeout_ms; tx_timeout_ms = requested_tx_timeout_ms;
} }
if(xPortInIsrContext()){ if(xPortInIsrContext()){
...@@ -157,33 +184,10 @@ HWCDC::~HWCDC(){ ...@@ -157,33 +184,10 @@ HWCDC::~HWCDC(){
end(); end();
} }
// It should return <true> just when USB is plugged and CDC is connected. // It should return <true> just when USB is plugged and CDC is connected.
HWCDC::operator bool() const HWCDC::operator bool() const
{ {
static bool running = false; return HWCDC::isCDC_Connected();
// USB may be unplugged
if (usb_serial_jtag_is_connected() == false) {
isConnected = false;
running = false;
return false;
}
if (isConnected) {
running = false;
return true;
}
if (running == false && !isConnected) { // enables it only once!
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
// this will feed CDC TX FIFO to trigger IN_EMPTY
uint8_t c = '\0';
usb_serial_jtag_ll_write_txfifo(&c, sizeof(c));
usb_serial_jtag_ll_txfifo_flush();
running = true;
return false;
} }
void HWCDC::onEvent(esp_event_handler_t callback){ void HWCDC::onEvent(esp_event_handler_t callback){
...@@ -267,7 +271,7 @@ void HWCDC::end() ...@@ -267,7 +271,7 @@ void HWCDC::end()
arduino_hw_cdc_event_loop_handle = NULL; arduino_hw_cdc_event_loop_handle = NULL;
} }
HWCDC::deinit(this); HWCDC::deinit(this);
isConnected = false; connected = false;
} }
void HWCDC::setTxTimeoutMs(uint32_t timeout){ void HWCDC::setTxTimeoutMs(uint32_t timeout){
...@@ -299,7 +303,7 @@ int HWCDC::availableForWrite(void) ...@@ -299,7 +303,7 @@ int HWCDC::availableForWrite(void)
if(tx_ring_buf == NULL || tx_lock == NULL){ if(tx_ring_buf == NULL || tx_lock == NULL){
return 0; return 0;
} }
if(usb_serial_jtag_is_connected()) { if(HWCDC::isCDC_Connected()) {
tx_timeout_ms = requested_tx_timeout_ms; tx_timeout_ms = requested_tx_timeout_ms;
} }
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
...@@ -331,10 +335,10 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size) ...@@ -331,10 +335,10 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
if(buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL){ if(buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL){
return 0; return 0;
} }
if(usb_serial_jtag_is_connected()) { if(HWCDC::isCDC_Connected()) {
tx_timeout_ms = requested_tx_timeout_ms; tx_timeout_ms = requested_tx_timeout_ms;
} else { } else {
isConnected = false; connected = false;
} }
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return 0; return 0;
...@@ -354,7 +358,7 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size) ...@@ -354,7 +358,7 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
so_far += space; so_far += space;
// Now trigger the ISR to read data from the ring buffer. // Now trigger the ISR to read data from the ring buffer.
usb_serial_jtag_ll_txfifo_flush(); usb_serial_jtag_ll_txfifo_flush();
if(isConnected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
while(to_send){ while(to_send){
if(max_size > to_send){ if(max_size > to_send){
...@@ -369,12 +373,12 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size) ...@@ -369,12 +373,12 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
to_send -= max_size; to_send -= max_size;
// Now trigger the ISR to read data from the ring buffer. // Now trigger the ISR to read data from the ring buffer.
usb_serial_jtag_ll_txfifo_flush(); usb_serial_jtag_ll_txfifo_flush();
if(isConnected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
} }
} }
// CDC is diconnected ==> flush all data from TX buffer // CDC is diconnected ==> flush all data from TX buffer
if(to_send && !usb_serial_jtag_ll_txfifo_writable()) { if(to_send && !usb_serial_jtag_ll_txfifo_writable()) {
isConnected = false; connected = false;
flushTXBuffer(); flushTXBuffer();
} }
xSemaphoreGive(tx_lock); xSemaphoreGive(tx_lock);
...@@ -392,10 +396,10 @@ void HWCDC::flush(void) ...@@ -392,10 +396,10 @@ void HWCDC::flush(void)
if(tx_ring_buf == NULL || tx_lock == NULL){ if(tx_ring_buf == NULL || tx_lock == NULL){
return; return;
} }
if(usb_serial_jtag_is_connected()) { if(HWCDC::isCDC_Connected()) {
tx_timeout_ms = requested_tx_timeout_ms; tx_timeout_ms = requested_tx_timeout_ms;
} else { } else {
isConnected = false; connected = false;
} }
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return; return;
...@@ -405,7 +409,7 @@ void HWCDC::flush(void) ...@@ -405,7 +409,7 @@ void HWCDC::flush(void)
if(uxItemsWaiting){ if(uxItemsWaiting){
// Now trigger the ISR to read data from the ring buffer. // Now trigger the ISR to read data from the ring buffer.
usb_serial_jtag_ll_txfifo_flush(); usb_serial_jtag_ll_txfifo_flush();
if(isConnected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
} }
uint8_t tries = 3; uint8_t tries = 3;
while(tries && uxItemsWaiting){ while(tries && uxItemsWaiting){
...@@ -415,7 +419,7 @@ void HWCDC::flush(void) ...@@ -415,7 +419,7 @@ void HWCDC::flush(void)
if (lastUxItemsWaiting == uxItemsWaiting) tries--; if (lastUxItemsWaiting == uxItemsWaiting) tries--;
} }
if (tries == 0) { // CDC isn't connected anymore... if (tries == 0) { // CDC isn't connected anymore...
isConnected = false; connected = false;
flushTXBuffer(); flushTXBuffer();
} }
xSemaphoreGive(tx_lock); xSemaphoreGive(tx_lock);
......
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD // Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <inttypes.h> #include <inttypes.h>
#include "esp_event.h" #include "esp_event.h"
#include "Stream.h" #include "Stream.h"
#include "driver/usb_serial_jtag.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS); ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS);
...@@ -46,6 +47,7 @@ class HWCDC: public Stream ...@@ -46,6 +47,7 @@ class HWCDC: public Stream
{ {
private: private:
static bool deinit(void * busptr); static bool deinit(void * busptr);
static bool isCDC_Connected();
public: public:
HWCDC(); HWCDC();
...@@ -69,6 +71,16 @@ public: ...@@ -69,6 +71,16 @@ public:
size_t write(const uint8_t *buffer, size_t size); size_t write(const uint8_t *buffer, size_t size);
void flush(void); void flush(void);
inline static bool isPlugged(void)
{
return usb_serial_jtag_is_connected();
}
inline static bool isConnected(void)
{
return isCDC_Connected();
}
inline size_t read(char * buffer, size_t size) inline size_t read(char * buffer, size_t size)
{ {
return read((uint8_t*) buffer, size); return read((uint8_t*) buffer, size);
......
...@@ -24,8 +24,6 @@ void loop(){} ...@@ -24,8 +24,6 @@ void loop(){}
HWCDC HWCDCSerial; HWCDC HWCDCSerial;
#endif #endif
#include "driver/usb_serial_jtag.h"
// USB Event Callback Function that will log CDC events into UART0 // USB Event Callback Function that will log CDC events into UART0
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == ARDUINO_HW_CDC_EVENTS) { if (event_base == ARDUINO_HW_CDC_EVENTS) {
...@@ -51,20 +49,16 @@ static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t eve ...@@ -51,20 +49,16 @@ static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t eve
} }
} }
bool isPlugged() {
return usb_serial_jtag_is_connected();
}
const char* _hwcdc_status[] = { const char* _hwcdc_status[] = {
" USB Plugged but CDC is not connected\r\n", " USB Plugged but CDC is NOT connected\r\n",
" USB Plugged and CDC is connected\r\n", " USB Plugged and CDC is connected\r\n",
" USB Unplugged and CDC not connected\r\n", " USB Unplugged and CDC NOT connected\r\n",
" USB Unplugged BUT CDC is connected :: PROBLEM\r\n", " USB Unplugged BUT CDC is connected :: PROBLEM\r\n",
}; };
const char* HWCDC_Status() { const char* HWCDC_Status() {
int i = isPlugged() ? 0 : 2; int i = HWCDCSerial.isPlugged() ? 0 : 2;
if(HWCDCSerial) i += 1; if(HWCDCSerial.isConnected()) i += 1;
return _hwcdc_status[i]; return _hwcdc_status[i];
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment