hello: interruptable timer sleep, with buffered serial IO
authorTero Marttila <terom@paivola.fi>
Thu, 03 Apr 2014 20:25:12 +0300
changeset 53 dfe67409fbcd
parent 52 237d1f5c1c32
child 54 ec42f36d8614
hello: interruptable timer sleep, with buffered serial IO
src/hello.c
src/serial.c
src/timer.c
--- a/src/hello.c	Thu Apr 03 19:44:53 2014 +0300
+++ b/src/hello.c	Thu Apr 03 20:25:12 2014 +0300
@@ -11,24 +11,29 @@
 
     // LED
     sbi(&DDRB, DDB5);
-    serial_enable();
 
     sei();
 
     // blink
     char c = 'X';
-    short timeout = 1000;
+    short timeout = 8000;
     short delta = 10;
 
     while (true) {
-        serial_write(c);
-
-        // bitflip
+        // toggle
         xbi(&PORTB, PORTB5);
         
-        //timer_sleep(timeout);
-        //timeout += delta;
+        // sleep
+        if (timer_sleep(timeout)) {
+            c = '.';
+
+        } else if ((c = serial_read())) {
+            // got serial data
+        } else {
+            c = '?';
+        }
         
-        c = serial_read();
+        // output...
+        serial_write(c);
     }
 }
--- 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);
+    }
 }
--- a/src/timer.c	Thu Apr 03 19:44:53 2014 +0300
+++ b/src/timer.c	Thu Apr 03 20:25:12 2014 +0300
@@ -64,8 +64,10 @@
 
 /*
  * Sleep on timer1 interrupt.
+ *
+ * Returns 0 on interrupt, 1 on timeout.
  */
-void timer_sleep (int cycles)
+char timer_sleep (int cycles)
 {
     // set timer
     timer1_start(cycles);
@@ -81,12 +83,20 @@
     );
     
     // sleep
-    while (tbi(&TIMER_FLAGS, TIMER1_BUSY)) {
-        __asm__ ( "sleep" :: );
-    }
+    __asm__ ( "sleep" :: );
 
     // cleanup
     SMCR = 0;
+    
+    if (tbi(&TIMER_FLAGS, TIMER1_BUSY)) {
+        timer1_stop();
+
+        // interrupt
+        return 0;
+    } else {
+        // timeout
+        return 1;
+    }
 }