pico_stdio improvements (#598)

* add stdio_usb_connected() method
* add PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS to allow waiting for CDC connection during init(* 
* add puts_raw and putchar_raw to skip any CR/LF translation
This commit is contained in:
Graham Sanderson 2021-10-08 09:01:30 -05:00 committed by GitHub
parent 12017a07ab
commit 13be546dc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 9 deletions

View File

@ -16,6 +16,9 @@ void stdio_uart_init();
static inline void stdio_init_all() { stdio_uart_init(); }
static inline void stdio_filter_driver(stdio_driver_t *driver) {}
static inline void stdio_set_translate_crlf(stdio_driver_t *driver, bool enabled) {}
static inline bool stdio_usb_connected(void) { return true; }
int getchar_timeout_us(uint32_t timeout_us);
#define puts_raw puts
#define putchar_raw putchar
#endif

View File

@ -49,6 +49,9 @@ typedef struct stdio_driver stdio_driver_t;
* Call this method once you have set up your clocks to enable the stdio support for UART, USB
* and semihosting based on the presence of the respective libraries in the binary.
*
* When stdio_usb is configured, this method can be optionally made to block, waiting for a connection
* via the variables specified in \ref stdio_usb_init (i.e. \ref PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS)
*
* \see stdio_uart, stdio_usb, stdio_semihosting
*/
void stdio_init_all(void);
@ -74,7 +77,7 @@ int getchar_timeout_us(uint32_t timeout_us);
/*! \brief Adds or removes a driver from the list of active drivers used for input/output
* \ingroup pico_stdio
*
* \note this method should always be called on an initialized driver
* \note this method should always be called on an initialized driver and is not re-entrant
* \param driver the driver
* \param enabled true to add, false to remove
*/
@ -100,6 +103,16 @@ void stdio_filter_driver(stdio_driver_t *driver);
*/
void stdio_set_translate_crlf(stdio_driver_t *driver, bool translate);
/*! \brief putchar variant that skips any CR/LF conversion if enabled
* \ingroup pico_stdio
*/
int putchar_raw(int c);
/*! \brief puts variant that skips any CR/LF conversion if enabled
* \ingroup pico_stdio
*/
int puts_raw(const char *s);
#ifdef __cplusplus
}
#endif

View File

@ -59,6 +59,9 @@ static bool stdout_serialize_begin(void) {
static void stdout_serialize_end(void) {
}
#endif
static void stdio_out_chars_no_crlf(stdio_driver_t *driver, const char *s, int len) {
driver->out_chars(s, len);
}
static void stdio_out_chars_crlf(stdio_driver_t *driver, const char *s, int len) {
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
@ -89,7 +92,7 @@ static void stdio_out_chars_crlf(stdio_driver_t *driver, const char *s, int len)
#endif
}
static bool stdio_put_string(const char *s, int len, bool newline) {
static bool stdio_put_string(const char *s, int len, bool newline, bool no_cr) {
bool serialized = stdout_serialize_begin();
if (!serialized) {
#if PICO_STDIO_IGNORE_NESTED_STDOUT
@ -97,13 +100,14 @@ static bool stdio_put_string(const char *s, int len, bool newline) {
#endif
}
if (len == -1) len = (int)strlen(s);
void (*out_func)(stdio_driver_t *, const char *, int) = no_cr ? stdio_out_chars_no_crlf : stdio_out_chars_crlf;
for (stdio_driver_t *driver = drivers; driver; driver = driver->next) {
if (!driver->out_chars) continue;
if (filter && filter != driver) continue;
stdio_out_chars_crlf(driver, s, len);
out_func(driver, s, len);
if (newline) {
const char c = '\n';
stdio_out_chars_crlf(driver, &c, 1);
out_func(driver, &c, 1);
}
}
if (serialized) {
@ -134,13 +138,26 @@ static int stdio_get_until(char *buf, int len, absolute_time_t until) {
int WRAPPER_FUNC(putchar)(int c) {
char cc = (char)c;
stdio_put_string(&cc, 1, false);
stdio_put_string(&cc, 1, false, false);
return c;
}
int WRAPPER_FUNC(puts)(const char *s) {
int len = (int)strlen(s);
stdio_put_string(s, len, true);
stdio_put_string(s, len, true, false);
stdio_flush();
return len;
}
int putchar_raw(int c) {
char cc = (char)c;
stdio_put_string(&cc, 1, false, true);
return c;
}
int puts_raw(const char *s) {
int len = (int)strlen(s);
stdio_put_string(s, len, true, true);
stdio_flush();
return len;
}
@ -154,7 +171,7 @@ int _read(int handle, char *buffer, int length) {
int _write(int handle, char *buffer, int length) {
if (handle == 1) {
stdio_put_string(buffer, length, false);
stdio_put_string(buffer, length, false, false);
return length;
}
return -1;
@ -243,7 +260,7 @@ int __printflike(1, 0) WRAPPER_FUNC(printf)(const char* format, ...)
return ret;
}
void stdio_init_all() {
void stdio_init_all(void) {
// todo add explicit custom, or registered although you can call stdio_enable_driver explicitly anyway
// These are well known ones
#if LIB_PICO_STDIO_UART

View File

@ -54,6 +54,16 @@
#define PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE 1200
#endif
// PICO_CONFIG: PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, Maximum number of milliseconds to wait during initialization for a CDC connection from the host (negative means indefinite) before returning, default=0, group=pico_stdio_usb
#ifndef PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS
#define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 0
#endif
// PICO_CONFIG: PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS, Number of milliseconds to wait during initialization after a host CDC connection is detected before returning (some host terminals seem to sometimes lose transmissions sent right after connection), default=50, group=pico_stdio_usb
#ifndef PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS
#define PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS 50
#endif
// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=29, group=pico_stdio_usb
// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED, Whether the pin specified by PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED is fixed or can be modified by picotool over the VENDOR USB interface, type=bool, default=0, group=pico_stdio_usb
@ -94,10 +104,22 @@ extern "C" {
extern stdio_driver_t stdio_usb;
/*! \brief Explicitly initialize USB stdio and add it to the current set of stdin drivers
* \ingroup pico_stdio_uart
* \ingroup pico_stdio_usb
*
* \ref PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS can be set to cause this method to wait for a CDC connection
* from the host before returning, which is useful if you don't want any initial stdout output to be discarded
* before the connection is established.
*
* \return true if the USB CDC was initialized, false if an error occurred
*/
bool stdio_usb_init(void);
/*! \brief Check if there is an active stdio CDC connection to a host
* \ingroup pico_stdio_usb
*
* \return true if stdio is connected over CDC
*/
bool stdio_usb_connected(void);
#ifdef __cplusplus
}
#endif

View File

@ -103,9 +103,29 @@ bool stdio_usb_init(void) {
bool rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);
if (rc) {
stdio_set_driver_enabled(&stdio_usb, true);
#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS
#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS > 0
absolute_time_t until = make_timeout_time_ms(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS);
#else
absolute_time_t until = at_the_end_of_time;
#endif
do {
if (stdio_usb_connected()) {
#if PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS != 0
sleep_ms(PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS);
#endif
break;
}
sleep_ms(10);
} while (!time_reached(until));
#endif
}
return rc;
}
bool stdio_usb_connected(void) {
return tud_cdc_connected();
}
#else
#include "pico/stdio_usb.h"
#warning stdio USB was configured, but is being disabled as TinyUSB is explicitly linked