Skip to content

Commit

Permalink
fixes default 100ms delay with HWCDC write() is CDC is not connected (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
SuGlider authored Feb 29, 2024
1 parent b7af090 commit 2fdd901
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 54 deletions.
88 changes: 46 additions & 42 deletions cores/esp32/HWCDC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static QueueHandle_t rx_queue = NULL;
static uint8_t rx_data_buf[64] = {0};
static intr_handle_t intr_handle = 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
static uint32_t requested_tx_timeout_ms = 100;
Expand Down Expand Up @@ -79,12 +79,12 @@ static void hw_cdc_isr_handler(void *arg) {
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
// Interrupt tells us the host picked up the data we sent.
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 is unplugged, nothing to be done here
return;
} else {
isConnected = true;
connected = true;
}
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.
Expand All @@ -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_txfifo_flush();
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?
//ets_printf("TX:%u\n", queued_size);
event.tx.len = queued_size;
Expand All @@ -122,23 +122,50 @@ static void hw_cdc_isr_handler(void *arg) {
}
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);
isConnected = true;
connected = true;
}

if (usbjtag_intr_status & 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);
isConnected = false;
connected = false;
}

if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}

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) {
uint32_t tx_timeout_ms = 0;
if(usb_serial_jtag_is_connected()) {
if(HWCDC::isConnected()) {
tx_timeout_ms = requested_tx_timeout_ms;
}
if(xPortInIsrContext()){
Expand All @@ -157,33 +184,10 @@ HWCDC::~HWCDC(){
end();
}


// It should return <true> just when USB is plugged and CDC is connected.
HWCDC::operator bool() const
{
static bool running = false;

// 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;
return HWCDC::isCDC_Connected();
}

void HWCDC::onEvent(esp_event_handler_t callback){
Expand Down Expand Up @@ -267,7 +271,7 @@ void HWCDC::end()
arduino_hw_cdc_event_loop_handle = NULL;
}
HWCDC::deinit(this);
isConnected = false;
connected = false;
}

void HWCDC::setTxTimeoutMs(uint32_t timeout){
Expand Down Expand Up @@ -299,7 +303,7 @@ int HWCDC::availableForWrite(void)
if(tx_ring_buf == NULL || tx_lock == NULL){
return 0;
}
if(usb_serial_jtag_is_connected()) {
if(HWCDC::isCDC_Connected()) {
tx_timeout_ms = requested_tx_timeout_ms;
}
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
Expand Down Expand Up @@ -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){
return 0;
}
if(usb_serial_jtag_is_connected()) {
if(HWCDC::isCDC_Connected()) {
tx_timeout_ms = requested_tx_timeout_ms;
} else {
isConnected = false;
connected = false;
}
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return 0;
Expand All @@ -354,7 +358,7 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
so_far += space;
// Now trigger the ISR to read data from the ring buffer.
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){
if(max_size > to_send){
Expand All @@ -369,12 +373,12 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
to_send -= max_size;
// Now trigger the ISR to read data from the ring buffer.
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
if(to_send && !usb_serial_jtag_ll_txfifo_writable()) {
isConnected = false;
connected = false;
flushTXBuffer();
}
xSemaphoreGive(tx_lock);
Expand All @@ -392,10 +396,10 @@ void HWCDC::flush(void)
if(tx_ring_buf == NULL || tx_lock == NULL){
return;
}
if(usb_serial_jtag_is_connected()) {
if(HWCDC::isCDC_Connected()) {
tx_timeout_ms = requested_tx_timeout_ms;
} else {
isConnected = false;
connected = false;
}
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
return;
Expand All @@ -405,7 +409,7 @@ void HWCDC::flush(void)
if(uxItemsWaiting){
// Now trigger the ISR to read data from the ring buffer.
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;
while(tries && uxItemsWaiting){
Expand All @@ -415,7 +419,7 @@ void HWCDC::flush(void)
if (lastUxItemsWaiting == uxItemsWaiting) tries--;
}
if (tries == 0) { // CDC isn't connected anymore...
isConnected = false;
connected = false;
flushTXBuffer();
}
xSemaphoreGive(tx_lock);
Expand Down
16 changes: 14 additions & 2 deletions cores/esp32/HWCDC.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// 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");
// you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,7 @@
#include <inttypes.h>
#include "esp_event.h"
#include "Stream.h"
#include "driver/usb_serial_jtag.h"

ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS);

Expand All @@ -46,6 +47,7 @@ class HWCDC: public Stream
{
private:
static bool deinit(void * busptr);
static bool isCDC_Connected();

public:
HWCDC();
Expand All @@ -68,7 +70,17 @@ class HWCDC: public Stream
size_t write(uint8_t);
size_t write(const uint8_t *buffer, size_t size);
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)
{
return read((uint8_t*) buffer, size);
Expand Down
14 changes: 4 additions & 10 deletions libraries/ESP32/examples/HWSerial_Events/HWSerial_Events.ino
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ void loop(){}
HWCDC HWCDCSerial;
#endif

#include "driver/usb_serial_jtag.h"

// 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) {
if (event_base == ARDUINO_HW_CDC_EVENTS) {
Expand All @@ -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[] = {
" 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 Unplugged and CDC not connected\r\n",
" USB Unplugged and CDC NOT connected\r\n",
" USB Unplugged BUT CDC is connected :: PROBLEM\r\n",
};

const char* HWCDC_Status() {
int i = isPlugged() ? 0 : 2;
if(HWCDCSerial) i += 1;
int i = HWCDCSerial.isPlugged() ? 0 : 2;
if(HWCDCSerial.isConnected()) i += 1;
return _hwcdc_status[i];
}

Expand Down

0 comments on commit 2fdd901

Please sign in to comment.