Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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