serial
authorTero Marttila <terom@paivola.fi>
Wed, 24 Sep 2014 22:54:13 +0300
changeset 5 652c31c10f91
parent 4 959d4b9c3f0a
child 6 c5ab059eadc1
serial
include/serial.h
src/serial.c
--- /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);
+    }
+}