UART1 buffered serial driver, requires RTOS

Dependents:   Serial_interrupts_buffered HARP2 HARP3

Buffered serial UART1 - Setup to work with UART1 (p13,p14)

Uses RTOS to block current thread.

Reference: http://mbed.org/users/tylerjw/notebook/buffered-serial-with-rtos/

buffered_serial.cpp

Committer:
tylerjw
Date:
2012-12-17
Revision:
4:d3122119f92b
Parent:
3:a4a21e18acd1

File content as of revision 4:d3122119f92b:

/*
 * @file buffered_serial.cpp
 * @author Tyler Weaver
 *
 * @section LICENSE
 *
 * 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.
 *
 * @section DESCRIPTION
 *
 * Buffered serial UART1 - Setup to work with UART1 (p13,p14)
 *
 * Uses RTOS to block current thread.
 */
#include "buffered_serial.h"

BufferedSerial::BufferedSerial() : Serial(p13,p14), /*led1(LED1), led2(LED2),*/ rx_sem_(0), tx_sem_(0)
{
    tx_in_=0;
    tx_out_=0;
    rx_in_=0;
    rx_out_=0;

    device_irqn = UART1_IRQn;

    // attach the interrupts
    Serial::attach(this, &BufferedSerial::Rx_interrupt, Serial::RxIrq);
    Serial::attach(this, &BufferedSerial::Tx_interrupt, Serial::TxIrq);
}

// Copy tx line buffer to large tx buffer for tx interrupt routine
void BufferedSerial::put_line(char *c)
{
    int i;
    char temp_char;
    bool empty;
    i = 0;
    strncpy(tx_line_,c,LINE_SIZE);
    // Start Critical Section - don't interrupt while changing global buffer variables
    NVIC_DisableIRQ(device_irqn);
    empty = (tx_in_ == tx_out_);
    while ((i==0) || (tx_line_[i-1] != '\n')) {
        // Wait if buffer full
        if (IS_TX_FULL) {
            // End Critical Section - need to let interrupt routine empty buffer by sending
            NVIC_EnableIRQ(device_irqn);
            //while (IS_TX_FULL) ; // buffer is full
            tx_sem_.wait();
            // Start Critical Section - don't interrupt while changing global buffer variables
            NVIC_DisableIRQ(device_irqn);
        }
        tx_buffer_[tx_in_] = tx_line_[i];
        i++;
        tx_in_ = NEXT(tx_in_);
    }
    if (Serial::writeable() && (empty)) {
        temp_char = tx_buffer_[tx_out_];
        tx_out_ = NEXT(tx_out_);
        // Send first character to start tx interrupts, if stopped
        LPC_UART1->THR = temp_char;
    }
    // End Critical Section
    NVIC_EnableIRQ(device_irqn);
}

// Read a line from the large rx buffer from rx interrupt routine
void BufferedSerial::get_line(char *c)
{
    int i;
    i = 0;
    // Start Critical Section - don't interrupt while changing global buffer variables
    NVIC_DisableIRQ(device_irqn);
    // Loop reading rx buffer characters until end of line character
    while ((i==0) || (rx_line_[i-1] != '\n')) {
        // Wait if buffer empty
        if (IS_RX_EMPTY) { // buffer empty
            // End Critical Section - need to allow rx interrupt to get new characters for buffer
            NVIC_EnableIRQ(device_irqn);
            
            rx_sem_.wait();
            // Start Critical Section - don't interrupt while changing global buffer variables
            NVIC_DisableIRQ(device_irqn);
        } else {
            rx_sem_.wait();
        }
        rx_line_[i] = rx_buffer_[rx_out_];
        i++;
        rx_out_ = NEXT(rx_out_);

        // prevent overflow on rx_line
        if(i == LINE_SIZE) {
            i--;
            break;
        }
    }
    rx_line_[i++] = 0;
    // End Critical Section
    NVIC_EnableIRQ(device_irqn);
    strncpy(c,rx_line_,i);
}

// Interupt Routine to read in data from serial port
void BufferedSerial::Rx_interrupt()
{
    uint32_t IRR1 = LPC_UART1->IIR;
    // led1=1;
    while (readable() && !(IS_RX_FULL)) {
        rx_buffer_[rx_in_] = LPC_UART1->RBR;
        rx_in_ = NEXT(rx_in_);
        rx_sem_.release();
    }
    // led1=0;
}

// Interupt Routine to write out data to serial port
void BufferedSerial::Tx_interrupt()
{
    uint32_t IRR = LPC_UART1->IIR;
    // led2=1;
    while ((writeable()) && (tx_in_ != tx_out_)) { // while serial is writeable and there are still characters in the buffer
        LPC_UART1->THR = tx_buffer_[tx_out_]; // send the character
        tx_out_ = NEXT(tx_out_);
    }
    if(!IS_TX_FULL) // if not full
        tx_sem_.release();
    // led2=0;
}