/****************************************************************************/
/* 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     2
#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      7372800             /* 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

#if BRGH_VAL
#define BRG_VAL      ((FCY/BAUDRATE)/4)-1 
#else
#define BRG_VAL      ((FCY/BAUDRATE)/16)-1 
#endif

/* 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.BRGH = BRGH_VAL;
    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;
    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;
}


int serGetByte(int port, unsigned char* b)
{
    int stat;

    /* make sure the port is valid */
    switch ( port )
    {
        case 1: break;
        case 2: break;
        default: *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 is_full;
    
    /* see if there is room in the FIFO */
    switch ( port )
    {
        case 1: is_full = U1STAbits.UTXBF; break;
        case 2: is_full = U2STAbits.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;
            case 2: U2TXREG = 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;

        /* Note: if the tx FIFO is full you could executes 
        tasks that don't use serPutString */
    }

    return 0;
}

int serIsRxFull(int port)
{
    int stat;
    
    /* make sure the port is valid */
    switch ( port )
    {
        case 1: break; 
        case 2: 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;
        default: is_full = TRUE;
    }

    return is_full;
}

int serGetRxOverFlow(int port)
{
    switch ( port )
    {
        case 1: break;
        case 2: break;
        default: return FALSE;
    }

    return rxOverFlowFlag[port - 1];
}

void serClrRxOverFlow(int port)
{
    switch ( port )
    {
        case 1: break;
        case 2: break;
        default: return;
    }

    rxOverFlowFlag[port - 1] = FALSE;
}


void serRxFlush(int port)
{
    unsigned reg;

    /* make sure the port is valid */
    switch ( port )
    {
        case 1: break;
        case 2: 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;
        default: break;
    }

    ExitCriticalSection();

    return;
}

void serTxFlush(int port)
{
    switch ( port )
    {
        case 1: U1STAbits.UTXEN = 0; Nop(); 
                U1STAbits.UTXEN = 1; Nop(); 
                break;
        case 2: U2STAbits.UTXEN = 0; Nop(); 
                U2STAbits.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 )
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlowFlag[0] = U1RXREG;

            /* 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 )
        {
            /* you need to get the byte out buffer or you will hang! */
            rxOverFlowFlag[1] = U2RXREG;

            /* 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;
}


