--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/serial.h Wed Sep 24 22:54:13 2014 +0300
@@ -0,0 +1,54 @@
+#ifndef QMSK_SERIAL_H
+#define QMSK_SERIAL_H
+
+/** Serial modes */
+enum serial_mode {
+ SERIAL_MODE_ASYNC = 0b00,
+
+ SERIAL_MODE_DEFAULT = SERIAL_MODE_ASYNC
+};
+
+enum serial_baud {
+ SERIAL_BAUD_9600 = 103,
+};
+
+enum serial_parity {
+ SERIAL_PARITY_N = 0b00,
+};
+
+enum serial_stopbits {
+ SERIAL_STOPBITS_1 = 0b0,
+};
+
+enum serial_chars {
+ SERIAL_CHARS_8 = 0b011,
+};
+
+/* Size of RX buffer */
+#define SERIAL_BUF 64
+
+
+/*
+ * Setup the UART for serial mode.
+ */
+void serial_init (
+ enum serial_baud baud,
+ enum serial_parity parity,
+ enum serial_stopbits stopbits
+);
+
+/*
+ * Read a single input char from the rx buffer.
+ *
+ * Returns 0 if the rx buf is empty.
+ */
+char serial_read ();
+
+/*
+ * Write a single output char, either directly or via the tx "buffer".
+ *
+ * XXX: currently only has a tx buffer of 1 byte.
+ */
+void serial_write (char c);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/serial.c Wed Sep 24 22:54:13 2014 +0300
@@ -0,0 +1,132 @@
+#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);
+ }
+}