/****************************************************************************/
/* Copyright 2009 - 2015 MBARI                                              */
/****************************************************************************/
/* Summary  : Serial Port module for OASIS5 Expander and Extender Boards    */
/* Filename : serial.c                                                      */
/* Author   : Mike Risi (mrisi), Robert Herlien (rah)                       */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 2009 by Mike Risi, Adapted for Oasis5 10/29/2015 by Bob Herlien*/
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 29oct2015 rah - adapted from Respirometer chamber controller             */
/****************************************************************************/

#include "serial.h"
#include "buffer.h"
#include "regs.h"
#include "port.h"
#include "dig_io.h"

#include <xc.h>
#include <stdlib.h>
#include <stdio.h>

/****************************************************/
/*      Serial Control Ports                        */
/****************************************************/

volatile unsigned int *UxSTARegs[] = 
    { &U1STA, &U2STA, &U3STA, &U4STA, &U5STA, &U6STA };

volatile unsigned int *UxBRGRegs[] = 
    { &U1BRG, &U2BRG, &U3BRG, &U4BRG, &U5BRG, &U6BRG };

static unsigned long baudRate[] = {0, 0, 0, 0, 0, 0};

#ifndef NO_IDLE
static unsigned long rxTick = 0UL;
#endif


/****************************************************************************/
/*                            Serial Buffer Data                            */
/****************************************************************************/

static ByteBuffer rxBuff[SER_PORT_TOTAL];
static BYTE rxBuffData[SER_PORT_TOTAL][RX_BUFF_SIZE];
//static BYTE _BSS(RX_BUFF_SIZE) rxBuffData[SER_PORT_TOTAL][RX_BUFF_SIZE];

static unsigned int rxErrorFlags[SER_PORT_TOTAL];
static unsigned int txBreakFlag[SER_PORT_TOTAL];


/************************************************************************/
/* Function    : serInit                                                */
/* Purpose     : Initialize serial I/O Module                           */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void serInit()
{
    int i;

    /* initialize serial data */
    for (i = 0; i < SER_PORT_TOTAL; i++)
    {
        /* init buffers */
        bufInit(&rxBuff[i], rxBuffData[i], RX_BUFF_SIZE);

        /* init flags */
        rxErrorFlags[i] = 0;
        txBreakFlag[i] = FALSE;
    }
    

    /* Set up default status bits, config, and baud rates               */
    for (i = 0; i < SER_PORT_TOTAL; i++)
    {
        serSetConfig(i, D8_NONE, S_1);
        serSetBaud(i, STD_BAUD);
        *UxSTARegs[i] = UxSTA;
    }

    /***************************** Setup UART 1 *****************************/

    /* clear any pending interrupts just in case */
    _U1RXIF = 0;
    _U1TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U1RXIP = 6;
    _U1TXIP = 5;

    /* enable receive interrupts, disable transmit interrupts */
    _U1RXIE = 1;
    _U1TXIE = 0;

    /***************************** Setup UART 2 *****************************/

    /* clear any pending interrupts just in case */
    _U2RXIF = 0;
    _U2TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U2RXIP = 6;
    _U2TXIP = 5;

    /* enable receive interrupts, disable transmit interrupts */
    _U2RXIE = 1;
    _U2TXIE = 0;
    
    /***************************** Setup UART 3 *****************************/

    /* clear any pending interrupts just in case */
    _U3RXIF = 0;
    _U3TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U3RXIP = 6;
    _U3TXIP = 5;

    /* enable receive interrupts, disable transmit interrupts */
    _U3RXIE = 1;
    _U3TXIE = 0;

    /***************************** Setup UART 4 *****************************/

    /* clear any pending interrupts just in case */
    _U4RXIF = 0;
    _U4TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U4RXIP = 6;
    _U4TXIP = 5;

    /* enable receive interrupts, disable transmit interrupts */
    _U4RXIE = 1;
    _U4TXIE = 0;

    /***************************** Setup UART 5 *****************************/

    /* clear any pending interrupts just in case */
    _U5RXIF = 0;
    _U5TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U5RXIP = 6;
    _U5TXIP = 5;

    /* enable receive interrupts, disable transmit interrupts */
    _U5RXIE = 1;
    _U5TXIE = 0;

#ifdef SER_PORT_5
   /***************************** Setup UART 6 *****************************/

    /* clear any pending interrupts just in case */
    _U6RXIF = 0;
    _U6TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U6RXIP = 6;
    _U6TXIP = 5;

    /* enable receive interrupts, disable transmit interrupts */
    _U6RXIE = 1;
    _U6TXIE = 0;
#endif
}


/************************************************************************/
/* Function    : serSetBaud                                             */
/* Purpose     : Set the baud rate for a serial port                    */
/* Inputs      : Ser port number, baud rate                             */
/* Outputs     : None                                                   */
/************************************************************************/
void serSetBaud(int port, unsigned long baud)
{
    if (isSerPortValid(port))
    {
        *UxBRGRegs[port] = (unsigned int)StdBRG(baud);
        baudRate[port] = baud;
    }
}


/************************************************************************/
/* Function    : serGetBaud                                             */
/* Purpose     : Get the baud rate for a serial port                    */
/* Inputs      : Ser port number                                        */
/* Outputs     : Baud rate                                              */
/************************************************************************/
unsigned long serGetBaud(int port)
{
    return( (isSerPortValid(port)) ? baudRate[port] : 0 );
}


/************************************************************************/
/* Function    : serSetConfig                                           */
/* Purpose     : Initialize a serial port with parity and stop bits     */
/* Inputs      : Ser port number, parity, stop bits                     */
/* Outputs     : None                                                   */
/* Comments    : Also sets up the other bits of UxMODE with default setup*/
/************************************************************************/
void serSetConfig(int port, int data_par, int stop)
{
    switch ( port )
    {
        case SER_PORT_0:
            U1MODE = UxMODE;
            U1MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U1MODEbits.STSEL = 1;
            break;

        case SER_PORT_1:
            U2MODE = UxMODE;
            U2MODEbits.PDSEL = data_par;
                          
            if ( stop == S_2 )
                U2MODEbits.STSEL = 1;
            break;

        case SER_PORT_2:
            U3MODE = UxMODE;
            U3MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U3MODEbits.STSEL = 1;
            break;

        case SER_PORT_3:
            U4MODE = UxMODE;
            U4MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U4MODEbits.STSEL = 1;
            break;

        case SER_PORT_4:
            U5MODE = UxMODE;
            U5MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U5MODEbits.STSEL = 1;
            break;

#ifdef SER_PORT_5
        case SER_PORT_5:
            U6MODE = UxMODE;
            U6MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U6MODEbits.STSEL = 1;
            break;

#endif
    }
}


/************************************************************************/
/* Function    : serGetConfig                                           */
/* Purpose     : Return parity and stop bits for serial port            */
/* Inputs      : Ser port number, ptrs to parity and stop bits          */
/* Outputs     : None                                                   */
/************************************************************************/
void serGetConfig(int port, int* data_par, int* stop)
{
    switch ( port )
    {
        case SER_PORT_0:
            *stop = U1MODEbits.STSEL + 1;
            *data_par = U1MODEbits.PDSEL;
            break;

        case SER_PORT_1:
            *stop = U2MODEbits.STSEL + 1;
            *data_par = U2MODEbits.PDSEL;
            break;

        case SER_PORT_2:
            *stop = U3MODEbits.STSEL + 1;
            *data_par = U3MODEbits.PDSEL;
            break;

        case SER_PORT_3:
            *stop = U4MODEbits.STSEL + 1;
            *data_par = U4MODEbits.PDSEL;
            break;

        case SER_PORT_4:
            *stop = U5MODEbits.STSEL + 1;
            *data_par = U5MODEbits.PDSEL;
            break;

#ifdef SER_PORT_5
        case SER_PORT_5:
            *stop = U6MODEbits.STSEL + 1;
            *data_par = U6MODEbits.PDSEL;
            break;
#endif
    }
}


/************************************************************************/
/* Function    : serGetByte                                             */
/* Purpose     : Return received serial character from port             */
/* Inputs      : Ser port number, ptr to byte to return                 */
/* Outputs     : TRUE if found Rx char, else FALSE                      */
/************************************************************************/
int serGetByte(int port, BYTE* b)
{
    int stat;

    /* if the port is valid, try to get the byte */
    if ( isSerPortValid(port) )
    {
        /* try to get a byte from the buffer */
        EnterAppCriticalSection();
        stat = bufGet(b, &rxBuff[port]);
        ExitAppCriticalSection();

        return stat;
    }
    
    *b = 0; 
    
    return FALSE;
}


/************************************************************************/
/* Function    : serPutByte                                             */
/* Purpose     : Queue a serial char for transmission                   */
/* Inputs      : Ser port number, byte to send                          */
/* Outputs     : TRUE if Tx char queued, else FALSE                     */
/************************************************************************/
int serPutByte(int port, BYTE b)
{
    switch(port)
    {
      case SER_PORT_0:
          if (U1STAbits.UTXBF == 0) {
              U1TXREG = b;
              return(TRUE);
          }

      case SER_PORT_1:
          if (U2STAbits.UTXBF == 0) {
              U2TXREG = b;
              return(TRUE);
          }

      case SER_PORT_2:
          if (U3STAbits.UTXBF == 0) {
              U3TXREG = b;
              return(TRUE);
          }

      case SER_PORT_3:
          if (U4STAbits.UTXBF == 0) {
              U4TXREG = b;
              return(TRUE);
          }

      case SER_PORT_4:
          if (U5STAbits.UTXBF == 0) {
              U5TXREG = b;
              return(TRUE);
          }

#ifdef SER_PORT_5
      case SER_PORT_5:
          if (U6STAbits.UTXBF == 0) {
              U6TXREG = b;
              return(TRUE);
          }
#endif
    }

    return(FALSE);
}


/************************************************************************/
/* Function    : serPutString                                           */
/* Purpose     : Queue a C string for transmission                      */
/* Inputs      : Ser port number, string to send                        */
/* Outputs     : Number of bytes queued                                 */
/************************************************************************/
int serPutString(int port, char* buff)
{
    int         i;
    char        *p;
    unsigned long stTick;

    if(isSerPortValid(port))
    {
        for (i = 0, p = buff; *p; p++)
        {
            
            for (stTick = tmrGetTicks(); (*UxSTARegs[port] & _U1STA_UTXBF_MASK); )
                if ((tmrGetTicks() - stTick) > 5)
                    return(i);

            if (serPutByte(port, *p))
                i++;
        }

        return(i);
    }

    return(0);
}


/************************************************************************/
/* Function    : serPutStringBool                                       */
/* Purpose     : Queue a string plus a literal "ON" or "OFF" to a serial port*/
/* Inputs      : Ser port number, string and boolean to send            */
/* Outputs     : Number of bytes queued                                 */
/************************************************************************/
int serPutStringBool(int port, char* buff, int onoff)
{
    int i = serPutString(port, buff);

    i += serPutString(port, onoff ? "ON\r\n" : "OFF\r\n");

    return(i);
}


/************************************************************************/
/* Function    : serRxFlush                                             */
/* Purpose     : Flush Rx queue for a serial port                       */
/* Inputs      : Ser port number                                        */
/* Outputs     : None                                                   */
/************************************************************************/
void serRxFlush(int port)
{
    unsigned reg;

    /* if the port is valid, flush the buffer */
    if ( isSerPortValid(port) )
    {
        EnterAppCriticalSection();

        /* clear out the software FIFO */
        bufClear(&rxBuff[port]);

        /* clear out the harware FIFO */
        switch ( port )
        {
            case SER_PORT_0: while ( U1STAbits.URXDA ) reg = U1RXREG; break;
            case SER_PORT_1: while ( U2STAbits.URXDA ) reg = U2RXREG; break;
            case SER_PORT_2: while ( U3STAbits.URXDA ) reg = U3RXREG; break;
            case SER_PORT_3: while ( U4STAbits.URXDA ) reg = U4RXREG; break;
            case SER_PORT_4: while ( U5STAbits.URXDA ) reg = U5RXREG; break;
#ifdef SER_PORT_5
            case SER_PORT_5: while ( U6STAbits.URXDA ) reg = U6RXREG; break;
#endif
            default: break;
        }

        ExitAppCriticalSection();
    }

    return;
}


/************************************************************************/
/* Function    : serTxFlush                                             */
/* Purpose     : Flush Tx queue for a serial port                       */
/* Inputs      : Ser port number                                        */
/* Outputs     : None                                                   */
/************************************************************************/
void serTxFlush(int port)
{
    if ( isSerPortValid(port) )
    {
        *UxSTARegs[port] &= ~_U1STA_UTXEN_MASK;
        Nop();
        *UxSTARegs[port] |= _U1STA_UTXEN_MASK;
        Nop();
    }
}


/************************************************************************/
/* Function    : serSetBreak                                            */
/* Purpose     : Send a break char on serial port                       */
/* Inputs      : Serial port number, state (ON or OFF)                  */
/* Outputs     : None                                                   */
/************************************************************************/
void serSetBreak(int port, int state)
{
    int enbl = state ? 1 : 0;

    switch ( port )
    {
        case SER_PORT_0:
            U1STAbits.UTXEN = enbl;
            U1MODEbits.UARTEN = enbl;
            txBreakFlag[SER_PORT_0] = (state ? TRUE : FALSE);
            break;

        case SER_PORT_1:
            U2STAbits.UTXEN = enbl;
            U2MODEbits.UARTEN = enbl;
            txBreakFlag[SER_PORT_1] = (state ? TRUE : FALSE);
            break;

        case SER_PORT_2:
            U3STAbits.UTXEN = enbl;
            U3MODEbits.UARTEN = enbl;
            txBreakFlag[SER_PORT_2] = (state ? TRUE : FALSE);
            break;

        case SER_PORT_3:
            U4STAbits.UTXEN = enbl;
            U4MODEbits.UARTEN = enbl;
            txBreakFlag[SER_PORT_3] = (state ? TRUE : FALSE);
            break;

        case SER_PORT_4:
            U5STAbits.UTXEN = enbl;
            U5MODEbits.UARTEN = enbl;
            txBreakFlag[SER_PORT_4] = (state ? TRUE : FALSE);
            break;

#ifdef SER_PORT_5
        case SER_PORT_5:
            U6STAbits.UTXEN = enbl;
            U6MODEbits.UARTEN = enbl;
            txBreakFlag[SER_PORT_5] = (state ? TRUE : FALSE);
            break;
#endif
    }
}


/************************************************************************/
/* Function    : serGetBreak                                            */
/* Purpose     : Return break state of serial port                      */
/* Inputs      : Serial port number                                     */
/* Outputs     : Break state                                            */
/************************************************************************/
int serGetBreak(int port)
{
    if ( isSerPortValid(port) )
        return txBreakFlag[port]; 

    return(FALSE);
}


/************************************************************************/
/* Function    : serIsRxFull                                            */
/* Purpose     : Return TRUE if serial Rx buffer is full                */
/* Inputs      : Serial port number                                     */
/* Outputs     : TRUE or FALSE                                          */
/************************************************************************/
int serIsRxFull(int port)
{
    int stat;
    
    if ( isSerPortValid(port) )
    {
        /* get the buffer status */
        EnterAppCriticalSection();
        stat = bufIsFull(rxBuff[port]);
        ExitAppCriticalSection();

        return(stat);
    }

    return(FALSE);    
}


/************************************************************************/
/* Function    : serRxAvailable                                         */
/* Purpose     : Return number of Rx chars available on serial port     */
/* Inputs      : Serial port number                                     */
/* Outputs     : Number of Rx chars                                     */
/************************************************************************/
int serRxAvailable(int port)
{
    int avail;
    
    if ( isSerPortValid(port) )
    {
        /* get the buffer status */
        EnterAppCriticalSection();
        avail = bufAvailable(rxBuff[port]);
        ExitAppCriticalSection();

        return(avail);
    }

    return(0);
}


/************************************************************************/
/* Function    : serIsTxFull                                            */
/* Purpose     : Return TRUE if serial Tx buffer is full                */
/* Inputs      : Serial port number                                     */
/* Outputs     : Zero or non-zero                                       */
/************************************************************************/
int serIsTxFull(int port)
{
    if ( isSerPortValid(port) )
        return(*UxSTARegs[port] & _U1STA_UTXBF_MASK);

    return(TRUE);
}


/************************************************************************/
/* Function    : serIsTxEmpty                                           */
/* Purpose     : Return TRUE if serial Tx buffer is empty               */
/* Inputs      : Serial port number                                     */
/* Outputs     : Zero or non-zero                                       */
/************************************************************************/
int serIsTxEmpty(int port)
{
    if ( isSerPortValid(port) )
        return(*UxSTARegs[port] & _U1STA_TRMT_MASK);

    return(TRUE);
}


/********************************************************/
/*      Get/Clear Error Flags                           */
/********************************************************/

int serGetErrFlag(int port)
{
    if ( isSerPortValid(port) )
        if ( rxErrorFlags[port] > 0 )
            return TRUE;

    return FALSE;
}


void serClrXERR(int port)
{
    if ( isSerPortValid(port) )
        rxErrorFlags[port] &= (~OVERFLOW_ERR);
}

int serGetXERR(int port)
{
    if ( isSerPortValid(port) )
        return((rxErrorFlags[port] & OVERFLOW_ERR) ? TRUE : FALSE);

    return FALSE;
}

void serClrOERR(int port)
{
    if ( isSerPortValid(port) )
    {
        rxErrorFlags[port] &= (~OVERRUN_ERR);

        /* also clear the OERR bit in the UxSTA register */
        *UxSTARegs[port] &= ~_U1STA_OERR_MASK;
    }

    return;
}

int serGetOERR(int port)
{
    if ( isSerPortValid(port) )
        return((rxErrorFlags[port] & OVERRUN_ERR) ? TRUE : FALSE);

    return(FALSE);
}

void serClrFERR(int port)
{
    if ( isSerPortValid(port) )
        rxErrorFlags[port] &= (~FRAMING_ERR);
}

int serGetFERR(int port)
{
    if ( isSerPortValid(port) )
        return((rxErrorFlags[port] & FRAMING_ERR) ? TRUE : FALSE);

    return(FALSE);
}

void serClrPERR(int port)
{
    if ( isSerPortValid(port) )
        rxErrorFlags[port] &= (~PARITY_ERR);
}


int serGetPERR(int port)
{
    if ( isSerPortValid(port) )
        return((rxErrorFlags[port] & PARITY_ERR) ? TRUE : FALSE);

    return FALSE;
}


/*******************************************************/
/*  Interrupt Masking Routines                         */
/*******************************************************/
static UCHAR intNesting = 0x00;

void EnterAppCriticalSection(void)
{
    /* disable ints     */
    __asm__ volatile("disi #0x3FFF");
    
    /* increment critical section nesting count */
    ++intNesting;
}

void ExitAppCriticalSection( void )
{
    /* decrement critical section nesting count */
    --intNesting;

    /* if you've nested all the way out of critical sections enable ints */
    if( intNesting == 0 )
        __asm__ volatile("disi #0x0000");
}


/************************************************************************/
/* Function    : serRxIsIdle                                            */
/* Purpose     : Check if Instrument Rx ports are idle					*/
/* Inputs      : None                                                   */
/* Outputs     : TRUE if all ports idle, else FALSE						*/
/************************************************************************/
int serRxIsIdle(void)
{
#ifdef NO_IDLE
	return(FALSE);
#else
	return((tmrGetTicks() - rxTick) > SER_RX_IDLE_TICKS);
#endif
}

/****************************************************************************/
/*                       Interrupt Service Routines                         */
/* note: the standard buffer functions are not used here as calling external*/
/* functions from an interrupt service routine increases interrupt overhead */
/****************************************************************************/

/* Received data var */
static volatile unsigned int rxOverFlow = 0;

/* This is UART1 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U1TXInterrupt(void)
{
    _U1TXIF = 0;
}

/* This is UART1 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U1RXInterrupt(void)
{
#ifndef NO_IDLE
	NoDoze();
	rxTick = tmrGetTicks();
#endif

    /* grab all the bytes from the buffer */
    while ( U1STAbits.URXDA )
    {
        /* if the buffer is full, mark overflow error   */
        if (bufAvailable(rxBuff[0]) >= RX_BUFF_SIZE-1)
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U1RXREG;

            /* set overflow flag and bail */
            rxErrorFlags[SER_PORT_0] |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags[SER_PORT_0] |= (U1STA & ERR_FLAG_MASK);
            
            /* if there is room in the buffer put the byte in it */
            *rxBuff[0].inptr++ = U1RXREG;
            rxBuff[0].occupied++;

            if (rxBuff[0].inptr >= rxBuff[0].endptr)
                rxBuff[0].inptr = rxBuff[0].startptr;
        }
    }

    U1STAbits.OERR = 0;         /* clear any overrun error or     */
                                /* you'll get no further Rx ints  */      
    _U1RXIF = 0;                /* clear the interrupt and return */
}


/* This is UART2 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U2TXInterrupt(void)
{
    _U2TXIF = 0;
}

/* This is UART2 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U2RXInterrupt(void)
{
#ifndef NO_IDLE
	NoDoze();
	rxTick = tmrGetTicks();
#endif

    /* grab all the bytes from the buffer */
    while ( U2STAbits.URXDA )
    {
        /* if the buffer is full, mark overflow error   */
        if (bufAvailable(rxBuff[1]) >= RX_BUFF_SIZE-1)
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U2RXREG;

            /* set overflow flag and bail */
            rxErrorFlags[SER_PORT_1] |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags[SER_PORT_1] |= (U2STA & ERR_FLAG_MASK);
            
            /* if there is room in the buffer put the byte in it */
            *rxBuff[1].inptr++ = U2RXREG;
            rxBuff[1].occupied++;

            if (rxBuff[1].inptr >= rxBuff[1].endptr)
                rxBuff[1].inptr = rxBuff[1].startptr;
        }
    }

    U2STAbits.OERR = 0;         /* clear any overrun error or     */
                                /* you'll get no further Rx ints  */      
    _U2RXIF = 0;                /* clear the interrupt and return */
}


/* This is UART3 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U3TXInterrupt(void)
{
    _U3TXIF = 0;
}

/* This is UART3 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U3RXInterrupt(void)
{
#ifndef NO_IDLE
	NoDoze();
	rxTick = tmrGetTicks();
#endif

    /* grab all the bytes from the buffer */
    while ( U3STAbits.URXDA )
    {
        /* if the buffer is full, mark overflow error   */
        if (bufAvailable(rxBuff[2]) >= RX_BUFF_SIZE-1)
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U3RXREG;

            /* set overflow flag and bail */
            rxErrorFlags[SER_PORT_2] |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags[SER_PORT_2] |= (U3STA & ERR_FLAG_MASK);
            
            /* if there is room in the buffer put the byte in it */
            *rxBuff[2].inptr++ = U3RXREG;
            rxBuff[2].occupied++;

            if (rxBuff[2].inptr >= rxBuff[2].endptr)
                rxBuff[2].inptr = rxBuff[2].startptr;
        }
    }

    U3STAbits.OERR = 0;         /* clear any overrun error or     */
                                /* you'll get no further Rx ints  */      
    _U3RXIF = 0;                /* clear the interrupt and return */
}


/* This is UART4 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U4TXInterrupt(void)
{
    _U4TXIF = 0;
}

/* This is UART4 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U4RXInterrupt(void)
{
#ifndef NO_IDLE
	NoDoze();
	rxTick = tmrGetTicks();
#endif

    /* grab all the bytes from the buffer */
    while ( U4STAbits.URXDA )
    {
        /* if the buffer is full, mark overflow error   */
        if (bufAvailable(rxBuff[3]) >= RX_BUFF_SIZE-1)
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U4RXREG;

            /* set overflow flag and bail */
            rxErrorFlags[SER_PORT_3] |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags[SER_PORT_3] |= (U4STA & ERR_FLAG_MASK);
            
            /* if there is room in the buffer put the byte in it */
            *rxBuff[3].inptr++ = U4RXREG;
            rxBuff[3].occupied++;

            if (rxBuff[3].inptr >= rxBuff[3].endptr)
                rxBuff[3].inptr = rxBuff[3].startptr;
        }
    }

    U4STAbits.OERR = 0;         /* clear any overrun error or     */
                                /* you'll get no further Rx ints  */      
    _U4RXIF = 0;                /* clear the interrupt and return */
}


/* This is UART5 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U5TXInterrupt(void)
{
    _U5TXIF = 0;
}

/* This is UART5 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U5RXInterrupt(void)
{
#ifndef NO_IDLE
	NoDoze();
	rxTick = tmrGetTicks();
#endif

    /* grab all the bytes from the buffer */
    while ( U5STAbits.URXDA )
    {
        /* if the buffer is full, mark overflow error   */
        if (bufAvailable(rxBuff[4]) >= RX_BUFF_SIZE-1)
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U5RXREG;

            /* set overflow flag and bail */
            rxErrorFlags[SER_PORT_4] |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags[SER_PORT_4] |= (U5STA & ERR_FLAG_MASK);
            
            /* if there is room in the buffer put the byte in it */
            *rxBuff[4].inptr++ = U5RXREG;
            rxBuff[4].occupied++;

            if (rxBuff[4].inptr >= rxBuff[4].endptr)
                rxBuff[4].inptr = rxBuff[4].startptr;
        }
    }

    U5STAbits.OERR = 0;         /* clear any overrun error or     */
                                /* you'll get no further Rx ints  */      
    _U5RXIF = 0;                /* clear the interrupt and return */
}


#ifdef SER_PORT_5

/* This is UART6 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U6TXInterrupt(void)
{
    _U6TXIF = 0;
}

/* This is UART6 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U6RXInterrupt(void)
{
#ifndef NO_IDLE
	NoDoze();
	rxTick = tmrGetTicks();
#endif

    /* grab all the bytes from the buffer */
    while ( U6STAbits.URXDA )
    {
        /* if the buffer is full, mark overflow error   */
        if (bufAvailable(rxBuff[5]) >= RX_BUFF_SIZE-1)
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U6RXREG;

            /* set overflow flag and bail */
            rxErrorFlags[SER_PORT_5] |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags[SER_PORT_5] |= (U6STA & ERR_FLAG_MASK);
            
            /* if there is room in the buffer put the byte in it */
            *rxBuff[5].inptr++ = U6RXREG;
            rxBuff[5].occupied++;

            if (rxBuff[5].inptr >= rxBuff[5].endptr)
                rxBuff[5].inptr = rxBuff[5].startptr;
        }
    }

    U6STAbits.OERR = 0;         /* clear any overrun error or     */
                                /* you'll get no further Rx ints  */      
    _U6RXIF = 0;                /* clear the interrupt and return */
}

#endif

