Skip to content

Commit 4a55ff9

Browse files
authored
Add support for the hardware CDC in ESP32-C3 (#5614)
* Add support for the hardware CDC in ESP32-C3
1 parent a62979d commit 4a55ff9

File tree

7 files changed

+390
-1
lines changed

7 files changed

+390
-1
lines changed

Diff for: CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(CORE_SRCS
3232
cores/esp32/stdlib_noniso.c
3333
cores/esp32/Stream.cpp
3434
cores/esp32/StreamString.cpp
35+
cores/esp32/HWCDC.cpp
3536
cores/esp32/USB.cpp
3637
cores/esp32/USBCDC.cpp
3738
cores/esp32/USBMSC.cpp

Diff for: boards.txt

+16
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ menu.EventsCore=Events Run On
2121
##############################################################
2222

2323
esp32c3.name=ESP32C3 Dev Module
24+
esp32c3.vid.0=0x303a
25+
esp32c3.pid.0=0x1001
2426

2527
esp32c3.upload.tool=esptool_py
2628
esp32c3.upload.maximum_size=1310720
2729
esp32c3.upload.maximum_data_size=327680
2830
esp32c3.upload.flags=
2931
esp32c3.upload.extra_flags=
32+
esp32c3.upload.use_1200bps_touch=false
33+
esp32c3.upload.wait_for_upload_port=false
3034

3135
esp32c3.serial.disableDTR=false
3236
esp32c3.serial.disableRTS=false
@@ -48,6 +52,18 @@ esp32c3.build.boot=qio
4852
esp32c3.build.partitions=default
4953
esp32c3.build.defines=
5054

55+
esp32c3.menu.CDCOnBoot.default=Disabled
56+
esp32c3.menu.CDCOnBoot.default.build.cdc_on_boot=0
57+
esp32c3.menu.CDCOnBoot.cdc=Enabled
58+
esp32c3.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
59+
60+
esp32c3.menu.UploadMode.default=UART0
61+
esp32c3.menu.UploadMode.default.upload.use_1200bps_touch=false
62+
esp32c3.menu.UploadMode.default.upload.wait_for_upload_port=false
63+
esp32c3.menu.UploadMode.cdc=Internal USB
64+
esp32c3.menu.UploadMode.cdc.upload.use_1200bps_touch=true
65+
esp32c3.menu.UploadMode.cdc.upload.wait_for_upload_port=true
66+
5167
esp32c3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
5268
esp32c3.menu.PartitionScheme.default.build.partitions=default
5369
esp32c3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS)

Diff for: cores/esp32/HWCDC.cpp

+283
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#include "USB.h"
15+
#if CONFIG_IDF_TARGET_ESP32C3
16+
17+
#include "esp32-hal.h"
18+
#include "HWCDC.h"
19+
#include "freertos/FreeRTOS.h"
20+
#include "freertos/semphr.h"
21+
#include "freertos/queue.h"
22+
#include "freertos/ringbuf.h"
23+
#include "esp_intr_alloc.h"
24+
#include "soc/periph_defs.h"
25+
#include "hal/usb_serial_jtag_ll.h"
26+
27+
static RingbufHandle_t tx_ring_buf = NULL;
28+
static xQueueHandle rx_queue = NULL;
29+
static uint8_t rx_data_buf[64];
30+
static intr_handle_t intr_handle = NULL;
31+
static volatile bool initial_empty = false;
32+
33+
static void hw_cdc_isr_handler(void *arg) {
34+
portBASE_TYPE xTaskWoken = 0;
35+
uint32_t usbjtag_intr_status = 0;
36+
usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
37+
38+
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
39+
// Interrupt tells us the host picked up the data we sent.
40+
if (usb_serial_jtag_ll_txfifo_writable() == 1) {
41+
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
42+
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
43+
44+
if(!initial_empty){
45+
initial_empty = true;
46+
//send event?
47+
//ets_printf("CONNECTED\n");
48+
}
49+
size_t queued_size;
50+
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64);
51+
// If the hardware fifo is avaliable, write in it. Otherwise, do nothing.
52+
if (queued_buff != NULL) { //Although tx_queued_bytes may be larger than 0. We may have interrupt before xRingbufferSend() was called.
53+
//Copy the queued buffer into the TX FIFO
54+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
55+
usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size);
56+
usb_serial_jtag_ll_txfifo_flush();
57+
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken);
58+
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
59+
//send event?
60+
//ets_printf("TX:%u\n", queued_size);
61+
}
62+
} else {
63+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
64+
}
65+
}
66+
67+
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) {
68+
// read rx buffer(max length is 64), and send avaliable data to ringbuffer.
69+
// Ensure the rx buffer size is larger than RX_MAX_SIZE.
70+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
71+
uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(rx_data_buf, 64);
72+
uint32_t i=0;
73+
for(i=0; i<rx_fifo_len; i++){
74+
if(rx_queue == NULL || !xQueueSendFromISR(rx_queue, rx_data_buf+i, &xTaskWoken)){
75+
break;
76+
}
77+
}
78+
//send event?
79+
//ets_printf("RX:%u/%u\n", i, rx_fifo_len);
80+
}
81+
82+
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
83+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
84+
initial_empty = false;
85+
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
86+
//ets_printf("BUS_RESET\n");
87+
}
88+
89+
if (xTaskWoken == pdTRUE) {
90+
portYIELD_FROM_ISR();
91+
}
92+
}
93+
94+
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
95+
if(xPortInIsrContext()){
96+
xRingbufferSendFromISR(tx_ring_buf, (void*) (&c), 1, NULL);
97+
} else {
98+
xRingbufferSend(tx_ring_buf, (void*) (&c), 1, 0);
99+
}
100+
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
101+
}
102+
103+
HWCDC::HWCDC() {
104+
105+
}
106+
107+
HWCDC::~HWCDC(){
108+
end();
109+
}
110+
111+
HWCDC::operator bool() const
112+
{
113+
return initial_empty;
114+
}
115+
116+
void HWCDC::begin(unsigned long baud)
117+
{
118+
setRxBufferSize(256);//default if not preset
119+
setTxBufferSize(256);//default if not preset
120+
121+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
122+
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
123+
if(!intr_handle && esp_intr_alloc(ETS_USB_INTR_SOURCE/*ETS_USB_SERIAL_JTAG_INTR_SOURCE*/, 0, hw_cdc_isr_handler, NULL, &intr_handle) != ESP_OK){
124+
isr_log_e("HW USB CDC failed to init interrupts");
125+
end();
126+
}
127+
}
128+
129+
void HWCDC::end()
130+
{
131+
//Disable tx/rx interrupt.
132+
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
133+
esp_intr_free(intr_handle);
134+
intr_handle = NULL;
135+
setRxBufferSize(0);
136+
setTxBufferSize(0);
137+
}
138+
139+
/*
140+
* WRITING
141+
*/
142+
143+
size_t HWCDC::setTxBufferSize(size_t tx_queue_len){
144+
if(tx_ring_buf){
145+
if(!tx_queue_len){
146+
vRingbufferDelete(tx_ring_buf);
147+
tx_ring_buf = NULL;
148+
}
149+
return 0;
150+
}
151+
tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF);
152+
if(!tx_ring_buf){
153+
return 0;
154+
}
155+
return tx_queue_len;
156+
}
157+
158+
int HWCDC::availableForWrite(void)
159+
{
160+
if(tx_ring_buf == NULL){
161+
return -1;
162+
}
163+
return xRingbufferGetCurFreeSize(tx_ring_buf);
164+
}
165+
166+
size_t HWCDC::write(const uint8_t *buffer, size_t size)
167+
{
168+
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
169+
if(xRingbufferSend(tx_ring_buf, (void*) (buffer), size, 200 / portTICK_PERIOD_MS) != pdTRUE){
170+
log_e("Write Failed");
171+
return 0;
172+
}
173+
// Now trigger the ISR to read data from the ring buffer.
174+
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
175+
return size;
176+
}
177+
178+
size_t HWCDC::write(uint8_t c)
179+
{
180+
return write(&c, 1);
181+
}
182+
183+
void HWCDC::flush(void)
184+
{
185+
if(tx_ring_buf == NULL){
186+
return;
187+
}
188+
UBaseType_t uxItemsWaiting = 0;
189+
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
190+
while(uxItemsWaiting){
191+
delay(5);
192+
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
193+
}
194+
}
195+
196+
/*
197+
* READING
198+
*/
199+
200+
size_t HWCDC::setRxBufferSize(size_t rx_queue_len){
201+
if(rx_queue){
202+
if(!rx_queue_len){
203+
vQueueDelete(rx_queue);
204+
rx_queue = NULL;
205+
}
206+
return 0;
207+
}
208+
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
209+
if(!rx_queue){
210+
return 0;
211+
}
212+
if(!tx_ring_buf){
213+
tx_ring_buf = xRingbufferCreate(rx_queue_len, RINGBUF_TYPE_BYTEBUF);
214+
}
215+
return rx_queue_len;
216+
}
217+
218+
int HWCDC::available(void)
219+
{
220+
if(rx_queue == NULL){
221+
return -1;
222+
}
223+
return uxQueueMessagesWaiting(rx_queue);
224+
}
225+
226+
int HWCDC::peek(void)
227+
{
228+
if(rx_queue == NULL){
229+
return -1;
230+
}
231+
uint8_t c;
232+
if(xQueuePeek(rx_queue, &c, 0)) {
233+
return c;
234+
}
235+
return -1;
236+
}
237+
238+
int HWCDC::read(void)
239+
{
240+
if(rx_queue == NULL){
241+
return -1;
242+
}
243+
uint8_t c = 0;
244+
if(xQueueReceive(rx_queue, &c, 0)) {
245+
return c;
246+
}
247+
return -1;
248+
}
249+
250+
size_t HWCDC::read(uint8_t *buffer, size_t size)
251+
{
252+
if(rx_queue == NULL){
253+
return -1;
254+
}
255+
uint8_t c = 0;
256+
size_t count = 0;
257+
while(count < size && xQueueReceive(rx_queue, &c, 0)){
258+
buffer[count++] = c;
259+
}
260+
return count;
261+
}
262+
263+
/*
264+
* DEBUG
265+
*/
266+
267+
void HWCDC::setDebugOutput(bool en)
268+
{
269+
if(en) {
270+
uartSetDebug(NULL);
271+
ets_install_putc1((void (*)(char)) &cdc0_write_char);
272+
} else {
273+
ets_install_putc1(NULL);
274+
}
275+
}
276+
277+
#if ARDUINO_HW_CDC_ON_BOOT //Serial used for USB CDC
278+
HWCDC Serial;
279+
#else
280+
HWCDC USBSerial;
281+
#endif
282+
283+
#endif /* CONFIG_TINYUSB_CDC_ENABLED */

0 commit comments

Comments
 (0)