/****************************************************************************/
/* Copyright 2015 MBARI                                                     */
/****************************************************************************/
/* Summary  : Serial I/O for OASIS5 on PIC32MX470F512L                      */
/* Filename : serial.c                                                      */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 07/13/2015                                                    */
/*                                                                          */
/* 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:                                                    */
/* 13jul2015 rah - created                                                  */
/****************************************************************************/

#include <xc.h>
#include <GenericTypeDefs.h>

#include <stdint.h>
#include <stdlib.h>

#include "serial.h"
#include "buffer.h"
#include "dig_io.h"


/****************************************************************************/
/*                            Register Definitions                          */
/****************************************************************************/

PRIVATE unsigned int iec1_RxBits[] = 
    {0, _IEC1_U1RXIE_MASK, _IEC1_U2RXIE_MASK, _IEC1_U3RXIE_MASK, 0};

PRIVATE unsigned int iec2_RxBits[] = 
    {_IEC2_U5RXIE_MASK, 0, 0, 0, _IEC2_U4RXIE_MASK};

PRIVATE unsigned int iec1_TxBits[] = 
    {0, _IEC1_U1TXIE_MASK, _IEC1_U2TXIE_MASK, 0, 0};

PRIVATE unsigned int iec2_TxBits[] = 
    {_IEC2_U5TXIE_MASK, 0, 0, _IEC2_U3TXIE_MASK, _IEC2_U4TXIE_MASK};


/****************************************************************************/
/*                            Serial Buffer Data                            */
/****************************************************************************/
PUBLIC ByteBuffer rxBuff[PIC_UARTS];
PRIVATE BYTE rxBuffData[PIC_UARTS][RX_BUFF_SIZE];
PUBLIC int rxOverFlowFlag[PIC_UARTS];

PUBLIC ByteBuffer txBuff[PIC_UARTS];
PRIVATE BYTE txBuffData[PIC_UARTS][TX_BUFF_SIZE];
PUBLIC int txCount[PIC_UARTS];


/****************************************************/
/*      Module Local Data                           */
/****************************************************/

static unsigned long baudRate[SER_PORT_TOTAL] = {0, 0, 0, 0};

static volatile unsigned int rxOverFlow = 0;



/************************************************************************/
/* Function    : serInit                                                */
/* Purpose     : Initialize serial I/O Module                           */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void serInit()
{
    int i;

    /* UART Setup - Note that dig_io.c sets all the Tx bits to output 0 state */
    /* This will cause the port to go into break when the UART is disabled    */
    /* Map the PPS pins for the UARTs                                         */

    U1RXR = 0x01;                       /* UART1 RX = Ser 1             */
    RPG7R = 0x03;                       /* UART1 TX                     */
    U2RXR = 0x02;                       /* UART2 RX = Ser 2             */
    RPF5R = 0x01;                       /* UART2 TX                     */
    U3RXR = 0x0b;                       /* UART3 RX = Ser 3             */
    RPD14R = 0x01;                      /* UART3 TX                     */
    U4RXR = 0x02;                       /* UART4 RX = Ser 4             */
    RPB15R = 0x02;                      /* UART4 TX                     */
    U5RXR = 0x0b;                       /* UART5 RX = Ser 0 = Console   */
    RPF8R = 0x04;                       /* UART5 TX = Ser 0 = Console   */

    /* initialize serial data */
    for (i = 0; i < PIC_UARTS; i++)
    {
        bufInit(&rxBuff[i], rxBuffData[i], RX_BUFF_SIZE);
        bufInit(&txBuff[i], txBuffData[i], TX_BUFF_SIZE);

        serSetConfig(i, STD_BAUD, D8_NONE, S_1); /* Sets up UxMODE and UxSTA */

        rxOverFlowFlag[i] = FALSE;              /* init flags */
        txCount[i] = 0;
    }
    

/******************** Setup Serial Port 0 == UART 5 *********************/

    /* Configure uart5 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS2CLR = (_IFS2_U5RXIF_MASK | _IFS2_U5TXIF_MASK);

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC10bits.U5IP = 6;
    IPC10bits.U5IS = 0;

    /* enable receive interrupts, disable transmit interrupts */
    IEC2SET = _IEC2_U5RXIE_MASK;
    IEC2CLR = _IEC2_U5TXIE_MASK;


/******************** Setup Serial Port 1 == UART 1 *********************/

    /* Configure uart1 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS1CLR = _IFS1_U1RXIF_MASK;
    IFS1CLR = _IFS1_U1TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC7bits.U1IP = 6;
    IPC7bits.U1IS = 0;

    /* enable receive interrupts, disable transmit interrupts */
    IEC1SET = _IEC1_U1RXIE_MASK;
    IEC1CLR = _IEC1_U1TXIE_MASK;


/******************** Setup Serial Port 2 == UART 2 *********************/

    /* Configure uart2 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS1CLR = _IFS1_U2RXIF_MASK;
    IFS1CLR = _IFS1_U2TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC9bits.U2IP = 6;
    IPC9bits.U2IS = 0;

    /* enable receive interrupts, disable transmit interrupts */
    IEC1SET = _IEC1_U2RXIE_MASK;
    IEC1CLR = _IEC1_U2TXIE_MASK;
    
    
/******************** Setup Serial Port 3 == UART 3 *********************/

    /* Configure uart3 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS1CLR = _IFS1_U3RXIF_MASK;
    IFS2CLR = _IFS2_U3TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC9bits.U3IP = 6;
    IPC9bits.U3IS = 0;

    /* enable receive interrupts, disable transmit interrupts */
    IEC1SET = _IEC1_U3RXIE_MASK;
    IEC2CLR = _IEC2_U3TXIE_MASK;


/******************** Setup Serial Port 4 == UART 4 *********************/

    /* Configure uart4 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS2CLR = _IFS2_U4RXIF_MASK;
    IFS2CLR = _IFS2_U4TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC9bits.U4IP = 6;
    IPC9bits.U4IS = 0;

    /* enable receive interrupts, disable transmit interrupts */
    IEC2SET = _IEC2_U4RXIE_MASK;
    IEC2CLR = _IEC2_U4TXIE_MASK;

}


/************************************************************************/
/* Function    : serGetByte                                             */
/* Purpose     : Read one byte from serial port                         */
/* Inputs      : Serial port number, ptr to char                        */
/* Outputs     : TRUE if got char, else FALSE                           */
/************************************************************************/
int serGetByte(int port, unsigned char* b)
{
    int     stat;

    if (port < PIC_UARTS)                  /* Valid port?                  */
    {
//        __builtin_disable_interrupts();
        IEC1CLR = iec1_RxBits[port];    /* Turn off ints from the UART  */
        IEC2CLR = iec2_RxBits[port];    /* while manipulating the buffer*/

        stat = bufGet(b, &rxBuff[port]);

//        __builtin_enable_interrupts();
        IEC1SET = iec1_RxBits[port];    /* Turn UART ints back on       */
        IEC2SET = iec2_RxBits[port];    /* Turn UART ints back on       */

        return(stat);
    }

    /* Invalid port             */
    *b = 0;
    return(FALSE);
}

#if 1

/************************************************************************/
/* Function    : serPutByte                                             */
/* Purpose     : Write one byte to serial port                          */
/* Inputs      : Serial port number, char to write                      */
/* Outputs     : TRUE if wrote char, else FALSE                         */
/************************************************************************/
int serPutByte(int port, unsigned char b)
{
    int     rtn;

    if (port < PIC_UARTS)               /* Valid port?                  */
    {
        IEC1CLR = iec1_TxBits[port];    /* Turn off ints from the UART  */
        IEC2CLR = iec2_TxBits[port];    /* while manipulating the buffer*/

        rtn = bufPut(b, &txBuff[port]);

        IEC1SET = iec1_TxBits[port];    /* Turn on UART Tx ints        */
        IEC2SET = iec2_TxBits[port];

        return(rtn);
    }

    return(FALSE);
}

#else

/************************************************************************/
/* 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, unsigned char b)
{
    switch(port)
    {
      case SER_PORT_0:
          if (U5STAbits.UTXBF)
              return(FALSE);
          U5TXREG = b;
          return(TRUE);

      case SER_PORT_1:
          if (U1STAbits.UTXBF)
              return(FALSE);
          U1TXREG = b;
          return(TRUE);

      case SER_PORT_2:
          if (U2STAbits.UTXBF)
              return(FALSE);
          U2TXREG = b;
          return(TRUE);

      case SER_PORT_3:
          if (U3STAbits.UTXBF)
              return(FALSE);
          U3TXREG = b;
          return(TRUE);

      case SER_PORT_4:
          if (U4STAbits.UTXBF)
              return(FALSE);
          U4TXREG = b;
          return(TRUE);
    }

    return(FALSE);
}

#endif


/************************************************************************/
/* 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;

    if (port < PIC_UARTS)               /* Valid port?                  */
    {
        for (i = 0, p = buff; *p; )
            if (serPutByte(port, *p))
            {
                p++;
                i++;
            }
        
        return(i);
    }

    return(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, unsigned long baud, int data_par, int stop)
{
    switch(port)
    {
        case SER_PORT_0:
            U5MODE = UxMODE;
            U5MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U5MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U5BRG = StdBRG(baud);
            else
            {
                U5BRG = FastBRG(baud);
                U5MODEbits.BRGH = 1;
            }

            U5STA = UxSTA;
            break;

        case SER_PORT_1:
            U1MODE = UxMODE;
            U1MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U1MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U1BRG = StdBRG(baud);
            else
            {
                U1BRG = FastBRG(baud);
                U1MODEbits.BRGH = 1;
            }

            U1STA = UxSTA;
            break;

        case SER_PORT_2:
            U2MODE = UxMODE;
            U2MODEbits.PDSEL = data_par;
                          
            if ( stop == S_2 )
                U2MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U2BRG = StdBRG(baud);
            else
            {
                U2BRG = FastBRG(baud);
                U2MODEbits.BRGH = 1;
            }

            U2STA = UxSTA;
            break;

        case SER_PORT_3:
            U3MODE = UxMODE;
            U3MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U3MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U3BRG = StdBRG(baud);
            else
            {
                U3BRG = FastBRG(baud);
                U3MODEbits.BRGH = 1;
            }

            U3STA = UxSTA;
            break;

        case SER_PORT_4:
            U4MODE = UxMODE;
            U4MODEbits.PDSEL = data_par;

            if ( stop == S_2 )
                U4MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U4BRG = StdBRG(baud);
            else
            {
                U4BRG = FastBRG(baud);
                U4MODEbits.BRGH = 1;
            }

            U4STA = UxSTA;
            break;
    }

    if (port < PIC_UARTS)
        baudRate[port] = baud;
}


/************************************************************************/
/* 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, unsigned long *baud, int *data_par, int *stop)
{
    switch ( port )
    {
        case SER_PORT_0:
            *stop = U5MODEbits.STSEL + 1;
            *data_par = U5MODEbits.PDSEL;
            break;

        case SER_PORT_1:
            *stop = U1MODEbits.STSEL + 1;
            *data_par = U1MODEbits.PDSEL;
            break;

        case SER_PORT_2:
            *stop = U2MODEbits.STSEL + 1;
            *data_par = U2MODEbits.PDSEL;
            break;

        case SER_PORT_3:
            *stop = U3MODEbits.STSEL + 1;
            *data_par = U3MODEbits.PDSEL;
            break;

        case SER_PORT_4:
            *stop = U4MODEbits.STSEL + 1;
            *data_par = U4MODEbits.PDSEL;
            break;

        default:
            return;
    }

    *baud = baudRate[port];
}


/************************************************************************/
/* Function    : serRxAvail                                             */
/* Purpose     : Return number of bytes available on serial port        */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
int  serRxAvail(int port)
{
    return((port < PIC_UARTS) ? bufAvailable(rxBuff[port]) : 0);
}


/************************************************************************/
/* Function    : serRxFlush                                             */
/* Purpose     : Flush serial input FIFO                                */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serRxFlush(int port)
{
    unsigned int reg;

    if (port < PIC_UARTS)
    {
        __builtin_disable_interrupts();

        bufClear(&rxBuff[port]);

        switch ( port )
        {
          case 0: while ( U5STAbits.URXDA ) reg = U5RXREG; break;
          case 1: while ( U1STAbits.URXDA ) reg = U1RXREG; break;
          case 2: while ( U2STAbits.URXDA ) reg = U2RXREG; break;
          case 3: while ( U3STAbits.URXDA ) reg = U3RXREG; break;
          case 4: while ( U4STAbits.URXDA ) reg = U4RXREG; break;
        }

        __builtin_enable_interrupts();
    }
}


/************************************************************************/
/* Function    : serTxFlush                                             */
/* Purpose     : Flush serial output FIFO                               */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serTxFlush(int port)
{
    if (port < PIC_UARTS)
    {
        __builtin_disable_interrupts();

        bufClear(&txBuff[port]);

        switch ( port )
        {
          case 0: U5STAbits.UTXEN = 0; Nop();
                  U5STAbits.UTXEN = 1; Nop();
                  break;
          case 1: U1STAbits.UTXEN = 0; Nop(); 
                  U1STAbits.UTXEN = 1; Nop(); 
                  break;
          case 2: U2STAbits.UTXEN = 0; Nop();
                  U2STAbits.UTXEN = 1; Nop();
                  break;
          case 3: U3STAbits.UTXEN = 0; Nop();
                  U3STAbits.UTXEN = 1; Nop();
                  break;
          case 4: U4STAbits.UTXEN = 0; Nop();
                  U4STAbits.UTXEN = 1; Nop();
                  break;
        }
        __builtin_enable_interrupts();
    }
}


/****************************************************************************/
/*                       Interrupt Service Routines                         */
/* note: the standard buffer functions are not used here as calling external*/
/* functions from an interrupt service routine increases interrupt overhead */
/****************************************************************************/
/* TODO - Replace bufGet() in Tx legs                                       */
/****************************************************************************/

/************************************************************************/
/* Function    : _U5IntHandler                                          */
/* Purpose     : Interrupt handler for UART 5 (Ser Port 0, console)     */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_5_VECTOR)) ) _U5IntHandler(void)
{
    int         i;
    UINT8       b;

    if (IFS2bits.U5RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U5STAbits.URXDA )
        {
            /* if the buffer is full, mark overflow error   */
            if ( bufIsFull(rxBuff[SER_PORT_0]) )
            {
                /* you need to get the byte out buffer or you will hang! */
                rxOverFlow = U5RXREG;
                rxOverFlowFlag[SER_PORT_0] = TRUE;  /* set overflow flag and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_0].inptr++ = (unsigned char)U5RXREG;
                rxBuff[SER_PORT_0].occupied++;

                if (rxBuff[SER_PORT_0].inptr >= rxBuff[SER_PORT_0].endptr)
                    rxBuff[SER_PORT_0].inptr = rxBuff[SER_PORT_0].startptr;
            }
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U5RXIF_MASK;
    }

    if (IFS2bits.U5TXIF)
    {
        digTest2On();
    
        while (IEC2bits.U5TXIE && (U5STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_0].occupied)
            {
                U5TXREG = *txBuff[SER_PORT_0].outptr++;
                txBuff[SER_PORT_0].occupied--;
                txCount[SER_PORT_0]++;

                if (txBuff[SER_PORT_0].outptr >= txBuff[SER_PORT_0].endptr)
                    txBuff[SER_PORT_0].outptr = txBuff[SER_PORT_0].startptr;
            }
            else
                IEC2CLR = _IEC2_U5TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U5TXIF_MASK;
        digTest2Off();
    }
}

/************************************************************************/
/* Function    : _U1IntHandler                                          */
/* Purpose     : Interrupt handler for UART 1 (Ser Port 1)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_1_VECTOR)) ) _U1IntHandler(void)
{
    int         i;
    UINT8       b;

    if (IFS1bits.U1RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U1STAbits.URXDA )
        {
            /* if the buffer is full, mark overflow error   */
            if ( bufIsFull(rxBuff[SER_PORT_1]) )
            {
                /* you need to get the byte out buffer or you will hang! */
                rxOverFlow = U1RXREG;
                rxOverFlowFlag[SER_PORT_1] = TRUE;  /* set overflow flag and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_1].inptr++ = (unsigned char)U1RXREG;
                rxBuff[SER_PORT_1].occupied++;

                if (rxBuff[SER_PORT_1].inptr >= rxBuff[SER_PORT_1].endptr)
                    rxBuff[SER_PORT_1].inptr = rxBuff[SER_PORT_1].startptr;
            }
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U1RXIF_MASK;
    }

    if (IFS1bits.U1TXIF)
    {
        while (IEC1bits.U1TXIE && (U1STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_1].occupied)
            {
                U1TXREG = *txBuff[SER_PORT_1].outptr++;
                txBuff[SER_PORT_1].occupied--;
                txCount[SER_PORT_1]++;

                if (txBuff[SER_PORT_1].outptr >= txBuff[SER_PORT_1].endptr)
                    txBuff[SER_PORT_1].outptr = txBuff[SER_PORT_1].startptr;
            }
            else
                IEC1CLR = _IEC1_U1TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U1TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U2IntHandler                                          */
/* Purpose     : Interrupt handler for UART 2 (Ser Port 2)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_2_VECTOR)) ) _U2IntHandler(void)
{
    int         i;
    UINT8       b;

    if (IFS1bits.U2RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U2STAbits.URXDA )
        {
            /* if the buffer is full, mark overflow error   */
            if ( bufIsFull(rxBuff[SER_PORT_2]) )
            {
                /* you need to get the byte out buffer or you will hang! */
                rxOverFlow = U2RXREG;
                rxOverFlowFlag[SER_PORT_2] = TRUE;  /* set overflow flag and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_2].inptr++ = (unsigned char)U2RXREG;
                rxBuff[SER_PORT_2].occupied++;

                if (rxBuff[SER_PORT_2].inptr >= rxBuff[SER_PORT_2].endptr)
                    rxBuff[SER_PORT_2].inptr = rxBuff[SER_PORT_2].startptr;
            }
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U2RXIF_MASK;
    }

    if (IFS1bits.U2TXIF)
    {
        while (IEC1bits.U2TXIE && (U2STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_2].occupied)
            {
                U2TXREG = *txBuff[SER_PORT_2].outptr++;
                txBuff[SER_PORT_2].occupied--;
                txCount[SER_PORT_2]++;

                if (txBuff[SER_PORT_2].outptr >= txBuff[SER_PORT_2].endptr)
                    txBuff[SER_PORT_2].outptr = txBuff[SER_PORT_2].startptr;
            }
            else
                IEC1CLR = _IEC1_U2TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U2TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U3IntHandler                                          */
/* Purpose     : Interrupt handler for UART 3 (Ser Port 3)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_3_VECTOR)) ) _U3IntHandler(void)
{
    int         i;
    UINT8       b;

    if (IFS1bits.U3RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U3STAbits.URXDA )
        {
            /* if the buffer is full, mark overflow error   */
            if ( bufIsFull(rxBuff[SER_PORT_3]) )
            {
                /* you need to get the byte out buffer or you will hang! */
                rxOverFlow = U3RXREG;
                rxOverFlowFlag[SER_PORT_3] = TRUE;  /* set overflow flag and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_3].inptr++ = (unsigned char)U3RXREG;
                rxBuff[SER_PORT_3].occupied++;

                if (rxBuff[SER_PORT_3].inptr >= rxBuff[SER_PORT_3].endptr)
                    rxBuff[SER_PORT_3].inptr = rxBuff[SER_PORT_3].startptr;
            }
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U3RXIF_MASK;
    }

    if (IFS2bits.U3TXIF)
    {
        while (IEC2bits.U3TXIE && (U3STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_3].occupied)
            {
                U3TXREG = *txBuff[SER_PORT_3].outptr++;
                txBuff[SER_PORT_3].occupied--;
                txCount[SER_PORT_3]++;

                if (txBuff[SER_PORT_3].outptr >= txBuff[SER_PORT_3].endptr)
                    txBuff[SER_PORT_3].outptr = txBuff[SER_PORT_3].startptr;
            }
            else
                IEC2CLR = _IEC2_U3TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U3TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U4IntHandler                                          */
/* Purpose     : Interrupt handler for UART 4 (Ser Port 4)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_4_VECTOR)) ) _U4IntHandler(void)
{
    int         i;
    UINT8       b;

    if (IFS2bits.U4RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U4STAbits.URXDA )
        {
            /* if the buffer is full, mark overflow error   */
            if ( bufIsFull(rxBuff[SER_PORT_4]) )
            {
                /* you need to get the byte out buffer or you will hang! */
                rxOverFlow = U4RXREG;
                rxOverFlowFlag[SER_PORT_4] = TRUE;  /* set overflow flag and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_4].inptr++ = (unsigned char)U4RXREG;
                rxBuff[SER_PORT_4].occupied++;

                if (rxBuff[SER_PORT_4].inptr >= rxBuff[SER_PORT_4].endptr)
                    rxBuff[SER_PORT_4].inptr = rxBuff[SER_PORT_4].startptr;
            }
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U4RXIF_MASK;
    }

    if (IFS2bits.U4TXIF)
    {
        while (IEC2bits.U4TXIE && (U4STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_4].occupied)
            {
                U4TXREG = *txBuff[SER_PORT_4].outptr++;
                txBuff[SER_PORT_4].occupied--;
                txCount[SER_PORT_4]++;

                if (txBuff[SER_PORT_4].outptr >= txBuff[SER_PORT_4].endptr)
                    txBuff[SER_PORT_4].outptr = txBuff[SER_PORT_4].startptr;
            }
            else
                IEC2CLR = _IEC2_U4TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U4TXIF_MASK;
    }
}
