An RTOS-friendly Serial interface Its primary benefit is that it never hogs the CPU. An amusing alternative to the traditional ring-bufferd interrupt-serviced systems, it uses short mbed-rtos queues to buffer characters to and from the UART, and a thread to service the transmitter. Short interrupt service routines enqueue received characters and signal the transmit thread when the transmitter is available. WARNING: Do not create RTOS-Serial objects before the RTOS is running! Put them inside your main() block or another function, not in the global initialization.

Dependents:   Test_RDM880_rfid_reader

Revision:
2:891773cc33fd
Parent:
1:5a66fddad7c4
Child:
3:5865277b7710
--- a/rtos_serial.cpp	Fri Oct 18 03:39:24 2013 +0000
+++ b/rtos_serial.cpp	Fri Oct 18 06:04:55 2013 +0000
@@ -22,24 +22,26 @@
 
 #include "rtos_serial.h"
 
-//RTOS_Serial::tx_mail_t blah;
-
 RTOS_Serial::RTOS_Serial(PinName tx, PinName rx, const char *name)
  : Serial(tx, rx, name)
 {
     const PinName leds[] = {LED1,LED2,LED3,LED4};
     ledp = new DigitalOut(leds[get_index()]);
+    rsp[get_index()] = this;
     tx_emitter_threadp = new Thread(tx_emitter, (void *) this);
     tx_tp[get_index()] = tx_emitter_threadp;
-    attach(tx_isr[get_index()], Serial::TxIrq);
+    attach(tx_isr[get_index()], /*Serial::*/TxIrq);
+    tx_emitter_threadp->signal_set(0x01);   // "prime the pump" of the tx-ready signals
 }
 
+RTOS_Serial* RTOS_Serial::rsp[4] = { NULL, NULL, NULL, NULL };
+
 serial_t RTOS_Serial::get_serial() { return _serial; }
 
 int  RTOS_Serial::get_index() { return _serial.index; }
 
 int RTOS_Serial::putc(int c) {
-    //return Serial::putc(c);
+    //return Serial::putc(c); //DEBUG
     //if (tx_q.put((int *)c, osWaitForever) == osOK) return c; else return EOF;
     int status;
     if ( (status = tx_q.put((int *)c, osWaitForever)) == osOK) return c; else {
@@ -48,31 +50,50 @@
     }
 }   
 
+int RTOS_Serial::puts(const char *s) {
+    int rv = 0;
+    while (*s) {
+        if (putc(*s++) == EOF) {
+            rv = EOF;
+            break;
+        } else {
+            rv++;
+        }
+    }
+    return rv;
+}
+
 int RTOS_Serial::parent_putc(int c) {
     return Serial::putc(c);
 }
 
+int RTOS_Serial::getc() {
+    return Serial::getc();  //FIXME: stand-in
+}
+
+
+// ISR's for transmitter interrupts
 // class method
 void RTOS_Serial::UART0_TX_ISR(){
-    uint32_t IRR = LPC_UART0->IIR;
+    //uint32_t IRR = LPC_UART0->IIR;
     //tx_emitter_threadp->signal_set(0x1);
     tx_tp[0]->signal_set(0x01);
 }
 // class method
 void RTOS_Serial::UART1_TX_ISR(){
-    uint32_t IRR = LPC_UART1->IIR;
+    //uint32_t IRR = LPC_UART1->IIR;
     //tx_emitter_threadp->signal_set(0x1);
     tx_tp[1]->signal_set(0x01);
 }
 // class method
 void RTOS_Serial::UART2_TX_ISR(){
-    uint32_t IRR = LPC_UART2->IIR;
+    //uint32_t IRR = LPC_UART2->IIR;
     //tx_emitter_threadp->signal_set(0x1);
     tx_tp[2]->signal_set(0x01);
 }
 // class method
 void RTOS_Serial::UART3_TX_ISR(){
-    uint32_t IRR = LPC_UART3->IIR;
+    //uint32_t IRR = LPC_UART3->IIR;
     //tx_emitter_threadp->signal_set(0x1);
     tx_tp[3]->signal_set(0x01);
 }
@@ -84,30 +105,56 @@
     &RTOS_Serial::UART3_TX_ISR, 
 };
 
+/// ISR's for receiver interrupts
+// class method
+void RTOS_Serial::UART0_RX_ISR(){
+    RTOS_Serial::rsp[0]->rx_q.put((int *)LPC_UART0->RBR);
+}
+// class method
+void RTOS_Serial::UART1_RX_ISR(){
+    RTOS_Serial::rsp[1]->rx_q.put((int *)LPC_UART1->RBR);
+}
+// class method
+void RTOS_Serial::UART2_RX_ISR(){
+    RTOS_Serial::rsp[2]->rx_q.put((int *)LPC_UART2->RBR);
+}
+// class method
+void RTOS_Serial::UART3_RX_ISR(){
+    RTOS_Serial::rsp[3]->rx_q.put((int *)LPC_UART3->RBR);
+}
+
+func RTOS_Serial::rx_isr[4] = {
+    &RTOS_Serial::UART0_RX_ISR, 
+    &RTOS_Serial::UART1_RX_ISR, 
+    &RTOS_Serial::UART2_RX_ISR, 
+    &RTOS_Serial::UART3_RX_ISR, 
+};
+
 Thread* RTOS_Serial::tx_tp[4] = { NULL, NULL, NULL, NULL };
 
 // tx_emitter is a class method
 void RTOS_Serial::tx_emitter(void const *argument){
     RTOS_Serial *sp = (RTOS_Serial *) argument;
     osEvent evt;
+    //osStatus status;
     while(true){
-#if 0
-        *(sp->ledp) = 0;
-        osDelay(200);
-        *(sp->ledp) = 1;
-        osDelay(200);
-#endif        
-#if 1
         evt = sp->tx_q.get();
         if (evt.status == osEventMessage) {
-            Thread::signal_wait(0x1);
+            // There is no TX interrupt until the first byte is sent out the port,
+            // so we use a timeout on the signal from the interrupt service routine
+            // and just proceed to transmit the character. This should happen only
+            // once at most, to "prime the pump", but the timeout provides some
+            // safety in case something goes wrong.
+            // A first signal is sent by the RTOS_Serial constructor when the thread
+            // is created, so normally this will not come into effect.
+            // DEBUG: timeout omitted to search for instabilities
+            Thread::signal_wait(0x1/*, 10*/);   //FIXME: base the timeout on the baud rate
             *(sp->ledp) = 1;
             sp->parent_putc(evt.value.v);
             *(sp->ledp) = 0;
         } else {
             std::printf("tx_emitter() evt.status %d\n", evt.status);
         }
-#endif
         *(sp->ledp) = 0;
     }
 }
\ No newline at end of file