diff options
Diffstat (limited to 'avr/serial.c')
-rw-r--r-- | avr/serial.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/avr/serial.c b/avr/serial.c new file mode 100644 index 0000000..b2fea51 --- /dev/null +++ b/avr/serial.c @@ -0,0 +1,123 @@ +/* + */ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/atomic.h> +#include <errno.h> +#include <stdio.h> + +#include "ring.h" +#include "serial.h" + + +static int _write(char c, FILE *stream); +static FILE mystdout = FDEV_SETUP_STREAM(_write, + NULL, _FDEV_SETUP_WRITE); + + + +#define BUFFER_SIZE 64 + +#if ((BUFFER_SIZE-1) & BUFFER_SIZE) +# error: BUFFER_SIZE not power of 2 +#endif + +#if ((BUFFER_SIZE) > 256) +# error: BUFFER_SIZE +#endif + +struct ring rx_ring; +struct ring tx_ring; +uint8_t rx_ring_buffer[BUFFER_SIZE]; +uint8_t tx_ring_buffer[BUFFER_SIZE]; + + + +/* Initialize UART */ + +void usart0_setup(void) { + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + + PRR0 &= ~_BV(PRUSART0); + UCSR0B = 0; + + /* Initialize ring buffers. */ + ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE); + ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE); + + UCSR0A = 0; + UBRR0 = F_CPU / BAUD / 16 - 1; + UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); + UCSR0C = 3 << UCSZ00; + }; +} + +/*--------------------------------------------------------------------------*/ + +/* UART RXC interrupt */ + +ISR(USART0_RX_vect) +{ + uint8_t d; + + d = UDR0; + ring_write_ch(&rx_ring, d); +} + +/* UART UDRE interrupt */ + +ISR(USART0_UDRE_vect) +{ + int d = ring_read_ch(&tx_ring); + + if (d < 0) { + /* Disable TX empty interrupt. */ + UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); + } else { + UDR0 = d; + } +} + +/*--------------------------------------------------------------------------*/ + +void serial_setup(void) +{ + stdout = &mystdout; + usart0_setup(); +} + +/*--------------------------------------------------------------------------*/ + +int _write(char c, FILE *stream) +{ + (void) stream; + + if (c == '\n') + serial_putc('\r'); + serial_putc(c); + + return 0; +} + +int serial_getc(void) +{ + return ring_read_ch(&rx_ring); +} + +void serial_putc(char data) +{ + while (ring_write_ch(&tx_ring, data) < 0) + ; + + /* Enable the TXE interrupt. */ + UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0); +} + +uint_fast8_t serial_tstc(void) +{ + return !ring_is_empty(&rx_ring); +} + + |