// Serial driver for AVR USART
// Uses 24 bytes for buffers etc
// This work is provided as-is without any warranty whatsoever
// Use it at your own risk
// Modify and redistribute at will, as long as this disclaimer remains
// (C) Brian Starkey, 2011

#define F_CPU 8000000

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "usart.h"

const char comma PROGMEM = ',';
const char newline[] PROGMEM = "\r\n";

volatile uint8_t tx_buffer[4];
volatile uint8_t rx_buffer[16];
volatile uint8_t tx_bytes = 0;
volatile uint8_t rx_bytes = 0;
volatile uint8_t tx_buffer_pos = 0;
volatile uint8_t rx_buffer_pos = 0;

void init_usart() {
	/* Set baud rate (19200) */
	UBRRL = 25;
	/* Enable receiver and transmitter */
	UCSRB = (1<<RXEN)|(1<<TXEN);
	/* Set frame format: 8data, 1stop bit, no parity */
	UCSRC = (3 << UCSZ0);
	/* Enable receive complete interrupt*/
	UCSRB |= (1 << RXCIE);
}

void usart_send(char * data, uint8_t len) {
	uint8_t i;
	for (i = 0; i < len; i++) {
		while (tx_bytes >= (tx_buffer_pos + 0x03));
		uint8_t index = tx_bytes & 0x03;
		tx_buffer[index] = data[i];
		tx_bytes++;
		UCSRB |= (1 << UDRIE);
	}	
}

void usart_send_pmem(PGM_P data, uint8_t len) {
	char in_ram;
	uint8_t i;
	for (i = 0; i < len; i++) {
		in_ram = pgm_read_byte(&data[i]);
		usart_send(&in_ram, 1);
	};
}

uint8_t usart_recv(char * data, uint8_t len, uint16_t o_timeout) {
	uint8_t i;
	uint16_t timeout = o_timeout;
	for (i = 0; i < len; i++) {
		while (rx_bytes >= rx_buffer_pos) {
			_delay_us(250);
			if ((timeout-- == 0) && (o_timeout > 0)) {
				return i;
			}
		}
		timeout = o_timeout;
		if (rx_bytes < rx_buffer_pos) {
			data[i] = rx_buffer[rx_bytes & 0x0F];
			rx_bytes++;
		}
	}
	return i;
}

ISR(USART_UDRE_vect) {
	if (tx_buffer_pos < tx_bytes) {
		UDR = tx_buffer[(tx_buffer_pos++ & 0x03)];
	}
	else {
		UCSRB &= ~(1 << UDRIE);
		tx_bytes = 0;
		tx_buffer_pos = 0;
	}
}

ISR(USART_RX_vect) {
	rx_buffer[rx_buffer_pos++ & 0x0F] = UDR;
}
