/****************************************************************************/
/* 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     1
#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;
    U1STA = 0x0400;
	// U1STAbits.UTXEN = 1;

    /* 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;
}
    


int serGetByte(int port, unsigned char* b)
{
    int stat;

    /* make sure the port is valid */
       switch ( port )
    {
        case 1: 
	//	serPutString(SER_CONSOLE, "PORT VALID\r\n");
		break;
        default: *b = 0; return FALSE;
	//	serPutString(SER_CONSOLE, "PORT NOT VALID\r\n");
    }

    /* try to get a byte from the buffer */
    EnterCriticalSection();
    stat = bufByteGet(b, &rxBuff[(port - 1)]);
    ExitCriticalSection();

    return stat;
}

int serPutByte(int port, unsigned char b)
{
    int is_full;
    
    /* see if there is room in the FIFO */
    switch ( port )
    {
        case 1: is_full = U1STAbits.UTXBF; break;		
        default: is_full = TRUE;
    }
    
    /* if there is room put in the next byte */
    if ( !is_full )
    {
        switch (port)
        {
            case 1: U1TXREG = b; return TRUE;			
            default: 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;		
        default: return FALSE;
    }

    /* get the buffer status */
    EnterCriticalSection();
    stat = bufByteIsFull(&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;		
        default: is_full = TRUE;
    }

    return is_full;
}

int serGetRxOverFlow(int port)
{
    switch ( port )
    {
        case 1: return rxOverFlowFlag[port];		
        default: return FALSE;
    }
}

void serClrRxOverFlow(int port)
{
    switch ( port )
    {
        case 1: break;
		default: return;
    }

    rxOverFlowFlag[port] = FALSE;
}


void serRxFlush(int port)
{
    unsigned reg;

    /* make sure the port is valid */
    switch ( port )
    {
        case 1: break;
	    default: return;
    }
    
    EnterCriticalSection();
    
    /* clear out the software FIFO */
    bufByteClear(&rxBuff[(port - 1)]);
    
    /* clear out the harware FIFO */
    switch ( port )
    {
        case 1: while ( U1STAbits.URXDA ) reg = U1RXREG; break;
		default: break;
    }

    ExitCriticalSection();

    return;
}

void serTxFlush(int port)
{
    switch ( port )
    {
        case 1: U1STAbits.UTXEN = 0; Nop(); 
                U1STAbits.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)
{

    /* 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;
}
