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
rtos_serial.cpp
- Committer:
- altasoul
- Date:
- 2013-10-21
- Revision:
- 7:dd892347b524
- Parent:
- 6:438a6c0acbd4
- Child:
- 8:3644d12758da
File content as of revision 7:dd892347b524:
/* * Copyright (c) 2013 Tom Soulanille * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "rtos_serial.h" RTOS_Serial::RTOS_Serial(PinName tx, PinName rx, const char *name) : Serial(tx, rx, name) { uart_number = get_index(); const PinName leds[] = {LED1,LED2,LED3,LED4}; ledp = new DigitalOut(leds[uart_number]); rtos_serial_pointers_by_index[uart_number] = this; tx_emitter_threadp = new Thread(tx_emitter, (void *) this); tx_tp[uart_number] = tx_emitter_threadp; rx_isr_pFP = attach(rx_isr[uart_number], RxIrq); tx_isr_pFP = attach(tx_isr[uart_number], TxIrq); tx_emitter_threadp->signal_set(0x01); // "prime the pump" of the tx-ready signals } RTOS_Serial::~RTOS_Serial() { remove_handler(rx_isr_pFP, RxIrq); remove_handler(tx_isr_pFP, TxIrq); tx_emitter_threadp->terminate(); tx_tp[uart_number] = NULL; delete tx_emitter_threadp; rtos_serial_pointers_by_index[uart_number] = NULL; } RTOS_Serial* RTOS_Serial::rtos_serial_pointers_by_index[4] = { NULL, NULL, NULL, NULL }; serial_t RTOS_Serial::get_serial() { return _serial; } int RTOS_Serial::get_index() { return _serial.index; } #ifdef RTOS_Serial_underscore_putc int RTOS_Serial::_putc(int c) { #else int RTOS_Serial::putc(int c) { #endif //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 { std::printf("\r\nRTOS_Serial::tx_q.put() returned %d\r\n", status); return EOF; } } 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) { #ifdef RTOS_Serial_underscore_putc return Serial::_putc(c); #else return Serial::putc(c); #endif } #ifdef RTOS_Serial_underscore_getc int RTOS_Serial::_getc() { #else int RTOS_Serial::getc() { #endif int rv; //return Serial::getc(); //FIXME: stand-in, which fails if we use our RX ISR osEvent evt = rx_q.get(); if (evt.status == osEventMessage) { rv = (int) evt.value.v; } else { //FIXME: find appropriate error reporting if any std::printf("\r\nRTOS_Serial::getc() evt.status %d\n", evt.status); rv = EOF; } return rv; } // ISR's for transmitter interrupts // class method void RTOS_Serial::UART0_TX_ISR(){ //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; //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; //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; //DEBUG: maybe this? //tx_emitter_threadp->signal_set(0x1); tx_tp[3]->signal_set(0x01); } func RTOS_Serial::tx_isr[4] = { &RTOS_Serial::UART0_TX_ISR, &RTOS_Serial::UART1_TX_ISR, &RTOS_Serial::UART2_TX_ISR, &RTOS_Serial::UART3_TX_ISR, }; /// ISR's for receiver interrupts // class method void RTOS_Serial::UART0_RX_ISR(){ RTOS_Serial::rtos_serial_pointers_by_index[0]->rx_q.put((int *)LPC_UART0->RBR); } // class method void RTOS_Serial::UART1_RX_ISR(){ RTOS_Serial::rtos_serial_pointers_by_index[1]->rx_q.put((int *)LPC_UART1->RBR); } // class method void RTOS_Serial::UART2_RX_ISR(){ RTOS_Serial::rtos_serial_pointers_by_index[2]->rx_q.put((int *)LPC_UART2->RBR); } // class method void RTOS_Serial::UART3_RX_ISR(){ RTOS_Serial::rtos_serial_pointers_by_index[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){ evt = sp->tx_q.get(); if (evt.status == osEventMessage) { // 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("\r\nRTOS_Serial::tx_emitter() evt.status %d\n", evt.status); } *(sp->ledp) = 0; } }