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
Diff: rtos_serial.cpp
- 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