+#include <avr/interrupt.h>
#include <avr/io.h>
+#include "delay.h"
+#include "ringbuffer.h"
#include "serial.h"
#define BIT(n) (1<<(n))
+#ifndef SERIAL_BUFSIZE
+#define SERIAL_BUFSIZE 16
+#endif
+
+#ifdef SERIAL_ASYNC
+static RINGBUFFER(rx, SERIAL_BUFSIZE);
+static RINGBUFFER(tx, SERIAL_BUFSIZE);
+static uint8_t rx_overrun;
+#define INTERRUPTS BIT(RXCIE0)
+#else
+#define INTERRUPTS 0
+#endif
+
+static inline void set_baud(uint16_t baud)
+{
+ baud = (F_CPU/16+baud/2)/baud-1;
+ UBRR0H = baud>>8;
+ UBRR0L = baud;
+}
+
void serial_init(uint16_t baud)
{
DDRD = (DDRD&~0x03) | 0x02;
PORTD &= ~0x03;
- baud = (F_CPU/16+baud/2)/baud-1;
- UBRR0H = baud>>8;
- UBRR0L = baud;
+ set_baud(baud);
+ UCSR0A = 0;
UCSR0C = BIT(UCSZ00) | BIT(UCSZ01); // 8N1
- UCSR0B = BIT(RXEN0) | BIT(TXEN0) | BIT(RXCIE0);
+ UCSR0B = BIT(RXEN0) | BIT(TXEN0) | INTERRUPTS;
+}
+
+void serial_set_baud(uint16_t baud)
+{
+#ifdef SERIAL_ASYNC
+ while(!(UCSR0A&BIT(UDRE0))) ;
+#endif
+ /* It's impossible to reliably find out when transmission has finished.
+ Wait for one character duration to be safe. */
+ uint16_t delay = (UBRR0H<<8)|UBRR0L;
+ for(uint8_t i=0; i<40; ++i)
+ delay_loop16(delay);
+ set_baud(baud);
+}
+
+uint8_t serial_read()
+{
+#ifdef SERIAL_ASYNC
+ return ringbuffer_blocking_pop(rx);
+#else
+ while(!(UCSR0A&BIT(RXC0))) ;
+ return UDR0;
+#endif
+}
+
+uint8_t serial_read_available()
+{
+#ifdef SERIAL_ASYNC
+ return ringbuffer_fill(rx);
+#else
+ return (UCSR0A&BIT(RXC0))!=0;
+#endif
+}
+
+uint8_t serial_read_overrun()
+{
+#ifdef SERIAL_ASYNC
+ uint8_t o = rx_overrun;
+ rx_overrun = 0;
+ return o;
+#else
+ return (UCSR0A&BIT(DOR0))!=0;
+#endif
}
void serial_write(uint8_t c)
{
- while(!(UCSR0A&(1<<UDRE0))) ;
+#ifdef SERIAL_ASYNC
+ ringbuffer_blocking_push(tx, c);
+ UCSR0B |= BIT(UDRIE0);
+#else
+ while(!(UCSR0A&BIT(UDRE0))) ;
UDR0 = c;
+#endif
+}
+
+uint8_t serial_write_space()
+{
+#ifdef SERIAL_ASYNC
+ return ringbuffer_space(tx);
+#else
+ return (UCSR0A&BIT(UDRE0))!=0;
+#endif
+}
+
+#ifdef SERIAL_ASYNC
+ISR(USART_UDRE_vect)
+{
+ if(ringbuffer_fill(tx))
+ UDR0 = ringbuffer_pop(tx);
+ else
+ UCSR0B &= ~BIT(UDRIE0);
+}
+
+ISR(USART_RX_vect)
+{
+ uint8_t c = UDR0;
+ if(!ringbuffer_space(rx))
+ rx_overrun = 1;
+ else
+ ringbuffer_push(rx, c);
}
+#endif