src/serial.c
author Tero Marttila <terom@paivola.fi>
Wed, 08 Oct 2014 23:20:34 +0300
changeset 11 a383e22204f2
parent 5 652c31c10f91
permissions -rw-r--r--
adxl345: control/data registers read/write
#include "serial.h"

#include "stdlib.h"

#include <avr/io.h>
#include <avr/interrupt.h>

/** Serial state */
static enum serial_mode serial_mode = SERIAL_MODE_ASYNC;
static enum serial_chars serial_chars = SERIAL_CHARS_8;

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

char serial_tx = 0;

static void serial_enable (char rx, char tx);

/*
 * Setup the UART for serial mode.
 */
void serial_init (
    enum serial_baud baud,
    enum serial_parity parity,
    enum serial_stopbits stopbits
)
{
    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
        |   (parity << UPM00)

            // Stop Bit Select
        |   (stopbits << USBS0)

            // Character Size
        |   ((serial_chars & 0b11) << UCSZ00)

            // Clock Polarity
        |   (0b0 << UCPOL0)
    );
    UBRR0 = baud;

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

static 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);
    }
}