Skip to content

Commit c7387f6

Browse files
authored
Merge pull request #97 from WestfW-patches/master
Allow multiple CDCs.
2 parents 8f4bc1f + 2ae50f2 commit c7387f6

File tree

4 files changed

+198
-24
lines changed

4 files changed

+198
-24
lines changed

examples/CDC/cdc_multi/cdc_multi.ino

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
This example demonstrates the use of multiple USB CDC/ACM "Virtual
3+
Serial" ports, using Ha Thach's TinyUSB library, and the port of
4+
that library to the Arduino environment
5+
6+
https://github.com/hathach/tinyusb
7+
https://github.com/adafruit/Adafruit_TinyUSB_Arduino
8+
9+
Written by Bill Westfield (aka WestfW), June 2021.
10+
Copyright 2021 by Bill Westfield
11+
MIT license, check LICENSE for more information
12+
13+
The example creates three virtual serial ports. Text entered on
14+
any of the ports will be echoed to the all ports.
15+
16+
The max number of CDC ports (CFG_TUD_CDC) has to be changed to at
17+
least 3, changed in the core tusb_config.h file.
18+
*/
19+
20+
#include <Adafruit_TinyUSB.h>
21+
22+
#define LED LED_BUILTIN
23+
24+
/*
25+
Create extra USB Serial Ports. "Serial" is already created.
26+
*/
27+
Adafruit_USBD_CDC USBSer1;
28+
Adafruit_USBD_CDC USBSer2;
29+
30+
void setup() {
31+
pinMode(LED, OUTPUT);
32+
33+
// start up all of the USB Vitual ports, and wait for them to enumerate.
34+
Serial.begin(115200);
35+
USBSer1.begin(115200);
36+
USBSer2.begin(115200);
37+
38+
while (!Serial || !USBSer1 || !USBSer2) {
39+
if (Serial) {
40+
Serial.println("Waiting for other USB ports");
41+
}
42+
if (USBSer1) {
43+
USBSer1.println("Waiting for other USB ports");
44+
}
45+
if (USBSer2) {
46+
USBSer2.println("Waiting for other USB ports");
47+
}
48+
delay(1000);
49+
}
50+
51+
Serial.print("You are port 0\n\r\n0> ");
52+
USBSer1.print("You are port 1\n\r\n1> ");
53+
USBSer2.print("You are port 2\n\r\n2> ");
54+
}
55+
56+
int LEDstate = 0;
57+
58+
void loop() {
59+
int ch;
60+
61+
ch = Serial.read();
62+
if (ch > 0) {
63+
printAll(ch);
64+
}
65+
66+
ch = USBSer1.read();
67+
if (ch > 0) {
68+
printAll(ch);
69+
}
70+
71+
ch = USBSer2.read();
72+
if (ch > 0) {
73+
printAll(ch);
74+
}
75+
76+
if (delay_without_delaying(500)) {
77+
LEDstate = !LEDstate;
78+
digitalWrite(LED, LEDstate);
79+
}
80+
}
81+
82+
// print to all CDC ports
83+
void printAll(int ch) {
84+
// print as it is
85+
Serial.write(ch);
86+
87+
// always lower case
88+
USBSer1.write(tolower(ch));
89+
90+
// always upper case
91+
USBSer2.write(toupper(ch));
92+
}
93+
94+
// Helper: non-blocking "delay" alternative.
95+
boolean delay_without_delaying(unsigned long time) {
96+
// return false if we're still "delaying", true if time ms has passed.
97+
// this should look a lot like "blink without delay"
98+
static unsigned long previousmillis = 0;
99+
unsigned long currentmillis = millis();
100+
if (currentmillis - previousmillis >= time) {
101+
previousmillis = currentmillis;
102+
return true;
103+
}
104+
return false;
105+
}

src/arduino/Adafruit_TinyUSB_API.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ void TinyUSB_Device_Task(void) {
4646
}
4747
#endif
4848

49+
// TODO should use getInstanceCount() API (when closed to BSP release cycle)
50+
// from Adafruit_USBD_CDC.cpp
51+
extern uint8_t _cdc_instance_count;
52+
4953
void TinyUSB_Device_FlushCDC(void) {
50-
// TODO multiple CDCs
51-
tud_cdc_n_write_flush(0);
54+
for (uint8_t instance = 0; instance < _cdc_instance_count; instance++) {
55+
tud_cdc_n_write_flush(instance);
56+
}
5257
}
5358
}
5459

src/arduino/Adafruit_USBD_CDC.cpp

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,12 @@
3333
#include "Adafruit_USBD_CDC.h"
3434
#include "Adafruit_USBD_Device.h"
3535

36-
// TODO Multiple instances supports
37-
// static uint8_t _itf_count;
38-
// static Adafruit_USBD_CDC* _itf_arr[]
39-
36+
// Starting endpoints; adjusted elsewhere as needed
4037
#define EPOUT 0x00
4138
#define EPIN 0x80
4239

40+
uint8_t _cdc_instance_count = 0;
41+
4342
Adafruit_USBD_CDC Serial;
4443

4544
Adafruit_USBD_CDC::Adafruit_USBD_CDC(void) {
@@ -70,10 +69,15 @@ void Adafruit_USBD_CDC::begin(uint32_t baud) {
7069
return;
7170
}
7271

73-
_begun = true;
72+
// too many instances
73+
if (!(_cdc_instance_count < CFG_TUD_CDC)) {
74+
return;
75+
}
7476

75-
Serial.setStringDescriptor("TinyUSB Serial");
76-
USBDevice.addInterface(Serial);
77+
_begun = true;
78+
_itf = _cdc_instance_count++;
79+
this->setStringDescriptor("TinyUSB Serial");
80+
USBDevice.addInterface(*this);
7781
}
7882

7983
void Adafruit_USBD_CDC::begin(uint32_t baud, uint8_t config) {
@@ -84,40 +88,67 @@ void Adafruit_USBD_CDC::begin(uint32_t baud, uint8_t config) {
8488
void Adafruit_USBD_CDC::end(void) {
8589
// Resset configuration descriptor without Serial as CDC
8690
USBDevice.clearConfiguration();
91+
_cdc_instance_count = 0;
8792
}
8893

8994
uint32_t Adafruit_USBD_CDC::baud(void) {
95+
if (!_begun) {
96+
return 0;
97+
}
98+
9099
cdc_line_coding_t coding;
91-
tud_cdc_get_line_coding(&coding);
100+
tud_cdc_n_get_line_coding(_itf, &coding);
92101

93102
return coding.bit_rate;
94103
}
95104

96105
uint8_t Adafruit_USBD_CDC::stopbits(void) {
106+
if (!_begun) {
107+
return 0;
108+
}
109+
97110
cdc_line_coding_t coding;
98-
tud_cdc_get_line_coding(&coding);
111+
tud_cdc_n_get_line_coding(_itf, &coding);
99112

100113
return coding.stop_bits;
101114
}
102115

103116
uint8_t Adafruit_USBD_CDC::paritytype(void) {
117+
if (!_begun) {
118+
return 0;
119+
}
120+
104121
cdc_line_coding_t coding;
105-
tud_cdc_get_line_coding(&coding);
122+
tud_cdc_n_get_line_coding(_itf, &coding);
106123

107124
return coding.parity;
108125
}
109126

110127
uint8_t Adafruit_USBD_CDC::numbits(void) {
128+
if (!_begun) {
129+
return 0;
130+
}
131+
111132
cdc_line_coding_t coding;
112-
tud_cdc_get_line_coding(&coding);
133+
tud_cdc_n_get_line_coding(_itf, &coding);
113134

114135
return coding.data_bits;
115136
}
116137

117-
int Adafruit_USBD_CDC::dtr(void) { return tud_cdc_connected(); }
138+
int Adafruit_USBD_CDC::dtr(void) {
139+
if (!_begun) {
140+
return 0;
141+
}
142+
143+
return tud_cdc_n_connected(_itf);
144+
}
118145

119146
Adafruit_USBD_CDC::operator bool() {
120-
bool ret = tud_cdc_connected();
147+
if (!_begun) {
148+
return false;
149+
}
150+
151+
bool ret = tud_cdc_n_connected(_itf);
121152

122153
// Add an yield to run usb background in case sketch block wait as follows
123154
// while( !Serial ) {}
@@ -128,7 +159,11 @@ Adafruit_USBD_CDC::operator bool() {
128159
}
129160

130161
int Adafruit_USBD_CDC::available(void) {
131-
uint32_t count = tud_cdc_available();
162+
if (!_begun) {
163+
return 0;
164+
}
165+
166+
uint32_t count = tud_cdc_n_available(_itf);
132167

133168
// Add an yield to run usb background in case sketch block wait as follows
134169
// while( !Serial.available() ) {}
@@ -140,20 +175,39 @@ int Adafruit_USBD_CDC::available(void) {
140175
}
141176

142177
int Adafruit_USBD_CDC::peek(void) {
178+
if (!_begun) {
179+
return -1;
180+
}
181+
143182
uint8_t ch;
144-
return tud_cdc_peek(&ch) ? (int)ch : -1;
183+
return tud_cdc_n_peek(_itf, &ch) ? (int)ch : -1;
184+
}
185+
186+
int Adafruit_USBD_CDC::read(void) {
187+
if (!_begun) {
188+
return -1;
189+
}
190+
return (int)tud_cdc_n_read_char(_itf);
145191
}
146192

147-
int Adafruit_USBD_CDC::read(void) { return (int)tud_cdc_read_char(); }
193+
void Adafruit_USBD_CDC::flush(void) {
194+
if (!_begun) {
195+
return;
196+
}
148197

149-
void Adafruit_USBD_CDC::flush(void) { tud_cdc_write_flush(); }
198+
tud_cdc_n_write_flush(_itf);
199+
}
150200

151201
size_t Adafruit_USBD_CDC::write(uint8_t ch) { return write(&ch, 1); }
152202

153203
size_t Adafruit_USBD_CDC::write(const uint8_t *buffer, size_t size) {
204+
if (!_begun) {
205+
return 0;
206+
}
207+
154208
size_t remain = size;
155-
while (remain && tud_cdc_connected()) {
156-
size_t wrcount = tud_cdc_write(buffer, remain);
209+
while (remain && tud_cdc_n_connected(_itf)) {
210+
size_t wrcount = tud_cdc_n_write(_itf, buffer, remain);
157211
remain -= wrcount;
158212
buffer += wrcount;
159213

@@ -167,20 +221,23 @@ size_t Adafruit_USBD_CDC::write(const uint8_t *buffer, size_t size) {
167221
}
168222

169223
int Adafruit_USBD_CDC::availableForWrite(void) {
170-
return tud_cdc_write_available();
224+
if (!_begun) {
225+
return 0;
226+
}
227+
return tud_cdc_n_write_available(_itf);
171228
}
172229

173230
extern "C" {
174231

175232
// Invoked when cdc when line state changed e.g connected/disconnected
176233
// Use to reset to DFU when disconnect with 1200 bps
177-
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
234+
void tud_cdc_line_state_cb(uint8_t instance, bool dtr, bool rts) {
178235
(void)rts;
179236

180237
// DTR = false is counted as disconnected
181238
if (!dtr) {
182239
// touch1200 only with first CDC instance (Serial)
183-
if (itf == 0) {
240+
if (instance == 0) {
184241
cdc_line_coding_t coding;
185242
tud_cdc_get_line_coding(&coding);
186243

src/arduino/Adafruit_USBD_CDC.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ class Adafruit_USBD_CDC : public Stream, public Adafruit_USBD_Interface {
7474
private:
7575
bool _begun;
7676
uint8_t _itf;
77+
78+
/* TODO when closed to BSP release cycle (SAMD, nRF, rp2040)
79+
* rename _itf to _instance, _begun can be removed
80+
*
81+
* static uint8_t getInstanceCount(void);
82+
* static uint8_t _instance_count;
83+
*/
7784
};
7885

7986
extern Adafruit_USBD_CDC Serial;

0 commit comments

Comments
 (0)