/* * (C) Copyright 2014 Leo C. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #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 128 #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(unsigned long baud) { 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(unsigned long baud) { stdout = &mystdout; usart0_setup(baud); } /*--------------------------------------------------------------------------*/ 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); }