/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "port.h"
#include "dig_io.h"
#include "sys_timer.h"


/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/

/* ----------------------- Static variables ---------------------------------*/
static UCHAR ucCriticalNesting = 0x00;
static BOOL txEnable;
static unsigned int rxByte;
static ULONG modRxTick;
/* ----------------------- Start implementation -----------------------------*/

void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    ENTER_CRITICAL_SECTION(  );
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.*/
    /* For RS-232, RX interrupt is always enabled */
    if ( xRxEnable )
        _U6RXIE = 1;
    else
        _U6RXIE = 0;

    txEnable = xTxEnable;
    if ( xTxEnable )
    {
        digSnd485();                    /* Enable 485 Tx        */

        _U6TXIE = 1;                    /* enable the interrupt */
        _U6TXIF = 1;        /* set interrupt flag to kick start the xmit FSM */
    }
    else
    {
        digRcv485();                    /* Disable 485 Tx (enable Rx) */
        _U6TXIE = 0;                    /* Disable Tx interrupt       */
    }

    EXIT_CRITICAL_SECTION(  );
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    BOOL status = TRUE;

/*********************************************/
/*           Baud Rate Calculation           */
/*********************************************/
/*                                           */
/* BRGH = 0                                  */
/*                                           */
/*   UxBRG = ( Fcy / ( 16 * baud )) - 1      */
/*                                           */
/* BRGH = 1                                  */
/*                                           */
/*   UxBRG = ( Fcy / ( 4 * baud )) - 1       */
/*                                           */
/*   Current Settings                        */
/*   Fosc = 3.6864 MHz                       */
/*   Fcy = Fosc / 2                          */
/*   BRGH = 1                                */
/*                                           */
/*    ___________________________            */
/*   |  Baud          |  UxBRG  |            */
/*   ----------------------------            */
/*   |  19200        |   23     |            */
/*   ----------------------------            */
/*   |  38400        |   11     |            */
/*   ----------------------------            */
/*   |  57600        |   7      |            */
/*   ----------------------------            */
/*   |  115200       |   3      |            */
/*   ---------------------------             */
/*                                           */
/*********************************************/

    /* set baud to rate */
    U6MODEbits.BRGH = 1;
    
    /* set the baud rate */
#if defined(O5_DB) && defined(PROTO)
    switch ( ulBaudRate )
    {
        case  9600: U6BRG = 259; break;
        case 19200: U6BRG = 129; break;
        case 38400: U6BRG = 64; break;
        case 57600: U6BRG = 42;  break;
        case 115200: U6BRG = 21; break;
        /* if the baud rate is not supported set it  to 19200, but return FALSE */
        default:    U6BRG = 129;
                    status = FALSE;
    }
#else
    switch ( ulBaudRate )
    {
        case  9600: U6BRG = 47; break;
        case 19200: U6BRG = 23; break;
        case 38400: U6BRG = 11; break;
        case 57600: U6BRG = 7;  break;
        case 115200: U6BRG = 3; break;
        /* if the baud rate is not supported set it  to 19200, but return FALSE */
        default:    U6BRG = 23;
                    status = FALSE;
    }
#endif

    /* No parity, one stop bit, no autobaud, polled */
    U6MODEbits.UARTEN = 1;
    U6MODEbits.ABAUD = 0;

    /* no parity */
    U6MODEbits.PDSEL0 = 0;
    U6MODEbits.PDSEL1 = 0;

    /* 1 stop bits */
    U6MODEbits.STSEL = 0;
    
    /* UTXINV: Transmit Polarity Inversion, UTXEN: Transmit Enable */
    U6STA = 0x0400;

    /* set tx int mode to interrupt when the shift register is empty */
    U6STAbits.UTXISEL1 = 0;
    U6STAbits.UTXISEL0 = 1;

    /* Configure uart1 receive and transmit interrupt */

    /* clear any pending interrupts just in case */
    _U6RXIF = 0;
    _U6TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    /* NOTE - these priorities must be < 4, to make critical region code work!!!*/
    _U6RXIP = 3;
    _U6TXIP = 2;

    /* make sure interrupts are disabled */
    _U6RXIE = 0;
    _U6TXIE = 0;
    
    return status;
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    U6TXREG = ucByte;
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* grab the last byte received */
    *pucByte = (CHAR)rxByte;
    return TRUE;
}

/************************************************************************/
/* Function    : modBusIsIdle											*/
/* Purpose     : Function to support putting CPU into Doze mode when idle*/
/* Inputs      : None													*/
/* Outputs     : TRUE if Modbus is idle, else FALSE						*/
/************************************************************************/
int	modBusIsIdle(void)
{
#ifdef NO_IDLE
	return(FALSE);
#else
	return(!txEnable &&
		   ((tmrGetTicks() - modRxTick) > MOD_IDLE_TICKS));
#endif
}


/************************************************************************/
/*                      ISRs                                            */
/************************************************************************/
/* This is UART6 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U6TXInterrupt(void)
{
	NoDoze();
    _U6TXIF = 0;
    
    pxMBFrameCBTransmitterEmpty();
}

/* This is UART6 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U6RXInterrupt(void)
{
    unsigned int perr_flag = 0;
    unsigned int ferr_flag = 0;
    
	NoDoze();
    _U6RXIF = 0;
	modRxTick = tmrGetTicks();

    /* if you get an overrun error clear it and bail out */
    if ( U6STAbits.OERR )
    {
        /* clear the overrun */
        U6STAbits.OERR = 0;
        
        /* empty the FIFO */
        while ( U6STAbits.URXDA )
            rxByte = U6RXREG;

        return;
    }
    
    /* read bytes out while data is available */
    while ( U6STAbits.URXDA )
    {
        /* set local parity error flag */
        if ( U6STAbits.PERR )
            perr_flag = 1;

        /* set local framing error flag */
        if ( U6STAbits.FERR )
            ferr_flag = 1;
        
        rxByte = U6RXREG;

        /* notify the RX FSM that a byte has been received 
        if there are no framing errors*/
        if ( ferr_flag == 0 )
            pxMBFrameCBByteReceived();
    }
}

void EnterCriticalSection( void )
{
    /* disable ints < 4, which are all the Modbus stack interrupts */
    SRbits.IPL = 4;

    /* increment critical section nesting count */
    ++ucCriticalNesting;
}

void ExitCriticalSection( void )
{
    /* if you've nested all the way out  of critical sections, enable ints*/
    if( --ucCriticalNesting == 0 )
	SRbits.IPL = 0;
}

