-
Notifications
You must be signed in to change notification settings - Fork 225
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
Add EXTRA_UART cmake option, bridges UART0 over USB-CDC as tty1 #102
Changes from all commits
d47b308
0761424
1586ef0
58fa7a1
d13775e
e516474
2658c2c
721b69c
0e01fc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,8 +34,14 @@ | |
TaskHandle_t uart_taskhandle; | ||
TickType_t last_wake, interval = 100; | ||
|
||
static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE]; | ||
static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE]; | ||
/* Max 1 FIFO worth of data */ | ||
#define TX_BUF_SIZ 32 | ||
#define RX_BUF_SIZ 32 | ||
static uint8_t tx_buf[CDC_UARTS][TX_BUF_SIZ]; | ||
static uint8_t rx_buf[CDC_UARTS][RX_BUF_SIZ]; | ||
static int was_connected[CDC_UARTS]; | ||
static uint cdc_tx_oe[CDC_UARTS]; | ||
|
||
// Actually s^-1 so 25ms | ||
#define DEBOUNCE_MS 40 | ||
static uint debounce_ticks = 5; | ||
|
@@ -54,75 +60,116 @@ void cdc_uart_init(void) { | |
gpio_set_pulls(PICOPROBE_UART_TX, 1, 0); | ||
gpio_set_pulls(PICOPROBE_UART_RX, 1, 0); | ||
uart_init(PICOPROBE_UART_INTERFACE, PICOPROBE_UART_BAUDRATE); | ||
for (int n = 0; n < CDC_UARTS; n++) { | ||
was_connected[n] = 0; cdc_tx_oe[n] = 0; | ||
} | ||
|
||
#ifdef PICOPROBE_UART_RTS | ||
gpio_init(PICOPROBE_UART_RTS); | ||
gpio_set_dir(PICOPROBE_UART_RTS, GPIO_OUT); | ||
gpio_put(PICOPROBE_UART_RTS, 1); | ||
#endif | ||
#ifdef PICOPROBE_UART_DTR | ||
gpio_init(PICOPROBE_UART_DTR); | ||
gpio_set_dir(PICOPROBE_UART_DTR, GPIO_OUT); | ||
gpio_put(PICOPROBE_UART_DTR, 1); | ||
#endif | ||
#if (CDC_UARTS > 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be a compile-time error or warning if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will add that error once you give me the go/no-go on other two points... |
||
gpio_set_function(PICOPROBE_EX_UART_TX, GPIO_FUNC_UART); | ||
gpio_set_function(PICOPROBE_EX_UART_RX, GPIO_FUNC_UART); | ||
gpio_set_pulls(PICOPROBE_EX_UART_TX, 1, 0); | ||
gpio_set_pulls(PICOPROBE_EX_UART_RX, 1, 0); | ||
uart_init(PICOPROBE_EX_UART_INTERFACE, PICOPROBE_EX_UART_BAUDRATE); | ||
#endif | ||
} | ||
|
||
void cdc_task(void) | ||
void cdc_task(uint8_t tty) | ||
{ | ||
static int was_connected = 0; | ||
uint rx_len = 0; | ||
|
||
uart_inst_t* uart_ptr = PICOPROBE_UART_INTERFACE; | ||
#if (CDC_UARTS > 1) | ||
if (tty != 0) { uart_ptr = PICOPROBE_EX_UART_INTERFACE; } | ||
#endif | ||
|
||
// Consume uart fifo regardless even if not connected | ||
while(uart_is_readable(PICOPROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) { | ||
rx_buf[rx_len++] = uart_getc(PICOPROBE_UART_INTERFACE); | ||
while(uart_is_readable(uart_ptr) && (rx_len < RX_BUF_SIZ)) { | ||
rx_buf[tty][rx_len++] = uart_getc(uart_ptr); | ||
} | ||
|
||
if (tud_cdc_connected()) { | ||
was_connected = 1; | ||
if (tud_cdc_n_connected(tty)) { | ||
was_connected[tty] = 1; | ||
int written = 0; | ||
/* Implicit overflow if we don't write all the bytes to the host. | ||
* Also throw away bytes if we can't write... */ | ||
if (rx_len) { | ||
if (!tty) { | ||
#ifdef PICOPROBE_UART_RX_LED | ||
gpio_put(PICOPROBE_UART_RX_LED, 1); | ||
rx_led_debounce = debounce_ticks; | ||
gpio_put(PICOPROBE_UART_RX_LED, 1); | ||
rx_led_debounce = debounce_ticks; | ||
#endif | ||
written = MIN(tud_cdc_write_available(), rx_len); | ||
} | ||
written = MIN(tud_cdc_n_write_available(tty), rx_len); | ||
if (rx_len > written) | ||
cdc_tx_oe[tty]++; | ||
|
||
if (written > 0) { | ||
tud_cdc_write(rx_buf, written); | ||
tud_cdc_write_flush(); | ||
tud_cdc_n_write(tty, rx_buf[tty], written); | ||
tud_cdc_n_write_flush(tty); | ||
} | ||
} else { | ||
if (!tty) { | ||
#ifdef PICOPROBE_UART_RX_LED | ||
if (rx_led_debounce) | ||
rx_led_debounce--; | ||
else | ||
gpio_put(PICOPROBE_UART_RX_LED, 0); | ||
if (rx_led_debounce) | ||
rx_led_debounce--; | ||
else | ||
gpio_put(PICOPROBE_UART_RX_LED, 0); | ||
#endif | ||
} | ||
} | ||
|
||
/* Reading from a firehose and writing to a FIFO. */ | ||
size_t watermark = MIN(tud_cdc_available(), sizeof(tx_buf)); | ||
size_t watermark = MIN(tud_cdc_n_available(tty), TX_BUF_SIZ); | ||
if (watermark > 0) { | ||
size_t tx_len; | ||
if (!tty) { | ||
#ifdef PICOPROBE_UART_TX_LED | ||
gpio_put(PICOPROBE_UART_TX_LED, 1); | ||
tx_led_debounce = debounce_ticks; | ||
gpio_put(PICOPROBE_UART_TX_LED, 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if it might be useful to have e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't use it, but happy to add - again let me know your preference There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually, those are There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll also change all instances of |
||
tx_led_debounce = debounce_ticks; | ||
#endif | ||
} | ||
/* Batch up to half a FIFO of data - don't clog up on RX */ | ||
watermark = MIN(watermark, 16); | ||
tx_len = tud_cdc_read(tx_buf, watermark); | ||
uart_write_blocking(PICOPROBE_UART_INTERFACE, tx_buf, tx_len); | ||
tx_len = tud_cdc_n_read(tty, tx_buf[tty], watermark); | ||
uart_write_blocking(uart_ptr, tx_buf[tty], tx_len); | ||
} else { | ||
if (!tty) { | ||
#ifdef PICOPROBE_UART_TX_LED | ||
if (tx_led_debounce) | ||
tx_led_debounce--; | ||
else | ||
gpio_put(PICOPROBE_UART_TX_LED, 0); | ||
#endif | ||
} | ||
} | ||
} else if (was_connected) { | ||
tud_cdc_write_clear(); | ||
was_connected = 0; | ||
} else if (was_connected[tty]) { | ||
tud_cdc_n_write_clear(tty); | ||
was_connected[tty] = 0; | ||
cdc_tx_oe[tty] = 0; | ||
} | ||
} | ||
|
||
void cdc_tasks(void) { | ||
for (int n = 0; n < CDC_UARTS; n++) { cdc_task(n); } | ||
} | ||
|
||
void cdc_thread(void *ptr) | ||
{ | ||
BaseType_t delayed; | ||
last_wake = xTaskGetTickCount(); | ||
/* Threaded with a polling interval that scales according to linerate */ | ||
while (1) { | ||
cdc_task(); | ||
cdc_tasks(); | ||
delayed = xTaskDelayUntil(&last_wake, interval); | ||
if (delayed == pdFALSE) | ||
last_wake = xTaskGetTickCount(); | ||
|
@@ -131,6 +178,9 @@ void cdc_thread(void *ptr) | |
|
||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) | ||
{ | ||
uint8_t tty = itf; | ||
uart_parity_t parity; | ||
uint data_bits, stop_bits; | ||
/* Set the tick thread interval to the amount of time it takes to | ||
* fill up half a FIFO. Millis is too coarse for integer divide. | ||
*/ | ||
|
@@ -139,29 +189,92 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) | |
vTaskSuspend(uart_taskhandle); | ||
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ)); | ||
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS)); | ||
picoprobe_info("New baud rate %d micros %d interval %u\n", | ||
picoprobe_info("New baud rate %ld micros %ld interval %lu\n", | ||
line_coding->bit_rate, micros, interval); | ||
uart_deinit(PICOPROBE_UART_INTERFACE); | ||
tud_cdc_write_clear(); | ||
tud_cdc_read_flush(); | ||
uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate); | ||
|
||
uart_inst_t* uart_ptr = PICOPROBE_UART_INTERFACE; | ||
#if defined(PICOPROBE_EXTRA_UART) | ||
if (tty) { uart_ptr = PICOPROBE_EX_UART_INTERFACE; } | ||
#endif | ||
|
||
uart_deinit(uart_ptr); | ||
tud_cdc_n_write_clear(tty); | ||
tud_cdc_n_read_flush(tty); | ||
uart_init(uart_ptr, line_coding->bit_rate); | ||
|
||
switch (line_coding->parity) { | ||
case CDC_LINE_CODING_PARITY_ODD: | ||
parity = UART_PARITY_ODD; | ||
break; | ||
case CDC_LINE_CODING_PARITY_EVEN: | ||
parity = UART_PARITY_EVEN; | ||
break; | ||
default: | ||
picoprobe_info("invalid parity setting %u\n", line_coding->parity); | ||
/* fallthrough */ | ||
case CDC_LINE_CODING_PARITY_NONE: | ||
parity = UART_PARITY_NONE; | ||
break; | ||
} | ||
|
||
switch (line_coding->data_bits) { | ||
case 5: | ||
case 6: | ||
case 7: | ||
case 8: | ||
data_bits = line_coding->data_bits; | ||
break; | ||
default: | ||
picoprobe_info("invalid data bits setting: %u\n", line_coding->data_bits); | ||
data_bits = 8; | ||
break; | ||
} | ||
|
||
/* The PL011 only supports 1 or 2 stop bits. 1.5 stop bits is translated to 2, | ||
* which is safer than the alternative. */ | ||
switch (line_coding->stop_bits) { | ||
case CDC_LINE_CONDING_STOP_BITS_1_5: | ||
case CDC_LINE_CONDING_STOP_BITS_2: | ||
stop_bits = 2; | ||
break; | ||
default: | ||
picoprobe_info("invalid stop bits setting: %u\n", line_coding->stop_bits); | ||
/* fallthrough */ | ||
case CDC_LINE_CONDING_STOP_BITS_1: | ||
stop_bits = 1; | ||
break; | ||
} | ||
|
||
uart_set_format(uart_ptr, data_bits, stop_bits, parity); | ||
vTaskResume(uart_taskhandle); | ||
} | ||
|
||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) | ||
{ | ||
uint8_t tty = itf; | ||
if (!tty) { | ||
#ifdef PICOPROBE_UART_RTS | ||
gpio_put(PICOPROBE_UART_RTS, !rts); | ||
#endif | ||
#ifdef PICOPROBE_UART_DTR | ||
gpio_put(PICOPROBE_UART_DTR, !dtr); | ||
#endif | ||
} | ||
|
||
/* CDC drivers use linestate as a bodge to activate/deactivate the interface. | ||
* Resume our UART polling on activate, stop on deactivate */ | ||
if (!dtr && !rts) { | ||
vTaskSuspend(uart_taskhandle); | ||
if (!tty) { | ||
#ifdef PICOPROBE_UART_RX_LED | ||
gpio_put(PICOPROBE_UART_RX_LED, 0); | ||
rx_led_debounce = 0; | ||
gpio_put(PICOPROBE_UART_RX_LED, 0); | ||
rx_led_debounce = 0; | ||
#endif | ||
#ifdef PICOPROBE_UART_RX_LED | ||
gpio_put(PICOPROBE_UART_TX_LED, 0); | ||
tx_led_debounce = 0; | ||
gpio_put(PICOPROBE_UART_TX_LED, 0); | ||
tx_led_debounce = 0; | ||
#endif | ||
} | ||
} else | ||
vTaskResume(uart_taskhandle); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not immediately obvious what
EX
stands for here - could beEXternal
,EXtra
,EXtended
,EXpired
? 😉There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was originally EXTRA, but just seemed too long - let me know preferred