/****************************************************************************/
/* Copyright 2009-2013 MBARI.                                               */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Written by Mike Risi for Respirometer project in 2009                    */
/* Modified by Bob Herlien for SensorNode for xFOCE, May 2013               */
/****************************************************************************/
#include "serial.h"
#include "buffer.h"
#include "port.h"
#include "dig_io.h"

#include "p24Fxxxx.h"

#include <stdlib.h>
#include <stdio.h>

/****************************************************************************/
/*                            Serial Buffer Data                            */
/****************************************************************************/
#define RX_BUFF_SIZE    256
static ByteBuffer rxBuff[SER_PORT_TOTAL];
static BYTE rxBuffData[SER_PORT_TOTAL][RX_BUFF_SIZE];

static unsigned int rxErrorFlags[SER_PORT_TOTAL];
static unsigned int txBreakFlag[SER_PORT_TOTAL];

/* Error flag deines */
#define OVERFLOW_ERR    0x01

#define OVERRUN_ERR     0x02
#define FRAMING_ERR     0x04
#define PARITY_ERR      0x08
/* note: ERR_FLAG_MASK is for overrun, framing, and partiy 
only, it is used to mask off those bits in UxSTA register */
#define ERR_FLAG_MASK   0x0E

/* Baud rate calc defines */
#define FOSC        3686400
#define FCY         (FOSC/2)

#define BRGH_VAL    1

#if BRGH_VAL
#define BRG_DIV     4
#else
#define BRG_DIV     16
#endif

/* Local functions */
int isSerPortValid(int port);
unsigned long brgToBaud(unsigned int brg);
unsigned int baudToBrg(unsigned long baud);

/* init registers, init software buffers, install IRQ */
void serInit()
{
    int i;

    /* initialize serial data */
    for (i = 0; i < SER_PORT_TOTAL; ++i)
    {
        /* init buffers */
        rxBuff[i].data = rxBuffData[i];
        rxBuff[i].head = 0;
        rxBuff[i].tail = 0;
        rxBuff[i].ptr_mask = (RX_BUFF_SIZE - 1);
        
        /* init flags */
        rxErrorFlags[i] = 0;
        txBreakFlag[i] = FALSE;
    }

    /***************************** Setup UART 1 *****************************/
    /* set baud to 9600 */
    U1BRG = baudToBrg(9600);
    
    /* No parity, one stop bit, no autobaud, polled */
    U1MODEbits.UARTEN = 1;
    U1MODEbits.ABAUD = 0;
    U1MODEbits.BRGH = BRGH_VAL;
    U1MODEbits.PDSEL = 0;
    U1MODEbits.STSEL = 0;

//    U1STAbits.UTXEN = 1;
    U1STA = 0x400;

    /* Once the UART is enabled set the TRIS bit for output and 
    set the output latch to the break state. This will cause the 
    port to go into break when the UART is disabled. */
    Nop();
    TRISDbits.TRISD0 = 0;
    Nop();
    LATDbits.LATD0 = 0;
    Nop();

    /* Configure uart1 receive and transmit interrupt */

    /* clear any pending interrupts just in case */
    _U1RXIF = 0;
    _U1TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U1RXIP = 6;
    _U1TXIP = 2;

    /* enable receive interrupts, disable transmit interrupts */
    _U1RXIE = 1;
    _U1TXIE = 0;

    /***************************** Setup UART 2 *****************************/
    /* set baud to 9600 */
    U2BRG = baudToBrg(9600);
    
    /* No parity, one stop bit, autobaud, polled */
    U2MODEbits.UARTEN = 1;
    U2MODEbits.ABAUD = 0;
    U2MODEbits.BRGH = BRGH_VAL;
    U2MODEbits.PDSEL = 0;
    U2MODEbits.STSEL = 0;

//    U2STAbits.UTXEN = 1;
    U2STA = 0x400;

    /* Once the UART is enabled set the TRIS bit for output and 
    set the output latch to the break state. This will cause the 
    port to go into break when the UART is disabled. */
    Nop();
    TRISDbits.TRISD2 = 0;
    Nop();
    LATDbits.LATD2 = 0;
    Nop();
    
    /* Configure uart2 receive and transmit interrupt */

    /* clear any pending interrupts just in case */
    _U2RXIF = 0;
    _U2TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U2RXIP = 6;
    _U2TXIP = 2;

    /* enable receive interrupts, disable transmit interrupts */
    _U2RXIE = 1;
    _U2TXIE = 0;
    
    /***************************** Setup UART 3 *****************************/
    /* set baud to 9600 */
    U3BRG = baudToBrg(9600);
    
    /* No parity, one stop bit, no autobaud, polled */
    U3MODEbits.UARTEN = 1;
    U3MODEbits.ABAUD = 0;
    U3MODEbits.BRGH = BRGH_VAL;
    U3MODEbits.PDSEL = 0;
    U3MODEbits.STSEL = 0;

//    U3STAbits.UTXEN = 1;
    U3STA = 0x400;

    /* Once the UART is enabled set the TRIS bit for output and 
    set the output latch to the break state. This will cause the 
    port to go into break when the UART is disabled. */
    Nop();
    TRISDbits.TRISD4 = 0;
    Nop();
    LATDbits.LATD4 = 0;
    Nop();
    
    /* Configure uart3 receive and transmit interrupt */

    /* clear any pending interrupts just in case */
    _U3RXIF = 0;
    _U3TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    _U3RXIP = 6;
    _U3TXIP = 2;

    /* enable receive interrupts, disable transmit interrupts */
    _U3RXIE = 1;
    _U3TXIE = 0;
}

void serSetEnable(int val)
{
    return;
}

int serGetEnable()
{
    return TRUE;
}

int serSetConfig(int port, unsigned long baud, int data_par, int stop)
{   
    unsigned int brg_val;

    brg_val = baudToBrg(baud);

    switch ( port )
    {
        case SER_PORT_0: U1BRG = brg_val;
                          U1MODEbits.PDSEL = data_par;

                          if ( stop == S_2 )
                              U1MODEbits.STSEL = 1;
                          else
                              U1MODEbits.STSEL = 0;

                          break;
        case SER_PORT_1: U2BRG = brg_val;
                          U2MODEbits.PDSEL = data_par;
                          
                          if ( stop == S_2 )
                              U2MODEbits.STSEL = 1;
                          else
                              U2MODEbits.STSEL = 0;

                          break;
        case SER_PORT_2: U3BRG = brg_val;
                          U3MODEbits.PDSEL = data_par;

                          if ( stop == S_2 )
                              U3MODEbits.STSEL = 1;
                          else
                              U3MODEbits.STSEL = 0;

                          break;
        default: return -1;
    }


    return 0;
}

int serGetConfig(int port, unsigned long* baud, int* data_par, int* stop)
{
    *baud = 0;

    switch ( port )
    {
        case SER_PORT_0: *baud = brgToBaud(U1BRG);
                          *stop = U1MODEbits.STSEL + 1;
                          *data_par = U1MODEbits.PDSEL;
                          break;
        case SER_PORT_1: *baud = brgToBaud(U2BRG);
                          *stop = U2MODEbits.STSEL + 1;
                          *data_par = U2MODEbits.PDSEL;
                          break;
        case SER_PORT_2: *baud = brgToBaud(U3BRG);
                          *stop = U3MODEbits.STSEL + 1;
                          *data_par = U3MODEbits.PDSEL;
                          break;
        default: return -1;
    }

    return 0;
}

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 */
        EnterCriticalSection();
        stat = bufGet(b, &rxBuff[port]);
        ExitCriticalSection();

        return stat;
    }
    
    *b = 0; 
    
    return FALSE;
}

int serPutByte(int port, BYTE b)
{
    int is_full;
    
    /* see if there is room in the FIFO */
    switch ( port )
    {
        case SER_PORT_0: is_full = U1STAbits.UTXBF; break;
        case SER_PORT_1: is_full = U2STAbits.UTXBF; break;
        case SER_PORT_2: is_full = U3STAbits.UTXBF; break;
        default: return(TRUE);
    }
    
    /* if there is room put in the next byte */
    if ( !is_full )
    {
        switch (port)
        {
            case SER_PORT_0: U1TXREG = b; return TRUE;
            case SER_PORT_1: U2TXREG = b; return TRUE;
            case SER_PORT_2: U3TXREG = b; return TRUE;
        }
    }
    
    return(FALSE);
}

int serPutString(int port, char* buff)
{
    int i = 0;

    if ((port < SER_PORT_0) || (port > SER_PORT_2))
	return(0);

    while ( buff[i] != '\0' )
    {
        if ( serPutByte(port, (BYTE)buff[i]) )
            ++i;
    }

    return 0;
}

int serPutStringBool(int port, char* buff, int onoff)
{
    serPutString(port, buff);

    serPutString(port, onoff ? "ON\r\n" : "OFF\r\n");

    return 0;
}

void serRxFlush(int port)
{
    unsigned reg;

    /* if the port is valid, flush the buffer */
    if ( isSerPortValid(port) )
    {
        EnterCriticalSection();

        /* 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;
            default: break;
        }

        ExitCriticalSection();
    }

    return;
}

void serTxFlush(int port)
{
    switch ( port )
    {
        case SER_PORT_0: U1STAbits.UTXEN = 0; Nop(); 
                          U1STAbits.UTXEN = 1; Nop(); 
                          break;
        case SER_PORT_1: U2STAbits.UTXEN = 0; Nop(); 
                          U2STAbits.UTXEN = 1; Nop(); 
                          break;
        case SER_PORT_2: U3STAbits.UTXEN = 0; Nop(); 
                          U3STAbits.UTXEN = 1; Nop(); 
                          break;
        default: return;
    }
}

void serSetBreak(int port, int state)
{
    if ( state )
    {
        switch ( port )
        {
            case SER_PORT_0:   /* disable UART transmit */
                                U1STAbits.UTXEN = 0;
                                /* disable UART module */
                                U1MODEbits.UARTEN = 0;
                                /* set break flag */
                                txBreakFlag[SER_PORT_0] = TRUE;
                                break;

            case SER_PORT_1:   /* disable UART transmit */
                                U2STAbits.UTXEN = 0;
                                /* disable UART module */
                                U2MODEbits.UARTEN = 0;
                                /* set break flag */
                                txBreakFlag[SER_PORT_1] = TRUE;
                                break;

            case SER_PORT_2:   /* disable UART transmit */
                                U3STAbits.UTXEN = 0;
                                /* disable UART module */
                                U3MODEbits.UARTEN = 0;
                                /* set break flag */
                                txBreakFlag[SER_PORT_2] = TRUE;
                                break;
        }
    }
    else
    {
        switch ( port )
        {
            case SER_PORT_0:   /* enable UART module */
                                U1MODEbits.UARTEN = 1;
                                /* enable UART transmit */
                                U1STAbits.UTXEN = 1;
                                /* clear break flag */
                                txBreakFlag[SER_PORT_0] = FALSE;
                                break;

            case SER_PORT_1:   /* enable UART module */
                                U2MODEbits.UARTEN = 1;
                                /* enable UART transmit */
                                U2STAbits.UTXEN = 1;
                                /* clear break flag */
                                txBreakFlag[SER_PORT_1] = FALSE;
                                break;

            case SER_PORT_2:   /* enable UART module */
                                U3MODEbits.UARTEN = 1;
                                /* enable UART transmit */
                                U3STAbits.UTXEN = 1;
                                /* clear break flag */
                                txBreakFlag[SER_PORT_2] = FALSE;
                                break;
        }
    }

    return;
}

int serGetBreak(int port)
{
    if ( isSerPortValid(port) )
        return txBreakFlag[port]; 

    return FALSE;
}

int serIsRxFull(int port)
{
    int stat;
    
    /* if the port is valid, flush the buffer */
    if ( isSerPortValid(port) )
    {
        /* get the buffer status */
        EnterCriticalSection();
        stat = bufIsFull(&rxBuff[port]);
        ExitCriticalSection();

        return stat;
    }

    return FALSE;        
}

int serIsTxFull(int port)
{
    int is_full;

    /* return the hardware FIFO status */
    switch ( port )
    {
        case SER_PORT_0: is_full = U1STAbits.UTXBF; break;
        case SER_PORT_1: is_full = U2STAbits.UTXBF; break;
        case SER_PORT_2: is_full = U3STAbits.UTXBF; break;
        default: is_full = TRUE;
    }

    return is_full;
}

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);

    return;
}

int serGetXERR(int port)
{
    if ( isSerPortValid(port) )
    {
        if ( rxErrorFlags[port] & OVERFLOW_ERR )
            return TRUE;
        else
            return FALSE;
    }

    return FALSE;
}

void serClrOERR(int port)
{
    if ( isSerPortValid(port) )
    {
        rxErrorFlags[port] &= (~OVERRUN_ERR);

        /* also clear the OERR bit in the UxSTA register */
        switch ( port )
        {
            case SER_PORT_0: U1STAbits.OERR = 0; 
            case SER_PORT_1: U2STAbits.OERR = 0;
            case SER_PORT_2: U3STAbits.OERR = 0;
        }
    }

    return;
}

int serGetOERR(int port)
{
    /* note: you have to check the the OERR bit directly when you 
    have an overrun condition.  When you have an overrun condition 
    interrupts stop occurring and the bit will never get set in 
    the rxErrorFlags[] variable. */

    switch ( port )
    {
        case SER_PORT_0: if (U1STAbits.OERR) return TRUE;
        case SER_PORT_1: if (U2STAbits.OERR) return TRUE;
        case SER_PORT_2: if (U3STAbits.OERR) return TRUE;
    }

    return FALSE;
}

void serClrFERR(int port)
{
    if ( isSerPortValid(port) )
        rxErrorFlags[port] &= (~FRAMING_ERR);

    return;
}

int serGetFERR(int port)
{
    if ( isSerPortValid(port) )
    {
        if ( rxErrorFlags[port] & FRAMING_ERR )
            return TRUE;
        else
            return FALSE;
    }

    return FALSE;
}

void serClrPERR(int port)
{
    if ( isSerPortValid(port) )
        rxErrorFlags[port] &= (~PARITY_ERR);

    return;
}


int serGetPERR(int port)
{
    if ( isSerPortValid(port) )
    {
        if ( rxErrorFlags[port] & PARITY_ERR )
            return TRUE;
        else
            return FALSE;
    }

    return FALSE;
}

void serXcvrSet(int port, int state)
{
    /* state is non zero, set it to one */
    if ( state )
        state = 1;
    
    Nop();

    switch (port)
    {
        case SER_PORT_0: UART_1_SHDN = state; break;
        case SER_PORT_1: UART_2_SHDN = state; break;
        case SER_PORT_2: UART_3_SHDN = state; break;
        default: return;
    }

    Nop();
    return;
}

int serXcvrGet(int port)
{
    int state;

    Nop();

    switch ( port )
    {
        case SER_PORT_0: state =  UART_1_SHDN ? TRUE : FALSE; break;
        case SER_PORT_1: state =  UART_2_SHDN ? TRUE : FALSE; break;
        case SER_PORT_2: state =  UART_3_SHDN ? TRUE : FALSE; break;
        default: state = FALSE;
    }

    Nop();
    
    return state;
}

/****************************************************************************/
/*                       Interrupt Service Routines                         */
/****************************************************************************/

/* Received data var */
static unsigned long txCount = 0;

/* This is UART1 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U1TXInterrupt(void)
{
    ++txCount;
    _U1TXIF = 0;
}

/* This is UART1 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U1RXInterrupt(void)
{
    /* note: the standard buffer functions are not used here 
    as calling an external function from and interrupt service 
    routine increases interrupt overhead */
    
    /* grab all the bytes from the buffer */
    while ( U1STAbits.URXDA )
    {
        /* if the buffer is not full, put in the byte */
        if ( ((rxBuff[0].head + 1) & rxBuff[0].ptr_mask) == rxBuff[0].tail )
        {
            /* 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].data[rxBuff[0].head] = (unsigned char)U1RXREG;
            rxBuff[0].head = (rxBuff[0].head + 1) & rxBuff[0].ptr_mask;
        }
    }
    
    /* clear the interrupt and return */
    _U1RXIF = 0;
    return;
}

/* This is UART2 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U2TXInterrupt(void)
{
    ++txCount;
    _U2TXIF = 0;

}

/* This is UART2 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U2RXInterrupt(void)
{
    /* note: the standard buffer functions are not used here 
    as calling an external function from and interrupt service 
    routine increases interrupt overhead */
    
    /* grab all the bytes from the buffer */
    while ( U2STAbits.URXDA )
    {
        /* if the buffer is not full, put in the byte */
        if ( ((rxBuff[1].head + 1) & rxBuff[1].ptr_mask) == rxBuff[1].tail )
        {
            /* 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].data[rxBuff[1].head] = (unsigned char)U2RXREG;
            rxBuff[1].head = (rxBuff[1].head + 1) & rxBuff[1].ptr_mask;
        }
    }

    /* clear the interrupt and return */
    _U2RXIF = 0;
    return;
}


/* This is UART3 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U3TXInterrupt(void)
{
    ++txCount;
    _U3TXIF = 0;
}

/* This is UART3 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U3RXInterrupt(void)
{
    /* note: the standard buffer functions are not used here 
    as calling an external function from and interrupt service 
    routine increases interrupt overhead */
    
    /* grab all the bytes from the buffer */
    while ( U3STAbits.URXDA )
    {
        /* if the buffer is not full, put in the byte */
        if ( ((rxBuff[2].head + 1) & rxBuff[2].ptr_mask) == rxBuff[2].tail )
        {
            /* 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].data[rxBuff[2].head] = (unsigned char)U3RXREG;
            rxBuff[2].head = (rxBuff[2].head + 1) & rxBuff[2].ptr_mask;
        }
    }

    /* clear the interrupt and return */
    _U3RXIF = 0;
    return;
}

/* local functions */

int isSerPortValid(int port)
{
    if ( (port >= SER_PORT_0) && (port <= SER_PORT_2) )
         return TRUE;

    return FALSE;
}

unsigned long brgToBaud(unsigned int brg)
{
    return (FCY / (BRG_DIV * (brg + 1)));
}

unsigned int baudToBrg(unsigned long baud)
{
    if ( baud == 0 )
        return 0xFFFF;
    else if ( baud > (FCY / BRG_DIV) )
        return 0;
    else
        return (((FCY / baud) / BRG_DIV) - 1);
}

