osmo-opencm3-projects/libcommon/src/iob_stm32_nonblocking.c

112 lines
2.7 KiB
C

#include <stdio.h>
#include <sys/cdefs.h>
#include <libopencm3/cm3/assert.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/usart.h>
#include <libcommon/iob.h>
#include <librfn/ringbuf.h>
/* STM32F0 has slightly different status register #defines */
#ifdef USART_ISR_RXNE
#define USART_SR_RXNE USART_ISR_RXNE
#define USART_SR_TXE USART_ISR_TXE
#define USART_SR(x) USART_ISR(x)
#endif
/* picolibc iob implementation for non-blocking I/O on USART via ring-buffer */
static uint32_t stdio_usart;
static uint8_t outbuf[1024];
static ringbuf_t outring = RINGBUF_VAR_INIT(outbuf, sizeof(outbuf));
static uint8_t inbuf[256];
static ringbuf_t inring = RINGBUF_VAR_INIT(inbuf, sizeof(inbuf));
void iob_init(uint32_t usart)
{
cm3_assert(usart == USART2);
stdio_usart = usart;
nvic_enable_irq(NVIC_USART2_IRQ);
/* Enable RX non-empty interrupt */
USART_CR1(stdio_usart) |= USART_CR1_RXNEIE;
}
/* FIXME: this obviously doesn't work with iob_init() getting passed a dynamic UART */
void usart2_isr(void)
{
if ((USART_CR1(stdio_usart) & USART_CR1_RXNEIE) &&
(USART_SR(stdio_usart) & USART_SR_RXNE)) {
uint16_t c = usart_recv(stdio_usart);
ringbuf_put(&inring, c);
}
if ((USART_CR1(stdio_usart) & USART_CR1_TXEIE) &&
(USART_SR(stdio_usart) & USART_SR_TXE)) {
int data = ringbuf_get(&outring);
if (data == -1) {
USART_CR1(stdio_usart) &= ~USART_CR1_TXEIE;
} else {
usart_send(stdio_usart, data);
}
}
}
/* buffered output to stdout */
static int my_stdout_putc(char c, FILE *file)
{
(void) file;
cm3_assert(stdio_usart);
ringbuf_put(&outring, c);
USART_CR1(stdio_usart) |= USART_CR1_TXEIE;
return c;
}
/* unbuffered, blocking output to stderr (e.g. for assert, fault handlers, ...) */
static int my_stderr_putc(char c, FILE *file)
{
(void) file;
cm3_assert(stdio_usart);
usart_send_blocking(stdio_usart, c);
return c;
}
static int my_stdin_getc(FILE *file)
{
(void) file;
int data = ringbuf_get(&inring);
if (data == -1)
return EOF;
else
return data;
}
static int my_stdout_flush(FILE *file)
{
(void) file;
do { } while (!ringbuf_empty(&outring));
return 0;
}
static FILE __stdin = FDEV_SETUP_STREAM(NULL, my_stdin_getc, NULL, _FDEV_SETUP_READ);
static FILE __stdout = FDEV_SETUP_STREAM(my_stdout_putc, NULL, my_stdout_flush, _FDEV_SETUP_WRITE);
static FILE __stderr = FDEV_SETUP_STREAM(my_stderr_putc, NULL, NULL, _FDEV_SETUP_WRITE);
#ifndef PICOLIBC_STDIO_GLOBALS
FILE *const __iob[3] = { &__stdin, &__stdout, &__stderr };
#else
FILE *const stdin = &__stdin;
FILE *const stdout = &__stdout;
FILE *const stderr = &__stderr;
#endif
#include <libcommon/microvty.h>
bool microvty_cb_uart_rx_not_empty(void)
{
return !ringbuf_empty(&inring);
}