diff --git a/src/cdc_uart.c b/src/cdc_uart.c index 95124ff..45942cf 100644 --- a/src/cdc_uart.c +++ b/src/cdc_uart.c @@ -24,11 +24,16 @@ */ #include +#include "FreeRTOS.h" +#include "task.h" #include "tusb.h" #include "picoprobe_config.h" +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]; @@ -40,7 +45,9 @@ void cdc_uart_init(void) { uart_init(PICOPROBE_UART_INTERFACE, PICOPROBE_UART_BAUDRATE); } -void cdc_task(void) { +void cdc_task(void) +{ + static int was_connected = 0; uint rx_len = 0; // Consume uart fifo regardless even if not connected @@ -49,6 +56,7 @@ void cdc_task(void) { } if (tud_cdc_connected()) { + was_connected = 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... */ @@ -69,17 +77,50 @@ void cdc_task(void) { tx_len = tud_cdc_read(tx_buf, watermark); uart_write_blocking(PICOPROBE_UART_INTERFACE, tx_buf, tx_len); } - } else { - /* Clear out characters that may be left - - note that this doesn't include buffers already under hardware control */ + } else if (was_connected) { tud_cdc_write_clear(); + was_connected = 0; } } -void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) { - picoprobe_info("New baud rate %d\n", line_coding->bit_rate); - uart_deinit(PICOPROBE_UART_INTERFACE); - tud_cdc_write_clear(); - tud_cdc_read_flush(); - uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate); +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(); + delayed = xTaskDelayUntil(&last_wake, interval); + if (delayed == pdFALSE) + last_wake = xTaskGetTickCount(); + } +} + +void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) +{ + /* 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. + */ + uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(line_coding->bit_rate, 1); + + /* Modifying state, so park the thread before changing it. */ + vTaskSuspend(uart_taskhandle); + interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ)); + picoprobe_info("New baud rate %d micros %d interval %u\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); + vTaskResume(uart_taskhandle); +} + +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) +{ + /* 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); + else + vTaskResume(uart_taskhandle); } diff --git a/src/cdc_uart.h b/src/cdc_uart.h index 186ad50..8782b64 100644 --- a/src/cdc_uart.h +++ b/src/cdc_uart.h @@ -26,7 +26,10 @@ #ifndef CDC_UART_H #define CDC_UART_H +void cdc_thread(void *ptr); void cdc_uart_init(void); void cdc_task(void); +extern TaskHandle_t uart_taskhandle; + #endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index fb4247d..d3ee842 100644 --- a/src/main.c +++ b/src/main.c @@ -47,9 +47,40 @@ static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE]; static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE]; -void dap_task(void); +#define THREADED 1 + +#define UART_TASK_PRIO (tskIDLE_PRIORITY + 3) +#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2) +#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1) + +static TaskHandle_t dap_taskhandle, tud_taskhandle; + +void usb_thread(void *ptr) +{ + do { + tud_task(); + // Trivial delay to save power + vTaskDelay(1); + } while (1); +} + +void dap_thread(void *ptr) +{ + uint32_t resp_len; + do { + if (tud_vendor_available()) { + tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer)); + resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer); + tud_vendor_write(TxDataBuffer, resp_len); + } else { + // Trivial delay to save power + vTaskDelay(2); + } + } while (1); +} int main(void) { + uint32_t resp_len; board_init(); usb_serial_init(); @@ -64,31 +95,33 @@ int main(void) { picoprobe_info("Welcome to Picoprobe!\n"); - while (1) { - tud_task(); // tinyusb device task + if (THREADED) { + /* UART needs to preempt USB as if we don't, characters get lost */ + xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle); + xTaskCreate(usb_thread, "TUD", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &tud_taskhandle); + /* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */ + xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle); + vTaskStartScheduler(); + } + + while (!THREADED) { + tud_task(); cdc_task(); #if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM) probe_task(); led_task(); #elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2) - dap_task(); + if (tud_vendor_available()) { + tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer)); + resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer); + tud_vendor_write(TxDataBuffer, resp_len); + } #endif } return 0; } -void dap_task(void) -{ - uint32_t count; - uint32_t resp_len; - if (tud_vendor_available()) { - count = tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer)); - resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer); - tud_vendor_write(TxDataBuffer, resp_len); - } -} - uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { // TODO not Implemented