/*
 * 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
 *
 * File: $Id: portserial.c,v 1.1 2015/12/02 21:57:35 mrisi Exp $
 */

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

#include "sys_defs.h"
#include "dig_in.h"
/* ----------------------- static functions ---------------------------------*/

/* ----------------------- Static variables ---------------------------------*/
static unsigned int rxByte;
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.*/

    ENTER_CRITICAL_SECTION(  );

    if ( xRxEnable )
    {
        _U1RXIE = 1;
    }    
    else
    {
        _U1RXIE = 0;
    }

    if ( xTxEnable )
    {
        /* enable the interrupt */
        _U1TXIE = 1;
        /* set the the interrupt flag to kick start the xmit FSM */
        _U1TXIF = 1;
    }
    else
    {
        _U1TXIE = 0;
    }

    EXIT_CRITICAL_SECTION(  );
}

#define BRGH_BIT_VAL 1
unsigned int calcBrgReg(unsigned long baud)
{
   unsigned int brg_reg;

   if ( BRGH_BIT_VAL )
      brg_reg = ( FCY / ( 4 * baud )) - 1;
   else
      brg_reg = ( FCY / ( 16 * baud )) - 1;

   return brg_reg;
}

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       */
/*                                           */
/*   Example Settings                        */
/*   Fosc = 3.6864 MHz                       */
/*   Fcy = Fosc / 2                          */
/*   BRGH = 1                                */
/*                                           */
/*    ___________________________            */
/*   |  Baud          |  UxBRG  |            */
/*   ----------------------------            */
/*   |  9600         |   47     |            */
/*   ----------------------------            */
/*   |  19200        |   23     |            */
/*   ----------------------------            */
/*   |  38400        |   11     |            */
/*   ----------------------------            */
/*   |  57600        |   7      |            */
/*   ----------------------------            */
/*   |  115200       |   3      |            */
/*   ---------------------------             */
/*                                           */
/*********************************************/

    /* set baud to rate */
    U1MODEbits.BRGH = BRGH_BIT_VAL;
    
    /* set the baud rate */
    switch ( ulBaudRate )
    {
        case 9600: U1BRG = calcBrgReg(9600L); break;
        case 19200: U1BRG = calcBrgReg(19200L); break;
        case 38400: U1BRG = calcBrgReg(38400L); break;
        case 57600: U1BRG = calcBrgReg(57600L);  break;
        case 115200: U1BRG = calcBrgReg(115200L); break;
        /* if the baud rate is not supported set it 
        to 9600, but return FALSE */
        default:    U1BRG = calcBrgReg(9600L);
                    status = FALSE;
    }

    /* No parity, one stop bit, no autobaud, polled */
    U1MODEbits.UARTEN = 1;
    U1MODEbits.ABAUD = 0;

/* TODO for now stick with 8N2, we can clean this up later */

    /* no parity */
    U1MODEbits.PDSEL0 = 0;
    U1MODEbits.PDSEL1 = 0;

    /* 2 stop bits */
    U1MODEbits.STSEL = 1;
    
    /* UTXEN: Transmit Enable */
    U1STA = 0x0400;

    /* set tx int mode to interrupt when the shift register is empty */
    U1STAbits.UTXISEL1 = 0;
    U1STAbits.UTXISEL0 = 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;

    /* make sure interrupts are disabled */
    _U1RXIE = 0;
    _U1TXIE = 0;
    
    return status;
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    U1TXREG = ucByte;
    if (IO_Uart0Mode_GetValue() == 1) {
        Nop();
        _LATF4 = 1;
        Nop();
    }
            
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* grab the last byte received */
    *pucByte = (CHAR)rxByte;
    return TRUE;
}

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte( ) to send the character.
 */

void __attribute__ ((interrupt,no_auto_psv)) _U1TXInterrupt(void)
{
    /* always disable the RS485 DE */
    Nop();
    _LATF4 = 0;
    Nop();
    
    /* clear the interrupt flag */
    _U1TXIF = 0;
    
    /* tell the protocol stack the that tx queue is empty */
    pxMBFrameCBTransmitterEmpty();
}

/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
static unsigned int rxOverFlow = 0;
void __attribute__ ((interrupt,no_auto_psv)) _U1RXInterrupt(void)
{
    unsigned int perr_flag = 0;
    unsigned int ferr_flag = 0;
    
    /* clear the interrupt flag */
    _U1RXIF = 0;

    /* if you get an overrun error clear it and bail out */
    if ( U1STAbits.OERR )
    {
        /* clear the overrun */
        U1STAbits.OERR = 0;
        
        /* empty the FIFO */
        while ( U1STAbits.URXDA )
            rxByte = U1RXREG;

        return;
    }
    
    /* read bytes out while data is available */
    while ( U1STAbits.URXDA )
    {
        if ( (IO_Uart0Mode_GetValue() == 1) & (U1STAbits.TRMT == 0) ) {
            /* empty the character if we are in half-duplex rs485, 
             * then continue */
            rxOverFlow = U1RXREG;
            continue;
        } 
        /* set local parity error flag */
        if ( U1STAbits.PERR )
            perr_flag = 1;

        /* set local framing error flag */
        if ( U1STAbits.FERR )
            ferr_flag = 1;
        
        rxByte = U1RXREG;

        /* notify the RX FSM that a byte has been received 
        if there are no framing errors*/
        if ( ferr_flag == 0 )
            pxMBFrameCBByteReceived();
    }
}

