/****************************************************************************/
/* Copyright 2009 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include "serial.h"
#include "buffer.h"
#include "misc.h"
#include "sys_defs.h"

#include "p24Fxxxx.h"

#include <stdlib.h>

/****************************************************************************/
/*                            Serial Buffer Data                            */
/****************************************************************************/
#define TOTAL_UARTS     3
#define RX_BUFF_SIZE    128

static ByteBuffer rxBuff[TOTAL_UARTS];
static unsigned char rxBuffData[TOTAL_UARTS][RX_BUFF_SIZE];
static int rxOverFlowFlag[TOTAL_UARTS];

#define XTFREQ      3686400             /* On-board Crystal frequency   */
#define PLLMODE     1                   /* On-chip PLL setting          */
#define FOSC        (XTFREQ * PLLMODE)  /* Internal clock frequency     */
#define FCY         (FOSC / 2)          /* Instruction Cycle frequency  */

#define BAUDRATE    9600       
#define BRGH_VAL    0
// #define SER_CONSOLE 1

#if BRGH_VAL
#define BRG_VAL      ((FCY/BAUDRATE)/4)-1 
#else
#define BRG_VAL      ((FCY/BAUDRATE)/16)-1 
#endif

// BRG_VAL should be equal to 11

/* init registers, init software buffers, install IRQ */
void serInit()
{
    int i;

    /* initialize serial data */
    for (i = 0; i < TOTAL_UARTS; ++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 */
        rxOverFlowFlag[i] = FALSE;
    }
    
    /***************************** Setup UART 1 *****************************/
    /* set baud */
    U1BRG = BRG_VAL;
    
    /* No parity, one stop bit, no autobaud, polled */
    U1MODEbits.UARTEN = 1;
    U1MODEbits.ABAUD = 0;
	// U1MODEbits.LPBACK = 0;
    U1MODEbits.BRGH = BRGH_VAL;
    U1MODEbits.PDSEL = 0;
    U1MODEbits.STSEL = 0;
    U1STA = 0x0400;

    /* 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 */
    U2BRG = BRG_VAL;
    
    /* No parity, one stop bit, autobaud, polled */
    U2MODEbits.UARTEN = 1;
    U2MODEbits.ABAUD = 0;
    U2MODEbits.BRGH = BRGH_VAL;
    U2MODEbits.PDSEL = 0;
    U2MODEbits.STSEL = 0;
    U2STA = 0x0400;

    /* 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 */
    U3BRG = BRG_VAL;
    
    /* No parity, one stop bit, autobaud, polled */
    U3MODEbits.UARTEN = 1;
    U3MODEbits.ABAUD = 0;
    U3MODEbits.BRGH = BRGH_VAL; 
    U3MODEbits.PDSEL = 0;
    U3MODEbits.STSEL = 0;
    U3STA = 0x0400;

    /* 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;
}


int serGetByte(int port, unsigned char* b)
{
    int stat;

    /* make sure the port is valid */
    if ((port > TOTAL_UARTS) || (port <= 0))
    {
	*b = 0; return FALSE;
    }

    /* try to get a byte from the buffer */
    EnterCriticalSection();
    stat = bufGet(b, &rxBuff[(port - 1)]);
    ExitCriticalSection();

    return stat;
}

int serPutByte(int port, unsigned char b)
{
    int i;

    switch(port)
    {
      case 1:
	  for (i = 0; i < 1000; i++)
	      if (U1STAbits.UTXBF == 0)
	      {
		  U1TXREG = b;
		  return(TRUE);
	      }
	  break;

      case 2:
	  for (i = 0; i < 1000; i++)
	      if (U2STAbits.UTXBF == 0)
	      {
		  U2TXREG = b;
		  return(TRUE);
	      }
	  break;

      case 3:
	  for (i = 0; i < 1000; i++)
	      if (U3STAbits.UTXBF == 0)
	      {
		  U3TXREG = b;
		  return(TRUE);
	      }
	  break;
    }
    return(FALSE);
}
    
int serPutString(int port, char* buff)
{
    int i = 0;
    
    while ( buff[i] != '\0' )
    {
        if ( serPutByte(port, (unsigned char)buff[i]) )
            ++i;
    }

    return 0;
}

int serIsRxFull(int port)
{
    int stat;
    
    /* make sure the port is valid */
    switch ( port )
    {
        case 1: break;
	case 2: break;
        case 3: break;
        default: return FALSE;
    }

    /* get the buffer status */
    EnterCriticalSection();
    stat = bufIsFull(&rxBuff[(port - 1)]);
    ExitCriticalSection();

    return stat;
}

int serIsTxFull(int port)
{
    int is_full;

    /* return the hardware FIFO status */
    switch ( port )
    {
        case 1: is_full = U1STAbits.UTXBF; break;
	case 2: is_full = U2STAbits.UTXBF; break;
        case 3: is_full = U3STAbits.UTXBF; break;
        default: is_full = TRUE;
    }

    return is_full;
}

int serGetRxOverFlow(int port)
{
    switch ( port )
    {
        case 1: return rxOverFlowFlag[port];
	case 2: return rxOverFlowFlag[port];
        case 3: return rxOverFlowFlag[port];
        default: return FALSE;
    }
}

void serClrRxOverFlow(int port)
{
    switch ( port )
    {
        case 1: break;
	case 2: break;
        case 3: break;
        default: return;
    }

    rxOverFlowFlag[port] = FALSE;
}


void serRxFlush(int port)
{
    unsigned reg;

    /* make sure the port is valid */
    switch ( port )
    {
        case 1: break;
	case 2: break;
        case 3: break;
        default: return;
    }
    
    EnterCriticalSection();
    
    /* clear out the software FIFO */
    bufClear(&rxBuff[(port - 1)]);
    
    /* clear out the harware FIFO */
    switch ( port )
    {
        case 1: while ( U1STAbits.URXDA ) reg = U1RXREG; break;
	case 2: while ( U2STAbits.URXDA ) reg = U2RXREG; break;
        case 3: while ( U3STAbits.URXDA ) reg = U3RXREG; break;
        default: break;
    }

    ExitCriticalSection();

    return;
}

void serTxFlush(int port)
{
    switch ( port )
    {
        case 1: U1STAbits.UTXEN = 0; Nop(); 
                U1STAbits.UTXEN = 1; Nop(); 
        case 2: U2STAbits.UTXEN = 0; Nop();
		U2STAbits.UTXEN = 1; Nop();
        case 3: U3STAbits.UTXEN = 0; Nop();
		U3STAbits.UTXEN = 1; Nop();
	break;
        default: return;
    }
}

/****************************************************************************/
/*                       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)
{

/* fun processor at full speed */    
/*CLKDIVbits.DOZEN = 0;*/

    /* 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 */
            rxOverFlowFlag[0] = TRUE;
        }
        else
        {
            /* 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 */
            rxOverFlowFlag[1] = TRUE;
        }
        else
        {
            /* 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 */
            rxOverFlowFlag[2] = TRUE;
        }
        else
        {
            /* 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;
}


