Skip to content

Commit 855d1b1

Browse files
committed
Use Ringbuf for CDC write to improve responsiveness and throughput
1 parent 92ce408 commit 855d1b1

File tree

2 files changed

+90
-48
lines changed

2 files changed

+90
-48
lines changed

cores/esp32/USBCDC.cpp

+86-47
Original file line numberDiff line numberDiff line change
@@ -67,58 +67,72 @@ void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms){
6767

6868
// Invoked when space becomes available in TX buffer
6969
void tud_cdc_tx_complete_cb(uint8_t itf){
70-
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL && devices[itf]->tx_sem != NULL){
71-
xSemaphoreGive(devices[itf]->tx_sem);
70+
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
7271
devices[itf]->_onTX();
7372
}
7473
}
7574

76-
static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size){
77-
if(itf >= MAX_USB_CDC_DEVICES || devices[itf] == NULL || devices[itf]->tx_sem == NULL){
78-
return 0;
79-
}
75+
static size_t ring_buf_flush(uint8_t itf, RingbufHandle_t ring_buf){
8076
if(!tud_cdc_n_connected(itf)){
8177
return 0;
8278
}
83-
size_t tosend = size, sofar = 0;
84-
while(tosend){
85-
uint32_t space = tud_cdc_n_write_available(itf);
86-
if(!space){
87-
//make sure that we do not get previous semaphore
88-
xSemaphoreTake(devices[itf]->tx_sem, 0);
89-
//wait for tx_complete
90-
if(xSemaphoreTake(devices[itf]->tx_sem, 200 / portTICK_PERIOD_MS) == pdTRUE){
91-
space = tud_cdc_n_write_available(itf);
92-
}
93-
if(!space){
94-
return sofar;
95-
}
79+
size_t max_size = tud_cdc_n_write_available(itf);
80+
size_t queued_size = 0;
81+
if(max_size == 0){
82+
if(tud_cdc_n_write_flush(itf) > 0){
83+
// no space but we were able to flush some out
84+
max_size = tud_cdc_n_write_available(itf);
9685
}
97-
if(tosend < space){
98-
space = tosend;
86+
if(max_size == 0){
87+
// no space and could not flush
88+
return 0;
9989
}
100-
uint32_t sent = tud_cdc_n_write(itf, buffer + sofar, space);
101-
if(!sent){
102-
return sofar;
103-
}
104-
sofar += sent;
105-
tosend -= sent;
90+
}
91+
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(ring_buf, &queued_size, 0, max_size);
92+
if (queued_buff == NULL) {
93+
//log_d("nothing to send");
94+
return 0;
95+
}
96+
size_t sent = tud_cdc_n_write(itf, queued_buff, queued_size);
97+
if(sent && sent < max_size){
10698
tud_cdc_n_write_flush(itf);
107-
//xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
10899
}
109-
return sofar;
100+
vRingbufferReturnItem(ring_buf, queued_buff);
101+
return sent;
110102
}
111103

112-
static void ARDUINO_ISR_ATTR cdc0_write_char(char c)
113-
{
114-
tinyusb_cdc_write(0, (const uint8_t *)&c, 1);
104+
static size_t ring_buf_write_nb(uint8_t itf, RingbufHandle_t ring_buf, const uint8_t *buffer, size_t size){
105+
if(buffer == NULL || ring_buf == NULL || size == 0){
106+
return 0;
107+
}
108+
size_t space = xRingbufferGetCurFreeSize(ring_buf);
109+
if(space == 0){
110+
// ring buff is full, maybe we can flush some?
111+
if(ring_buf_flush(itf, ring_buf)){
112+
space = xRingbufferGetCurFreeSize(ring_buf);
113+
}
114+
}
115+
if(size > space){
116+
size = space;
117+
}
118+
if(size && xRingbufferSend(ring_buf, (void*) (buffer), size, 0) != pdTRUE){
119+
return 0;
120+
}
121+
ring_buf_flush(itf, ring_buf);
122+
return size;
123+
}
124+
125+
static void ARDUINO_ISR_ATTR cdc0_write_char(char c){
126+
if(devices[0] != NULL){
127+
devices[0]->write(c);
128+
}
115129
}
116130

117131
static void usb_unplugged_cb(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
118132
((USBCDC*)arg)->_onUnplugged();
119133
}
120134

121-
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_sem(NULL) {
135+
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_ring_buf(NULL) {
122136
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
123137
if(itf < MAX_USB_CDC_DEVICES){
124138
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
@@ -151,13 +165,25 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
151165
return rx_queue_len;
152166
}
153167

168+
size_t USBCDC::setTxBufferSize(size_t tx_queue_len){
169+
if(tx_ring_buf){
170+
if(!tx_queue_len){
171+
vRingbufferDelete(tx_ring_buf);
172+
tx_ring_buf = NULL;
173+
}
174+
return 0;
175+
}
176+
tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF);
177+
if(!tx_ring_buf){
178+
return 0;
179+
}
180+
return tx_queue_len;
181+
}
182+
154183
void USBCDC::begin(unsigned long baud)
155184
{
156-
if(tx_sem == NULL){
157-
tx_sem = xSemaphoreCreateBinary();
158-
xSemaphoreTake(tx_sem, 0);
159-
}
160185
setRxBufferSize(256);//default if not preset
186+
setTxBufferSize(256);//default if not preset
161187
devices[itf] = this;
162188
}
163189

@@ -166,10 +192,7 @@ void USBCDC::end()
166192
connected = false;
167193
devices[itf] = NULL;
168194
setRxBufferSize(0);
169-
if (tx_sem != NULL) {
170-
vSemaphoreDelete(tx_sem);
171-
tx_sem = NULL;
172-
}
195+
setTxBufferSize(0);
173196
}
174197

175198
void USBCDC::_onUnplugged(void){
@@ -278,6 +301,7 @@ void USBCDC::_onRX(){
278301
}
279302

280303
void USBCDC::_onTX(){
304+
ring_buf_flush(itf, tx_ring_buf);
281305
arduino_usb_cdc_event_data_t p = {0};
282306
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_TX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
283307
}
@@ -336,23 +360,38 @@ size_t USBCDC::read(uint8_t *buffer, size_t size)
336360

337361
void USBCDC::flush(void)
338362
{
339-
if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
363+
if(itf >= MAX_USB_CDC_DEVICES || tx_ring_buf == NULL){
340364
return;
341365
}
342-
tud_cdc_n_write_flush(itf);
366+
UBaseType_t uxItemsWaiting = 0;
367+
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
368+
while(uxItemsWaiting){
369+
ring_buf_flush(itf, tx_ring_buf);
370+
delay(5);
371+
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
372+
}
343373
}
344374

345375
int USBCDC::availableForWrite(void)
346376
{
347-
if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
348-
return -1;
377+
if(itf >= MAX_USB_CDC_DEVICES || tx_ring_buf == NULL){
378+
return 0;
349379
}
350-
return tud_cdc_n_write_available(itf);
380+
return xRingbufferGetCurFreeSize(tx_ring_buf);
351381
}
352382

353383
size_t USBCDC::write(const uint8_t *buffer, size_t size)
354384
{
355-
return tinyusb_cdc_write(itf, buffer, size);
385+
if(itf >= MAX_USB_CDC_DEVICES || tx_ring_buf == NULL){
386+
return 0;
387+
}
388+
size_t sent = ring_buf_write_nb(itf, tx_ring_buf, buffer, size);
389+
if(sent < size){
390+
//log_w("sent %u less bytes", size - sent);
391+
//maybe wait to send the rest?
392+
//xRingbufferSend(tx_ring_buf, (void*) (buffer+sent), size-sent, timeout_ms / portTICK_PERIOD_MS)
393+
}
394+
return sent;
356395
}
357396

358397
size_t USBCDC::write(uint8_t c)

cores/esp32/USBCDC.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include <inttypes.h>
2020
#include "esp_event.h"
21+
#include "freertos/FreeRTOS.h"
22+
#include "freertos/ringbuf.h"
2123
#include "Stream.h"
2224

2325
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);
@@ -59,6 +61,7 @@ class USBCDC: public Stream
5961
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
6062

6163
size_t setRxBufferSize(size_t);
64+
size_t setTxBufferSize(size_t);
6265
void begin(unsigned long baud=0);
6366
void end();
6467

@@ -113,7 +116,6 @@ class USBCDC: public Stream
113116
void _onRX(void);
114117
void _onTX(void);
115118
void _onUnplugged(void);
116-
xSemaphoreHandle tx_sem;
117119

118120
protected:
119121
uint8_t itf;
@@ -126,6 +128,7 @@ class USBCDC: public Stream
126128
bool connected;
127129
bool reboot_enable;
128130
xQueueHandle rx_queue;
131+
RingbufHandle_t tx_ring_buf;
129132

130133
};
131134

0 commit comments

Comments
 (0)