//
//
//  File:       serial.c
//  Author:     Eric J Martin
//  Copyright:  2016 MBARI
//  
//  Summary: serial port driver.
//  Project: Ocean Imaging Sensor Board 
//
//

#include "serial.h"


// 
// Serial Port Data
//

#define RX_BUFF_SIZE    32
static ByteBuffer rxBuff;
static unsigned char rxBuffData[RX_BUFF_SIZE];

static unsigned int rxErrorFlags;
static unsigned int txBreakFlag;


/* Error flag defines */
#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 BRGH_VAL    1

#if BRGH_VAL
#define BRG_DIV     4
#else
#define BRG_DIV     16
#endif

/* Local functions */
unsigned long brgToBaud(unsigned int brg);
unsigned int baudToBrg(unsigned long baud);

/* init registers, init software buffers, install IRQ */
void serInit()
{

    /* initialize serial data */
    /* init buffers */
    rxBuff.data = rxBuffData;
    rxBuff.head = 0;
    rxBuff.tail = 0;
    rxBuff.ptr_mask = (RX_BUFF_SIZE - 1);

    /* init flags */
    rxErrorFlags = 0;
    txBreakFlag = false;


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

    /* set baud to 19200 */
    U1BRG = baudToBrg(19200);

    /* 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;

    /* 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();
    TRIS_TX_UART1 = 0;
    Nop();
    LAT_TX_UART1 = 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, enable transmit interrupts */
    _U1RXIE = 1;
    _U1TXIE = 0;
}

void serSetEnable(int val)
{
    return;
}

int serGetEnable()
{
    return true;
}

int serSetConfig( unsigned long baud, int data_par, int stop)
{   
    unsigned int brg_val;

    brg_val = baudToBrg(baud);

    U1BRG = brg_val;
    U1MODEbits.PDSEL = data_par;

    if ( stop == S_2 )
        U1MODEbits.STSEL = 1;
    else
        U1MODEbits.STSEL = 0;

    return 0;
}

int serGetConfig( unsigned long* baud, int* data_par, int* stop)
{
    *baud = 0;

    *baud = brgToBaud(U1BRG);
    *stop = U1MODEbits.STSEL + 1;
    *data_par = U1MODEbits.PDSEL;

    return 0;
}

int serGetByte( unsigned char* b)
{
    int stat;

    /* try to get a byte from the buffer */
    EnterCriticalSection();
    stat = bufGet(b, &rxBuff);
    ExitCriticalSection();

    return stat;

    *b = 0; 

    return false;
}

int serPutByte( unsigned char b)
{
    int is_full;

    /* see if there is room in the FIFO */
    is_full = U1STAbits.UTXBF; 

    /* if there is room put in the next byte */
    if ( !is_full )
    {
        U1TXREG = b; 
        return true;
    }

    return false;
}

int serPutString( char* buff)
{
    int i = 0;

    while ( buff[i] != '\0' )
    {
        if ( serPutByte((unsigned char)buff[i]) )
            ++i;
    }

    return 0;
}

void serRxFlush()
{
    unsigned reg;

    EnterCriticalSection();

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

    /* clear out the harware FIFO */
    while ( U1STAbits.URXDA ) reg = U1RXREG; 

    ExitCriticalSection();
    return;
}

void serTxFlush()
{
    U1STAbits.UTXEN = 0; Nop(); 
    U1STAbits.UTXEN = 1; Nop(); 
}

void serSetBreak( int state)
{
    if ( state )
    {
        /* disable UART transmit */
        U1STAbits.UTXEN = 0;
        /* disable UART module */
        U1MODEbits.UARTEN = 0;
        /* set break flag */
        txBreakFlag = true;
    }
    else
    {
        /* enable UART module */
        U1MODEbits.UARTEN = 1;
        /* enable UART transmit */
        U1STAbits.UTXEN = 1;
        /* clear break flag */
        txBreakFlag = false;
    }

    return;
}

int serGetBreak()
{
    return txBreakFlag; 
}

int serIsRxFull()
{
    int stat;

    /* get the buffer status */
    EnterCriticalSection();
    stat = bufIsFull(&rxBuff);
    ExitCriticalSection();

    return stat;

}

int serIsTxFull()
{
    int is_full;

    /* return the hardware FIFO status */
    is_full = U1STAbits.UTXBF; 


    return is_full;
}

int serGetErrFlag()
{
    if ( rxErrorFlags > 0 ) return true;

    return false;
}


void serClrXERR()
{
    rxErrorFlags &= (~OVERFLOW_ERR);

    return;
}

int serGetXERR()
{
    if ( rxErrorFlags & OVERFLOW_ERR )
        return true;
    else
        return false;
}

void serClrOERR()
{
    rxErrorFlags &= (~OVERRUN_ERR);

    /* also clear the OERR bit in the UxSTA register */
    U1STAbits.OERR = 0; 
    return;
}

int serGetOERR()
{
    /* 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. */
    if (U1STAbits.OERR) return true;

    return false;
}

void serClrFERR()
{
    rxErrorFlags &= (~FRAMING_ERR);

    return;
}

int serGetFERR()
{
    if ( rxErrorFlags & FRAMING_ERR )
        return true;
    else
        return false;

}

void serClrPERR()
{
    rxErrorFlags &= (~PARITY_ERR);

    return;
}


int serGetPERR()
{
    if ( rxErrorFlags & PARITY_ERR )
        return true;
    else
        return false;

}

/****************************************************************************/
/*                       Interrupt Service Routines                         */
/****************************************************************************/

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

/***************************** UART 1 IRQ routines ***************************/
/* 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)
{
    /* note: the standard buffer functions are not used here 
       as calling an external function from and interruppt service 
       routine increases interrupt overhead */

    /* test for whether rs485 is enabled, forcing us into half duplex. */
    /* grab all the bytes from the buffer */
    while ( U1STAbits.URXDA )
    {
        /* if the buffer is not full, put in the byte */
        //TODO this is a bad use of the TRMT, at least in the case of RS232, good for half duplex rs485
        if ( (((rxBuff.head + 1) & rxBuff.ptr_mask) == rxBuff.tail ) | (U1STAbits.TRMT == 0) )
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlow = U1RXREG;

            /* set overflow flag and bail */
            rxErrorFlags |= OVERFLOW_ERR;
            break;
        }
        else
        {
            /* check error flags */
            rxErrorFlags |= (U1STA & ERR_FLAG_MASK);

            /* if there is room in the buffer put the byte in it */
            rxBuff.data[rxBuff.head] = (unsigned char)U1RXREG;
            rxBuff.head = (rxBuff.head + 1) & rxBuff.ptr_mask;
        }
    }

    /* clear the interrupt and return */
    _U1RXIF = 0;
    return;
}


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);
}

