src/serial.c
author Tero Marttila <terom@paivola.fi>
Fri, 11 Apr 2014 19:30:41 +0300
changeset 69 ef9fe98ebf72
parent 53 dfe67409fbcd
permissions -rw-r--r--
serial: read buffering
/** Serial modes */
static enum {
    SERIAL_MODE_ASYNC   = 0b00,
} serial_mode = SERIAL_MODE_ASYNC;

static enum {
    SERIAL_BAUD_9600    = 103,
} serial_baud = SERIAL_BAUD_9600;

static enum {
    SERIAL_PARITY_N     = 0b00,
} serial_parity = SERIAL_PARITY_N;

static enum {
    SERIAL_STOPBITS_1   = 0b0,
} serial_stopbits = SERIAL_STOPBITS_1;

static enum {
    SERIAL_CHARS_8      = 0b011,
} serial_chars = SERIAL_CHARS_8;

/** Serial state */
#define SERIAL_BUF 64

struct serial {
    char buf[SERIAL_BUF];
    byte in, out;
} serial_rx;

char serial_tx = 0;

void serial_enable (char rx, char tx);

/*
 * Setup the UART for serial mode.
 */
void serial_init ()
{
    UCSR0A = (
            // Double the USART Transmission Speed
            (0b0 << U2X0)   // single speed
            
            // Multi-processor Communication Mode
        |   (0b0 << MPCM0)  // off
    );
    UCSR0B = (
            // Character Size
            ((serial_chars >> 2) << UCSZ02) // standard character sizes
    );
    UCSR0C = (
            // USART Mode Select
            (serial_mode << UMSEL00) // async

            // Parity Mode
        |   (serial_parity << UPM00)
            
            // Stop Bit Select
        |   (serial_stopbits << USBS0)

            // Character Size
        |   ((serial_chars & 0b11) << UCSZ00)
        
            // Clock Polarity
        |   (0b0 << UCPOL0)
    );
    UBRR0 = serial_baud;

    // active RX, idle TX
    serial_enable(1, 0);
}

void serial_enable (char rx, char tx)
{
    UCSR0B = (
            // RX Complete Interrupt Enable
            (!!rx << RXCIE0)

            // USART Data Register Empty Interrupt Enable
        |   (!!tx << UDRIE0)

            // Receiver Enable
        |   (0b1 << RXEN0)  // enabled

            // Transmitter Enable
        |   (0b1 << TXEN0)  // enabled

            // Character Size
        |   ((serial_chars >> 2) << UCSZ02) // standard character sizes
    );
}

/*
 * Read incoming into buffer.
 */
ISR(USART_RX_vect)
{
    serial_rx.buf[serial_rx.in++] = UDR0;

    if (serial_rx.in >= SERIAL_BUF)
        serial_rx.in = 0;
}

/*
 * Write outgoing buffer.
 */
ISR(USART_UDRE_vect)
{
    UDR0 = serial_tx;

    // tx buffer consumed
    serial_tx = 0;

    // idle tx
    serial_enable(1, 0);
}

char serial_read ()
{
    if (serial_rx.out == serial_rx.in)
        return 0;

    char rx = serial_rx.buf[serial_rx.out++];

    if (serial_rx.out >= SERIAL_BUF)
        serial_rx.out = 0;

    return rx;
}

void serial_write (char c)
{
    // XXX: atomic?
    if (tbi(&UCSR0A, UDRE0)) {
        // direct
        UDR0 = c;
    } else {
        // buffered
        serial_tx = c;
        serial_enable(1, 1);
    }
}