src/serial.c
changeset 53 dfe67409fbcd
parent 52 237d1f5c1c32
child 69 ef9fe98ebf72
--- a/src/serial.c	Thu Apr 03 19:44:53 2014 +0300
+++ b/src/serial.c	Thu Apr 03 20:25:12 2014 +0300
@@ -1,27 +1,30 @@
-
+/** 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 */
+char serial_rx = 0;
+char serial_tx = 0;
+
+void serial_enable (char rx, char tx);
+
 /*
  * Setup the UART for serial mode.
  */
@@ -55,13 +58,22 @@
         |   (0b0 << UCPOL0)
     );
     UBRR0 = serial_baud;
+
+    // active RX, idle TX
+    serial_enable(1, 0);
 }
 
-void serial_enable ()
+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
+        |   (0b1 << RXEN0)  // enabled
 
             // Transmitter Enable
         |   (0b1 << TXEN0)  // enabled
@@ -71,18 +83,47 @@
     );
 }
 
+/*
+ * Read incoming into buffer.
+ */
+ISR(USART_RX_vect)
+{
+    serial_rx = UDR0;
+}
+
+/*
+ * 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 ()
 {
-    while (!tbi(&UCSR0A, RXC0))
-        ;
+    char rx = serial_rx;
 
-    return UDR0;
+    // rx buffer consumed
+    serial_rx = 0;
+
+    return rx;
 }
 
 void serial_write (char c)
 {
-    while (!tbi(&UCSR0A, UDRE0))
-        ;
-
-    UDR0 = c;
+    // XXX: atomic?
+    if (tbi(&UCSR0A, UDRE0)) {
+        // direct
+        UDR0 = c;
+    } else {
+        // buffered
+        serial_tx = c;
+        serial_enable(1, 1);
+    }
 }