From c007f781853ce1a53cba4fa5f971c094f4357952 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 2 Oct 2023 14:50:19 +0100 Subject: [PATCH] Add picoprobe-only EXTRA_UART cmake option bridging 2nd hardware UART over USB-CDC as tty1 New CDC_UARTS count placed in CMakeLists.txt to ensure tusb_config.h and cdc_uart.c can see it. CMakeLists.txt, picoprobe_config.h, board_pico_config.h, tusb_config.h, main.c, cdc_uart.h, cdc_uart.c, usb_descriptors.c Extra usb descriptor, interfaces and endpoints when needed. desc_ms_os_20 NOT updated. cdc_task() gets new tty index param (extant UART always tty0). was_connected and usb rx/tx buffers now module-static arrays with tty index. All tud_cdc_* calls changed to tud_cdc_n_* variants, PICOPROBE_UART_RX/TX_LED remain tty0-only. Basic testing: Pi4B host, THREADED 1, DAP_V2: minicom receives uart output(s), openocd 'program xxx.elf verify reset exit' works. --- CMakeLists.txt | 12 +++++ include/board_pico_config.h | 12 ++++- src/cdc_uart.c | 105 ++++++++++++++++++++++++------------ src/cdc_uart.h | 4 +- src/main.c | 4 +- src/picoprobe_config.h | 16 ++++-- src/tusb_config.h | 2 +- src/usb_descriptors.c | 17 +++++- 8 files changed, 126 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7df996f7..bda7c151b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,10 +48,22 @@ option (DEBUGPROBE "compile for the debugprobe" OFF) if (DEBUGPROBE) target_compile_definitions (picoprobe PRIVATE DEBUGPROBE=1 + CDC_UARTS=1 ) set_target_properties(picoprobe PROPERTIES OUTPUT_NAME "debugprobe" ) +else () + option (EXTRA_UART "bridge both probe UARTs") + if (EXTRA_UART) + target_compile_definitions (picoprobe PRIVATE + CDC_UARTS=2 + ) + else () + target_compile_definitions (picoprobe PRIVATE + CDC_UARTS=1 + ) + endif () endif () diff --git a/include/board_pico_config.h b/include/board_pico_config.h index dd5f766d2..64298cc33 100644 --- a/include/board_pico_config.h +++ b/include/board_pico_config.h @@ -39,14 +39,22 @@ #define PROBE_PIN_RESET 1 #endif -// UART config +// UART config, ttyACMn #define PICOPROBE_UART_TX 4 #define PICOPROBE_UART_RX 5 #define PICOPROBE_UART_INTERFACE uart1 #define PICOPROBE_UART_BAUDRATE 115200 +#if CDC_UARTS == 2 +// if enabled, always ttyACM(n+1) +#define PICOPROBE_EXTRA_UART_TX 0 +#define PICOPROBE_EXTRA_UART_RX 1 +#define PICOPROBE_EXTRA_UART_INTERFACE uart0 +#define PICOPROBE_EXTRA_UART_BAUDRATE 115200 +#endif + #define PICOPROBE_USB_CONNECTED_LED 25 #define PROBE_PRODUCT_STRING "Picoprobe (CMSIS-DAP)" -#endif \ No newline at end of file +#endif diff --git a/src/cdc_uart.c b/src/cdc_uart.c index 525b0121a..c4109ccc8 100644 --- a/src/cdc_uart.c +++ b/src/cdc_uart.c @@ -34,8 +34,10 @@ 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]; +static uint8_t tx_buf[CDC_UARTS][CFG_TUD_CDC_TX_BUFSIZE]; +static uint8_t rx_buf[CDC_UARTS][CFG_TUD_CDC_RX_BUFSIZE]; +static int was_connected[CDC_UARTS]; + // Actually s^-1 so 25ms #define DEBOUNCE_MS 40 static uint debounce_ticks = 5; @@ -54,75 +56,100 @@ 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); + +#if CDC_UARTS == 2 + gpio_set_function(PICOPROBE_EXTRA_UART_TX, GPIO_FUNC_UART); + gpio_set_function(PICOPROBE_EXTRA_UART_RX, GPIO_FUNC_UART); + gpio_set_pulls(PICOPROBE_EXTRA_UART_TX, 1, 0); + gpio_set_pulls(PICOPROBE_EXTRA_UART_RX, 1, 0); + uart_init(PICOPROBE_EXTRA_UART_INTERFACE, PICOPROBE_EXTRA_UART_BAUDRATE); +#endif + for (int n = 0; n < CDC_UARTS; n++) { was_connected[n] = 0; } } -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 == 2 + if (tty) { uart_ptr = PICOPROBE_EXTRA_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 < sizeof(rx_buf[tty]))) { + 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 (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), sizeof(tx_buf[tty])); 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); + 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; } } +void cdc_tasks(void) { + for (int tty = 0; tty < CDC_UARTS; tty++) { cdc_task(tty); } +} + 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 +158,7 @@ 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; /* 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. */ @@ -141,27 +169,36 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS)); 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 CDC_UARTS == 2 + if (tty) { uart_ptr = PICOPROBE_EXTRA_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); vTaskResume(uart_taskhandle); } void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + uint8_t tty = itf; /* 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); } diff --git a/src/cdc_uart.h b/src/cdc_uart.h index 8782b64c1..05db5e387 100644 --- a/src/cdc_uart.h +++ b/src/cdc_uart.h @@ -28,8 +28,8 @@ void cdc_thread(void *ptr); void cdc_uart_init(void); -void cdc_task(void); +void cdc_tasks(void); extern TaskHandle_t uart_taskhandle; -#endif \ No newline at end of file +#endif diff --git a/src/main.c b/src/main.c index ac090111e..0b8c69c82 100644 --- a/src/main.c +++ b/src/main.c @@ -41,7 +41,7 @@ #include "led.h" #include "DAP.h" -// UART0 for Picoprobe debug +// UART0 available / for Picoprobe debug // UART1 for picoprobe to target device static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE]; @@ -116,7 +116,7 @@ int main(void) { while (!THREADED) { tud_task(); - cdc_task(); + cdc_tasks(); #if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2) if (tud_vendor_available()) { diff --git a/src/picoprobe_config.h b/src/picoprobe_config.h index fd46f31eb..357d955bf 100644 --- a/src/picoprobe_config.h +++ b/src/picoprobe_config.h @@ -65,11 +65,21 @@ do { \ // TODO tie this up with PICO_BOARD defines in the main SDK -#ifndef DEBUGPROBE +#ifndef DEBUGPROBE + +#if CDC_UARTS < 1 || CDC_UARTS > 2 +#error "PICOPROBE only supports one or two UARTs" +#endif #include "board_pico_config.h" -#else -#include "board_debugprobe_config.h" + +#else /* DEBUGPROBE */ + +#if CDC_UARTS != 1 +#error "DEBUGPROBE only supports one UART" #endif +#include "board_debugprobe_config.h" + +#endif /* DEBUGPROBE */ //#include "board_example_config.h" diff --git a/src/tusb_config.h b/src/tusb_config.h index cc9f80c80..81b9e2626 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -63,7 +63,7 @@ //------------- CLASS -------------// #define CFG_TUD_HID 1 -#define CFG_TUD_CDC 1 +#define CFG_TUD_CDC CDC_UARTS #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 1 diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index cb778b3dc..91706efe3 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -71,6 +71,10 @@ enum ITF_NUM_PROBE, // Old versions of Keil MDK only look at interface 0 ITF_NUM_CDC_COM, ITF_NUM_CDC_DATA, +#if (CDC_UARTS == 2) + ITF_NUM_CDC_EX_COM, + ITF_NUM_CDC_EX_DATA, +#endif ITF_NUM_TOTAL }; @@ -80,10 +84,16 @@ enum #define PROBE_OUT_EP_NUM 0x04 #define PROBE_IN_EP_NUM 0x85 +#if (CDC_UARTS == 2) +#define CDC_EX_NOTIFICATION_EP_NUM 0x86 +#define CDC_EX_DATA_OUT_EP_NUM 0x07 +#define CDC_EX_DATA_IN_EP_NUM 0x88 +#endif + #if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1) -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN) +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN * CDC_UARTS + TUD_HID_INOUT_DESC_LEN) #else -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN) +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN * CDC_UARTS + TUD_VENDOR_DESC_LEN) #endif static uint8_t const desc_hid_report[] = @@ -113,6 +123,9 @@ uint8_t const desc_configuration[] = #endif // Interface 1 + 2 TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 6, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64), +#if (CDC_UARTS == 2) + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_EX_COM, 6, CDC_EX_NOTIFICATION_EP_NUM, 64, CDC_EX_DATA_OUT_EP_NUM, CDC_EX_DATA_IN_EP_NUM, 64), +#endif }; // Invoked when received GET CONFIGURATION DESCRIPTOR