/****************************************************************************/
/* Copyright 2009-2014 MBARI                                                */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Original code written by Mike Risi                                       */
/* Ported and modified by Bob Herlien for the MV Converter Board, 1/28/2014 */
/* This module just supports the debug serial port, UART3 (and possibly UART2)*/
/* The serial port(s) for Modbus are supported by modbus/port/portserial.c  */
/****************************************************************************/
#include "serial.h"
#include "buffer.h"
#include "misc.h"
#include "sys_defs.h"

#include "p24Fxxxx.h"

#include <stdlib.h>

/****************************************************************************/
/*                            Serial Buffer Data                            */
/****************************************************************************/
#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 FOSC        3686400		/* Internal clock frequency     */
#define FCY         (FOSC / 2)          /* Instruction Cycle frequency  */

#define BAUDRATE    9600       
#define BRGH_VAL    0

#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()
{
    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 3 *****************************/
    /* set baud */
    U3BRG = baudToBrg(9600);
    
    /* 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;


/***************************** Setup UART 2 *****************************/
/* Note - UART 2 is currently unused.  We may use it for an alternate	*/
/* debug serial port, which is why it's set up here.  Or we may use it	*/
/* for a 2nd Modbus port, in which case we'll remove this code; but that*/
/* will require a significant rewrite of the freemodbus code		*/
/************************************************************************/

    /* set baud */
    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;
    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 */
    if (isSerPortValid(port))
    {
	/* try to get a byte from the buffer */
	EnterCriticalSection();
	stat = bufGet(b, &rxBuff[(port - 1)]);
	ExitCriticalSection();
	return(stat);
    }

    /* Invalid port	*/
    *b = 0;
    return(FALSE);
}

int serPutByte(int port, unsigned char b)
{
    int i;

    for (i = 0; i < 1000; i++)
	switch(port)
	{
	  case SER_CONSOLE:
	      if (U3STAbits.UTXBF == 0)
	      {
		  U3TXREG = b;
		  return(TRUE);
	      }
	      break;

	  case SER_ALT_CONSOLE:
	      if (U2STAbits.UTXBF == 0)
	      {
		  U2TXREG = b;
		  return(TRUE);
	      }
	      break;
	}

    return(FALSE);
}
    
int serPutString(int port, const char *buff)
{
    int i = 0;
    
    while ( buff[i] != '\0' )
        if ( serPutByte(port, (unsigned char)buff[i]) )
            i++;

    return 0;
}

int serPutStringBool(int port, const char *buff, int onoff)
{
    serPutString(port, buff);

    serPutString(port, onoff ? "ON\r\n" : "OFF\r\n");

    return 0;
}

/* Return TRUE if char avail, FALSE if not */
int serStatus(int port)
{
    int stat;

    /* make sure the port is valid */
    if (isSerPortValid(port))
    {
	/* try to get a byte from the buffer */
	EnterCriticalSection();
	stat = bufIsEmpty(&rxBuff[(port - 1)]);
	ExitCriticalSection();
	return(!stat);
    }

    return(FALSE);
}

int serIsRxFull(int port)
{
    int stat;
    
    /* make sure the port is valid */
    if (isSerPortValid(port))
    {
	/* get the buffer status */
	EnterCriticalSection();
	stat = bufIsFull(&rxBuff[(port - 1)]);
	ExitCriticalSection();

	return(stat);
    }
    
    return(FALSE);
}

int serIsTxFull(int port)
{
    int is_full;

    /* return the hardware FIFO status */
    switch ( port )
    {
	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)
{
    if (isSerPortValid(port))
	return(rxOverFlowFlag[port]);
    
    return(FALSE);
}

void serClrRxOverFlow(int port)
{
    if (isSerPortValid(port))
	rxOverFlowFlag[port] = FALSE;
}


void serRxFlush(int port)
{
    unsigned reg;

    /* make sure the port is valid */
    if (!isSerPortValid(port))
	return;

    EnterCriticalSection();
    
    /* clear out the software FIFO */
    bufClear(&rxBuff[(port - 1)]);
    
    /* clear out the harware FIFO */
    switch ( port )
    {
	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 2: U2STAbits.UTXEN = 0; Nop();
		U2STAbits.UTXEN = 1; Nop();
		break;
        case 3: U3STAbits.UTXEN = 0; Nop();
		U3STAbits.UTXEN = 1; Nop();
		break;
    }
}

int isSerPortValid(int port)
{
    if ((port == SER_CONSOLE) || (port == SER_ALT_CONSOLE))
	return TRUE;

    return(FALSE);
}


/* local functions */

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);
}


/****************************************************************************/
/*                       Interrupt Service Routines                         */
/****************************************************************************/

/* Received data var */
static unsigned long txCount = 0;

/* UART1 is handled in ../modbus/port/portserial.c	*/

/* 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;
}


