Compare commits

...

3 Commits

Author SHA1 Message Date
Jonathan Bell
99c5156cd7 cdc_acm: turn on UART_TX LED when sending break signals
Software typically asserts line break for an extended period
(e.g. PuTTY will assert continuously until the next keypress), so
provide feedback via the LED.

Also declare variables used in both tinyusb callback and uart_thread context
as volatile.
2024-03-18 15:58:03 +00:00
Jonathan Bell
30a605f5cd cdc_uart: add CTS/RTS configuration options to board_example_config
For high data rate applications it's desirable to use hardware flow control
to prevent characters getting dropped when faced with the vagaries of RTOS
and kernel latencies. Adding PROBE_UART_HWFC enables the UART's CTS/RTS pins,
and SET_LINE_STATE messages no longer affect the RTS pin.
2024-03-18 15:16:37 +00:00
Jonathan Bell
68f01543c6 cdc_uart: add break handling
Implement break set/unset and declare the interface as capable of sending
line breaks.
2024-03-18 15:16:37 +00:00
4 changed files with 77 additions and 9 deletions

View File

@@ -37,6 +37,10 @@
/* Include CDC interface to bridge to target UART. Omit if not used. */
#define PROBE_CDC_UART
/* Board implements hardware flow control for UART RTS/CTS instead of ACM control */
#define PROBE_UART_HWFC
/* Target reset GPIO (active-low). Omit if not used.*/
#define PROBE_PIN_RESET 1
@@ -68,9 +72,17 @@
#define PROBE_UART_RX 5
#define PROBE_UART_INTERFACE uart1
#define PROBE_UART_BAUDRATE 115200
/* Flow control - some or all of these can be omitted if not used */
#if defined(PROBE_UART_HWFC)
/* Hardware flow control - see 1.4.3 in the RP2040 datasheet for valid pin settings */
#define PROBE_UART_CTS 6
#define PROBE_UART_RTS 7
#else
/* Software flow control - RTS and DTR can be omitted if not used */
#define PROBE_UART_RTS 9
#endif
#define PROBE_UART_DTR 10
#endif
/* LED config - some or all of these can be omitted if not used */

View File

@@ -33,6 +33,8 @@
TaskHandle_t uart_taskhandle;
TickType_t last_wake, interval = 100;
volatile TickType_t break_expiry;
volatile bool timed_break;
/* Max 1 FIFO worth of data */
static uint8_t tx_buf[32];
@@ -42,7 +44,7 @@ static uint8_t rx_buf[32];
static uint debounce_ticks = 5;
#ifdef PROBE_UART_TX_LED
static uint tx_led_debounce;
static volatile uint tx_led_debounce;
#endif
#ifdef PROBE_UART_RX_LED
@@ -56,11 +58,23 @@ void cdc_uart_init(void) {
gpio_set_pulls(PROBE_UART_RX, 1, 0);
uart_init(PROBE_UART_INTERFACE, PROBE_UART_BAUDRATE);
#ifdef PROBE_UART_HWFC
/* HWFC implies that hardware flow control is implemented and the
* UART operates in "full-duplex" mode (See USB CDC PSTN120 6.3.12).
* Default to pulling in the active direction, so an unconnected CTS
* behaves the same as if CTS were not enabled. */
gpio_set_pulls(PROBE_UART_CTS, 0, 1);
gpio_set_function(PROBE_UART_RTS, GPIO_FUNC_UART);
gpio_set_function(PROBE_UART_CTS, GPIO_FUNC_UART);
uart_set_hw_flow(PROBE_UART_INTERFACE, true, true);
#else
#ifdef PROBE_UART_RTS
gpio_init(PROBE_UART_RTS);
gpio_set_dir(PROBE_UART_RTS, GPIO_OUT);
gpio_put(PROBE_UART_RTS, 1);
#endif
#endif
#ifdef PROBE_UART_DTR
gpio_init(PROBE_UART_DTR);
gpio_set_dir(PROBE_UART_DTR, GPIO_OUT);
@@ -68,11 +82,12 @@ void cdc_uart_init(void) {
#endif
}
void cdc_task(void)
bool cdc_task(void)
{
static int was_connected = 0;
static uint cdc_tx_oe = 0;
uint rx_len = 0;
bool keep_alive = false;
// Consume uart fifo regardless even if not connected
while(uart_is_readable(PROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
@@ -126,23 +141,40 @@ void cdc_task(void)
gpio_put(PROBE_UART_TX_LED, 0);
#endif
}
/* Pending break handling */
if (timed_break) {
if (((int)break_expiry - (int)xTaskGetTickCount()) < 0) {
timed_break = false;
uart_set_break(PROBE_UART_INTERFACE, false);
tx_led_debounce = 0;
} else {
keep_alive = true;
}
}
} else if (was_connected) {
tud_cdc_write_clear();
uart_set_break(PROBE_UART_INTERFACE, false);
timed_break = false;
was_connected = 0;
tx_led_debounce = 0;
cdc_tx_oe = 0;
}
return keep_alive;
}
void cdc_thread(void *ptr)
{
BaseType_t delayed;
last_wake = xTaskGetTickCount();
bool keep_alive;
/* 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();
keep_alive = cdc_task();
if (!keep_alive) {
delayed = xTaskDelayUntil(&last_wake, interval);
if (delayed == pdFALSE)
last_wake = xTaskGetTickCount();
}
}
}
@@ -236,3 +268,25 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
} else
vTaskResume(uart_taskhandle);
}
void tud_cdc_send_break_cb(uint8_t itf, uint16_t wValue) {
switch(wValue) {
case 0:
uart_set_break(PROBE_UART_INTERFACE, false);
timed_break = false;
break;
case 0xffff:
uart_set_break(PROBE_UART_INTERFACE, true);
timed_break = false;
gpio_put(PROBE_UART_TX_LED, 1);
tx_led_debounce = 1 << 30;
break;
default:
uart_set_break(PROBE_UART_INTERFACE, true);
timed_break = true;
gpio_put(PROBE_UART_TX_LED, 1);
break_expiry = xTaskGetTickCount() + (wValue * (configTICK_RATE_HZ / 1000));
tx_led_debounce = 1 << 30;
break;
}
}

View File

@@ -28,7 +28,7 @@
void cdc_thread(void *ptr);
void cdc_uart_init(void);
void cdc_task(void);
bool cdc_task(void);
extern TaskHandle_t uart_taskhandle;

View File

@@ -97,7 +97,7 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
return desc_hid_report;
}
uint8_t const desc_configuration[] =
uint8_t desc_configuration[] =
{
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100),
// Interface 0
@@ -121,6 +121,8 @@ uint8_t const desc_configuration[] =
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
/* Hack in CAP_BREAK support */
desc_configuration[CONFIG_TOTAL_LEN - TUD_CDC_DESC_LEN + 8 + 9 + 5 + 5 + 4 - 1] = 0x6;
return desc_configuration;
}