Add stdio_set_chars_available_callback (#1213)
There's currently no way to be notified in a "stdio" agnostic way whether there's an incoming character available. You can poll with getchar_timeout_us, but that's far from ideal. Add a method that takes a callback to notify that a character might be available.
This commit is contained in:
parent
0e4e25a343
commit
31780aed2c
@ -109,6 +109,14 @@ int putchar_raw(int c);
|
|||||||
*/
|
*/
|
||||||
int puts_raw(const char *s);
|
int puts_raw(const char *s);
|
||||||
|
|
||||||
|
/*! \brief get notified when there are input characters available
|
||||||
|
* \ingroup pico_stdio
|
||||||
|
*
|
||||||
|
* \param fn Callback function to be called when characters are available. Pass NULL to cancel any existing callback
|
||||||
|
* \param param Pointer to pass to the callback
|
||||||
|
*/
|
||||||
|
void stdio_set_chars_available_callback(void (*fn)(void*), void *param);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,6 +13,7 @@ struct stdio_driver {
|
|||||||
void (*out_chars)(const char *buf, int len);
|
void (*out_chars)(const char *buf, int len);
|
||||||
void (*out_flush)(void);
|
void (*out_flush)(void);
|
||||||
int (*in_chars)(char *buf, int len);
|
int (*in_chars)(char *buf, int len);
|
||||||
|
void (*set_chars_available_callback)(void (*fn)(void*), void *param);
|
||||||
stdio_driver_t *next;
|
stdio_driver_t *next;
|
||||||
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
||||||
bool last_ended_with_cr;
|
bool last_ended_with_cr;
|
||||||
|
@ -345,4 +345,10 @@ void stdio_set_translate_crlf(stdio_driver_t *driver, bool enabled) {
|
|||||||
|
|
||||||
panic_unsupported();
|
panic_unsupported();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void stdio_set_chars_available_callback(void (*fn)(void*), void *param) {
|
||||||
|
for (stdio_driver_t *s = drivers; s; s = s->next) {
|
||||||
|
if (s->set_chars_available_callback) s->set_chars_available_callback(fn, param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,6 +23,11 @@
|
|||||||
#define PICO_STDIO_UART_DEFAULT_CRLF PICO_STDIO_DEFAULT_CRLF
|
#define PICO_STDIO_UART_DEFAULT_CRLF PICO_STDIO_DEFAULT_CRLF
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// PICO_CONFIG: PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK, Enable UART STDIO support for stdio_set_chars_available_callback. Can be disabled to make use of the uart elsewhere, type=bool default=1, group=pico_stdio_uart
|
||||||
|
#ifndef PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
#define PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -11,6 +11,11 @@
|
|||||||
|
|
||||||
static uart_inst_t *uart_instance;
|
static uart_inst_t *uart_instance;
|
||||||
|
|
||||||
|
#if PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
static void (*chars_available_callback)(void*);
|
||||||
|
static void *chars_available_param;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PICO_NO_BI_STDIO_UART
|
#if PICO_NO_BI_STDIO_UART
|
||||||
#define stdio_bi_decl_if_func_used(x)
|
#define stdio_bi_decl_if_func_used(x)
|
||||||
#else
|
#else
|
||||||
@ -87,12 +92,47 @@ int stdio_uart_in_chars(char *buf, int length) {
|
|||||||
while (i<length && uart_is_readable(uart_instance)) {
|
while (i<length && uart_is_readable(uart_instance)) {
|
||||||
buf[i++] = uart_getc(uart_instance);
|
buf[i++] = uart_getc(uart_instance);
|
||||||
}
|
}
|
||||||
|
#if PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
if (chars_available_callback) {
|
||||||
|
// Re-enable interrupts after reading a character
|
||||||
|
uart_set_irq_enables(uart_instance, true, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return i ? i : PICO_ERROR_NO_DATA;
|
return i ? i : PICO_ERROR_NO_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
static void on_uart_rx(void) {
|
||||||
|
if (chars_available_callback) {
|
||||||
|
// Interrupts will go off until the uart is read, so disable them
|
||||||
|
uart_set_irq_enables(uart_instance, false, false);
|
||||||
|
chars_available_callback(chars_available_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stdio_uart_set_chars_available_callback(void (*fn)(void*), void *param) {
|
||||||
|
static_assert(UART1_IRQ == UART0_IRQ + 1, "");
|
||||||
|
const uint UART_IRQ = UART0_IRQ + uart_get_index(uart_instance);
|
||||||
|
if (fn && !chars_available_callback) {
|
||||||
|
irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
|
||||||
|
irq_set_enabled(UART_IRQ, true);
|
||||||
|
uart_set_irq_enables(uart_instance, true, false);
|
||||||
|
} else if (!fn && chars_available_callback) {
|
||||||
|
uart_set_irq_enables(uart_instance, false, false);
|
||||||
|
irq_set_enabled(UART_IRQ, false);
|
||||||
|
irq_remove_handler(UART_IRQ, on_uart_rx);
|
||||||
|
}
|
||||||
|
chars_available_callback = fn;
|
||||||
|
chars_available_param = param;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
stdio_driver_t stdio_uart = {
|
stdio_driver_t stdio_uart = {
|
||||||
.out_chars = stdio_uart_out_chars,
|
.out_chars = stdio_uart_out_chars,
|
||||||
.in_chars = stdio_uart_in_chars,
|
.in_chars = stdio_uart_in_chars,
|
||||||
|
#if PICO_STDIO_UART_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
.set_chars_available_callback = stdio_uart_set_chars_available_callback,
|
||||||
|
#endif
|
||||||
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
||||||
.crlf_enabled = PICO_STDIO_UART_DEFAULT_CRLF
|
.crlf_enabled = PICO_STDIO_UART_DEFAULT_CRLF
|
||||||
#endif
|
#endif
|
||||||
|
@ -107,6 +107,11 @@
|
|||||||
#define PICO_STDIO_USB_DEVICE_SELF_POWERED 0
|
#define PICO_STDIO_USB_DEVICE_SELF_POWERED 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// PICO_CONFIG: PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK, Enable USB STDIO support for stdio_set_chars_available_callback. Can be disabled to make use of USB CDC RX callback elsewhere, type=bool default=1, group=pico_stdio_usb
|
||||||
|
#ifndef PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
#define PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "pico/stdio/driver.h"
|
#include "pico/stdio/driver.h"
|
||||||
#include "pico/mutex.h"
|
#include "pico/mutex.h"
|
||||||
#include "hardware/irq.h"
|
#include "hardware/irq.h"
|
||||||
|
#include "device/usbd_pvt.h" // for usbd_defer_func
|
||||||
|
|
||||||
static mutex_t stdio_usb_mutex;
|
static mutex_t stdio_usb_mutex;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@ -36,6 +37,10 @@ static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ >= NUM_IRQS - NUM_USER_IRQS, "");
|
|||||||
#else
|
#else
|
||||||
static uint8_t low_priority_irq_num;
|
static uint8_t low_priority_irq_num;
|
||||||
#endif
|
#endif
|
||||||
|
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
static void (*chars_available_callback)(void*);
|
||||||
|
static void *chars_available_param;
|
||||||
|
#endif
|
||||||
|
|
||||||
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
|
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
|
||||||
int64_t repeat_time;
|
int64_t repeat_time;
|
||||||
@ -137,12 +142,29 @@ int stdio_usb_in_chars(char *buf, int length) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
void tud_cdc_rx_cb(__unused uint8_t itf) {
|
||||||
|
if (chars_available_callback) {
|
||||||
|
usbd_defer_func(chars_available_callback, chars_available_param, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdio_usb_set_chars_available_callback(void (*fn)(void*), void *param) {
|
||||||
|
chars_available_callback = fn;
|
||||||
|
chars_available_param = param;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
stdio_driver_t stdio_usb = {
|
stdio_driver_t stdio_usb = {
|
||||||
.out_chars = stdio_usb_out_chars,
|
.out_chars = stdio_usb_out_chars,
|
||||||
.in_chars = stdio_usb_in_chars,
|
.in_chars = stdio_usb_in_chars,
|
||||||
|
#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK
|
||||||
|
.set_chars_available_callback = stdio_usb_set_chars_available_callback,
|
||||||
|
#endif
|
||||||
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
||||||
.crlf_enabled = PICO_STDIO_USB_DEFAULT_CRLF
|
.crlf_enabled = PICO_STDIO_USB_DEFAULT_CRLF
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool stdio_usb_init(void) {
|
bool stdio_usb_init(void) {
|
||||||
|
Loading…
Reference in New Issue
Block a user