Skip to content

Commit 7a627f2

Browse files
authored
Merge branch 'master' into feature/mac-adress-refactor
2 parents 08b6c12 + 2fdd901 commit 7a627f2

File tree

5 files changed

+209
-40
lines changed

5 files changed

+209
-40
lines changed

cores/esp32/HWCDC.cpp

Lines changed: 105 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
1+
// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -28,19 +28,19 @@
2828
#include "hal/usb_serial_jtag_ll.h"
2929
#pragma GCC diagnostic warning "-Wvolatile"
3030
#include "rom/ets_sys.h"
31+
#include "driver/usb_serial_jtag.h"
3132

3233
ESP_EVENT_DEFINE_BASE(ARDUINO_HW_CDC_EVENTS);
3334

3435
static RingbufHandle_t tx_ring_buf = NULL;
3536
static QueueHandle_t rx_queue = NULL;
3637
static uint8_t rx_data_buf[64] = {0};
3738
static intr_handle_t intr_handle = NULL;
38-
static volatile bool initial_empty = false;
3939
static SemaphoreHandle_t tx_lock = NULL;
40+
static volatile bool connected = false;
4041

41-
// workaround for when USB CDC is not connected
42-
static uint32_t tx_timeout_ms = 0;
43-
static bool tx_timeout_change_request = false;
42+
// timeout has no effect when USB CDC is unplugged
43+
static uint32_t requested_tx_timeout_ms = 100;
4444

4545
static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL;
4646

@@ -78,21 +78,17 @@ static void hw_cdc_isr_handler(void *arg) {
7878

7979
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
8080
// Interrupt tells us the host picked up the data we sent.
81+
if(!usb_serial_jtag_is_connected()) {
82+
connected = false;
83+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
84+
// USB is unplugged, nothing to be done here
85+
return;
86+
} else {
87+
connected = true;
88+
}
8189
if (usb_serial_jtag_ll_txfifo_writable() == 1) {
8290
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
8391
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
84-
if(!initial_empty){
85-
initial_empty = true;
86-
// First time USB is plugged and the application has not explicitly set TX Timeout, set it to default 100ms.
87-
// Otherwise, USB is still unplugged and the timeout will be kept as Zero in order to avoid any delay in the
88-
// application whenever it uses write() and the TX Queue gets full.
89-
if (!tx_timeout_change_request) {
90-
tx_timeout_ms = 100;
91-
}
92-
//send event?
93-
//ets_printf("CONNECTED\n");
94-
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_CONNECTED_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
95-
}
9692
size_t queued_size;
9793
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64);
9894
// If the hardware fifo is avaliable, write in it. Otherwise, do nothing.
@@ -102,7 +98,7 @@ static void hw_cdc_isr_handler(void *arg) {
10298
usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size);
10399
usb_serial_jtag_ll_txfifo_flush();
104100
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken);
105-
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
101+
if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
106102
//send event?
107103
//ets_printf("TX:%u\n", queued_size);
108104
event.tx.len = queued_size;
@@ -124,32 +120,60 @@ static void hw_cdc_isr_handler(void *arg) {
124120
break;
125121
}
126122
}
127-
//send event?
128-
//ets_printf("RX:%u/%u\n", i, rx_fifo_len);
129123
event.rx.len = i;
130124
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
125+
connected = true;
131126
}
132127

133128
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
134129
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
135-
initial_empty = false;
136-
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
137-
//ets_printf("BUS_RESET\n");
138130
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
131+
connected = false;
139132
}
140133

141134
if (xTaskWoken == pdTRUE) {
142135
portYIELD_FROM_ISR();
143136
}
144137
}
145138

139+
bool HWCDC::isCDC_Connected()
140+
{
141+
static bool running = false;
142+
143+
// USB may be unplugged
144+
if (usb_serial_jtag_is_connected() == false) {
145+
connected = false;
146+
running = false;
147+
return false;
148+
}
149+
150+
if (connected) {
151+
running = false;
152+
return true;
153+
}
154+
155+
if (running == false && !connected) { // enables it only once!
156+
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
157+
}
158+
// this will feed CDC TX FIFO to trigger IN_EMPTY
159+
uint8_t c = '\0';
160+
usb_serial_jtag_ll_write_txfifo(&c, sizeof(c));
161+
usb_serial_jtag_ll_txfifo_flush();
162+
running = true;
163+
return false;
164+
}
165+
146166
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
167+
uint32_t tx_timeout_ms = 0;
168+
if(HWCDC::isConnected()) {
169+
tx_timeout_ms = requested_tx_timeout_ms;
170+
}
147171
if(xPortInIsrContext()){
148172
xRingbufferSendFromISR(tx_ring_buf, (void*) (&c), 1, NULL);
149173
} else {
150174
xRingbufferSend(tx_ring_buf, (void*) (&c), 1, tx_timeout_ms / portTICK_PERIOD_MS);
151175
}
152-
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
176+
usb_serial_jtag_ll_txfifo_flush();
153177
}
154178

155179
HWCDC::HWCDC() {
@@ -160,9 +184,10 @@ HWCDC::~HWCDC(){
160184
end();
161185
}
162186

187+
// It should return <true> just when USB is plugged and CDC is connected.
163188
HWCDC::operator bool() const
164189
{
165-
return initial_empty;
190+
return HWCDC::isCDC_Connected();
166191
}
167192

168193
void HWCDC::onEvent(esp_event_handler_t callback){
@@ -206,9 +231,9 @@ void HWCDC::begin(unsigned long baud)
206231
log_e("HW CDC RX Buffer error");
207232
}
208233
}
209-
//TX Buffer default has 256 bytes if not preset
234+
//TX Buffer default has 16 bytes if not preset
210235
if (tx_ring_buf == NULL) {
211-
if (!setTxBufferSize(256)) {
236+
if (!setTxBufferSize(16)) {
212237
log_e("HW CDC TX Buffer error");
213238
}
214239
}
@@ -227,8 +252,6 @@ void HWCDC::begin(unsigned long baud)
227252
} else {
228253
log_e("Serial JTAG Pins can't be set into Peripheral Manager.");
229254
}
230-
231-
usb_serial_jtag_ll_txfifo_flush();
232255
}
233256

234257
void HWCDC::end()
@@ -248,13 +271,11 @@ void HWCDC::end()
248271
arduino_hw_cdc_event_loop_handle = NULL;
249272
}
250273
HWCDC::deinit(this);
274+
connected = false;
251275
}
252276

253277
void HWCDC::setTxTimeoutMs(uint32_t timeout){
254-
tx_timeout_ms = timeout;
255-
// it registers that the user has explicitly requested to use a value as TX timeout
256-
// used for the workaround with unplugged USB and TX Queue Full that causes a delay on every write()
257-
tx_timeout_change_request = true;
278+
requested_tx_timeout_ms = timeout;
258279
}
259280

260281
/*
@@ -278,9 +299,13 @@ size_t HWCDC::setTxBufferSize(size_t tx_queue_len){
278299

279300
int HWCDC::availableForWrite(void)
280301
{
302+
uint32_t tx_timeout_ms = 0;
281303
if(tx_ring_buf == NULL || tx_lock == NULL){
282304
return 0;
283305
}
306+
if(HWCDC::isCDC_Connected()) {
307+
tx_timeout_ms = requested_tx_timeout_ms;
308+
}
284309
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
285310
return 0;
286311
}
@@ -289,11 +314,32 @@ int HWCDC::availableForWrite(void)
289314
return a;
290315
}
291316

317+
static void flushTXBuffer()
318+
{
319+
if (!tx_ring_buf) return;
320+
UBaseType_t uxItemsWaiting = 0;
321+
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
322+
323+
size_t queued_size = 0;
324+
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, uxItemsWaiting);
325+
if (queued_size && queued_buff != NULL) {
326+
vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff);
327+
}
328+
// flushes CDC FIFO
329+
usb_serial_jtag_ll_txfifo_flush();
330+
}
331+
292332
size_t HWCDC::write(const uint8_t *buffer, size_t size)
293333
{
334+
uint32_t tx_timeout_ms = 0;
294335
if(buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL){
295336
return 0;
296337
}
338+
if(HWCDC::isCDC_Connected()) {
339+
tx_timeout_ms = requested_tx_timeout_ms;
340+
} else {
341+
connected = false;
342+
}
297343
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
298344
return 0;
299345
}
@@ -311,8 +357,9 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
311357
to_send -= space;
312358
so_far += space;
313359
// Now trigger the ISR to read data from the ring buffer.
314-
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
315-
360+
usb_serial_jtag_ll_txfifo_flush();
361+
if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
362+
316363
while(to_send){
317364
if(max_size > to_send){
318365
max_size = to_send;
@@ -325,9 +372,15 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
325372
so_far += max_size;
326373
to_send -= max_size;
327374
// Now trigger the ISR to read data from the ring buffer.
328-
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
375+
usb_serial_jtag_ll_txfifo_flush();
376+
if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
329377
}
330378
}
379+
// CDC is diconnected ==> flush all data from TX buffer
380+
if(to_send && !usb_serial_jtag_ll_txfifo_writable()) {
381+
connected = false;
382+
flushTXBuffer();
383+
}
331384
xSemaphoreGive(tx_lock);
332385
return size;
333386
}
@@ -339,21 +392,35 @@ size_t HWCDC::write(uint8_t c)
339392

340393
void HWCDC::flush(void)
341394
{
395+
uint32_t tx_timeout_ms = 0;
342396
if(tx_ring_buf == NULL || tx_lock == NULL){
343397
return;
344398
}
399+
if(HWCDC::isCDC_Connected()) {
400+
tx_timeout_ms = requested_tx_timeout_ms;
401+
} else {
402+
connected = false;
403+
}
345404
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
346405
return;
347406
}
348407
UBaseType_t uxItemsWaiting = 0;
349408
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
350409
if(uxItemsWaiting){
351410
// Now trigger the ISR to read data from the ring buffer.
352-
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
411+
usb_serial_jtag_ll_txfifo_flush();
412+
if(connected) usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
353413
}
354-
while(uxItemsWaiting){
414+
uint8_t tries = 3;
415+
while(tries && uxItemsWaiting){
355416
delay(5);
417+
UBaseType_t lastUxItemsWaiting = uxItemsWaiting;
356418
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
419+
if (lastUxItemsWaiting == uxItemsWaiting) tries--;
420+
}
421+
if (tries == 0) { // CDC isn't connected anymore...
422+
connected = false;
423+
flushTXBuffer();
357424
}
358425
xSemaphoreGive(tx_lock);
359426
}

cores/esp32/HWCDC.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
1+
// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
#include <inttypes.h>
2222
#include "esp_event.h"
2323
#include "Stream.h"
24+
#include "driver/usb_serial_jtag.h"
2425

2526
ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS);
2627

@@ -46,6 +47,7 @@ class HWCDC: public Stream
4647
{
4748
private:
4849
static bool deinit(void * busptr);
50+
static bool isCDC_Connected();
4951

5052
public:
5153
HWCDC();
@@ -68,7 +70,17 @@ class HWCDC: public Stream
6870
size_t write(uint8_t);
6971
size_t write(const uint8_t *buffer, size_t size);
7072
void flush(void);
71-
73+
74+
inline static bool isPlugged(void)
75+
{
76+
return usb_serial_jtag_is_connected();
77+
}
78+
79+
inline static bool isConnected(void)
80+
{
81+
return isCDC_Connected();
82+
}
83+
7284
inline size_t read(char * buffer, size_t size)
7385
{
7486
return read((uint8_t*) buffer, size);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)